<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Orangeの小窝</title>
    <link>https://qspidy.github.io/</link>
    <description>Never Too Late to Learn.</description>
    <language>en</language>
    <copyright>All rights reserved 2026, MrOrange</copyright>
    <lastBuildDate>Fri, 24 Apr 2026 14:17:37 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://qspidy.github.io/rss2.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>图床，但是命令行，在任何地方</title>
      <link>https://qspidy.github.io/2026/04/24/%E5%9B%BE%E5%BA%8A%EF%BC%8C%E4%BD%86%E6%98%AF%E5%91%BD%E4%BB%A4%E8%A1%8C%EF%BC%8C%E5%9C%A8%E4%BB%BB%E4%BD%95%E5%9C%B0%E6%96%B9/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/AI/">AI</category>
      <category domain="https://qspidy.github.io/tags/Github/">Github</category>
      <category domain="https://qspidy.github.io/tags/Image-host/">Image-host</category>
      <category domain="https://qspidy.github.io/tags/Cloudflare/">Cloudflare</category>
      <category domain="https://qspidy.github.io/tags/Free/">Free</category>
      <category domain="https://qspidy.github.io/tags/CLI/">CLI</category>
      <pubDate>Fri, 24 Apr 2026 13:50:47 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>前段时间用<code>claude+glm4.7</code>整了个使用<code>nginx+lua</code>脚本实现的<a href="https://github.com/qspidy/imgng">个人图床方案</a>，很简洁啊，主要逻辑都集中在一个<a href="https://github.com/qspidy/imgng/blob/master/img-host-full.conf">nginx配置文件</a>内，不到120行，兼具性能（nginx特性）和访问速度（套cdn）。</p><p>然而这种部署方式绕不开一个问题：得有一台服务器。专门搞一台服务器做个人图床多少是有点大材小用了，个人使用又能存多少东西呢？不如先薅大厂羊毛，等到羊毛不够用了再换不迟。</p><p>于是这个<del>轮子</del>(项目)诞生了，在<code>codex+gpt5.4</code>和本人的努力下，花了一晚上时间。<br><img src="https://img.wastaken.xyz/images/978820ea1ddaa21db8a8ca8ea2144424.png"></p><h2 id="项目简介"><a href="#项目简介" class="headerlink" title="项目简介"></a>项目简介</h2><blockquote><p>curl-powered image hosting for hackers. One-click deploy on Cloudflare, or integrate with existing nginx server.</p></blockquote><p>该项目<a href="https://github.com/qspidy/imgng">qspidy&#x2F;imgng</a>的设计理念是，能在身边任何一台联网的机器上传图片到图床并获取链接，只要记住这条几乎不能再短的<code>curl</code>命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -u user:pass --data-binary @photo.jpg https://example.com/upload</span><br></pre></td></tr></table></figure><p>之所以说是“几乎”不能再短，是因为还可以使用<code>alias</code>或者<code>shell函数</code>给它<a href="https://github.com/qspidy/imgng#upload-api">设置别名</a>，然后像使用一个普通linux命令一样使用它：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">imgng</span></span>() &#123;</span><br><span class="line">  curl -s -u user:pass --data-binary @<span class="string">&quot;<span class="variable">$1</span>&quot;</span> https://example.com/upload</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">imgng photo.jpg</span><br></pre></td></tr></table></figure><p><img src="https://img.wastaken.xyz/images/62b8125d3f136a9ebb51e85070584fc6.gif"></p><p>这次相比上一篇博客<a href="https://orange.wastaken.xyz/2026/02/01/%E4%BD%BF%E7%94%A8Nginx%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%9B%BE%E5%BA%8A%EF%BC%8C%E8%AE%A9%E9%93%BE%E6%8E%A5%E6%B0%B8%E8%BF%9C%E6%9C%89%E6%95%88%EF%BC%81">使用Nginx搭建个人图床，让链接永远有效！</a>的主要变化是，提供一个零成本的服务端部署方案，使用<code>Cloudflare+R2</code>。当然还有很多其它<del>薅羊毛</del>（免费部署）方案，参考文末的<a href="#%E5%A4%96%E9%83%A8%E9%93%BE%E6%8E%A5">外部链接</a>，相信各路大神都能找到适合自己的方案。</p><p>从某种程度上而言，它更像是提供了一个相较传统<code>S3 API</code>而言更简洁的接口，在客户端做到了极致的简洁（命令行爱好者狂喜！）。</p><p>结合当前<code>CLI</code>越来越热的趋势，这小项目还有更多存在和参考的意义，后续或许可以进一步发展成一整套图床命令行<del>增删改查</del>(接口方案)。再者可以通过Skills&#x2F;MCP等方式和Agent无缝集成，成为Agent工作流的一部分。</p><p>要长期使用的话，它目前还缺少一些关键的功能：图片展示列表，重复图片处理等。接下来会继续完善这些关键的功能。</p><p>另外还有些可选功能：支持多用户，备份恢复等。因为它当前的定位是个人使用，所以支持多用户暂不考虑，而文件存储在R2上也基本不用担心文件丢失的问题，所以目前不急（备份也就一条命令的事，考虑到前段时间Cloudflare还崩了一次，备份还是有必要的）。</p><h2 id="碎碎念"><a href="#碎碎念" class="headerlink" title="碎碎念"></a>碎碎念</h2><p>用了codex+gpt5.4才知道原来不是Agent不好用，而是<del>钱没到位</del>（选错了模型和Agent），回想当初用 claude+glm4.7，那真是改了又改，5小时额度用完了都没跑通。这次几乎是首次提示词一下去出来的东西就能用，后面只需做些功能完善和文档生成的工作。</p><p>心情很复杂。很兴奋，在AI有加持下个人能力边界得到了极大的扩张，很悲伤，AI对程序员群体造成了很大的冲击，许多知识与技能似乎都没有了意义。</p><p>虽然如此，技术在发展，时代在进步，拥抱未来是唯一能走的路。</p><h2 id="外部链接"><a href="#外部链接" class="headerlink" title="外部链接"></a>外部链接</h2><h3 id="类似项目（更适合广大人类GUI用户食用）"><a href="#类似项目（更适合广大人类GUI用户食用）" class="headerlink" title="类似项目（更适合广大人类GUI用户食用）"></a>类似项目（更适合<del>广大人类</del>GUI用户食用）</h3><ul><li><a href="https://github.com/MarSeventh/CloudFlare-ImgBed">https://github.com/MarSeventh/CloudFlare-ImgBed</a></li><li><a href="https://github.com/Molunerfinn/picgo">https://github.com/Molunerfinn/picgo</a></li></ul><h3 id="其它基于CF的薅羊毛项目"><a href="#其它基于CF的薅羊毛项目" class="headerlink" title="其它基于CF的薅羊毛项目"></a><a href="https://github.com/topics/cloudflare-r2">其它基于CF的<del>薅羊毛</del>项目</a></h3>]]>
      </content:encoded>
    </item>
    <item>
      <title>使用Nginx搭建个人图床，让链接永远有效！</title>
      <link>https://qspidy.github.io/2026/02/01/%E4%BD%BF%E7%94%A8Nginx%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%9B%BE%E5%BA%8A%EF%BC%8C%E8%AE%A9%E9%93%BE%E6%8E%A5%E6%B0%B8%E8%BF%9C%E6%9C%89%E6%95%88%EF%BC%81/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/AI/">AI</category>
      <category domain="https://qspidy.github.io/tags/Development/">Development</category>
      <category domain="https://qspidy.github.io/tags/OpenCode/">OpenCode</category>
      <category domain="https://qspidy.github.io/tags/Claude/">Claude</category>
      <category domain="https://qspidy.github.io/tags/Github/">Github</category>
      <category domain="https://qspidy.github.io/tags/Nginx/">Nginx</category>
      <category domain="https://qspidy.github.io/tags/Image-host/">Image-host</category>
      <pubDate>Sun, 01 Feb 2026 15:02:48 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>这周发生了很多有意思的事情。</p><p><code>Clawdbot</code>改名成<code>Moltbot</code>，然后某个B站UP发了条动态：</p><blockquote><p>做个opencode desktop的视频，第二天就爆火Clawdbot，Clawdbot要发了把名字改成了moltbot。这个时代不要为难老人家好不好~</p></blockquote><p>前天一看又把名字改成了<code>OpenClaw</code>，想蹭个热点真不容易啊2333。</p><p>CloudCone机房罕见的出故障了，因为<code>虚拟机WEB控制面板 Virtualizor 上存在一个严重漏洞</code>，本来还准备在周末在CC服务器上整个<a href="https://github.com/mjl-/mox">mjl-&#x2F;mox</a>，结果什么也干不了。</p><p>周末要结束了还没修复：<br><img src="https://img.wastaken.xyz/images/eee0d7a3207cee36a9dc1840d0e89cb9.png"></p><p>于是在用<a href="https://github.com/usememos/memos">Memos</a>记录这些事情的时候，发现Memos不能直接在post里上传图片，只能用图床。</p><p>既然现在没法部署<a href="https://github.com/mjl-/mox">mjl-&#x2F;mox</a>，那就<code>整(shui)个(pian)图(bo)床(ke)</code>好了。</p><p>这次主要用的是Claude + GLM-4.7，体验下Claude和OpenCode的区别。</p><h2 id="为什么要自建"><a href="#为什么要自建" class="headerlink" title="为什么要自建"></a>为什么要自建</h2><p>没怎么用图床，相关的工具倒是收藏了不少。</p><p>其中杀手级的App当属<a href="https://github.com/Molunerfinn/PicGo">Molunerfinn&#x2F;PicGo</a>，功能非常全面，支持各种存储服务，还能和常见的文本编辑器交互：<br><img src="https://raw.githubusercontent.com/Molunerfinn/test/master/picgo/picgo-2.0.gif"></p><p>虽然有很多免费的存储服务可以用，但这些都逃不开一个最大的问题：<strong>很难保证链接长期有效</strong>。</p><p>比如把文件上传到OneDrive，得到一个链接，但如果哪天想要把图床迁移到另一个服务，那就不得不修改原来使用的链接，而用到这个链接的地方可能很多，emmm…</p><p>更不用说有些免费图床用着用着就跑路的。</p><p>而如果是用自己的域名访问的话，就能解决这个痛点。只在域名还在，做好备份恢复，就完全可以保证链接一直有效。</p><h2 id="搭建思路"><a href="#搭建思路" class="headerlink" title="搭建思路"></a>搭建思路</h2><p>图床这种算是静态网站服务，有很多平台可以提供免费部署，比如Vercel，Github等，也能自定义域名。但这里用的是服务器+Nginx，毕竟现在的服务器很便宜了，这样也更方便备份迁移。</p><p>如果追求极致精简，直接把图片放在Nginx的静态文件目录就好。但这样服务器端是简单，客户端操作就有点复杂了，得用<code>rsync</code>, <code>scp</code>之类的工具把文件传上去，不方便也不安全。</p><p>所以设计原则是：<strong>客户端操作尽可能简单，无论在哪都能轻松上传图片</strong>。</p><p>于是我选择使用<code>curl</code>，通过http请求的方式上传图片，这命令是个电脑都应该会有吧，而且命令也不会很长，很容易就能记住。</p><h2 id="开始干活"><a href="#开始干活" class="headerlink" title="开始干活"></a>开始干活</h2><p>当然不是我干活，而是指挥AI干活。</p><p>要实现的功能也不多，需求一描述，用Claude和GLM-4.7生成的代码直接就能跑起来。</p><p>然后不出意外的出意外了，在首次生成的代码上继续改进时，上传图片的权限突然从一开始的<code>600</code>变成了<code>666</code>：<br><img src="https://img.wastaken.xyz/images/e48322358d7625e558ad1027392aea61.png"></p><p>这可不是什么好事。</p><p>让Claude定位原因把图片权限改回去，但Claude并不知道是什么导致的权限改变，虽然加上了改变文件权限的代码，但最后图片权限依旧是<code>666</code>。</p><p>眼看Claude解决不了，只好亲自出手了。</p><p>扫了一眼本就不多的Lua脚本，唯一对图片有修改是调用ImagicMagick压缩图片这段</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- Optimize images (requires ImageMagick)</span></span><br><span class="line"><span class="keyword">local</span> image_extensions = &#123;jpg=<span class="literal">true</span>, jpeg=<span class="literal">true</span>, png=<span class="literal">true</span>, webp=<span class="literal">true</span>, gif=<span class="literal">true</span>, avif=<span class="literal">true</span>&#125;</span><br><span class="line"><span class="keyword">if</span> image_extensions[ext:<span class="built_in">lower</span>()] <span class="keyword">then</span></span><br><span class="line"><span class="built_in">os</span>.<span class="built_in">execute</span>(<span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">&quot;/usr/bin/mogrify -quality 85 -strip %s&quot;</span>, target, target))</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>于是把这段lua注掉一试，果然，就是用<code>/usr/bin/mogrify</code>压缩图片后图片文件的权限从<code>600</code>变成了<code>666</code> 。</p><p>而虽然Claude加上了改变文件权限的代码</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">os</span>.<span class="built_in">execute</span>(<span class="string">&quot;/bin/chmod 600 &quot;</span> .. target .. <span class="string">&quot; 2&gt;/dev/null&quot;</span>)</span><br></pre></td></tr></table></figure><p>但因这段代码放在ImagicMagick压缩图片之前，所以没能发挥任何效果。</p><h2 id="收尾"><a href="#收尾" class="headerlink" title="收尾"></a>收尾</h2><p>最后照例生成了Git项目，把代码传到了Github上：<a href="https://github.com/qspidy/imgng">qspidy&#x2F;imgng</a>。</p><p>不得不夸一下OpenCode生成的README了。相比Claude，OpenCode生成的又有图标又有Badge，看起来很现代。</p><p>最后让Claude参考之前的项目才生成的现在这么现代的README：<br><img src="https://img.wastaken.xyz/images/0545a8d69181719f4419ee3605aee4c2.png"></p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>不得不感慨AI写代码之神速啊，虽然就目前看来还有一定的局限，有些问题还是得有人的配合才能解决。</p><p>但是，如果这次AI可以在本地调试，它应该能很快定位问题吧。</p><p>如果调试也被AI完全掌握，那或许真的就是程序员的末日了。</p><p>感觉也快了啊。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>用OpenCode + GLM-4.7给Real-ESRGAN套Web UI，只用了一晚上？</title>
      <link>https://qspidy.github.io/2026/01/25/%E7%94%A8OpenCode-GLM-4-7%E7%BB%99Real-ESRGAN%E5%A5%97Web-UI%EF%BC%8C%E5%8F%AA%E7%94%A8%E4%BA%86%E4%B8%80%E6%99%9A%E4%B8%8A%EF%BC%9F/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/AI/">AI</category>
      <category domain="https://qspidy.github.io/tags/Development/">Development</category>
      <category domain="https://qspidy.github.io/tags/OpenCode/">OpenCode</category>
      <category domain="https://qspidy.github.io/tags/Github/">Github</category>
      <category domain="https://qspidy.github.io/tags/Anime/">Anime</category>
      <pubDate>Sun, 25 Jan 2026 04:45:55 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>前不久在网上找到一张喜欢的壁纸，但是查了半天没找着高清原图，只好先将就用着。而最近看着这略显模糊的壁纸实在是受不了了，就想着给它upscale一下。很自然的想起了以前用过的<a href="https://github.com/xinntao/Real-ESRGAN">xinntao&#x2F;Real-ESRGAN</a>，当时就觉得这东西巨好用，只是一直没有使用的场景，现在是时候给用起来了。</p><h2 id="ComfyUI-vs-Script"><a href="#ComfyUI-vs-Script" class="headerlink" title="ComfyUI vs Script"></a>ComfyUI vs Script</h2><p>虽然说想到要用Real-ESRGAN来让图像更清晰，一开始并没打算直接用它原有的命令行工具，而是准备用<a href="https://github.com/AUTOMATIC1111/stable-diffusion-webui">stable-diffusion-webui</a>或者是<a href="https://github.com/Comfy-Org/ComfyUI">ComfyUI</a>这类功能更丰富的项目。</p><p>花一上午的时间在本地装好了ComfyUI：</p><img src="/2026/01/25/%E7%94%A8OpenCode-GLM-4-7%E7%BB%99Real-ESRGAN%E5%A5%97Web-UI%EF%BC%8C%E5%8F%AA%E7%94%A8%E4%BA%86%E4%B8%80%E6%99%9A%E4%B8%8A%EF%BC%9F/1.jpg" class=""><p>用<code>RealESRGAN_x4plus_anime_6B.pth</code>试着增强下壁纸，结果让人哭笑不得：</p><img src="/2026/01/25/%E7%94%A8OpenCode-GLM-4-7%E7%BB%99Real-ESRGAN%E5%A5%97Web-UI%EF%BC%8C%E5%8F%AA%E7%94%A8%E4%BA%86%E4%B8%80%E6%99%9A%E4%B8%8A%EF%BC%9F/2.jpg" class=""><p>或许是我的处理方式不对，也或许是因为没有照安装文档说的用最适配的python13&#x2F;12版本导致。</p><p>不过这个模型在ComfyUI里确实有点水土不服，参考<a href="https://github.com/zentrocdot/ComfyUI-RealESRGAN_Upscaler">zentrocdot&#x2F;ComfyUI-RealESRGAN_Upscaler</a>和<a href="https://www.reddit.com/r/comfyui/comments/18fcpk4/reproduce_a1111_hires_fix_with_resrgan_in_comfyui/">Reproduce A1111 hires fix with R-ESRGAN in Comfyui?</a>。</p><p>不得已只好用<a href="https://github.com/xinntao/Real-ESRGAN">xinntao&#x2F;Real-ESRGAN</a>本身提供的安装包和脚本了，好在虽然已经2年多没更新了，它的功能还能正常使用，生成的图也没有问题。</p><img src="/2026/01/25/%E7%94%A8OpenCode-GLM-4-7%E7%BB%99Real-ESRGAN%E5%A5%97Web-UI%EF%BC%8C%E5%8F%AA%E7%94%A8%E4%BA%86%E4%B8%80%E6%99%9A%E4%B8%8A%EF%BC%9F/4.jpg" class=""><h2 id="OpenCode开始发力"><a href="#OpenCode开始发力" class="headerlink" title="OpenCode开始发力"></a>OpenCode开始发力</h2><p>在对比生成前和生成后的壁纸时突然有了个想法，要是有工具可以直观的对比两张图的区别就好了。</p><p>查了一圈发现这个功能基本都是在线网站在提供，没找着离线工具。</p><p>那就自己做一个好了，正好试试<code>OpenCode + GLM-4.7</code>的本事。</p><p>于是差不多快晚上的时候，我开始用OpenCode生成代码，先是生成了一个可以让两图片通过滑块对比的单html页面，然后在此基础上让它支持上传文件、后台命令调用、文件下载等功能。</p><p>很快啊，这些个小功能它很快就完成了，而且运行得很完美。</p><h2 id="OpenCode开始吃力"><a href="#OpenCode开始吃力" class="headerlink" title="OpenCode开始吃力"></a>OpenCode开始吃力</h2><p>事情发生了转机。</p><p>我让它修改代码以支持在公网上安全部署，它咔咔生成完，结果没法上传文件了。</p><p>我把现象告诉它让它自己找出问题，它整了半天没把问题给找出来，最后我不得不打开了前端控制台：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(index):309 Executing inline script violates the following Content Security Policy directive <span class="string">&#x27;script-src &#x27;</span>self<span class="string">&#x27; https://unpkg.com&#x27;</span>. Either the <span class="string">&#x27;unsafe-inline&#x27;</span> keyword, a <span class="built_in">hash</span> (<span class="string">&#x27;sha256-b6+B5SDP+VwnXrplXir/gTF5QsRBt5n146EYh701WGM=&#x27;</span>), or a nonce (<span class="string">&#x27;nonce-...&#x27;</span>) is required to <span class="built_in">enable</span> inline execution. The action has been blocked.</span><br></pre></td></tr></table></figure><p>好嘛，原来是安全策略把自己给限了，把错误信息交给它后，终于是把问题修复了。</p><p>之后又用最近流行起来的<a href="https://github.com/nextlevelbuilder/ui-ux-pro-max-skill">ui-ux-pro-max-skill</a>给项目的前端页面升了个级：</p><img src="/2026/01/25/%E7%94%A8OpenCode-GLM-4-7%E7%BB%99Real-ESRGAN%E5%A5%97Web-UI%EF%BC%8C%E5%8F%AA%E7%94%A8%E4%BA%86%E4%B8%80%E6%99%9A%E4%B8%8A%EF%BC%9F/3.jpg" class=""><p>已经是个有模有样的工具了。</p><h2 id="OpenCode完美结算"><a href="#OpenCode完美结算" class="headerlink" title="OpenCode完美结算"></a>OpenCode完美结算</h2><p>最后截了几张图，直接让它生成<code>README.md</code>这些上传GitHub需要准备的东西，一气呵成，项目传送门：<a href="https://github.com/qspidy/realesrgan-web-ui">realesrgan-web-ui</a>。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>不得不承认现在的AI发展神速啊，以后这种小工具完全可以用AI直接生成，再也不怕这些冷门的小需求无处安放。不管是让AI基于已有的东西开发新功能，还是直接生成一个小工具用完就扔，简直…</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Use NNG(Nanomsg-Next-Generation) with NNGPP(NNG C++ wrapper)</title>
      <link>https://qspidy.github.io/2026/01/07/Use-NNG-Nanomsg-Next-Generation-with-NNGPP-NNG-C-wrapper/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/C-Cpp/">C/Cpp</category>
      <category domain="https://qspidy.github.io/tags/Development/">Development</category>
      <pubDate>Wed, 07 Jan 2026 15:25:49 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>NNG, like its predecessors nanomsg (and to some extent ZeroMQ), is a lightweight, broker-less library, offering a simple API to solve common recurring messaging problems, such as publish&#x2F;subscribe, RPC-style request&#x2F;reply, or service discovery. – <a href="https://github.com/nanomsg/nng#nng-lightweight-messaging-library">nng</a></p><p>NNGPP, C++ wrapper around the nanomsg NNG API. – <a href="https://github.com/cwzx/nngpp">nngpp</a><br>Instead of Kafaka and DDS, NNG is broker-less and less compleicated but full featured with messaging patterns such as publish&#x2F;subscribe, RPC-style request&#x2F;reply, or service discovery. </p><hr><h2 id="Using-NNG-with-NNGPP"><a href="#Using-NNG-with-NNGPP" class="headerlink" title="Using NNG with NNGPP"></a>Using NNG with NNGPP</h2><p>Actually gpt is more good at demostrating usage cases of nng, but notice that nngpp’s last commit is 6 years ago, gpt can’t tell the right usage case of nngpp in my case and there are few problems with this old repository.</p><p>Let’s go straight, when using nngpp for sub&#x2F;pub case, code like this:</p><p><code>sub.cpp</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;nngpp/nngpp.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;nngpp/protocol/sub0.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="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>&#123;</span><br><span class="line">  nng::socket sub_sock = nng::sub::<span class="built_in">open</span>();</span><br><span class="line"></span><br><span class="line">  nng::sub::<span class="built_in">set_opt_subscribe</span>(sub_sock, <span class="string">&quot;&quot;</span>);</span><br><span class="line"></span><br><span class="line">  sub_sock.<span class="built_in">dial</span>(<span class="string">&quot;tcp://127.0.0.1:5000&quot;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="keyword">auto</span> msg = sub_sock.<span class="built_in">recv_msg</span>();</span><br><span class="line">    <span class="function">std::string <span class="title">s</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">reinterpret_cast</span>&lt;<span class="type">char</span>*&gt;(msg.body().data()),</span></span></span><br><span class="line"><span class="params"><span class="function">        msg.body().size()</span></span></span><br><span class="line"><span class="params"><span class="function">    )</span></span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;recv: &quot;</span> &lt;&lt; s &lt;&lt; std::endl;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>pub.cpp</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;nngpp/nngpp.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;nngpp/protocol/pub0.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><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>&#123;</span><br><span class="line"></span><br><span class="line">  nng::socket pub = nng::pub::<span class="built_in">open</span>();</span><br><span class="line">  pub.<span class="built_in">listen</span>(<span class="string">&quot;tcp://127.0.0.1:5000&quot;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">    pub.<span class="built_in">send</span>(nng::<span class="built_in">view</span>(<span class="string">&quot;ping&quot;</span>, <span class="number">4</span>));</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The SUB can’t receive the message from the PUB even if the SUB subscribes all topics by <code>set_opt_subscribe(sub_sock, &quot;&quot;);</code>, and when I review the source of nngpp, everything looks ok. </p><p>Then I tried using bearly nng for sub&#x2F;pub, it works can’t be more right. So it’s clearly that there is a problem with nngpp. It’s reasonalbe since it’s last commit has been so long.</p><p>Digging into code with gdb, finally caught the bug: when calling <code>set_opt_subscribe</code> in nngpp, it finally calls the nng api <code>nng_socket_set</code>, but pass the wrong parameter “topic string size”.</p><p><code>set_opt_subscribe</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">set_opt_subscribe</span><span class="params">( socket_view s, view v )</span> </span>&#123;</span><br><span class="line">s.<span class="built_in">set_opt</span>( <span class="built_in">to_name</span>(option::subscribe), v );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>set_opt</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">set_opt</span><span class="params">( <span class="type">const</span> <span class="type">char</span>* name, view v )</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> r = <span class="built_in">nng_socket_set</span>(s,name,v.<span class="built_in">data</span>(),v.<span class="built_in">size</span>());</span><br><span class="line">    <span class="keyword">if</span>( r != <span class="number">0</span> ) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="built_in">exception</span>(r,<span class="string">&quot;nng_socket_set&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Check the code above, it pass the <code>v.size()</code> as the parameter “topic string size”. When the string is <code>&quot;&quot;</code>, the size should be <code>0</code>.</p><p>But in fact the <code>v.size()</code>(nngpp’s custom class <code>view</code> for data manage) returns <code>1</code> while <code>v.data()</code> is <code>&quot;&quot;</code>, since it allocated buffer for <code>\0</code>.</p><img src="/2026/01/07/Use-NNG-Nanomsg-Next-Generation-with-NNGPP-NNG-C-wrapper/1.1.jpg" class=""><p>I’m not sure if it’s because of the update of nng that make nngpp doesn’t work as expected any more, or the sub&#x2F;pub is never tested in nngpp, but it’s indeed a bug which make pub&#x2F;sub in nngpp doesn’t work any more.</p><p>Here is the correct version of <code>set_opt</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">set_opt</span><span class="params">( <span class="type">const</span> <span class="type">char</span>* name, view v )</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> r = <span class="built_in">nng_socket_set</span>(s,name,v.<span class="built_in">data</span>(),v.<span class="built_in">size</span>() - <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span>( r != <span class="number">0</span> ) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="built_in">exception</span>(r,<span class="string">&quot;nng_socket_set&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="The-end"><a href="#The-end" class="headerlink" title="The end"></a>The end</h2><p>I may fork nngpp and maintain it as long as I am using it for my project.</p><p>Transport types supported by nng, awesome!</p><table><thead><tr><th>Transport</th><th>Scope</th><th>Speed</th><th>Secure</th><th>Typical Use</th></tr></thead><tbody><tr><td><code>inproc://</code></td><td>same process</td><td>⭐⭐⭐⭐⭐</td><td>❌</td><td>threads</td></tr><tr><td><code>ipc://</code></td><td>same host</td><td>⭐⭐⭐⭐</td><td>OS perms</td><td>local IPC</td></tr><tr><td><code>tcp://</code></td><td>network</td><td>⭐⭐⭐</td><td>❌</td><td>simple network</td></tr><tr><td><code>tls+tcp://</code></td><td>network</td><td>⭐⭐</td><td>✅</td><td>secure backend</td></tr><tr><td><code>ws://</code></td><td>network</td><td>⭐⭐</td><td>❌</td><td>browser</td></tr><tr><td><code>wss://</code></td><td>network</td><td>⭐⭐</td><td>✅</td><td>browser secure</td></tr><tr><td><code>quic://</code></td><td>network</td><td>⭐⭐⭐⭐</td><td>✅</td><td>modern low-latency</td></tr></tbody></table><p>Enjoy!</p><hr><h2 id="See-also"><a href="#See-also" class="headerlink" title="See also"></a>See also</h2><ul><li><a href="https://nng.nanomsg.org/">NNG: Lightweight Messaging Library</a></li></ul><hr><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://github.com/nanomsg/nng">nanomsg&#x2F;nng</a></li><li><a href="https://github.com/cwzx/nngpp">cwzx&#x2F;nngpp</a></li></ul>]]>
      </content:encoded>
    </item>
    <item>
      <title>Install and Setup NixOS + Hyprland from Scratch</title>
      <link>https://qspidy.github.io/2025/05/05/Install-and-Setup-NixOS-Hyprland-from-Scratch/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <category domain="https://qspidy.github.io/tags/WM/">WM</category>
      <category domain="https://qspidy.github.io/tags/NixOS/">NixOS</category>
      <pubDate>Mon, 05 May 2025 12:52:51 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>First of all, though it’s from SCRATCH, I suppose the reader know basic Linux concepts and commands and is able to use basic tools for os installation such as Vectory. Of course this is mainly based on my walk through, so remember using google to finish what is not mentioned for you.</p><p>I fell in love with Nix for the first I have it installed cause it is definitely what I am searching for after more than three years of Arch usage. I have reinstalled my Arch many times, and each time I have to reinstall the packages manually and spend time to change the configurations, it’s fun and I enjoy it. But recently I don’t have much time left for it, so NixOS is definitely what I want.</p><p>As for Hyprland, I found it very popular recently and it’s written in cpp, so I tried it with Arch from scratch and found it a fancy wm.</p><hr><h2 id="Download-and-boot-from-image"><a href="#Download-and-boot-from-image" class="headerlink" title="Download  and boot from image"></a>Download  and boot from image</h2><p>Get NixOS image <a href="https://nixos.org/download/">here</a>, I chose Minimal ISO image in my case.</p><p>To begin the installation, you have to boot your computer from the install drive.</p><ol><li>Plug in the install drive. Then turn on or restart your computer.</li><li>Open the boot menu by pressing the appropriate key, which is usually shown on the display on early boot. Select the USB flash drive (the option usually contains the word “USB”). If you choose the incorrect drive, your computer will likely continue to boot as normal. In that case restart your computer and pick a different drive.</li></ol><hr><h2 id="Setup-WIFI-Optional"><a href="#Setup-WIFI-Optional" class="headerlink" title="Setup WIFI (Optional)"></a>Setup WIFI (Optional)</h2><p>There is no <code>iwd</code> preinstalled in NixOS like Arch, so use <code>wpa_supplicant</code> instead.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Bring the interface up</span></span><br><span class="line"><span class="built_in">sudo</span> ip <span class="built_in">link</span> <span class="built_in">set</span> wlan0 up</span><br><span class="line"></span><br><span class="line"><span class="comment"># Scan avaiable networks</span></span><br><span class="line"><span class="built_in">sudo</span> iw wlan0 scan | grep SSID</span><br><span class="line"></span><br><span class="line"><span class="comment"># Generate a wpa_supplicant config</span></span><br><span class="line">wpa_passphrase <span class="string">&quot;YourSSID&quot;</span> <span class="string">&quot;YourPassword&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/wpa_supplicant.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># Start wpa_supplicant</span></span><br><span class="line"><span class="built_in">sudo</span> wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get an IP address</span></span><br><span class="line"><span class="built_in">sudo</span> dhcpcd wlan0</span><br><span class="line"></span><br><span class="line"><span class="comment"># Check connectivity</span></span><br><span class="line">ping -c 4 8.8.8.8</span><br><span class="line"></span><br><span class="line"><span class="comment"># Optional: Stop wpa_supplicant</span></span><br><span class="line"><span class="comment"># sudo pkill wpa_supplicant</span></span><br></pre></td></tr></table></figure><hr><h2 id="Try-installing-new-packages"><a href="#Try-installing-new-packages" class="headerlink" title="Try installing new packages"></a>Try installing new packages</h2><p>Before installing a new package, you may want to check if the package exists and how to install from <a href="https://search.nixos.org/packages">here</a>.</p><p>Most of the time, install a package like this:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nix-shell -p cmatrix</span><br></pre></td></tr></table></figure><hr><h2 id="Partitioning-and-formatting"><a href="#Partitioning-and-formatting" class="headerlink" title="Partitioning and formatting"></a>Partitioning and formatting</h2><p>Run <code>lsblk</code> to see where you gonna install NixOS into, the output may looks like this:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">NAME                         MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS</span><br><span class="line">nvme1n1                      259:0    0   3.7T  0 disk</span><br><span class="line">├─nvme1n1p1                  259:1    0    16M  0 part</span><br><span class="line">├─nvme1n1p2                  259:2    0 515.4G  0 part</span><br><span class="line">└─nvme1n1p3                  259:3    0   300G  0 part</span><br></pre></td></tr></table></figure><p>Suppose the <code>nvme1n1p3</code> is what you want and you are SURE to format it:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> mkfs.ext4 -L nixos /dev/nvme1n1p3</span><br></pre></td></tr></table></figure><hr><h2 id="Install-NixOS"><a href="#Install-NixOS" class="headerlink" title="Install NixOS"></a>Install NixOS</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Mount the target file system on which NixOS should be installed on `/mnt`</span></span><br><span class="line">mount /dev/disk/by-label/nixos /mnt</span><br><span class="line"></span><br><span class="line"><span class="comment"># Suppose you UEFI partition is /dev/nvme1n1p1</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /mnt/boot</span><br><span class="line">mount /dev/nvme1n1p1 /mnt/boot</span><br><span class="line"></span><br><span class="line"><span class="comment"># Generate default configuration file</span></span><br><span class="line">nixos-generate-config --root /mnt</span><br><span class="line"></span><br><span class="line"><span class="comment"># Edit configuration file</span></span><br><span class="line">vim /mnt/etc/nixos/configuration.nix</span><br></pre></td></tr></table></figure><p>(Optinal) 将清华源做为默认源，添加以下内容到配置文件大括号内</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nix.settings.substituters = [ &quot;https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store&quot; ];</span><br></pre></td></tr></table></figure><p>在安装 NixOS 时临时使用清华源</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nixos-install --option substituters <span class="string">&quot;https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store&quot;</span></span><br></pre></td></tr></table></figure><p>For more detailed processes, check <a href="https://nixos.org/manual/nixos/stable/#sec-installation-manual-partitioning">this</a></p><hr><h2 id="Start-NixOS"><a href="#Start-NixOS" class="headerlink" title="Start NixOS"></a>Start NixOS</h2><p>Reboot and choose the NixOS just installed. Setup wifi following the same procedure above, and this time you can make it start automatically via <code>systemctl enable --now wpa_supplicant</code>.</p><p>Or you can use NetworkManager instead, procedure will not be presented here.</p><hr><h2 id="Install-Hyprland"><a href="#Install-Hyprland" class="headerlink" title="Install Hyprland"></a>Install Hyprland</h2><p>Edit <code>/etc/nixos/configuration.nix</code> and add following:</p><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">&#123;</span><br><span class="line">  <span class="attr">programs.hyprland</span> <span class="operator">=</span> &#123;</span><br><span class="line">    <span class="attr">enable</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="attr">withUWSM</span> <span class="operator">=</span> <span class="literal">true</span>; <span class="comment"># recommended for most users</span></span><br><span class="line">    <span class="attr">xwayland.enable</span> <span class="operator">=</span> <span class="literal">true</span>; <span class="comment"># Xwayland can be disabled.</span></span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Then install necessary packages, I use <code>alacritty</code> as my default terminal on Hyprland:</p><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">&#123;</span><br><span class="line">  <span class="attr">environment.systemPackages</span> <span class="operator">=</span> <span class="keyword">with</span> pkgs; [</span><br><span class="line">    alacritty</span><br><span class="line">  ];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Rebuild NixOS:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> nixos-rebuild switch</span><br></pre></td></tr></table></figure><p>Edit Hyprland’s config file <code>~/.config/hypr/hyprland.conf</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"># change the default terminal from kitty to alacritty</span><br><span class="line"># $terminal = kitty</span><br><span class="line">$terminal = alacrityy</span><br></pre></td></tr></table></figure><p>Start Hyprland.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uwsm start <span class="keyword">select</span></span><br></pre></td></tr></table></figure><hr><h2 id="The-end"><a href="#The-end" class="headerlink" title="The end"></a>The end</h2><p>The most basic setup has been done, you can now walk around the Hyprland and NixOS’s wiki and forums for more useful configurations and build up the final system that you want.</p><p>Enjoy!</p><hr><h2 id="See-also"><a href="#See-also" class="headerlink" title="See also"></a>See also</h2><ul><li><a href="https://discourse.nixos.org/t/screenshots-on-hyprland-on-nixos/29055">Screenshots on hyprland on nixos</a></li></ul><hr><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://nixos.org/manual/nixos/stable/">NixOS Manual</a></li><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/nix-channels/">清华源 Nix Channels</a></li><li><a href="https://wiki.nixos.org/wiki/Hyprland">Hyprland NixOS Wiki</a></li></ul>]]>
      </content:encoded>
    </item>
    <item>
      <title>Setup Orange Pi Zero 3 as a Debian Server</title>
      <link>https://qspidy.github.io/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/Tech/">Tech</category>
      <category domain="https://qspidy.github.io/tags/WiFi/">WiFi</category>
      <category domain="https://qspidy.github.io/tags/OpenWrt/">OpenWrt</category>
      <category domain="https://qspidy.github.io/tags/Single-Board/">Single-Board</category>
      <category domain="https://qspidy.github.io/tags/Router/">Router</category>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <pubDate>Sun, 05 Jan 2025 04:29:31 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>I got the board several days ago, and now it’s time to get it to work. However I got stuck on WiFi configuration again though I did it many times before. So I write this post to make the steps more clear in my mind.</p><hr><h2 id="About-the-board"><a href="#About-the-board" class="headerlink" title="About the board"></a>About the board</h2><p>It’s <a href="http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-3.html">Orange Pi Zero 3</a>, a really small and powerful single-board computer! Here is its introduction from official:</p><blockquote><p>Orange Pi Zero 3 is powered by Allwinner H618 quad-core Cortex-A53 processor, Arm Mali-G31 MP2 GPU, supports OpenGL ES 1.0&#x2F;2.0&#x2F;3.2, OpenCL 2.0, Vulkan 1.1, etc.,with 1GB&#x2F;1.5GB&#x2F;2GB&#x2F;4GB memory options, decoding of multiple video formats is supported, and the Micro-HDMI output supports 4K display. In addition, Orange Pi Zero 3 can also be extended with headphones, TVout, USB2.0, IR reception and other functions via 13Pin expansion ports with an adapter board, in addition to the 26Pin expansion function port on the board, further enriching the functional interface of the motherboard.</p></blockquote><p>In my case, it has 4GB LPDDR4 which makes it sufficient for my various services.</p><p>Here is what I got:</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.1.jpg" class=""><p>It’s easy to setup, and here is finally what I got:</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.2.jpg" class=""><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.3.jpg" class=""><h2 id="Burn-the-image"><a href="#Burn-the-image" class="headerlink" title="Burn the image"></a>Burn the image</h2><p>There are several images on official site:</p><ul><li>Orange Pi OS(Arch)</li><li>Ubuntu Image</li><li>Debian Image</li><li>Android Image</li><li>OpenWRT</li></ul><p>I chose debian as it’s one of my most familiar distros, there are also imaging tools on official site:</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.4.jpg" class=""><p>Burning…</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.5.jpg" class=""><p>Insert sd card and power on:</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.6.jpg" class=""><p>Finally got to the shell (BTW, the default password for orangepi is <code>orangepi</code>):</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.7.jpg" class=""><h2 id="Basic-setup"><a href="#Basic-setup" class="headerlink" title="Basic setup"></a>Basic setup</h2><h3 id="WiFi"><a href="#WiFi" class="headerlink" title="WiFi"></a>WiFi</h3><p>The first thing to do about this new sever must be setting up wifi connection cause I don’t have another net cable.</p><p>There are generally three choices or tools on command line to manage wifi connections: <code>iwconfig</code>, <code>wpa_supplicant</code> and <code>nmcli</code>. After asking chatgpt for assistance, I finally decide to leave all of them behind and just edit <code>/etc/network/interfaces</code> to setup the wifi.</p><p>Actually when I asked chatgpt about how to setup wifi connection on linux,  it didn’t mention editing <code>/etc/network/interfaces</code>. I happened to know it when I was configuring network for pve, and it is really a simple and basic way to setup wifi connections. And this time, I happened to know that I can setup a roaming wifi, so it can auto connect to different wifi if the current wifi connection corrupts, it’s useful but I don’t need it for the moment.</p><p>Edit <code>/etc/network/interfaces</code> and add following like this</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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">auto wlan0</span><br><span class="line">iface wlan0 inet dhcp</span><br><span class="line">wpa-ssid &lt;wifi-name&gt;</span><br><span class="line">wpa-psk &lt;password&gt;</span><br></pre></td></tr></table></figure><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.8.jpg" class=""><p>Save and then restart, then the wifi will automatic be connected and got an ip:</p><img src="/2025/01/05/Setup-Orange-Pi-Zero-3-as-a-Debian-Server/1.9.jpg" class=""><h3 id="Mirror-source"><a href="#Mirror-source" class="headerlink" title="Mirror source"></a>Mirror source</h3><p>The default mirror source is rather slow, so I change <code>/etc/apt/sources.list</code> to the following:</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></pre></td><td class="code"><pre><span class="line">deb http://mirrors.ustc.edu.cn/debian bookworm main contrib non-free non-free-firmware</span><br><span class="line"><span class="comment">#deb http://repo.huaweicloud.com/debian bookworm main contrib non-free non-free-firmware</span></span><br><span class="line"><span class="comment">#deb-src http://repo.huaweicloud.com/debian bookworm main contrib non-free non-free-firmware</span></span><br><span class="line"></span><br><span class="line">deb http://mirrors.ustc.edu.cn/debian bookworm-updates main contrib non-free non-free-firmware</span><br><span class="line"><span class="comment">#deb http://repo.huaweicloud.com/debian bookworm-updates main contrib non-free non-free-firmware</span></span><br><span class="line"><span class="comment">#deb-src http://repo.huaweicloud.com/debian bookworm-updates main contrib non-free non-free-firmware</span></span><br><span class="line"></span><br><span class="line">deb http://mirrors.ustc.edu.cn/debian bookworm-backports main contrib non-free non-free-firmware</span><br><span class="line"><span class="comment">#deb http://repo.huaweicloud.com/debian bookworm-backports main contrib non-free non-free-firmware</span></span><br><span class="line"><span class="comment">#deb-src http://repo.huaweicloud.com/debian bookworm-backports main contrib non-free non-free-firmware</span></span><br></pre></td></tr></table></figure><p>and remove the useless source in <code>/etc/apt/sources.list.d/docker.list</code>.</p><h3 id="Shell"><a href="#Shell" class="headerlink" title="Shell"></a>Shell</h3><p>It surprised me that the image preinstalled zsh and oh-my-zsh, also tools like tmux, git, docker. here is the default <code>.zshrc</code> file:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># If you come from bash you might have to change your $PATH.</span></span><br><span class="line"><span class="comment"># export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Path to your Oh My Zsh installation.</span></span><br><span class="line"><span class="built_in">export</span> ZSH=/etc/oh-my-zsh</span><br><span class="line"><span class="built_in">export</span> ZSH_CACHE_DIR=~/.oh-my-zsh/cache</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set name of the theme to load --- if set to &quot;random&quot;, it will</span></span><br><span class="line"><span class="comment"># load a random theme each time Oh My Zsh is loaded, in which case,</span></span><br><span class="line"><span class="comment"># to know which specific one was loaded, run: echo $RANDOM_THEME</span></span><br><span class="line"><span class="comment"># See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes</span></span><br><span class="line">ZSH_THEME=<span class="string">&quot;mrtazz&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Set list of themes to pick from when loading at random</span></span><br><span class="line"><span class="comment"># Setting this variable when ZSH_THEME=random will cause zsh to load</span></span><br><span class="line"><span class="comment"># a theme from this variable instead of looking in $ZSH/themes/</span></span><br><span class="line"><span class="comment"># If set to an empty array, this variable will have no effect.</span></span><br><span class="line"><span class="comment"># ZSH_THEME_RANDOM_CANDIDATES=( &quot;robbyrussell&quot; &quot;agnoster&quot; )</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to use case-sensitive completion.</span></span><br><span class="line"><span class="comment"># CASE_SENSITIVE=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to use hyphen-insensitive completion.</span></span><br><span class="line"><span class="comment"># Case-sensitive completion must be off. _ and - will be interchangeable.</span></span><br><span class="line"><span class="comment"># HYPHEN_INSENSITIVE=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment one of the following lines to change the auto-update behavior</span></span><br><span class="line"><span class="comment"># zstyle &#x27;:omz:update&#x27; mode disabled  # disable automatic updates</span></span><br><span class="line"><span class="comment"># zstyle &#x27;:omz:update&#x27; mode auto      # update automatically without asking</span></span><br><span class="line"><span class="comment"># zstyle &#x27;:omz:update&#x27; mode reminder  # just remind me to update when it&#x27;s time</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to change how often to auto-update (in days).</span></span><br><span class="line"><span class="comment"># zstyle &#x27;:omz:update&#x27; frequency 13</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line if pasting URLs and other text is messed up.</span></span><br><span class="line"><span class="comment"># DISABLE_MAGIC_FUNCTIONS=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to disable colors in ls.</span></span><br><span class="line"><span class="comment"># DISABLE_LS_COLORS=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to disable auto-setting terminal title.</span></span><br><span class="line"><span class="comment"># DISABLE_AUTO_TITLE=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to enable command auto-correction.</span></span><br><span class="line"><span class="comment"># ENABLE_CORRECTION=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line to display red dots whilst waiting for completion.</span></span><br><span class="line"><span class="comment"># You can also set it to another string to have that shown instead of the default red dots.</span></span><br><span class="line"><span class="comment"># e.g. COMPLETION_WAITING_DOTS=&quot;%F&#123;yellow&#125;waiting...%f&quot;</span></span><br><span class="line"><span class="comment"># Caution: this setting can cause issues with multiline prompts in zsh &lt; 5.7.1 (see #5765)</span></span><br><span class="line"><span class="comment"># COMPLETION_WAITING_DOTS=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line if you want to disable marking untracked files</span></span><br><span class="line"><span class="comment"># under VCS as dirty. This makes repository status check for large repositories</span></span><br><span class="line"><span class="comment"># much, much faster.</span></span><br><span class="line"><span class="comment"># DISABLE_UNTRACKED_FILES_DIRTY=&quot;true&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Uncomment the following line if you want to change the command execution time</span></span><br><span class="line"><span class="comment"># stamp shown in the history command output.</span></span><br><span class="line"><span class="comment"># You can set one of the optional three formats:</span></span><br><span class="line"><span class="comment"># &quot;mm/dd/yyyy&quot;|&quot;dd.mm.yyyy&quot;|&quot;yyyy-mm-dd&quot;</span></span><br><span class="line"><span class="comment"># or set a custom format using the strftime function format specifications,</span></span><br><span class="line"><span class="comment"># see &#x27;man strftime&#x27; for details.</span></span><br><span class="line"><span class="comment"># HIST_STAMPS=&quot;mm/dd/yyyy&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Would you like to use another custom folder than $ZSH/custom?</span></span><br><span class="line"><span class="comment"># ZSH_CUSTOM=/path/to/new-custom-folder</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Which plugins would you like to load?</span></span><br><span class="line"><span class="comment"># Standard plugins can be found in $ZSH/plugins/</span></span><br><span class="line"><span class="comment"># Custom plugins may be added to $ZSH_CUSTOM/plugins/</span></span><br><span class="line"><span class="comment"># Example format: plugins=(rails git textmate ruby lighthouse)</span></span><br><span class="line"><span class="comment"># Add wisely, as too many plugins slow down shell startup.</span></span><br><span class="line">plugins=(evalcache git git-extras debian tmux screen <span class="built_in">history</span> extract colorize web-search docker)</span><br><span class="line"></span><br><span class="line"><span class="built_in">source</span> <span class="variable">$ZSH</span>/oh-my-zsh.sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># User configuration</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># export MANPATH=&quot;/usr/local/man:$MANPATH&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># You may need to manually set your language environment</span></span><br><span class="line"><span class="comment"># export LANG=en_US.UTF-8</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Preferred editor for local and remote sessions</span></span><br><span class="line"><span class="comment"># if [[ -n $SSH_CONNECTION ]]; then</span></span><br><span class="line"><span class="comment">#   export EDITOR=&#x27;vim&#x27;</span></span><br><span class="line"><span class="comment"># else</span></span><br><span class="line"><span class="comment">#   export EDITOR=&#x27;mvim&#x27;</span></span><br><span class="line"><span class="comment"># fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Compilation flags</span></span><br><span class="line"><span class="comment"># export ARCHFLAGS=&quot;-arch x86_64&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Set personal aliases, overriding those provided by Oh My Zsh libs,</span></span><br><span class="line"><span class="comment"># plugins, and themes. Aliases can be placed here, though Oh My Zsh</span></span><br><span class="line"><span class="comment"># users are encouraged to define aliases within a top-level file in</span></span><br><span class="line"><span class="comment"># the $ZSH_CUSTOM folder, with .zsh extension. Examples:</span></span><br><span class="line"><span class="comment"># - $ZSH_CUSTOM/aliases.zsh</span></span><br><span class="line"><span class="comment"># - $ZSH_CUSTOM/macos.zsh</span></span><br><span class="line"><span class="comment"># For a full list of active aliases, run `alias`.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Example aliases</span></span><br><span class="line"><span class="comment"># alias zshconfig=&quot;mate ~/.zshrc&quot;</span></span><br><span class="line"><span class="comment"># alias ohmyzsh=&quot;mate ~/.oh-my-zsh&quot;</span></span><br></pre></td></tr></table></figure><p>I leave the shell configurations be cause it is well enough.</p><h3 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h3><p>Note that it’s defaultly installed docker, however, I can’t use <code>docker compose</code> to start a container from compose file cause it said <code>docker: &#39;compose&#39; is not a docker command.</code>.</p><p>I have to reinstall the docker. It’s rather easy, just run:</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 -fsSL https://get.docker.com | sh</span><br></pre></td></tr></table></figure><p>After waiting for a while, it’s done. Now I can use  <code>docker compose</code> to start a container from compose file.</p><h3 id="Disable-auto-login-optional"><a href="#Disable-auto-login-optional" class="headerlink" title="Disable auto login (optional)"></a>Disable auto login (optional)</h3><p>By default, when I’m use the tty to start a session, it will automaticly logs in to the terminal as orangepi. But I don’t want it to be like this after I setted it up so that it is necessary to disable it. There is document on official doc site on <strong>How to set automatic terminal login in linux system</strong>, the url is at the end of the post.</p><blockquote><p>Use the following command to set the root user to automatically log in to the terminal<br><code>orangepi@orangepi:~$ sudo auto_login_cli.sh root</code></p><p>Use the following command to disable automatic login terminal<br><code>orangepi@orangepi:~$ sudo auto_login_cli.sh -d</code></p><p>Use the following command to set the orangepi user to automatically log in to the terminal again<br><code>orangepi@orangepi:~$ sudo auto_login_cli.sh orangepi</code></p></blockquote><hr><h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>Orange Pi Zero 3 is small, powerful and flexible. I can burn different images to different sd cards to achieve easily switching to different operating systems and performing various expriments which would be a quite fancy journey. </p><p>In this blog post, I just present the most basic use case for the Orange Pi Zero 3 and explore its capabilities and there are a lot more to be explored. Wish it can help those who are new to Orange Pi Zero 3.</p><p>Feel free to leave comments below! :)</p><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-3.html">Orange Pi Zero3 Introduction</a></li><li><a href="http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-Zero-3.html">Orange Pi Zero 3 Downloads</a></li><li><a href="http://www.orangepi.org/orangepiwiki/index.php/Orange_Pi_Zero_3#How_to_set_automatic_terminal_login_in_linux_system">How to set automatic terminal login in linux system</a></li></ul>]]>
      </content:encoded>
    </item>
    <item>
      <title>BPI-R3mini 从入门到入土</title>
      <link>https://qspidy.github.io/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/Tech/">Tech</category>
      <category domain="https://qspidy.github.io/tags/WiFi/">WiFi</category>
      <category domain="https://qspidy.github.io/tags/OpenWrt/">OpenWrt</category>
      <category domain="https://qspidy.github.io/tags/Single-Board/">Single-Board</category>
      <category domain="https://qspidy.github.io/tags/Router/">Router</category>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <pubDate>Sun, 01 Dec 2024 11:25:26 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>今年早些时候入手了巡天AX3000Pro+，本以为可以一步到位，结果发现它没法刷OpenWrt，无法配置DHCP分配固定IP以及配置DNS解析，使的我只能使用IP来访问内网设备，设备IP甚至还会不停的变，买之前还是功课做少了啊。所以又看了一圈，相中了BPI-R3mini，虽然有点贵，但鉴于我喜欢折腾，且BPI-R3mini有官方提供OpenWrt镜像支持，就咬牙入手了。</p><hr><h2 id="硬件组装"><a href="#硬件组装" class="headerlink" title="硬件组装"></a>硬件组装</h2><p>到手后的东西如下：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201141956.jpg" class=""><p>安装方法参考：<a href="https://www.bilibili.com/video/BV1XM411D7hm">Banana Pi BPI-R3 Mini 迷你开源路由器开发板外壳安装教学视频</a></p><p>组装完的效果长这样：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201150613.jpg" class=""><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201151724.jpg" class=""><p>BPI-R3mini有两种启动方式，分别是从Nand启动和从eMMC启动，一般来说是把Nand当成外插SD卡，所以Nand启动一般只是用来做特殊操作时候用，比如手动更新eMMC里的镜像的时候。切换方式是拨动板上面的一个开关：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201163809.jpg" class=""><p>因为eMMC和Nand里面正常都是默认装有镜像的，所以只用把开关调到左边eMMC位置再通电启动就好（与图中相反）。如果没有的话请参考官方Wiki的<a href="https://wiki.banana-pi.org/Getting_Started_with_BPI-R3_MINI#Warnning">镜像烧录手册</a></p><p>装好通上电后，风扇就开始转了，默认镜像里有配置风扇PWM (Pulse Width Modulation&#x2F;脉宽调制)，所以不用再做其它修改，如果要修改的话可参考Wiki：<a href="https://wiki.banana-pi.org/Getting_Started_with_BPI-R3_MINI#FAN">PWM FAN Control</a></p><p>不得不说这玩意发热量还是有点大的，难怪要整成全金属的外壳，只要它在跑，那金属外壳就烫手。风扇效果大不大，不好说，没感受到这小风扇的作用。</p><h2 id="软件配置"><a href="#软件配置" class="headerlink" title="软件配置"></a>软件配置</h2><p>通过eMMC启动，里面默认配置了WiFi，分别是<code>MTK_MT7986_AP_AX4200_5G</code>和<code>MTK_MT7986_AP_AX4200_2.4G</code>，直接连上，访问默认网关<code>192.168.1.1</code>，默认密码是<code>bananapi</code>，登录成功后界面如下图：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201170323.jpg" class=""><p>在<code>System-&gt;Aministration</code>下可以修改网关密码，在<code>MTK-&gt;WiFi configuration</code>下可以修改WiFi的SSID以及添加WiFi密码，下图是WiFi配置页：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201170839.jpg" class=""><p>可配置的东西有不少，我这只要加个WiFi密码改个WiFi名就够用了。</p><p>另外它还能配置许多有用的工具，比如Xray等。可以在<code>System-&gt;Software-&gt;Installed</code>下看到已经安装的软件包，也可以在这里安装新的软件包：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201172503.jpg" class=""><p>可玩性还是很高的，只是可选的包似乎不是很多，大概是因为这玩意用的人少吧。</p><h2 id="使用SSH远程访问"><a href="#使用SSH远程访问" class="headerlink" title="使用SSH远程访问"></a>使用SSH远程访问</h2><p>BPI-R3mini默认有启用SSH Server（<a href="https://github.com/mkj/dropbear">Dropbear SSH</a>），所以可以通过SSH连上去，使用之前配置的密码，或者是在<code>System-&gt;Aministration</code>配置SSH公钥。连上后查看系统信息：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201173627.jpg" class=""><p>里面用的包管理器是<code>opkg</code>，官方介绍在<a href="https://openwrt.org/docs/guide-user/additional-software/opkg">这里</a>，比起常见的<code>apt</code>, <code>pacman</code>还是有点不太好使。</p><p>可以在<code>/etc/opkg.conf</code>里给<code>opkg</code>配置代理，执行<code>vim /etc/opkg.conf</code>在文件末添加下面两行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">option http_proxy http://your.proxy.server:port</span><br><span class="line">option https_proxy https://your.proxy.server:port</span><br></pre></td></tr></table></figure><p>通过端口扫描可以看到它默认开了445端口，也就是SMB服务，所以可以直接使用Windows访问它的共享目录：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201175737.jpg" class=""><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201175803.jpg" class=""><p>也就是说可以把它当成一个小NAS来用，但这默认配置似乎是只读的（也可能是我登录用户的原因），所以想往里放东西的话还得再配置一下，我这里还没用到这功能就不详说了。不过有件事不得不提，我专门买了个美光P310 2230 SSD，2T，结果插上后它无法识别（可以通过<code>fdisk -l</code>看到它的信息，但这信息里的设备路径事实上不存在，也就挂载不了），是因为2T容量太大了还是驱动啥的问题，还没整明白。</p><p>这玩意似乎并不能桥接WiFi，也就是不能当我的手机热点信号中继放大器，因为它似乎不支持作为AP Client去连WiFi：</p><img src="/2024/12/01/BPI-R3mini-%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F/image20241201181736.jpg" class=""><p>这点我的巡天都可以做到。是我的操作有问题？还是少了什么驱动啥的？这已经到我的知识盲区了，也找不着相关资料😔。</p><hr><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>作为一个软路由它还是合格的，另外我准备给它安上5G模块，这样就可以直接插SIM卡连网，不用再拿手机当热点了，如果能再把NAS功能，Xray配置好的话，就算大功告成，这样它就能完全顶替我的巡天了。所以之后应该还会记录安装这些功能的过程和效果，这样就可以再水一篇博客了😆。</p><h2 id="参考-x2F-引用"><a href="#参考-x2F-引用" class="headerlink" title="参考&#x2F;引用"></a>参考&#x2F;引用</h2><ol><li><a href="https://wiki.banana-pi.org/Getting_Started_with_BPI-R3_MINI">Getting Started with BPI-R3 MINI</a></li><li><a href="https://wiki.banana-pi.org/Banana_Pi_BPI-R3_Mini">Banana Pi BPI-R3 Mini</a></li><li><a href="https://www.bilibili.com/video/BV1XM411D7hm?spm_id_from=333.880.my_history.page.click">Banana Pi BPI-R3 Mini 迷你开源路由器开发板外壳安装教学视频</a>]</li></ol>]]>
      </content:encoded>
    </item>
    <item>
      <title>Implementing An Simple IRC Bot Using Python</title>
      <link>https://qspidy.github.io/2024/11/22/Implementing-An-Simple-IRC-Bot-Using-Python/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <category domain="https://qspidy.github.io/tags/Python/">Python</category>
      <category domain="https://qspidy.github.io/tags/IRC/">IRC</category>
      <category domain="https://qspidy.github.io/tags/MultiThread/">MultiThread</category>
      <category domain="https://qspidy.github.io/tags/AI/">AI</category>
      <category domain="https://qspidy.github.io/tags/Bot/">Bot</category>
      <pubDate>Fri, 22 Nov 2024 15:45:19 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>最近用国补买了台MiniPC，整了个PVE丢家里当小服务器使，在上面跑了几个docker服务，以及ollama，8854H + 32g 内存跑<code>llama3.1</code>基本没有压力。由于平时上班不在家，也就有了远程管理这MiniPC的想法，正好以前瞎整的IRC服务器SSL证书过期提醒了我，以及最近在整些Python脚本，于是就有了现在这个项目：</p><p><a href="https://github.com/qspidy/albot"><strong>Python实现简单IRC聊天机器人(A Simple IRC BOT Written in Python)</strong></a></p><hr><h2 id="关于IRC-Python"><a href="#关于IRC-Python" class="headerlink" title="关于IRC + Python"></a>关于IRC + Python</h2><p>作为一名命令行爱好者，我曾试图使用命令行实现所有我在电脑上要做的事，比如便捷的浏览文件、多终端同时运行、浏览网页、作为IDE写代码和即时通讯等，于是就找到了<code>ranger</code>、<code>tmux</code>、<code>w3m</code>、<code>NvChad</code>和<code>Irssi</code>等优秀的开源工具。</p><p>其中IRC终端客户端<code>Irssi</code>是我入门终端聊天的工具，让我对这个古老又相对简洁的通讯协议产生了兴趣，<strong>是时候基于它做点有意思的事了</strong>。</p><p>至于Python，众所周知：人生苦短，我用Python。如果后续对性能有需求，再用Go重构吧。</p><h2 id="Albot"><a href="#Albot" class="headerlink" title="Albot"></a>Albot</h2><p>我给这聊天机器人取名Albot，不是AI，似是AI。它实现的功能有：</p><ul><li>从后台<code>ollama</code>接口生成AI回复</li><li>针对不同的关键字触发不同的响应，比如收到<code>bilibili</code>后在服务器后台执行相应的脚本</li><li>同时响应多个请求，即同时与多个对象，在不同频道中聊天对话</li></ul><p>通过设计不同的关键字和实现不同的脚本，我就可以通过和albot聊天的形式来灵活管理MiniPC了。</p><h2 id="项目特点"><a href="#项目特点" class="headerlink" title="项目特点"></a>项目特点</h2><p>简单，简单，还是简单。使用最小的成本实现想要的效果，适合用来入门Python多线程和网络编程。</p><ul><li>网络模型参考的IOCP模型，主线程监听，监听到的连接分配给线程池中的线程去处理</li><li>由于MiniPC性能有限，通过加锁限制了同时只能有一个对话可以请求后台<code>ollama</code></li><li><code>ollama</code>每生成一行，<code>albot</code>会立即发出，而非等到AI回复全部生成</li><li>仅使用Python默认库</li><li>对于<code>ollama</code>的HTTP响应做了相对健壮的处理，同时以性能优先</li></ul><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p><strong>项目地址</strong>：<a href="https://github.com/qspidy/albot">https://github.com/qspidy/albot</a></p><p>后续还会根据需要对其进行改进以及功能的添加和完善，比如使用数据库存聊天记录和AI对话的Prompt、设置不同的命令来灵活控制和<code>ollama</code>的交互、使用Docker打包成镜像等。主要以学习为目的，顺带让其产生点实际价值，只有实践才能知道以前以为很简单的功能实现起来也有不少细节啊。</p><p>还在努力适应PDB中…</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>【DST Dedicated Server】简单开服流程（Writen by MrOrange)</title>
      <link>https://qspidy.github.io/2023/10/13/dst-dedicate-server-start/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/categories/%E6%8A%98%E8%85%BE%E5%90%88%E9%9B%86/">折腾合集</category>
      <category domain="https://qspidy.github.io/categories/%E6%8A%98%E8%85%BE%E5%90%88%E9%9B%86/Game/">Game</category>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <category domain="https://qspidy.github.io/tags/Game/">Game</category>
      <category domain="https://qspidy.github.io/tags/DST/">DST</category>
      <category domain="https://qspidy.github.io/tags/Server/">Server</category>
      <pubDate>Fri, 13 Oct 2023 11:15:33 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><hr><h2 id="拥有一台服务器"><a href="#拥有一台服务器" class="headerlink" title="拥有一台服务器"></a>拥有一台服务器</h2><ul><li>首先，当然是有一台已经装好Debian的服务器（1Mbps带宽大概对应2~4人，一个存档2核就够，棱镜+勋章至少得3G内存），不建议Ubuntu，一些依赖似乎无法安装。</li><li>安装之后可能用到的包：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install tmux btop htop fish ranger git</span><br></pre></td></tr></table></figure><hr><h2 id="配置基础环境（steamcmd-dst服务器）："><a href="#配置基础环境（steamcmd-dst服务器）：" class="headerlink" title="配置基础环境（steamcmd, dst服务器）："></a>配置基础环境（steamcmd, dst服务器）：</h2><ul><li>一行行复制运行，注意是否报错</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> dpkg --add-architecture i386 <span class="comment"># If running a 64bit OS</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install lib32gcc1    <span class="comment"># If running a 64bit OS</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> apt-get install lib32stdc++6 <span class="comment"># If running a 64bit OS</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get install libgcc1      <span class="comment"># If running a 32bit OS</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get install libcurl4-gnutls-dev:i386</span><br><span class="line"><span class="built_in">sudo</span> useradd -m steam</span><br><span class="line"><span class="built_in">sudo</span> su - steam</span><br><span class="line"><span class="built_in">mkdir</span> ~/steamcmd</span><br><span class="line"><span class="built_in">cd</span> ~/steamcmd</span><br><span class="line">wget &lt;https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz&gt;</span><br><span class="line">tar -xvzf steamcmd_linux.tar.gz</span><br><span class="line">./steamcmd.sh</span><br><span class="line">force_install_dir /home/steam/steamapps/DST (or whatever absolute path is wanted)</span><br><span class="line">login anonymous</span><br><span class="line">app_update 343050 validate</span><br><span class="line">quit</span><br><span class="line"><span class="built_in">cd</span> /home/steam/steamapps/DST/bin/</span><br><span class="line">./dontstarve_dedicated_server_nullrenderer</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>最后一句尝试启动服务器，会生成一些简单的配置，如无报错则基本环境已经配置好。</li></ul><hr><h2 id="克隆MrOrange的服务器管理脚本，给予执行权限："><a href="#克隆MrOrange的服务器管理脚本，给予执行权限：" class="headerlink" title="克隆MrOrange的服务器管理脚本，给予执行权限："></a>克隆MrOrange的服务器管理脚本，给予执行权限：</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/qspidy/dst-dedicated-server-admin-scripts.git</span><br><span class="line"><span class="built_in">chmod</span> +x dst-dedicated-server-admin-scripts/dst*</span><br></pre></td></tr></table></figure><hr><h2 id="生成并上传cluster-token-txt"><a href="#生成并上传cluster-token-txt" class="headerlink" title="生成并上传cluster_token.txt"></a>生成并上传cluster_token.txt</h2><ul><li>本地DST控制台运行<code>TheNet:GenerateClusterToken()</code>生成令牌文件<code>cluster_token.txt</code>，位置在<code>C:/Users/&lt;yourusername&gt;/Documents/Klei/DoNotStarveTogether</code>（大概，也可能在某个子文件夹），然后上传至服务器<code>/home/steam/.klei/DoNotStarveTogether/</code>目录。该文件相当于身份认证，即只有买了这游戏的人才能创服务器（当然也可以白嫖其它人的）。</li></ul><hr><h2 id="常规开服步骤"><a href="#常规开服步骤" class="headerlink" title="常规开服步骤"></a>常规开服步骤</h2><h3 id="创建新世界"><a href="#创建新世界" class="headerlink" title="创建新世界"></a>创建新世界</h3><ul><li>在本地DST游戏中创建新世界，配置想要的模组，世界类型等等。</li></ul><h3 id="上传存档文件夹"><a href="#上传存档文件夹" class="headerlink" title="上传存档文件夹"></a>上传存档文件夹</h3><ul><li>找到新世界的存档文件夹（游戏里存档设置页有“打开世界文件夹”选项），通常叫<code>Cluster_xx</code>，把<code>Cluster_xx</code>文件夹上传到服务器的存档目录<code>/home/steam/.klei/DoNotStarveTogether/</code>。</li></ul><h3 id="修改相应配置（主要是cluster-ini）"><a href="#修改相应配置（主要是cluster-ini）" class="headerlink" title="修改相应配置（主要是cluster.ini）"></a>修改相应配置（主要是cluster.ini）</h3><ul><li>打开服务器新世界存档文件夹<code>Cluster_xx</code>里的<code>cluster.ini</code>文件，修改相应的配置，主要是修改人数。</li><li>[可选]直连端口在<code>Cluster_xx/Master/server.ini</code>里可以查看和修改。同时开不同的存档需要修改<code>Cluster_xx/Master/server.ini</code>和<code>Cluster_xx/Caves/server.ini</code>里的<code>server_port</code>，以及<code>cluster.ini</code>里的<code>master_port</code>，以避免端口冲突。</li></ul><hr><h2 id="启动和维护"><a href="#启动和维护" class="headerlink" title="启动和维护"></a>启动和维护</h2><h3 id="启动dst服务器-这里假设存档文件夹为Cluster-12-："><a href="#启动dst服务器-这里假设存档文件夹为Cluster-12-：" class="headerlink" title="启动dst服务器(这里假设存档文件夹为Cluster_12 )："></a>启动dst服务器(这里假设存档文件夹为<code>Cluster_12</code> )：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">./dst-dedicated-server-admin-scripts/dst_start.sh -C 12</span><br><span class="line"><span class="comment"># 如要进行服务器交互和查看输出（默认 dst-12 为对应的tmux会话名）</span></span><br><span class="line">tmux attach -t dst-12</span><br></pre></td></tr></table></figure><h3 id="完全关闭dst服务器："><a href="#完全关闭dst服务器：" class="headerlink" title="完全关闭dst服务器："></a>完全关闭dst服务器：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./dst-dedicated-server-admin-scripts/dst_shutdown.sh -C 12</span><br></pre></td></tr></table></figure><h3 id="生成备份文件："><a href="#生成备份文件：" class="headerlink" title="生成备份文件："></a>生成备份文件：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./dst-dedicated-server-admin-scripts/dst_backup_cluster.sh -C 12</span><br></pre></td></tr></table></figure><h3 id="单独更新dst服务器和mods："><a href="#单独更新dst服务器和mods：" class="headerlink" title="单独更新dst服务器和mods："></a>单独更新dst服务器和mods：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">./dst-dedicated-server-admin-scripts/dst_update_modlist.sh 12</span><br><span class="line">./dst-dedicated-server-admin-scripts/dst_update.sh</span><br></pre></td></tr></table></figure><h3 id="更新中-发送服务器指令（如保存世界、踢出某人）："><a href="#更新中-发送服务器指令（如保存世界、踢出某人）：" class="headerlink" title="(更新中)发送服务器指令（如保存世界、踢出某人）："></a>(更新中)发送服务器指令（如保存世界、踢出某人）：</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 保存世界</span></span><br><span class="line">./dst-dedicated-server-admin-scripts/albin/tmux_send.al dst-12 c_save()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 踢出某人</span></span><br><span class="line">./dst-dedicated-server-admin-scripts/albin/tmux_send.al dst-12 c_kill(<span class="string">&quot;player_id&quot;</span>)</span><br></pre></td></tr></table></figure><ul><li>常用指令（大概）：<code>c_save()</code> , <code>c_shutdown()</code> , <code>c_announce(&quot;message&quot;)</code> , <code>c_regenerateworld()</code> , <code>c_rollback()</code> , <code>c_listallplayers()</code> , <code>c_kill(&quot;player_id&quot;)</code> .</li><li>打印当前世界信息（天数、季节、时段）指令： <code>TheNet:Announce(&quot;Current day: &quot;..TheWorld.state.cycles..&quot; Season: &quot;..TheWorld.state.season..&quot;, Phase: &quot;..TheWorld.state.phase)</code></li></ul><h3 id="查看跟踪日志："><a href="#查看跟踪日志：" class="headerlink" title="查看跟踪日志："></a>查看跟踪日志：</h3><ul><li>查看跟踪当前服务器日志：<code>tail -f ~/.klei/DoNotStarveTogether/Cluster_12/Master/server_log.txt</code></li><li>查看跟踪当前服务器聊天日志：<code>tail -f ~/.klei/DoNotStarveTogether/Cluster_12/Master/server_chat_log.txt</code></li></ul><hr><h2 id="参考链接："><a href="#参考链接：" class="headerlink" title="参考链接："></a>参考链接：</h2><ol><li><a href="https://dontstarve.fandom.com/wiki/Guides/Don%E2%80%99t_Starve_Together_Dedicated_Servers">Guides&#x2F;Don’t Starve Together Dedicated Servers - FANDOM</a></li><li><a href="https://forums.kleientertainment.com/forums/topic/64441-dedicated-server-quick-setup-guide-linux/">Dedicated Server Quick Setup Guide - Linux</a></li><li><strong><a href="https://www.youtube.com/watch?v=ZkGPlQsiXzQ&list=PLaovyrM6DB1BIqTXAF6LZjQHSZHWqSIYz&ab_channel=Jazzy%27sGames">DEDICATED SERVER QUICK SETUP (Windows) | Don’t Starve Together</a></strong></li><li><strong><a href="https://steamcommunity.com/sharedfiles/filedetails/?l=schinese&id=1616647350">Dedicated Server配置项和命令行参数详解</a></strong></li></ol>]]>
      </content:encoded>
    </item>
    <item>
      <title>使用 Qemu 替代 VirtualBox 并进行相关配置</title>
      <link>https://qspidy.github.io/2022/05/29/replace-vbox-with-qemu/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/categories/%E6%8A%98%E8%85%BE%E5%90%88%E9%9B%86/">折腾合集</category>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <category domain="https://qspidy.github.io/tags/Arch/">Arch</category>
      <category domain="https://qspidy.github.io/tags/Virtualization/">Virtualization</category>
      <category domain="https://qspidy.github.io/tags/tweak-toss/">tweak/toss</category>
      <pubDate>Sun, 29 May 2022 15:55:35 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>在使用了很长一段时间（大概 6-7 个月）的 VirtualBox 之后，我决定使用 Qemu 替代它。事实上我使用 VirtualBox 的体验非常好，尤其是它提供的 Host 和 Guest 之间的文件传输，剪切板共享功能，它对于网络的配置也非常的方便。</p><p>但对于一些复杂点的网络配置则需要用到命令行工具 <code>vboxmanage</code>，比如启用虚拟网卡的 dhcp 服务。</p><p>让我毅然决定放弃 VirtualBox 选择 Qemu 的原因是我在油管上看到的一则发表于 2022 年 1 月视频：<a href="https://www.youtube.com/watch?v=Kq849CpGd88">Stop using Virtualbox, Here’s how to use QEMU instead</a></p><p>油管博主 <em>Chris Titus</em> 在视频里展示了相同的虚拟机 Guest 在 Qemu 下和在 VirtualBox 下的启动时间，结论是 <code>11.17s</code> 比 <code>35.28s</code>！</p><p>我震惊了。如果说只是快个百分之二三十，我还可以认为只是简单的因为 VirtualBox 提供了更多的管理功能而导致的性能下降，但事实摆在我面前，速度提升 <code>68.3%</code>！</p><p>什么概念，要知道实际性能的提升和启动速度的提升并不是成正比的，通常来说实际性能的提升的比例会高于启动速度的提升比例，也就是说使用 Qemu 的虚拟机性能效果比 VirtualBox 的虚拟机性能效果提升超过一倍甚至更多。</p><p>这让我没有理由不选择使用 Qemu。</p><hr><h2 id="实际使用"><a href="#实际使用" class="headerlink" title="实际使用"></a>实际使用</h2><p>Qemu 本身只提供了命令行工具来管理虚拟机，也不会保存虚拟机的相关配置信息。实际使用时通常是同时安装 <code>Libvert</code>（一套用于管理硬件虚拟化的开源API、守护进程与管理工具）配合 <code>Libvert clients</code> 来实现使用图形界面管理 Qemu 虚拟机。</p><p><code>Libvert clients</code> 是 <code>Libvert</code> 的前端，常用的 <code>Libvert clients</code> 在<a href="https://wiki.archlinux.org/title/Libvirt#Client">这里</a>。我使用的是 Virt-Manager 来管理 Qemu 虚拟机，因为在使用过程中 Virt-Manager 提供的管理功能和 VirtualBox 比较接近。使用教程参考 <em>Derek Taylor</em> 的 <a href="https://www.youtube.com/watch?v=p1d_b_91YlU">Virt-Manager Is The Better Way To Manage VMs</a>。</p><p>使用 Virt-Manager 创建虚拟机的过程和 VirtualBox 类似，通常（对我来说）只需要改一下内存大小、CPU 数量和磁盘容量，其它无脑下一步就行。</p><p>与 VirtualBox 不同的是 Virt-Manager 还提供了通过网络安装（HTTP, HTTPS or FTP），安装过程要提供操作系统安装 URL，估计进行大规模安装的时候会用到，一般来说还是使用镜像文件来安装。</p><hr><h2 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h2><p>不得不承认，Qemu 虚拟机的启动速度是肉眼可见的提升了，对 CPU 的占用也比 VirtualBox 少。除了没有剪切板共享功能有点不方便之外，其它使用体验和 VirtualBox 基本一致。</p><p>我主要使用虚拟机来调试一些应用程序，比如在 Windows 上运行 IDA，在 Linux 上运行 GDB。另外也会在 Linux 上运行些有意思的 Github 项目，比如 QQ Bot。</p><p>顺便提一嘴文件共享的问题。我不喜欢使用虚拟机软件提供的文件共享服务，一个是麻烦，有时还得重启配置才能生效；另一个是不灵活，不方便配置多个共享文件夹和控制权限。</p><p>我使用网络来实现文件共享，因为对这方面比较熟。</p><p>对于 Windows 虚拟机，只要开启网络文件共享，然后把想要共享的文件夹设置为对指定用户共享，就可以在宿主机（我这里用的 Linux，Windows 类似）使用 <code>smb</code> 协议连上 Windows 虚拟机，访问共享文件。</p><p>对于 Linux 虚拟机就更简单了，在 Linux 虚拟机里开启 <code>SSHD/SSH</code> 服务，使用 <code>SSHFS</code> 挂载 Linux 的文件系统或者直接 <code>SCP</code>，又或者使用 <strong>VS Code</strong> 的远程连接来直接编辑 Linux 虚拟机里的文件。</p><p>这个过程在 Qemu 下更加方便，因为 Qemu 直接虚拟出一个主机层面的虚拟网卡，也就不需要像 VirtualBox 那样使用 <code>NAT</code> 时还得配置端口转发才能从主机连上虚拟机。</p><hr><h2 id="实际使用中的问题"><a href="#实际使用中的问题" class="headerlink" title="实际使用中的问题"></a>实际使用中的问题</h2><p>使用 Docker 和部署 Github 项目就不得不考虑配置代理的问题。</p><p>因为我在宿主机使用的是<em>透明代理</em>，所以在 VirtualBox 虚拟机里就不需要考虑配置网络代理的问题，网络数据自动由主机代理接管。</p><p>而 Qemu 虚拟的是主机层面的网卡，导致它的网络数据不由<em>透明代理</em>接管。（也可能是 <code>iptables</code> 配置的问题，弄了好久也没搞清楚是什么原因在开了 <code>cgproxy</code> 后 Qemu 虚拟机就连不上网，似乎可以对外请求，但返回的数据包没能回到虚拟机）</p><p>为了让虚拟机流量走主机代理，就只能是在虚拟机内使用配置 <code>http_proxy</code> 和 <code>https_proxy</code> 环境变量来试图让流量走主机代理了，但很多应用却并不使用 <code>http_proxy</code> 和 <code>https_proxy</code> 环境变量，搞自己的代理配置那一套。其中就包括 <code>apt</code>、<code>docker</code> 和 Python 脚本。</p><p>好在找到了一篇比较全的代理配置博文：<a href="https://qiita.com/daichi-ishida/items/b77c151067427806ede5">UbuntuのProxy設定備忘録</a>。里面包含了对 <code>http_proxy</code> 和 <code>https_proxy</code> 环境变量、<code>apt</code>、<code>git</code>、<code>docker</code> 和 <code>wget</code> 的代理配置方法。</p><p>关于 Python 的代理配置我参考的是 Stack Overflow 中的 <a href="https://stackoverflow.com/questions/31639742/how-to-pass-all-pythons-traffics-through-a-http-proxy">How to pass all Python’s traffics through a http proxy?</a></p><hr><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>当需要考虑虚拟机性能时，毫无疑问应该选择 Qemu&#x2F;KVM，这也是现在许多云服务厂商在使用的主流虚拟化方案。</p><p>但如果只是简单的虚拟需求如尝试各种 Linux 发行版啥的，那选择 VirtualBox 还是要更方便的，毕竟 VirtualBox 对虚拟机的管理配置很全，大部分需求都可以在 VirtualBox 的配置面板动动鼠标解决，这对新手非常友好。</p><p>折腾网络配置说实话挺烦的，尤其是还没深入研究过 <code>Netfilter</code> 和 <code>Iptables</code>。一些配置出问题虽然知道大概是哪有问题，但却不会修复。网上也不一定找得到答案，只能是论坛求助。</p><p>但折腾的好处则是我可以在解决问题的过程中学到不少新的东西，而这些经验会在未来不经意间发挥意想不到的作用。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>使用 gdb + pwndbg 轻松解决 BinaryBomb！</title>
      <link>https://qspidy.github.io/2022/04/21/binary-bomb/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/categories/CS-APP2e-Lab-Assignments/">CS:APP2e Lab Assignments</category>
      <category domain="https://qspidy.github.io/tags/Linux/">Linux</category>
      <category domain="https://qspidy.github.io/tags/C-Cpp/">C/Cpp</category>
      <category domain="https://qspidy.github.io/tags/gdb/">gdb</category>
      <category domain="https://qspidy.github.io/tags/Binarybomb/">Binarybomb</category>
      <category domain="https://qspidy.github.io/tags/Debug/">Debug</category>
      <category domain="https://qspidy.github.io/tags/Reverse/">Reverse</category>
      <category domain="https://qspidy.github.io/tags/CTF/">CTF</category>
      <category domain="https://qspidy.github.io/tags/Assembly/">Assembly</category>
      <category domain="https://qspidy.github.io/tags/Arch/">Arch</category>
      <pubDate>Thu, 21 Apr 2022 15:11:47 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>最近终于把鸽了近一年多的 <strong>BinaryBomb</strong> 给做完了，花了一天时间，内力修炼的还不够啊。这里记录一下解题思路，方便以后回顾，同时也疏理一下解题用到的技术点。</p><p>Note：<br>本文主要记录解题的过程和思路，相关的基础知识可在 <a href="https://github.com/NagleZhang/Binary-Bomb#readme">Binary-Bomb’s Readme</a> 中查看。</p><hr><h2 id="BinaryBomb-简介"><a href="#BinaryBomb-简介" class="headerlink" title="BinaryBomb 简介"></a><strong>BinaryBomb</strong> 简介</h2><p><strong>BinaryBomb</strong> 是 <em>Carnegie Mellon University (卡内基·梅隆大学)</em> CS 课上发布的一道作业，在 <a href="http://csapp.cs.cmu.edu/public/labs.html">“Lab Assignments” page of Bryant and O’Hallaron’s website</a> 中可以找到以下对 BinaryBomb 的介绍:</p><blockquote><p><code>A &quot;binary bomb&quot; is a program provided to students as an object code file. When run, it prompts the user to type in 6 different strings. If any of these is incorrect, the bomb ``explodes,&#39;&#39; printing an error message and logging the event on a grading server. Students must ``defuse&#39;&#39; their own unique bomb by disassembling and reverse engineering the program to determine what the 6 strings should be. The lab teaches students to understand assembly language, and also forces them to learn how to use a debugger. It&#39;s also great fun. A legendary lab among the CMU undergrads.</code></p></blockquote><p>译文：</p><blockquote><p><code>“二进制炸弹”是一个以目标代码文件形式提供给学生的程序。运行后，它会提示用户输入 6 个不同的字符串。如果其中任何一个字符串错误，炸弹就会“爆炸”，打印错误信息并将事件记录到评分服务器上。学生必须通过反汇编和逆向工程程序来确定这 6 个字符串应该是什么，从而“拆除”他们自己独特的“炸弹”。这个实验旨在帮助学生理解汇编语言，并迫使他们学习如何使用调试器。它也充满乐趣，是卡内基梅隆大学本科生中一个传奇的实验。</code></p></blockquote><p>正如介绍所说，BinaryBomb 非常有趣，虽然这作业是 2013 年发布的，但时至今日，仍能从中学到许多有用的东西。毕竟我们现在用的计算机除了从 32 位变成了 64 位外，寄存器种类，栈结构，函数调用机制等程序执行原理基本没有变化。</p><hr><h2 id="gdb-pwndbg-简介"><a href="#gdb-pwndbg-简介" class="headerlink" title="gdb + pwndbg 简介"></a>gdb + pwndbg 简介</h2><ul><li><p><strong>gdb</strong> 即 GNU Debugger，是 GNU 软件系统中的<em>标准调试器</em>，此外 gdb 也是个具有移携性的调试器，经过移携需求的调修与重新编译，如今许多的类 UNIX 操作系统上都可以使用 gdb，而现有 gdb 所能支持调试的编程语言有 C、C++、Pascal 以及 FORTRAN。</p></li><li><p><a href="https://github.com/pwndbg/pwndbg"><strong>pwndbg</strong></a> 是一个 GDB 和 LLDB 插件，可以减少调试工作，重点关注低级软件开发人员、硬件黑客、逆向工程和漏洞开发人员所需的功能。</p></li></ul><p>简单来说，本文使用 Linux 自带的古老而又强大的调试器搭配上一个现代而又好用的插件，在鸽了一年后终于解决了差不多十年前发布的 CS 课作业。也许单用 gdb 才是这个作业的初衷，但裸 gdb 用起来非常的麻烦，每次查看状态信息时都得重新输一次命令，等到作业做完，手都起茧喽。人生苦短，用 gdb 前记得上 pwndbg。</p><hr><h2 id="运行环境"><a href="#运行环境" class="headerlink" title="运行环境"></a>运行环境</h2><ul><li>OS: Arch Linux x86_64</li><li>Shell: zsh 5.8.1</li><li>Debuger: GNU gdb (gdb) 11.2 + pwndbg 1.0.3</li></ul><p>关于 pwndbg 的安装，在 <a href="https://github.com/pwndbg/pwndbg">github-pwndbg</a> 有详细描述；大部分桌面版 Linux 都有自带 gdb，没有的话可以使用对应包管理器安装，比如在 Ubuntu 运行 <code>apt install gdb</code> 进行安装。如果是 Windows 也可以用 WSL、MinGW 或者是虚拟机来搭建运行环境。具体操作教程网上很多，这里不再赘述。</p><p>BinaryBomb 二进制文件在 <a href="https://github.com/qspidy/BinaryBomb">github-qspidy-BinaryBomb</a> 下载，里面有许多答案不同的 BinaryBomb，但解答的过程基本一致。懒人命令：<code>git clone https://github.com/qspidy/BinaryBomb</code></p><p>同时附上<a href="http://csapp.cs.cmu.edu/3e/bomb.tar">官方 bomb 下载链接</a>。下载完成后在文件目录执行 <code>tar xvf bomb.tar</code> 解压。</p><hr><h2 id="gdb-常用命令表"><a href="#gdb-常用命令表" class="headerlink" title="gdb 常用命令表"></a>gdb 常用命令表</h2><table><thead><tr><th>命令（缩写）</th><th>说明</th></tr></thead><tbody><tr><td>r</td><td>run，开始运行调试的程序</td></tr><tr><td>x</td><td>检查内存 (Examine memory)，将指定地址的内容按指定格式和长度输出</td></tr><tr><td>b</td><td>break，在指定位置设置断点</td></tr><tr><td>c</td><td>continue，从当前开始继续运行</td></tr><tr><td>h</td><td>help，显示命令的帮助说明（善用 help 命令是变强的第一步）</td></tr><tr><td>si</td><td>stepi，执行一条汇编指令（遇到函数跳入）</td></tr><tr><td>ni</td><td>nexti，执行一条汇编指令（遇到函数跳过）</td></tr><tr><td>bt</td><td>backtrace，输出所有回溯栈帧，又称调用堆栈</td></tr><tr><td>fin</td><td>finish，执行完当前函数并 break</td></tr><tr><td>disas</td><td>disassemble，反汇编指定内存区域</td></tr><tr><td>stack</td><td>输出当前活动记录的栈信息</td></tr><tr><td>info r</td><td>info registers，列出寄存器和其中的内容</td></tr></tbody></table><hr><h2 id="开始调试-BinaryBomb"><a href="#开始调试-BinaryBomb" class="headerlink" title="开始调试 BinaryBomb"></a>开始调试 BinaryBomb</h2><ul><li>在 BinaryBomb 二进制文件目录下执行 <code>gdb bomb</code> 开始调试：<img src="/2022/04/21/binary-bomb/2022-04-22_15-31.png" class="" title="gdb bomb output"></li><li>个人习惯先把所有的 phases 给反汇编出来复制到记事本，方便之后查看和相关地址的复制。执行 <code>disas phase_1</code> 显示 <strong>phase_1</strong> 的反汇编：<img src="/2022/04/21/binary-bomb/2022-04-22_15-42.png" class="" title="disassemble **phase_1**"></li></ul><h3 id="phase-1"><a href="#phase-1" class="headerlink" title="phase_1"></a><strong>phase_1</strong></h3><ol><li>进入正题，一切准备就绪后，在 <strong>phase_1</strong> 函数入口处下断点：<code>b phase_1</code>，执行程序 <code>r</code>：<img src="/2022/04/21/binary-bomb/2022-04-22_15-55.png" class="" title="下断点，运行"></li><li>因为还不知道正确答案，这里随便输入一串字符 <code>helloworld</code>，继续执行 <code>c</code>，这时 <strong>pwndbg</strong> 的作用就开始体现了，每次运行到断点停下，都会默认显示以下信息：</li></ol><ul><li><strong>Registers</strong>，寄存器信息，与 gdb 中运行 <code>info r</code> 输出类似：<img src="/2022/04/21/binary-bomb/2022-04-22_16-29.png" class="" title="pwndbg registers"></li><li><strong>Disasm</strong>，反汇编信息，与 gdb 中运行 <code>disas</code> 输出类似：<img src="/2022/04/21/binary-bomb/2022-04-22_16-30.png" class="" title="pwndbg disasm"></li><li><strong>Stack</strong>，当前活动记录的栈信息，与 gdb 中运行 <code>stack</code> 输出类似：<img src="/2022/04/21/binary-bomb/2022-04-22_16-30_1.png" class="" title="pwndbg stack"></li><li><strong>Backtrack</strong>，堆栈回溯，显示线程的函数调用堆栈，与 gdb 中运行 <code>bt</code> 输出类似<img src="/2022/04/21/binary-bomb/2022-04-22_16-30_2.png" class="" title="pwndbg backtrace"></li></ul><ol start="3"><li>注意这句：<code>0x400e96 &lt;phase_1+9&gt;     call   strings_not_equal</code>，从函数名能看出是调用了一个比较字符串是否相等的函数，那比较的是哪两个字符串呢？上一句指令是 <code>0x400e91 &lt;phase_1+4&gt;     mov    esi, 0x402490</code>，可以合理怀疑地址 <code>0x402490</code> 是其中一个字符串首地址，且作为参数传入该函数。执行 <code>x/s 0x402490</code> 尝试以字符串格式输出该地址信息：<img src="/2022/04/21/binary-bomb/2022-04-22_18-45.png" class="" title="x&#x2F;s 0x402490">好家伙，看来这个就是答案了。</li><li>但假如没有经验，不知道应该看地址 <code>0x402490</code> 的信息呢？那只能一步步运行看看了，连续执行 <code>si</code> 直到 <code>strings_not_equal</code> 函数，可以注意到下面这一段：<img src="/2022/04/21/binary-bomb/2022-04-22_18-55.png" class="" title="call strings_not_equal">很明显，参与比较的两个字符串分别是 <code>rdi</code> 指向的 <code>helloworld</code> 和 <code>rsi</code> 指向的 <code>Why make trillions when we could make... billions?</code>，前者是之前随便输的，后者是程序里写好的。</li><li>后面的几句汇编是判断 eax 即 strings_not_equal 函数的返回值是否为 0，为 0 则说明两字符串相等，<strong>phase_1</strong> 退出，否则执行到 <code>explode_bomb</code> 函数，BOOM!!!</li></ol><h3 id="phase-2"><a href="#phase-2" class="headerlink" title="phase_2"></a><strong>phase_2</strong></h3><ol><li><p><strong>phase_1</strong> 顶多算是热身，接下来的才是真正的 puzzle。先看一眼 <strong>phase_2</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></pre></td><td class="code"><pre><span class="line">Dump of assembler code for function phase_2:</span><br><span class="line">   0x0000000000400ea9 &lt;+0&gt;:     push   rbp</span><br><span class="line">   0x0000000000400eaa &lt;+1&gt;:     push   rbx</span><br><span class="line">   0x0000000000400eab &lt;+2&gt;:     sub    rsp,0x28</span><br><span class="line">   0x0000000000400eaf &lt;+6&gt;:     mov    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x0000000000400eb8 &lt;+15&gt;:    mov    QWORD PTR [rsp+0x18],rax</span><br><span class="line">   0x0000000000400ebd &lt;+20&gt;:    xor    eax,eax</span><br><span class="line">   0x0000000000400ebf &lt;+22&gt;:    mov    rsi,rsp</span><br><span class="line">   0x0000000000400ec2 &lt;+25&gt;:    call   0x40152b &lt;read_six_numbers&gt;</span><br><span class="line">   0x0000000000400ec7 &lt;+30&gt;:    cmp    DWORD PTR [rsp],0x0</span><br><span class="line">   0x0000000000400ecb &lt;+34&gt;:    jne    0x400ed4 &lt;phase_2+43&gt;</span><br><span class="line">   0x0000000000400ecd &lt;+36&gt;:    cmp    DWORD PTR [rsp+0x4],0x1</span><br><span class="line">   0x0000000000400ed2 &lt;+41&gt;:    je     0x400ed9 &lt;phase_2+48&gt;</span><br><span class="line">   0x0000000000400ed4 &lt;+43&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400ed9 &lt;+48&gt;:    mov    rbx,rsp</span><br><span class="line">   0x0000000000400edc &lt;+51&gt;:    lea    rbp,[rsp+0x10]</span><br><span class="line">   0x0000000000400ee1 &lt;+56&gt;:    mov    eax,DWORD PTR [rbx+0x4]</span><br><span class="line">   0x0000000000400ee4 &lt;+59&gt;:    add    eax,DWORD PTR [rbx]</span><br><span class="line">   0x0000000000400ee6 &lt;+61&gt;:    cmp    DWORD PTR [rbx+0x8],eax</span><br><span class="line">   0x0000000000400ee9 &lt;+64&gt;:    je     0x400ef0 &lt;phase_2+71&gt;</span><br><span class="line">   0x0000000000400eeb &lt;+66&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400ef0 &lt;+71&gt;:    add    rbx,0x4</span><br><span class="line">   0x0000000000400ef4 &lt;+75&gt;:    cmp    rbx,rbp</span><br><span class="line">   0x0000000000400ef7 &lt;+78&gt;:    jne    0x400ee1 &lt;phase_2+56&gt;</span><br><span class="line">   0x0000000000400ef9 &lt;+80&gt;:    mov    rax,QWORD PTR [rsp+0x18]</span><br><span class="line">   0x0000000000400efe &lt;+85&gt;:    xor    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x0000000000400f07 &lt;+94&gt;:    je     0x400f0e &lt;phase_2+101&gt;</span><br><span class="line">   0x0000000000400f09 &lt;+96&gt;:    call   0x400b00 &lt;__stack_chk_fail@plt&gt;</span><br><span class="line">   0x0000000000400f0e &lt;+101&gt;:   add    rsp,0x28</span><br><span class="line">   0x0000000000400f12 &lt;+105&gt;:   pop    rbx</span><br><span class="line">   0x0000000000400f13 &lt;+106&gt;:   pop    rbp</span><br><span class="line">   0x0000000000400f14 &lt;+107&gt;:   ret    </span><br><span class="line">End of assembler dump.</span><br></pre></td></tr></table></figure><p>同样，在 <strong>phase_2</strong> 函数入口下断点 <code>b phase_2</code>，继续执行 <code>c</code>，因为注意到 <strong>phase_2</strong> 的反汇编中有调用一个叫 <code>read_six_numbers</code> 的函数，所以输入的应该是六个数字，这里输入了 <code>1 2 3 4 5 6</code>，因为计算机内通常不会有这种简单的数字，所以这样输可以帮助我们观察哪些指令对输入的哪些参数进行了处理，更方便观察程序逻辑。</p></li><li><p>多次执行命令 <code>si</code> 或直接 <code>ni 8</code> (执行 8次 <code>ni</code>) 到 <code>read_six_numbers</code> 函数结束：</p><img src="/2022/04/21/binary-bomb/2.1.png" class="" title="**phase_2** read_six_numbers"><p><code>cmp    dword ptr [rsp], 0</code> 一句表示将 <code>rsp</code> 寄存器指向的内容以 4字节 (dword) 格式解释，相当于 int 型，再与右边的 0 相减 (sub)，并改变相应的标志位。看一下当前的栈信息：</p><img src="/2022/04/21/binary-bomb/2.2.png" class="" title="**phase_2** stack"><p>可以看到当前的栈里存的是之前输入的 <code>1 2 3 4 5 6</code>，这样看可能不是很直观，输入 <code>x/10wx $rsp</code> 显示 10 个、地址从 <code>rsp</code> 开始连续的长度为 4字节 的内存信息：</p><img src="/2022/04/21/binary-bomb/2.3.png" class="" title="**phase_2**"><p>这里就能直观的看到栈里存储的数据格式了，其中 <code>dword ptr [rsp]</code> 是第一个参数即 <code>0x00000001</code>，<code>dword ptr [rsp+4]</code> 是第二个参数 <code>0x00000002</code>，<code>dword ptr [rsp+8]</code> 是第三个参数 <code>0x00000003</code>，以此类推。而 <code>dword ptr [rsp]</code> 指的正是栈顶的 4字节，也即之前输入的第一个参数。它和 0 一比较，不相等，便跳到了 <code>explode_bomb</code> 函数。所以推出第一个参数应该为 0。</p></li><li><p>知道了第一个参数，可以选择重新运行，把输入参数的第一个改为 0，也可以先不管它，手动跳过 <code>explode_bomb</code> 函数继续运行。找到 <code>jne    phase_2+43</code> 下一条指令地址为 <code>0x400ecd</code>，然后执行 <code>set $rip=0x400ecd</code> 设置 <code>rip</code> 寄存器值为该地址，因为 <code>rip</code> (指令指针寄存器) 始终存的是下一条即将执行的指令地址：</p><img src="/2022/04/21/binary-bomb/2.4.png" class="" title="**phase_2**"><p>发现这一段与上一段代码类似，可知第二个参数应该为 1。</p></li><li><p>同样的方法跳过 <code>explode_bomb</code> 函数，从这里开始就有对参数进行计算的操作了，之前全是将参数和某个字符或数字比较，很容易很获得答案。继续一步步执行，发现这样一段：</p><img src="/2022/04/21/binary-bomb/2.5.png" class="" title="**phase_2**"><p>既然有条件跳转，就存在分支，这里 pwndbg 的输出默认只显示将会运行的指令，也就是说它预先执行了一下，帮你预测好了接下来几步的指令。为查看 pwndbg 省略掉的反汇编代码，执行 <code>disas</code> 输出当前函数反汇编，当前指令位置会有箭头标注：</p><img src="/2022/04/21/binary-bomb/2.6.png" class="" title="**phase_2**"><p>很明显 <code>cmp    DWORD PTR [rbx+0x8],eax</code> 的结果应该为相等，其中前者 <code>DWORD PTR [rbx+0x8]</code> 是第三个参数，<code>eax</code> 存的是前几行指令的结果，再看前几行指令：</p><img src="/2022/04/21/binary-bomb/2.7.png" class="" title="**phase_2**"><p>对 <code>rbp</code> 和 <code>rbx</code> 进行赋值，然后计算第一参数与第二参数的和并赋于 <code>eax</code>，所以 <code>eax</code> 的值应该为 1，即第三个参数应该为 1。<br>这里有一个小细节，因为 <code>rbp</code> 和 <code>rbx</code> 的旧值在这里被覆盖了，所以在函数开头有这样一句将 <code>rbp</code> 和 <code>rbx</code> 的旧值保存在栈里：</p><img src="/2022/04/21/binary-bomb/2.8.png" class="" title="**phase_2**"><p>并在函数返回前进行恢复：</p><img src="/2022/04/21/binary-bomb/2.9.png" class="" title="**phase_2**"><p>经典的保存现场和恢复现场操作，这在之后也会经常看到。</p></li><li><p>继续向下执行，发现有这样三行指令：</p><img src="/2022/04/21/binary-bomb/2.10.png" class="" title="**phase_2**"><p><code>rbp</code> 和 <code>rbx</code> 分别是之前赋的 <code>rsp+0x10</code> 和 <code>rsp</code>，即第五个参数和第一个参数的地址，对 <code>rbx</code> 加 4 即让 <code>rbx</code> 指向第下一个参数，然后比较一下看 <code>rbx</code> 是不是已经到了第五个参数，不是则跳到之前的 <code>0x400ee1 &lt;phase_2+56&gt;    mov    eax, dword ptr [rbx + 4]</code> 位置执行，否则退出循环。循环里执行的即是上一步的内容：检查第 x 个参数与第 x+1 个参数的和是否等于第 x+2 个参数，x 值为 1 到 5。至此，<strong>phase_2</strong> 已解决。</p></li></ol><h3 id="phase-3"><a href="#phase-3" class="headerlink" title="phase_3"></a><strong>phase_3</strong></h3><ol><li><p>到这里基本上已经上手了，不同于 <strong>phase_2</strong> 的数学计算，<strong>phase_3</strong> 的重点在于理清程序执行逻辑，找出关键代码。同样附上 <strong>phase_3</strong> 的反汇编：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line">Dump of assembler code for function phase_3:</span><br><span class="line">   0x0000000000400f15 &lt;+0&gt;:     sub    rsp,0x28</span><br><span class="line">   0x0000000000400f19 &lt;+4&gt;:     mov    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x0000000000400f22 &lt;+13&gt;:    mov    QWORD PTR [rsp+0x18],rax</span><br><span class="line">   0x0000000000400f27 &lt;+18&gt;:    xor    eax,eax</span><br><span class="line">   0x0000000000400f29 &lt;+20&gt;:    lea    r8,[rsp+0x14]</span><br><span class="line">   0x0000000000400f2e &lt;+25&gt;:    lea    rcx,[rsp+0xf]</span><br><span class="line">   0x0000000000400f33 &lt;+30&gt;:    lea    rdx,[rsp+0x10]</span><br><span class="line">   0x0000000000400f38 &lt;+35&gt;:    mov    esi,0x4024ee</span><br><span class="line">   0x0000000000400f3d &lt;+40&gt;:    call   0x400bb0 &lt;__isoc99_sscanf@plt&gt;</span><br><span class="line">   0x0000000000400f42 &lt;+45&gt;:    cmp    eax,0x2</span><br><span class="line">   0x0000000000400f45 &lt;+48&gt;:    jg     0x400f4c &lt;phase_3+55&gt;</span><br><span class="line">   0x0000000000400f47 &lt;+50&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400f4c &lt;+55&gt;:    cmp    DWORD PTR [rsp+0x10],0x7</span><br><span class="line">   0x0000000000400f51 &lt;+60&gt;:    ja     0x401053 &lt;phase_3+318&gt;</span><br><span class="line">   0x0000000000400f57 &lt;+66&gt;:    mov    eax,DWORD PTR [rsp+0x10]</span><br><span class="line">=&gt; 0x0000000000400f5b &lt;+70&gt;:    jmp    QWORD PTR [rax*8+0x402500]</span><br><span class="line">   0x0000000000400f62 &lt;+77&gt;:    mov    eax,0x69</span><br><span class="line">   0x0000000000400f67 &lt;+82&gt;:    cmp    DWORD PTR [rsp+0x14],0x33c</span><br><span class="line">   0x0000000000400f6f &lt;+90&gt;:    je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400f75 &lt;+96&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400f7a &lt;+101&gt;:   mov    eax,0x69</span><br><span class="line">   0x0000000000400f7f &lt;+106&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400f84 &lt;+111&gt;:   mov    eax,0x66</span><br><span class="line">   0x0000000000400f89 &lt;+116&gt;:   cmp    DWORD PTR [rsp+0x14],0x30b</span><br><span class="line">   0x0000000000400f91 &lt;+124&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400f97 &lt;+130&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400f9c &lt;+135&gt;:   mov    eax,0x66</span><br><span class="line">   0x0000000000400fa1 &lt;+140&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400fa6 &lt;+145&gt;:   mov    eax,0x71</span><br><span class="line">   0x0000000000400fab &lt;+150&gt;:   cmp    DWORD PTR [rsp+0x14],0x2d5</span><br><span class="line">   0x0000000000400fb3 &lt;+158&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400fb9 &lt;+164&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400fbe &lt;+169&gt;:   mov    eax,0x71</span><br><span class="line">   0x0000000000400fc3 &lt;+174&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400fc8 &lt;+179&gt;:   mov    eax,0x7a</span><br><span class="line">   0x0000000000400fcd &lt;+184&gt;:   cmp    DWORD PTR [rsp+0x14],0x235</span><br><span class="line">   0x0000000000400fd5 &lt;+192&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400fdb &lt;+198&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400fe0 &lt;+203&gt;:   mov    eax,0x7a</span><br><span class="line">   0x0000000000400fe5 &lt;+208&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400fe7 &lt;+210&gt;:   mov    eax,0x6c</span><br><span class="line">   0x0000000000400fec &lt;+215&gt;:   cmp    DWORD PTR [rsp+0x14],0x192</span><br><span class="line">   0x0000000000400ff4 &lt;+223&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000400ff6 &lt;+225&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000400ffb &lt;+230&gt;:   mov    eax,0x6c</span><br><span class="line">   0x0000000000401000 &lt;+235&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000401002 &lt;+237&gt;:   mov    eax,0x70</span><br><span class="line">   0x0000000000401007 &lt;+242&gt;:   cmp    DWORD PTR [rsp+0x14],0x1a6</span><br><span class="line">   0x000000000040100f &lt;+250&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000401011 &lt;+252&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000401016 &lt;+257&gt;:   mov    eax,0x70</span><br><span class="line">   0x000000000040101b &lt;+262&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x000000000040101d &lt;+264&gt;:   mov    eax,0x73</span><br><span class="line">   0x0000000000401022 &lt;+269&gt;:   cmp    DWORD PTR [rsp+0x14],0x325</span><br><span class="line">   0x000000000040102a &lt;+277&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x000000000040102c &lt;+279&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000401031 &lt;+284&gt;:   mov    eax,0x73</span><br><span class="line">   0x0000000000401036 &lt;+289&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000401038 &lt;+291&gt;:   mov    eax,0x6d</span><br><span class="line">   0x000000000040103d &lt;+296&gt;:   cmp    DWORD PTR [rsp+0x14],0x217</span><br><span class="line">   0x0000000000401045 &lt;+304&gt;:   je     0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000401047 &lt;+306&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x000000000040104c &lt;+311&gt;:   mov    eax,0x6d</span><br><span class="line">   0x0000000000401051 &lt;+316&gt;:   jmp    0x40105d &lt;phase_3+328&gt;</span><br><span class="line">   0x0000000000401053 &lt;+318&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000401058 &lt;+323&gt;:   mov    eax,0x79</span><br><span class="line">   0x000000000040105d &lt;+328&gt;:   cmp    al,BYTE PTR [rsp+0xf]</span><br><span class="line">   0x0000000000401061 &lt;+332&gt;:   je     0x401068 &lt;phase_3+339&gt;</span><br><span class="line">   0x0000000000401063 &lt;+334&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000401068 &lt;+339&gt;:   mov    rax,QWORD PTR [rsp+0x18]</span><br><span class="line">   0x000000000040106d &lt;+344&gt;:   xor    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x0000000000401076 &lt;+353&gt;:   je     0x40107d &lt;phase_3+360&gt;</span><br><span class="line">   0x0000000000401078 &lt;+355&gt;:   call   0x400b00 &lt;__stack_chk_fail@plt&gt;</span><br><span class="line">   0x000000000040107d &lt;+360&gt;:   add    rsp,0x28</span><br><span class="line">   0x0000000000401081 &lt;+364&gt;:   ret</span><br><span class="line">End of assembler dump.</span><br></pre></td></tr></table></figure><p>不像 <strong>phase_2</strong> 中有 <code>read_six_numbers</code>，<strong>phase_3</strong> 没法直接从反汇编中窥见输入的参数格式，那就暂时输 <code>mrorange</code> 好了，然后一步步执行，并观察 pwndbg 输出的 registers 信息，直到 <code>scanf</code> 函数：</p><img src="/2022/04/21/binary-bomb/3.1.png" class="" title="**phase_3**"><p>pwndbg 直接将传入 <code>scanf</code> 的参数展示出来，很容易能注意到 <code>‘%d %c %d‘</code> 就是 <strong>phase_3</strong> 正解的输入格式。有经验的话就可以直接 <code>x/s 0x4024ee</code> 查看输入格式了，因为 <code>call scanf</code> 的上一句指令即是用格式字符串地址赋值 <code>rsi</code> 寄存器，再作为参数传给 <code>scanf</code> 函数。</p></li><li><p>确定了输入格式，重新运行输入 <code>1 o 1</code> 开始进一步分析：</p><img src="/2022/04/21/binary-bomb/3.2.png" class="" title="**phase_3**"><p>代码在 <code>scanf</code> 之后判断了输入参数个数是否大于 2 个，又判断了第一个参数是否小于等于 7，都通过则跳转到 <code>第一个参数 * 8 + 0x402500</code> 处，先不管这是哪，继续往下执行。注意到这句 <code>cmp    dword ptr [rsp + 0x14], 0x30b</code>，看似是在判断一个参数是否为 <code>0x30b</code>，但是哪一个参数呢？之前输入的 <code>1 o 1</code> 不是很合适，因为第一个参数和第三个参数一样，在栈里就看不出哪个位置对应哪个参数，我这里又将输入改成了 <code>1 o 2</code>，再看一下栈区：</p><img src="/2022/04/21/binary-bomb/3.3.png" class="" title="**phase_3**"><p><code>dword ptr [rsp + 0x14]</code> 的值是 2，所以 <code>dword ptr [rsp + 0x14]</code> 对应的是第三个参数。因此，第三个参数应该为 <code>0x30b</code>，但注意这个数是十六进制，而输入格式是 <code>%d</code> 即十进制，所以真正的第三参数是 <code>779</code>。</p></li><li><p>继续向下，跳转到了 <code>phase_3+3280x40105d &lt;phase_3+328&gt;    cmp    al, byte ptr [rsp + 0xf]</code>，<code>byte ptr [rsp + 0xf]</code> 的值可以在栈里看到是 <code>0x6f</code>，也即第二个参数 <code>o</code> 的 ASCII 码，可以输入 <code>x/bc $rsp+0xf</code> 验证。将第二个参数和 <code>al</code> 即 <code>rax</code> 的最后一字节比较，那现在 <code>rax</code> 里存的又是什么呢？可以注意到是在比较第三个参数前有不显眼的一行 <code>mov    eax, 0x66</code>，<code>0x66</code> 对应的字母是 <code>f</code>。至此，可以确定一组正确的答案为 <code>1 f 779</code>。</p></li></ol><ul><li>Tip: 在 Python 命令行中，输入十六进制数会输出它的十进制；使用 <code>print(&#39;&#123;:c&#125;&#39;.format(0x66))</code> 可以输出 <code>0x66</code> 对应的 ASCII 字符。<img src="/2022/04/21/binary-bomb/3.4.png" class="" title="Tip"></li></ul><ol start="4"><li>之所以说得到的只是其中一组正确答案，是因为第一个参数的值会决定跳转到的 <code>第一个参数 * 8 + 0x402500</code> 位置，而第一个参数值又小于等于 7，不能为负，所以对应不同的第一参数，一共会有八个不同的答案。得出答案的步骤完全一样，就不再赘述。</li></ol><h3 id="phase-4"><a href="#phase-4" class="headerlink" title="phase_4"></a><strong>phase_4</strong></h3><ol><li><p>先附上 <strong>phase_4</strong> 和内部函数 <strong>func4</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></pre></td><td class="code"><pre><span class="line">Dump of assembler code for function phase_4:</span><br><span class="line">   0x00000000004010b5 &lt;+0&gt;:     sub    rsp,0x18</span><br><span class="line">   0x00000000004010b9 &lt;+4&gt;:     mov    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x00000000004010c2 &lt;+13&gt;:    mov    QWORD PTR [rsp+0x8],rax</span><br><span class="line">   0x00000000004010c7 &lt;+18&gt;:    xor    eax,eax</span><br><span class="line">   0x00000000004010c9 &lt;+20&gt;:    lea    rcx,[rsp+0x4]</span><br><span class="line">   0x00000000004010ce &lt;+25&gt;:    mov    rdx,rsp</span><br><span class="line">   0x00000000004010d1 &lt;+28&gt;:    mov    esi,0x40268f</span><br><span class="line">   0x00000000004010d6 &lt;+33&gt;:    call   0x400bb0 &lt;__isoc99_sscanf@plt&gt;</span><br><span class="line">   0x00000000004010db &lt;+38&gt;:    cmp    eax,0x2</span><br><span class="line">   0x00000000004010de &lt;+41&gt;:    jne    0x4010e6 &lt;phase_4+49&gt;</span><br><span class="line">   0x00000000004010e0 &lt;+43&gt;:    cmp    DWORD PTR [rsp],0xe</span><br><span class="line">   0x00000000004010e4 &lt;+47&gt;:    jbe    0x4010eb &lt;phase_4+54&gt;</span><br><span class="line">   0x00000000004010e6 &lt;+49&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x00000000004010eb &lt;+54&gt;:    mov    edx,0xe</span><br><span class="line">   0x00000000004010f0 &lt;+59&gt;:    mov    esi,0x0</span><br><span class="line">   0x00000000004010f5 &lt;+64&gt;:    mov    edi,DWORD PTR [rsp]</span><br><span class="line">   0x00000000004010f8 &lt;+67&gt;:    call   0x401082 &lt;func4&gt;</span><br><span class="line">   0x00000000004010fd &lt;+72&gt;:    cmp    eax,0x13</span><br><span class="line">   0x0000000000401100 &lt;+75&gt;:    jne    0x401109 &lt;phase_4+84&gt;</span><br><span class="line">   0x0000000000401102 &lt;+77&gt;:    cmp    DWORD PTR [rsp+0x4],0x13</span><br><span class="line">   0x0000000000401107 &lt;+82&gt;:    je     0x40110e &lt;phase_4+89&gt;</span><br><span class="line">   0x0000000000401109 &lt;+84&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x000000000040110e &lt;+89&gt;:    mov    rax,QWORD PTR [rsp+0x8]</span><br><span class="line">   0x0000000000401113 &lt;+94&gt;:    xor    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x000000000040111c &lt;+103&gt;:   je     0x401123 &lt;phase_4+110&gt;</span><br><span class="line">   0x000000000040111e &lt;+105&gt;:   call   0x400b00 &lt;__stack_chk_fail@plt&gt;</span><br><span class="line">   0x0000000000401123 &lt;+110&gt;:   add    rsp,0x18</span><br><span class="line">   0x0000000000401127 &lt;+114&gt;:   ret    </span><br><span class="line">End of assembler dump.</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">Dump of assembler code for function func4:</span><br><span class="line">   0x0000000000401082 &lt;+0&gt;:     push   rbx</span><br><span class="line">   0x0000000000401083 &lt;+1&gt;:     mov    eax,edx</span><br><span class="line">   0x0000000000401085 &lt;+3&gt;:     sub    eax,esi</span><br><span class="line">   0x0000000000401087 &lt;+5&gt;:     mov    ebx,eax</span><br><span class="line">   0x0000000000401089 &lt;+7&gt;:     shr    ebx,0x1f</span><br><span class="line">   0x000000000040108c &lt;+10&gt;:    add    eax,ebx</span><br><span class="line">   0x000000000040108e &lt;+12&gt;:    sar    eax,1</span><br><span class="line">   0x0000000000401090 &lt;+14&gt;:    lea    ebx,[rax+rsi*1]</span><br><span class="line">   0x0000000000401093 &lt;+17&gt;:    cmp    ebx,edi</span><br><span class="line">   0x0000000000401095 &lt;+19&gt;:    jle    0x4010a3 &lt;func4+33&gt;</span><br><span class="line">   0x0000000000401097 &lt;+21&gt;:    lea    edx,[rbx-0x1]</span><br><span class="line">   0x000000000040109a &lt;+24&gt;:    call   0x401082 &lt;func4&gt;</span><br><span class="line">   0x000000000040109f &lt;+29&gt;:    add    eax,ebx</span><br><span class="line">   0x00000000004010a1 &lt;+31&gt;:    jmp    0x4010b3 &lt;func4+49&gt;</span><br><span class="line">   0x00000000004010a3 &lt;+33&gt;:    mov    eax,ebx</span><br><span class="line">   0x00000000004010a5 &lt;+35&gt;:    cmp    ebx,edi</span><br><span class="line">   0x00000000004010a7 &lt;+37&gt;:    jge    0x4010b3 &lt;func4+49&gt;</span><br><span class="line">   0x00000000004010a9 &lt;+39&gt;:    lea    esi,[rbx+0x1]</span><br><span class="line">   0x00000000004010ac &lt;+42&gt;:    call   0x401082 &lt;func4&gt;</span><br><span class="line">   0x00000000004010b1 &lt;+47&gt;:    add    eax,ebx</span><br><span class="line">   0x00000000004010b3 &lt;+49&gt;:    pop    rbx</span><br><span class="line">   0x00000000004010b4 &lt;+50&gt;:    ret    </span><br><span class="line">End of assembler dump.</span><br></pre></td></tr></table></figure><p>首先仍然是确定输入的格式。找到 <code>scanf</code> 函数，从其上一行中得到格式字符串地址，执行 <code>x/s 0x40268f</code>，得到格式字符串为 <code>&quot;%d %d&quot;</code>。</p></li><li><p><strong>phase_4</strong> 的主函数部分的逻辑很简单，有了前三个 phase 的热身，应该可以轻松从反汇编中看出其逻辑：先判断输入参数个数是否为 2，再判断第一个参数是否小于 14，通过则调用函数 <code>func4</code>，并判断返回值是否为 19，最后判断初始输入的第二个参数是否为 19，均为是则顺利过关。</p></li><li><p>光从第二步就能得知第一个参数是小于等于 14 的自然数，第二个参数是 19，通过暴力枚举 15 个可能的输入就能得到答案，甚至不用分析 <code>func4</code> 函数。但是前提是 <code>func4</code> 函数里没有改动最初输入的参数，也没有调用 <code>explode_bomb</code> 函数。前者在这种级别的题应该不会有，后者通过反汇编发现也满足。赶时间的话就可以开始试答案了，这里不再分析 <code>func4</code> 函数，附上伪代码（仅供参考，源自 IDA，其中参数 a1 &#x3D; 输入的第一参数, a2 &#x3D; 0, a3 &#x3D; 14）:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 <span class="title">func4</span><span class="params">(__int64 a1, __int64 a2, <span class="type">int</span> a3)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="type">int</span> v3; <span class="comment">// ebx</span></span><br><span class="line">  __int64 result; <span class="comment">// rax</span></span><br><span class="line"></span><br><span class="line">  v3 = (a3 - (<span class="type">int</span>)a2) / <span class="number">2</span> + a2;</span><br><span class="line">  <span class="keyword">if</span> ( v3 &gt; (<span class="type">int</span>)a1 )</span><br><span class="line">    <span class="keyword">return</span> v3 + (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">func4</span>(a1, a2);</span><br><span class="line">  result = (<span class="type">unsigned</span> <span class="type">int</span>)v3;</span><br><span class="line">  <span class="keyword">if</span> ( v3 &lt; (<span class="type">int</span>)a1 )</span><br><span class="line">    result = v3 + (<span class="type">unsigned</span> <span class="type">int</span>)<span class="built_in">func4</span>(a1, (<span class="type">unsigned</span> <span class="type">int</span>)(v3 + <span class="number">1</span>));</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h3 id="phase-5"><a href="#phase-5" class="headerlink" title="phase_5"></a><strong>phase_5</strong></h3><ol><li><p>先附上 <strong>phase_5</strong> 的反汇编:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">Dump of assembler code for function phase_5:</span><br><span class="line">=&gt; 0x0000000000401128 &lt;+0&gt;:     sub    rsp,0x18</span><br><span class="line">   0x000000000040112c &lt;+4&gt;:     mov    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x0000000000401135 &lt;+13&gt;:    mov    QWORD PTR [rsp+0x8],rax</span><br><span class="line">   0x000000000040113a &lt;+18&gt;:    xor    eax,eax</span><br><span class="line">   0x000000000040113c &lt;+20&gt;:    lea    rcx,[rsp+0x4]</span><br><span class="line">   0x0000000000401141 &lt;+25&gt;:    mov    rdx,rsp</span><br><span class="line">   0x0000000000401144 &lt;+28&gt;:    mov    esi,0x40268f</span><br><span class="line">   0x0000000000401149 &lt;+33&gt;:    call   0x400bb0 &lt;__isoc99_sscanf@plt&gt;</span><br><span class="line">   0x000000000040114e &lt;+38&gt;:    cmp    eax,0x1</span><br><span class="line">   0x0000000000401151 &lt;+41&gt;:    jg     0x401158 &lt;phase_5+48&gt;</span><br><span class="line">   0x0000000000401153 &lt;+43&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000401158 &lt;+48&gt;:    mov    eax,DWORD PTR [rsp]</span><br><span class="line">   0x000000000040115b &lt;+51&gt;:    and    eax,0xf</span><br><span class="line">   0x000000000040115e &lt;+54&gt;:    mov    DWORD PTR [rsp],eax</span><br><span class="line">   0x0000000000401161 &lt;+57&gt;:    cmp    eax,0xf</span><br><span class="line">   0x0000000000401164 &lt;+60&gt;:    je     0x401195 &lt;phase_5+109&gt;</span><br><span class="line">   0x0000000000401166 &lt;+62&gt;:    mov    ecx,0x0</span><br><span class="line">   0x000000000040116b &lt;+67&gt;:    mov    edx,0x0</span><br><span class="line">   0x0000000000401170 &lt;+72&gt;:    add    edx,0x1</span><br><span class="line">   0x0000000000401173 &lt;+75&gt;:    cdqe   </span><br><span class="line">   0x0000000000401175 &lt;+77&gt;:    mov    eax,DWORD PTR [rax*4+0x402540]</span><br><span class="line">   0x000000000040117c &lt;+84&gt;:    add    ecx,eax</span><br><span class="line">   0x000000000040117e &lt;+86&gt;:    cmp    eax,0xf</span><br><span class="line">   0x0000000000401181 &lt;+89&gt;:    jne    0x401170 &lt;phase_5+72&gt;</span><br><span class="line">   0x0000000000401183 &lt;+91&gt;:    mov    DWORD PTR [rsp],0xf</span><br><span class="line">   0x000000000040118a &lt;+98&gt;:    cmp    edx,0xf</span><br><span class="line">   0x000000000040118d &lt;+101&gt;:   jne    0x401195 &lt;phase_5+109&gt;</span><br><span class="line">   0x000000000040118f &lt;+103&gt;:   cmp    ecx,DWORD PTR [rsp+0x4]</span><br><span class="line">   0x0000000000401193 &lt;+107&gt;:   je     0x40119a &lt;phase_5+114&gt;</span><br><span class="line">   0x0000000000401195 &lt;+109&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x000000000040119a &lt;+114&gt;:   mov    rax,QWORD PTR [rsp+0x8]</span><br><span class="line">   0x000000000040119f &lt;+119&gt;:   xor    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x00000000004011a8 &lt;+128&gt;:   je     0x4011af &lt;phase_5+135&gt;</span><br><span class="line">   0x00000000004011aa &lt;+130&gt;:   call   0x400b00 &lt;__stack_chk_fail@plt&gt;</span><br><span class="line">   0x00000000004011af &lt;+135&gt;:   add    rsp,0x18</span><br><span class="line">   0x00000000004011b3 &lt;+139&gt;:   ret    </span><br><span class="line">End of assembler dump.</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>同样，通过观察反汇编得到格式字符串地址为 <code>0x40268f</code>，执行 <code>x/s 0x40268f</code> 得到格式字符串为 <code>&quot;%d %d&quot;</code>，这里输入 <code>5 10</code> 后继续分析。</p></li><li><p>从 <code>scanf</code> 函数下一行开始一步步执行：判断输入参数个数是否大于 1，是则继续；取第一参数的后四位，并覆盖原来的参数，且第一参数的后四位不全为 1；这时第一参数的值转化成为 0～14 之间一个数；从 <code>0x0000000000401170 &lt;+72&gt;:    add    edx,0x1</code> 开始进入循环：</p><img src="/2022/04/21/binary-bomb/5.1.png" class="" title="**phase_5**"></li></ol><ul><li>其中 <code>cdqe</code> 是使用 <code>eax</code> 的最高位拓展 <code>rax</code> 高 32 位的所有位。</li></ul><ol start="2"><li><p>注意到这一行 <code>mov    eax, dword ptr [rax*4 + 0x402540]</code>，不禁好奇地址 <code>0x402540</code> 里存了什么，执行 <code>x/20wx 0x402540</code> 查看内存：</p><img src="/2022/04/21/binary-bomb/5.2.png" class="" title="**phase_5**"><p>果然是一个数组，每个元素长度为 4字节，所以 <code>dword ptr [rax*4 + 0x402540]</code> 值为该数组的第 <code>rax</code> 个元素。再来看这段循环，它不断的将数组的第 <code>rax</code> 个元素再次赋予 <code>rax</code>，同时将 <code>rax</code> 的值累加到 <code>ecx</code> 寄存器。<code>edx</code> 记录循环的次数，直到 <code>rax</code> 值为 <code>0xf</code> 时退出循环：</p><img src="/2022/04/21/binary-bomb/5.3.png" class="" title="**phase_5**"></li><li><p>退出循环后，判断 <code>edx</code> 的值是否为 <code>0xf</code>，然后判断 <code>ecx</code> 和第二参数是否相等，均为是则过关。<code>edx</code> 值为 <code>0xf</code> 即循环次数为 15 次，数组长度为 16，所以很有可能 <code>ecx</code> 最后就是数组所有元素的和（除下标为 <code>0xf</code> 的元素）。再观察数组，发现通过不断的将数组下标对应元素作为新的数组下标，可以遍历完整个数组，因此在 15 次循环中必然完成了数组的一次遍历，<code>ecx</code> 值即所有元素和（除下标为 <code>0xf</code> 的元素）为 <code>115</code>。因此第二个参数应该为 <code>115</code>。</p></li><li><p>那么第一个参数呢？第一个参数是 <code>eax</code> 的初值，也就是第一个下标，然后需要让 <code>eax</code> 遍历数组的时候正好在最后才被赋值 <code>0xf</code>，并退出循环。可以暴力枚举，也可以用逆推的方法：最后 <code>eax</code> 值为 <code>0xf</code>，那么 <code>eax</code> 前一个值为元素 <code>0xf</code> 的下标 <code>0x6</code>，再前一个值为元素 <code>0x6</code> 的下标 <code>0xe</code>，以此类推，得到最初下标应该为 <code>0x5</code>。或者直接正推，下标为 <code>0xf</code> 的元素 <code>0x5</code> 即为所求值。故第一个参数应该为 <code>0x5</code>。</p></li></ol><h3 id="phase-6"><a href="#phase-6" class="headerlink" title="phase_6"></a>phase_6</h3><ol><li><p>先附上 <strong>phase_6</strong> 的反汇编：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line">Dump of assembler code for function phase_6:</span><br><span class="line">   0x00000000004011b4 &lt;+0&gt;:     push   r14</span><br><span class="line">   0x00000000004011b6 &lt;+2&gt;:     push   r13</span><br><span class="line">   0x00000000004011b8 &lt;+4&gt;:     push   r12</span><br><span class="line">   0x00000000004011ba &lt;+6&gt;:     push   rbp</span><br><span class="line">   0x00000000004011bb &lt;+7&gt;:     push   rbx</span><br><span class="line">   0x00000000004011bc &lt;+8&gt;:     sub    rsp,0x60</span><br><span class="line">   0x00000000004011c0 &lt;+12&gt;:    mov    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x00000000004011c9 &lt;+21&gt;:    mov    QWORD PTR [rsp+0x58],rax</span><br><span class="line">   0x00000000004011ce &lt;+26&gt;:    xor    eax,eax</span><br><span class="line">   0x00000000004011d0 &lt;+28&gt;:    mov    rsi,rsp</span><br><span class="line">   0x00000000004011d3 &lt;+31&gt;:    call   0x40152b &lt;read_six_numbers&gt;</span><br><span class="line">   0x00000000004011d8 &lt;+36&gt;:    mov    r12,rsp</span><br><span class="line">   0x00000000004011db &lt;+39&gt;:    mov    r13,rsp</span><br><span class="line">   0x00000000004011de &lt;+42&gt;:    mov    r14d,0x0</span><br><span class="line">   0x00000000004011e4 &lt;+48&gt;:    mov    rbp,r13</span><br><span class="line">   0x00000000004011e7 &lt;+51&gt;:    mov    eax,DWORD PTR [r13+0x0]</span><br><span class="line">   0x00000000004011eb &lt;+55&gt;:    sub    eax,0x1</span><br><span class="line">   0x00000000004011ee &lt;+58&gt;:    cmp    eax,0x5</span><br><span class="line">   0x00000000004011f1 &lt;+61&gt;:    jbe    0x4011f8 &lt;phase_6+68&gt;</span><br><span class="line">   0x00000000004011f3 &lt;+63&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x00000000004011f8 &lt;+68&gt;:    add    r14d,0x1</span><br><span class="line">   0x00000000004011fc &lt;+72&gt;:    cmp    r14d,0x6</span><br><span class="line">   0x0000000000401200 &lt;+76&gt;:    je     0x401223 &lt;phase_6+111&gt;</span><br><span class="line">   0x0000000000401202 &lt;+78&gt;:    mov    ebx,r14d</span><br><span class="line">   0x0000000000401205 &lt;+81&gt;:    movsxd rax,ebx</span><br><span class="line">   0x0000000000401208 &lt;+84&gt;:    mov    eax,DWORD PTR [rsp+rax*4]</span><br><span class="line">   0x000000000040120b &lt;+87&gt;:    cmp    DWORD PTR [rbp+0x0],eax</span><br><span class="line">   0x000000000040120e &lt;+90&gt;:    jne    0x401215 &lt;phase_6+97&gt;</span><br><span class="line">   0x0000000000401210 &lt;+92&gt;:    call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x0000000000401215 &lt;+97&gt;:    add    ebx,0x1</span><br><span class="line">   0x0000000000401218 &lt;+100&gt;:   cmp    ebx,0x5</span><br><span class="line">   0x000000000040121b &lt;+103&gt;:   jle    0x401205 &lt;phase_6+81&gt;</span><br><span class="line">   0x000000000040121d &lt;+105&gt;:   add    r13,0x4</span><br><span class="line">   0x0000000000401221 &lt;+109&gt;:   jmp    0x4011e4 &lt;phase_6+48&gt;</span><br><span class="line">   0x0000000000401223 &lt;+111&gt;:   lea    rcx,[rsp+0x18]</span><br><span class="line">   0x0000000000401228 &lt;+116&gt;:   mov    edx,0x7</span><br><span class="line">   0x000000000040122d &lt;+121&gt;:   mov    eax,edx</span><br><span class="line">   0x000000000040122f &lt;+123&gt;:   sub    eax,DWORD PTR [r12]</span><br><span class="line">   0x0000000000401233 &lt;+127&gt;:   mov    DWORD PTR [r12],eax</span><br><span class="line">   0x0000000000401237 &lt;+131&gt;:   add    r12,0x4</span><br><span class="line">   0x000000000040123b &lt;+135&gt;:   cmp    rcx,r12</span><br><span class="line">   0x000000000040123e &lt;+138&gt;:   jne    0x40122d &lt;phase_6+121&gt;</span><br><span class="line">   0x0000000000401240 &lt;+140&gt;:   mov    esi,0x0</span><br><span class="line">   0x0000000000401245 &lt;+145&gt;:   jmp    0x401261 &lt;phase_6+173&gt;</span><br><span class="line">   0x0000000000401247 &lt;+147&gt;:   mov    rdx,QWORD PTR [rdx+0x8]</span><br><span class="line">   0x000000000040124b &lt;+151&gt;:   add    eax,0x1</span><br><span class="line">   0x000000000040124e &lt;+154&gt;:   cmp    eax,ecx</span><br><span class="line">   0x0000000000401250 &lt;+156&gt;:   jne    0x401247 &lt;phase_6+147&gt;</span><br><span class="line">   0x0000000000401252 &lt;+158&gt;:   mov    QWORD PTR [rsp+rsi*2+0x20],rdx</span><br><span class="line">   0x0000000000401257 &lt;+163&gt;:   add    rsi,0x4</span><br><span class="line">   0x000000000040125b &lt;+167&gt;:   cmp    rsi,0x18</span><br><span class="line">   0x000000000040125f &lt;+171&gt;:   je     0x401275 &lt;phase_6+193&gt;</span><br><span class="line">   0x0000000000401261 &lt;+173&gt;:   mov    ecx,DWORD PTR [rsp+rsi*1]</span><br><span class="line">   0x0000000000401264 &lt;+176&gt;:   mov    eax,0x1</span><br><span class="line">   0x0000000000401269 &lt;+181&gt;:   mov    edx,0x6042f0</span><br><span class="line">   0x000000000040126e &lt;+186&gt;:   cmp    ecx,0x1</span><br><span class="line">   0x0000000000401271 &lt;+189&gt;:   jg     0x401247 &lt;phase_6+147&gt;</span><br><span class="line">   0x0000000000401273 &lt;+191&gt;:   jmp    0x401252 &lt;phase_6+158&gt;</span><br><span class="line">   0x0000000000401275 &lt;+193&gt;:   mov    rbx,QWORD PTR [rsp+0x20]</span><br><span class="line">   0x000000000040127a &lt;+198&gt;:   lea    rax,[rsp+0x20]</span><br><span class="line">   0x000000000040127f &lt;+203&gt;:   lea    rsi,[rsp+0x48]</span><br><span class="line">   0x0000000000401284 &lt;+208&gt;:   mov    rcx,rbx</span><br><span class="line">   0x0000000000401287 &lt;+211&gt;:   mov    rdx,QWORD PTR [rax+0x8]</span><br><span class="line">   0x000000000040128b &lt;+215&gt;:   mov    QWORD PTR [rcx+0x8],rdx</span><br><span class="line">   0x000000000040128f &lt;+219&gt;:   add    rax,0x8</span><br><span class="line">   0x0000000000401293 &lt;+223&gt;:   mov    rcx,rdx</span><br><span class="line">   0x0000000000401296 &lt;+226&gt;:   cmp    rsi,rax</span><br><span class="line">   0x0000000000401299 &lt;+229&gt;:   jne    0x401287 &lt;phase_6+211&gt;</span><br><span class="line">   0x000000000040129b &lt;+231&gt;:   mov    QWORD PTR [rdx+0x8],0x0</span><br><span class="line">   0x00000000004012a3 &lt;+239&gt;:   mov    ebp,0x5</span><br><span class="line">   0x00000000004012a8 &lt;+244&gt;:   mov    rax,QWORD PTR [rbx+0x8]</span><br><span class="line">   0x00000000004012ac &lt;+248&gt;:   mov    eax,DWORD PTR [rax]</span><br><span class="line">   0x00000000004012ae &lt;+250&gt;:   cmp    DWORD PTR [rbx],eax</span><br><span class="line">   0x00000000004012b0 &lt;+252&gt;:   jge    0x4012b7 &lt;phase_6+259&gt;</span><br><span class="line">   0x00000000004012b2 &lt;+254&gt;:   call   0x401509 &lt;explode_bomb&gt;</span><br><span class="line">   0x00000000004012b7 &lt;+259&gt;:   mov    rbx,QWORD PTR [rbx+0x8]</span><br><span class="line">   0x00000000004012bb &lt;+263&gt;:   sub    ebp,0x1</span><br><span class="line">   0x00000000004012be &lt;+266&gt;:   jne    0x4012a8 &lt;phase_6+244&gt;</span><br><span class="line">   0x00000000004012c0 &lt;+268&gt;:   mov    rax,QWORD PTR [rsp+0x58]</span><br><span class="line">   0x00000000004012c5 &lt;+273&gt;:   xor    rax,QWORD PTR fs:0x28</span><br><span class="line">   0x00000000004012ce &lt;+282&gt;:   je     0x4012d5 &lt;phase_6+289&gt;</span><br><span class="line">   0x00000000004012d0 &lt;+284&gt;:   call   0x400b00 &lt;__stack_chk_fail@plt&gt;</span><br><span class="line">   0x00000000004012d5 &lt;+289&gt;:   add    rsp,0x60</span><br><span class="line">   0x00000000004012d9 &lt;+293&gt;:   pop    rbx</span><br><span class="line">   0x00000000004012da &lt;+294&gt;:   pop    rbp</span><br><span class="line">   0x00000000004012db &lt;+295&gt;:   pop    r12</span><br><span class="line">   0x00000000004012dd &lt;+297&gt;:   pop    r13</span><br><span class="line">   0x00000000004012df &lt;+299&gt;:   pop    r14</span><br><span class="line">   0x00000000004012e1 &lt;+301&gt;:   ret    </span><br><span class="line">End of assembler dump.</span><br></pre></td></tr></table></figure><p>作为最后一个 phase，上来就给了个下马威，这反汇编在 6 个 phase 中最长，逻辑也是最复杂的。看一下反汇编，注意到 <strong>phase_6</strong> 调用了 <code>read_six_numbers</code> 函数，所以输入格式是和 <strong>phase_2</strong> 相同的六个数字。这里输入 <code>1 2 3 4 5 6</code> 继续分析。</p></li><li><p>直接看不容易看出程序的规律，那就从 <code>read_six_numbers</code> 函数下一行开始一步步运行：</p><img src="/2022/04/21/binary-bomb/6.1.png" class="" title="**phase_6**"><p>上面这第一段代码主要判断第一个参数是否小于 6，再往下执行：</p><img src="/2022/04/21/binary-bomb/6.2.png" class="" title="**phase_6**"><p>从这段可基本判断现在位于一个循环中，<code>r14d</code> 中存循环变量，且这个循环要执行 6 次，继续向下执行：</p><img src="/2022/04/21/binary-bomb/6.3.png" class="" title="**phase_6**"><p>这段将当前第 <code>ebx</code> 个参数即第二个参数赋予 <code>eax</code>，并判断和第一个参数是否不相等，是则继续，接下来似乎是这个循环的最后一段：</p><img src="/2022/04/21/binary-bomb/6.4.png" class="" title="**phase_6**"><p>出现了另一个循环变量 <code>ebx</code>，且其控制的循环要执行 5 次。</p></li><li><p>再往下，程序跳转到了 <code>0x401205 &lt;phase_6+81&gt;     movsxd rax, ebx</code>，从之前的分析看，这一段是判断第 <code>ebx</code> 个参数是否和第一个参数不相等，所以 <code>ebx</code> 控制的循环功能是判断 6 个参数中其它 5 个参数是否和第 1 个参数相等。</p></li><li><p>继续运行到 <code>ebx</code> 控制的循环结束：</p><img src="/2022/04/21/binary-bomb/6.5.png" class="" title="**phase_6**"><p>这里对 <code>r13</code> 加 4 后直接一个大跳转，继续跟进，发现继续执行的是第二步分析的判断参数是否小于 6，只不过这次判断的是第二个参数。分析到这，基本可以确定，以上这一整段是个双重循环，用于判断输入的六个数是不是为 1～6，且互不相等。</p></li><li><p>检验完输入后就快进入关键逻辑了，循环结束继续向下执行：</p><img src="/2022/04/21/binary-bomb/6.6.png" class="" title="**phase_6**"><p>这又是一个小循环，但比起之前那个要简单得多。其功能是对每个参数 x 进行处理，使每个参数 x 都变为 7-x，比如第一个参数为 1，处理后变成 6。</p></li><li><p>继续向下执行，程序跳转到了 <code>0x401261 &lt;phase_6+173&gt;    mov    ecx, dword ptr [rsp + rsi]</code>：</p><img src="/2022/04/21/binary-bomb/6.7.png" class="" title="**phase_6**"><p>这里开始又是一个循环，注意到出现了 <code>node1 &lt;0x6042f0&gt;</code>，不防看一眼里面都有什么，执行 <code>x/25wx 0x6042f0</code>：</p><img src="/2022/04/21/binary-bomb/6.8.png" class="" title="**phase_6**"><p>很明显这是一个单链表，<code>node1</code> 指向 <code>node2</code>，<code>node2</code> 指向 <code>node3</code>，依次类推。继续向下执行：</p><img src="/2022/04/21/binary-bomb/6.9.png" class="" title="**phase_6**"><p>令当前第 x 个参数为 a(x)，x 为 1～6。结合上一个片段看，可以判断这一段是遍历到第 a(1) 个节点，将其地址存入 <code>rsp + rsi*2 + 0x20</code> 位置，最后更改循环变量 <code>rsi</code>，判断是否要退出循环。</p></li><li><p>上一步在 <code>rsi</code> 控制下一共执行了六次，功能是将第 a(x) 个节点放入 <code>rsp + x*4*2 + 0x20</code> 位置，结果如下图：</p><img src="/2022/04/21/binary-bomb/6.10.png" class="" title="**phase_6**"><p>继续向下执行，又是一个小循环：</p><img src="/2022/04/21/binary-bomb/6.11.png" class="" title="**phase_6**"><p>令节点 n 的下一节点指针为 flink(n)。这个小循环的功能为将节点 a(x) 的 flink(a(x)) 置为节点 a(x+1) 的地址。比如当前参数为 <code>6 5 4 3 2 1</code>，则循环处理后六个节点就会变成 <code>node6 -&gt; node5 -&gt; node4 -&gt; node3 -&gt; node2 -&gt; node1</code>。</p></li><li><p>退出循环后继续向下执行：</p><img src="/2022/04/21/binary-bomb/6.12.png" class="" title="**phase_6**"><p>这段先将最末节点的 flink 置 0，然后比较链表中第一个节点的值和第二个节点的值，如果前者大于后者则跳转：</p><img src="/2022/04/21/binary-bomb/6.13.png" class="" title="**phase_6**"><p>这里让 <code>rbx</code> 指向下一个节点，然后循环变量 <code>ebp</code> 减 1，继续上一步，即比较 <code>rbx</code> 指向的当前节点和下一节点的值，前者大于后者则跳转，否则 BOOM！</p></li><li><p>上一步循环顺利退出即可过关。前提是当前链表中前一个节点中的数据始终大于后一个节点的数据，根据第六步中的节点信息，可以知道节点最后的顺序应该为 <code>node2 -&gt; node5 -&gt; node4 -&gt; node3 -&gt; node6 -&gt; node1</code>。所以经第五步处理后的参数应该为 <code>2 5 4 3 6 1</code>。即最初输入的参数应该为 <code>5 2 3 4 1 6</code>。</p></li></ol><hr><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>总的来说，这个作业难度不高，代码用到的都是最基本的逻辑和计算。但想要顺利的解出答案，除了要对 <strong>gdb</strong> 的操作和命令熟练外，还要对程序的基本运行原理和相关汇编指令有一定程度的掌握。</p><p>我认为这 6 个 Phase 中唯一棘手的是最后一个，几乎全是循环逻辑，甚至是双重循环，对这种逻辑的处理没太多经验的话很容易望而却步，哪怕是硬着头皮上，也会身心俱疲。但总之，程序的运行逻辑无非几种：顺序、分支、循环。基本逻辑搞定了，分析解决再复杂的逻辑也不过是时间问题。</p><p>当然，问题通常都不会只有一种解法，比如 <strong>phase_4</strong> 可以还能配合暴力枚举找出答案，节省大量时间。灵活运用工具，善于思考，尝试用不同的角度去审视难题，可以让思维更加开阔。</p><p>该篇是本博客第一个技术长篇，或许还有许多表述不周的地方，欢迎在下方评论，我们一起，变得更强！</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Welcome Orangeの小窝!!!</title>
      <link>https://qspidy.github.io/2022/04/12/firstpost/</link>
      <description>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="]]>
      </description>
      <author>MrOrange</author>
      <category domain="https://qspidy.github.io/tags/Greetings/">Greetings</category>
      <pubDate>Tue, 12 Apr 2022 08:52:07 GMT</pubDate>
      <content:encoded>
        <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script><p>Welcome to <a href="https://orange.wastaken.xyz/">Orangeの小窝</a>!!!</p><p>This is my first blog in the site, mainly for testing and will be removed when the site is fully built.</p><p>If you are new here and like this blog site, feel free to fav the website and leave your opinions of the site below as comments, anyway, it’s free, and you can always change your mind.</p><p>Enjoy the <a href="https://orange.wastaken.xyz/">Orangeの小窝</a>!!!</p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
