<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Gossip on Azuk's Blog</title><link>https://blog.sigsegv.top/tags/gossip/</link><description>Recent content in Gossip on Azuk's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Sat, 09 May 2026 22:29:22 +0800</lastBuildDate><atom:link href="https://blog.sigsegv.top/tags/gossip/index.xml" rel="self" type="application/rss+xml"/><item><title>Docker sshd 镜像</title><link>https://blog.sigsegv.top/posts/docker-sshd/</link><pubDate>Sat, 09 May 2026 22:29:22 +0800</pubDate><guid>https://blog.sigsegv.top/posts/docker-sshd/</guid><description>&lt;p>Ubuntu 26.04 基础镜像：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Dockerfile" data-lang="Dockerfile">&lt;span class="line">&lt;span class="cl">&lt;span class="k">FROM&lt;/span>&lt;span class="s"> ubuntu:26.04&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ARG&lt;/span> &lt;span class="nv">USERNAME&lt;/span>&lt;span class="o">=&lt;/span>ubuntu
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">ENV&lt;/span> &lt;span class="nv">TZ&lt;/span>&lt;span class="o">=&lt;/span>Asia/Shanghai&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">USER&lt;/span>&lt;span class="s"> root&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> openssh-server sudo locales tzdata &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> locale-gen en_US.UTF-8 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> ln -snf /usr/share/zoneinfo/&lt;span class="si">${&lt;/span>&lt;span class="nv">TZ&lt;/span>&lt;span class="si">}&lt;/span> /etc/localtime &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="si">${&lt;/span>&lt;span class="nv">TZ&lt;/span>&lt;span class="si">}&lt;/span> &amp;gt; /etc/timezone &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get clean &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> rm -rf /var/lib/apt/lists/*&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> usermod -l &lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span> ubuntu &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> groupmod -n &lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span> ubuntu &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> usermod -d /home/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span> -m &lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> ALL=(ALL) NOPASSWD: ALL&amp;#34;&lt;/span> &amp;gt; /etc/sudoers.d/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> chmod &lt;span class="m">0440&lt;/span> /etc/sudoers.d/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;${USERNAME}:${USERNAME}&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> chpasswd &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> mkdir /var/run/sshd &lt;span class="o">||&lt;/span> &lt;span class="nb">true&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> sed -i &lt;span class="s1">&amp;#39;s/#PasswordAuthentication yes/PasswordAuthentication no/&amp;#39;&lt;/span> /etc/ssh/sshd_config &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> ssh-keygen -A&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">USER&lt;/span>&lt;span class="s"> ${USERNAME}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">WORKDIR&lt;/span>&lt;span class="s"> /home/${USERNAME}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> mkdir -p /home/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>/.ssh &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> chmod &lt;span class="m">700&lt;/span> /home/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>/.ssh&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">COPY&lt;/span> --chown&lt;span class="o">=&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>:&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span> authorized_keys /home/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>/.ssh/authorized_keys&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> chmod &lt;span class="m">600&lt;/span> /home/&lt;span class="si">${&lt;/span>&lt;span class="nv">USERNAME&lt;/span>&lt;span class="si">}&lt;/span>/.ssh/authorized_keys&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">ENTRYPOINT&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/bin/bash&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;-c&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">CMD&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;sudo /usr/sbin/sshd -D&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">EXPOSE&lt;/span>&lt;span class="s"> 22&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>实现联通 IPTV 认证</title><link>https://blog.sigsegv.top/posts/iptv-auth/</link><pubDate>Sun, 22 Feb 2026 14:28:37 +0800</pubDate><guid>https://blog.sigsegv.top/posts/iptv-auth/</guid><description>&lt;p>参考中国联通家庭宽带多媒体应用平台技术规范与盒端接口分册。
虽然各地区的实现基本上都仍然是不规范的，
但相比网上的说法已经改了一些地方。&lt;/p>
&lt;p>整体流程可以参考&lt;a href="https://web.archive.org/web/20260222064547/https://xyx.moe/018-RouterOS-IPTV-packet-capture-and-authentication-implementation.html">这篇文章&lt;/a>，
有些流程相对有出入，具体如下。&lt;/p>
&lt;h2 id="stb-登录">STB 登录&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">getSTBAuthenticator&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">STBID&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Authentication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CTCGetConfig&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;STBID&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">EncryptToken&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;................................&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Authenticator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Authentication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CTCGetAuthInfo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">EncryptToken&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userToken&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;................................&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">authform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mac&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">Authentication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CTCGetConfig&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;mac&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里 CTCGetAuthInfo 仍然是只有 8 位，同样退化成了 DES ，但值的范围可能不一样。
密码的内容可以直接 &lt;code>HMW_config_read(&amp;quot;Iptv.Password&amp;quot;, pBuf, bufSize);&lt;/code> 。&lt;/p>
&lt;p>重新加密的方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">des_encrypt_ecb&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">plaintext&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cipher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">DES&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DES&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">MODE_ECB&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">padded_plaintext&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pad&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plaintext&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;utf-8&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">DES&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">block_size&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">encrypted_bytes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cipher&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encrypt&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">padded_plaintext&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">binascii&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hexlify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">encrypted_bytes&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;utf-8&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">des_encrypt_ecb&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s1">&amp;#39;99999$&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">EncryToken&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">$&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">UserID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">$&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">STBID&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">$&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">IP&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">$&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">MAC&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">$$CTC&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意这里的 random ，在某些配置下只会用默认值，也就是 99999 ，用随机值反而不对。&lt;/p>
&lt;h2 id="tempkey-的算法">tempkey 的算法&lt;/h2>
&lt;p>&lt;code>md5(hashCode(SESSIONID + 标识符 + MAC地址))&lt;/code>。&lt;/p>
&lt;p>MAC地址加冒号、大写。
标识符是写死的，除非系统升级可能会变。
hashCode 是 Java 同款的 String.hashCode 。&lt;/p>
&lt;p>所以计算代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">hashlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">md5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">functools&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">reduce&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">calc_key&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">h&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">lambda&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">reduce&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">lambda&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">31&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nb">ord&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="mi">4294967295&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">u&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mh">0xcdaa61c1&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mh">0x870f92bc&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">u&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="mi">31&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="mi">32&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="o">**&lt;/span>&lt;span class="mi">31&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">md5&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">str&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hexdigest&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">upper&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;00000000000000000000000000000000&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mac_addr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;00:AA:BB:CC:DD:EE&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">calc_key&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">session_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mac_addr&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>容器化 OpenLDAP 迁移</title><link>https://blog.sigsegv.top/posts/dockerize-openldap/</link><pubDate>Fri, 08 Aug 2025 15:45:52 +0800</pubDate><guid>https://blog.sigsegv.top/posts/dockerize-openldap/</guid><description>&lt;p>这次使用的是 bitnami/openldap 镜像。
唯一不好的是，在第一次启动初始化数据如果出错，会直接退出而不是打印错误信息，只能对照错误码去找。&lt;/p>
&lt;p>文档在 &lt;a href="https://hub.docker.com/r/bitnami/openldap">Docker Hub: bitnami/openldap&lt;/a> 上。&lt;/p>
&lt;p>docker compose 文件：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">openldap&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">bitnami/openldap:latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">openldap&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="m">389&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">1389&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="m">636&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">1636&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">TZ=Asia/Shanghai&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">LDAP_ADMIN_USERNAME=ldapadm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">LDAP_ADMIN_PASSWORD=password&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">LDAP_ALLOW_ANON_BINDING=no&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_EXTRA_SCHEMAS=cosine, inetorgperson&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_ADMIN_DN=cn=ldapadm,dc=azuk,dc=top&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_ROOT=dc=azuk,dc=top&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_ENABLE_TLS=yes&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_TLS_CERT_FILE=/cert/ldap.pem&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_TLS_KEY_FILE=/cert/ldap.key&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;LDAP_TLS_CA_FILE=/cert/ca.pem&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./data:/bitnami/openldap&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;./cert:/cert:ro&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;./custom-schema.ldif:/schema/custom.ldif:ro&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">./ldifs:/ldifs&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里设置 &lt;code>LDAP_EXTRA_SCHEMAS&lt;/code> 是为了排除它默认有的 nis ，因为要用 rfc2307bis ，防止冲突。&lt;/p>
&lt;p>容器默认用户/组是 1001:1001 ，证书需要设置权限，否则初始化报错。&lt;/p></description></item><item><title>修改 7-Zip 的文件夹选择对话框</title><link>https://blog.sigsegv.top/posts/7zip-modern-folder-dialog/</link><pubDate>Fri, 30 May 2025 13:24:42 +0800</pubDate><guid>https://blog.sigsegv.top/posts/7zip-modern-folder-dialog/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>7-Zip 在解压文件时使用的是旧版的文件夹选择对话框 &lt;code>SHBrowseForFolder&lt;/code> ，需要逐级展开树状目录，使用起来不是很好。
于是就想在不改动 7-Zip 源程序的情况下，把对话框给换成现代的 &lt;code>IFileDialog&lt;/code> 。&lt;/p>
&lt;div style="display:flex;">
&lt;figure>&lt;img src="https://blog.sigsegv.top/img/post/7zip-modern-folder-dialog/old-dialog.webp"
alt="旧版对话框">&lt;figcaption>
&lt;p>旧版对话框 &lt;code>SHBrowseForFolder&lt;/code>&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;figure>&lt;img src="https://blog.sigsegv.top/img/post/7zip-modern-folder-dialog/new-dialog.webp"
alt="新版对话框">&lt;figcaption>
&lt;p>新版对话框 &lt;code>IFileDialog&lt;/code>&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;/div>
&lt;p>以 7-Zip 24.09 源码为例。
结构初始化部分的代码在 &lt;code>CPP\Windows\Shell.cpp#666&lt;/code> 附近，整理后大概如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">BrowseForFolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LPBROWSEINFO&lt;/span> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CSysString&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">resultPath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">resultPath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Empty&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">NWindows&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">NCOM&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CComInitializer&lt;/span> &lt;span class="n">comInitializer&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LPITEMIDLIST&lt;/span> &lt;span class="n">itemIDList&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">::&lt;/span>&lt;span class="n">SHBrowseForFolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">browseInfo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">itemIDList&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CItemIDList&lt;/span> &lt;span class="n">itemIDListHolder&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">itemIDListHolder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Attach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">itemIDList&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">GetPathFromIDList&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">itemIDList&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">resultPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">CALLBACK&lt;/span> &lt;span class="nf">BrowseCallbackProc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HWND&lt;/span> &lt;span class="n">hwnd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">UINT&lt;/span> &lt;span class="n">uMsg&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LPARAM&lt;/span> &lt;span class="cm">/* lp */&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LPARAM&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">uMsg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">BFFM_INITIALIZED&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SendMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hwnd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BFFM_SETSELECTION&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">TRUE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">default&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="nf">BrowseForFolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HWND&lt;/span> &lt;span class="n">owner&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LPCTSTR&lt;/span> &lt;span class="n">title&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">UINT&lt;/span> &lt;span class="n">ulFlags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LPCTSTR&lt;/span> &lt;span class="n">initialFolder&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CSysString&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">resultPath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CSysString&lt;/span> &lt;span class="n">displayName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BROWSEINFO&lt;/span> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">hwndOwner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">owner&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pidlRoot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pszDisplayName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">displayName&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetBuf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lpszTitle&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">title&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ulFlags&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ulFlags&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lpfn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">initialFolder&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nl">BrowseCallbackProc&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">browseInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lParam&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">LPARAM&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">initialFolder&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">BrowseForFolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">browseInfo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">resultPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>除了要把 &lt;code>SHBrowseForFolder&lt;/code> detour 掉以外，还需要检查下 &lt;code>bi&lt;/code> 的回调函数，如果存在的话说明 &lt;code>bi.lParam&lt;/code> 里放了当前文件夹的路径。&lt;/p>
&lt;h3 id="dll-proxy-不可行">DLL Proxy ：不可行&lt;/h3>
&lt;p>7z 的提取文件对话框由 7zG.exe 提供，检查了下导入表，发现都是 known dll ，没有什么操作空间。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">COMCTL32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">comdlg32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GDI32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OLEAUT32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ole32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">USER32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ADVAPI32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">SHELL32.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">msvcrt.dll
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">KERNEL32.dll
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="7z-插件方法可行">7z 插件方法：可行&lt;/h3>
&lt;p>7zG 会尝试从其目录下的 &lt;code>Codecs&lt;/code> 和 &lt;code>Formats&lt;/code> 下加载 dll 。
（如果要 patch 7zFM 则麻烦些，因为它只在打开压缩文件时，才会去加载插件。）
因此尝试写一个 dll 去 detour 一下。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="nf">LPITEMIDLIST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">WINAPI&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">Original_SHBrowseForFolderW&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="n">LPBROWSEINFOW&lt;/span> &lt;span class="n">lpbi&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SHBrowseForFolderW&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LPITEMIDLIST&lt;/span> &lt;span class="n">WINAPI&lt;/span> &lt;span class="nf">Detoured_SHBrowseForFolderW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LPBROWSEINFOW&lt;/span> &lt;span class="n">lpbi&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MessageBoxW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;World&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Original_SHBrowseForFolderW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BOOL&lt;/span> &lt;span class="n">APIENTRY&lt;/span> &lt;span class="nf">DllMain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HMODULE&lt;/span> &lt;span class="n">hModule&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">ul_reason_for_call&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LPVOID&lt;/span> &lt;span class="n">lpReserved&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">DetourIsHelperProcess&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">TRUE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ul_reason_for_call&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">DLL_PROCESS_ATTACH&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourRestoreAfterWith&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourTransactionBegin&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourUpdateThread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GetCurrentThread&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourAttach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PVOID&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">Original_SHBrowseForFolderW&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">PVOID&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">Detoured_SHBrowseForFolderW&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourTransactionCommit&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">TRUE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>编译运行，报 access violation 退出了。
仔细一看， 7z 如果发现插件 dll 里没有可用的 codec/format 就会主动卸载 dll 。
因此在 dll 加载时，我们手动 pin 一下自己，防止被卸载。&lt;/p>
&lt;p>完整代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define WIN32_LEAN_AND_MEAN
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define UNICODE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define _UNICODE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1">// clang-format off
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;windows.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;detours.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;shlobj_core.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;comdef.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;pathcch.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1">// clang-format on
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// mini unsafe com wrapper
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">template&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="k">typename&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">com_ptr&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">T&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ptr_&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com_ptr&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">com_ptr&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ptr_&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ptr_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Release&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">T&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nf">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">ptr_&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">T&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="k">operator&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">ptr_&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">T&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="k">operator&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">ptr_&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">inline&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HRESULT&lt;/span> &lt;span class="n">hr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">FAILED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_com_error&lt;/span> &lt;span class="n">err&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#ifndef NDEBUG
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">MessageBoxW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ErrorMessage&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;Unexpected error&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MB_ICONERROR&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">MB_OK&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">runtime_error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;unexpected error&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="nf">LPITEMIDLIST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">WINAPI&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">Original_SHBrowseForFolderW&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="n">LPBROWSEINFOW&lt;/span> &lt;span class="n">lpbi&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SHBrowseForFolderW&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">TryGetAvailableFolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LPCWSTR&lt;/span> &lt;span class="n">pszPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">IShellItem&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">ppsi&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">WCHAR&lt;/span> &lt;span class="n">szPath&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">wcscpy_s&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">szPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pszPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// try to parse current path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">hr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SHCreateItemFromParsingName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">szPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">IID_PPV_ARGS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ppsi&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SUCCEEDED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// try to remove trailing backslash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">PathCchRemoveBackslash&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">szPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// try to get the parent folder
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">hr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PathCchRemoveFileSpec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">szPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">FAILED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SHCreateItemFromParsingName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">szPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">IID_PPV_ARGS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ppsi&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SUCCEEDED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LPITEMIDLIST&lt;/span> &lt;span class="nf">ModernFolderDialog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LPBROWSEINFOW&lt;/span> &lt;span class="n">lpbi&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IFileDialog&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">pfd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CoCreateInstance&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CLSID_FileOpenDialog&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CLSCTX_INPROC_SERVER&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">IID_PPV_ARGS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">pfd&lt;/span>&lt;span class="p">)));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">dwFlags&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pfd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetOptions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dwFlags&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dwFlags&lt;/span> &lt;span class="o">|=&lt;/span> &lt;span class="n">FOS_PICKFOLDERS&lt;/span> &lt;span class="o">|&lt;/span> &lt;span class="n">FOS_FORCEFILESYSTEM&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pfd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SetOptions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dwFlags&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pfd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SetTitle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">lpszTitle&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">lpfn&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// lParam is LPCWSTR initialFolder
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">com_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IShellItem&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">psiFolder&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">TryGetAvailableFolder&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">LPCWSTR&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">lParam&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">psiFolder&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pfd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SetFolder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">psiFolder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">hr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pfd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Show&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">hwndOwner&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">HRESULT_FROM_WIN32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ERROR_CANCELLED&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hr&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">com_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IShellItem&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">psi&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pfd&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">psi&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LPITEMIDLIST&lt;/span> &lt;span class="n">pidl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">check_hr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SHGetIDListFromObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">psi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">pidl&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">pidl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LPITEMIDLIST&lt;/span> &lt;span class="n">WINAPI&lt;/span> &lt;span class="nf">Detoured_SHBrowseForFolderW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LPBROWSEINFOW&lt;/span> &lt;span class="n">lpbi&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ModernFolderDialog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(...)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Original_SHBrowseForFolderW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">lpbi&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BOOL&lt;/span> &lt;span class="n">APIENTRY&lt;/span> &lt;span class="nf">DllMain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HMODULE&lt;/span> &lt;span class="n">hModule&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">ul_reason_for_call&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LPVOID&lt;/span> &lt;span class="n">lpReserved&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">DetourIsHelperProcess&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">TRUE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ul_reason_for_call&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">DLL_PROCESS_ATTACH&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// avoid 7zip unloading the DLL
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">WCHAR&lt;/span> &lt;span class="n">moduleName&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GetModuleFileNameW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hModule&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">moduleName&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">MAX_PATH&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">HMODULE&lt;/span> &lt;span class="n">hModuleTmp&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">GetModuleHandleExW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GET_MODULE_HANDLE_EX_FLAG_PIN&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">moduleName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">hModuleTmp&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourRestoreAfterWith&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourTransactionBegin&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourUpdateThread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GetCurrentThread&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourAttach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PVOID&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">Original_SHBrowseForFolderW&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">PVOID&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">Detoured_SHBrowseForFolderW&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DetourTransactionCommit&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">TRUE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>DockerHub 反向代理 Missing x-amz-content-sha256</title><link>https://blog.sigsegv.top/posts/docker-reverse-proxy-fix/</link><pubDate>Sun, 16 Feb 2025 10:51:58 +0800</pubDate><guid>https://blog.sigsegv.top/posts/docker-reverse-proxy-fix/</guid><description>&lt;p>使用 CF Workers 做反向代理时，一般直接把原请求直接 passthrough 到 dockerhub 服务器，类似：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">newUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">URL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">upstream&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">url&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pathname&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">req&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Request&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newUrl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">method&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">method&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">headers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">headers&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">redirect&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;follow&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">return&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>假如我们请求的 url 是 &lt;code>https://registry-1.docker.io/v2/library/[image_name]/blobs/sha256:[sha256_value]&lt;/code> ，那么在验证完 &lt;code>Authorization&lt;/code> 请求头后，会 307 到对象存储的地址，比如 R2 的地址 &lt;code>https://docker-images-prod.[...].r2.cloudflarestorage.com/registry-v2/[...]/data?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;amp;X-Amz-Credential=[...]&amp;amp;X-Amz-Date=[...]&amp;amp;X-Amz-Expires=1200&amp;amp;X-Amz-SignedHeaders=host&amp;amp;X-Amz-Signature=[...]&lt;/code>&lt;/p>
&lt;p>请求到 R2 的时候，这个 &lt;code>Authorization&lt;/code> 头如果带上了，就会报 &lt;code>Missing x-amz-content-sha256&lt;/code> 。 CF Workers 的 fetch 直接把请求头带到了第二个请求里，这个 bug 导致了报错。&lt;/p>
&lt;p>那么到底 fetch 遇到跳转是否应该带原来的请求头呢？当跳转时 origin 改变了，某些 header 就应该被去掉。见 &lt;a href="https://fetch.spec.whatwg.org/#http-redirect-fetch">Fetch Standard - 4.4 HTTP-redirect fetch&lt;/a>：&lt;/p>
&lt;blockquote>
&lt;p>Note: I.e., the moment another origin is seen after the initial request, the &lt;code>Authorization&lt;/code> header is removed.&lt;/p>
&lt;/blockquote>
&lt;p>这个 bug 理论上会对安全性造成影响，把另一个站点的凭据泄漏给另外一个站点。&lt;/p>
&lt;p>Node.js 无此 bug 。（ fetch 是写在 v8 里的吗？）&lt;/p></description></item><item><title>MSVC Ignores Empty Initializer List in C</title><link>https://blog.sigsegv.top/posts/msvc-ignores-empty-initializer-list-c/</link><pubDate>Mon, 30 Sep 2024 21:57:33 +0800</pubDate><guid>https://blog.sigsegv.top/posts/msvc-ignores-empty-initializer-list-c/</guid><description>&lt;p>Before C23, ISO C forbids empty initializer braces. Take the following code as an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">typedef&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">tagFoo&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span> &lt;span class="n">Foo&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="n">Foo&lt;/span> &lt;span class="n">foos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;sizeof(foos): %zu&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">foos&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;sizeof(Foo): %zu&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Foo&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;foo[0].i: %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">foos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;foo[1].i: %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">foos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;foo[2].i: %d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">foos&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compile the program in gcc 14.2 with &lt;code>-pedantic&lt;/code>, we got:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sizeof(foos): 12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sizeof(Foo): 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">foo[0].i: 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">foo[1].i: 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">foo[2].i: 2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Without &lt;code>-pedantic&lt;/code>, the result will not change, despite no warning would be thrown.&lt;/p>
&lt;p>However, in MSVC v19.40 (vs 17.10), the code compiles without any warning, but the result is weird:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">sizeof(foos): 12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sizeof(Foo): 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">foo[0].i: 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">foo[1].i: 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">foo[2].i: 0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>MSVC ignores the empty initializer list, and shifted the struct array.&lt;/p>
&lt;p>FYI, currently C23 empty initializers are partially supported in gcc, and not supported in MSVC.&lt;/p></description></item><item><title>Cross Compiling Dropbear for Android(API 23) with Auto Run on Boot</title><link>https://blog.sigsegv.top/posts/cross-compiling-dropbear-android-api23/</link><pubDate>Thu, 05 Sep 2024 14:55:50 +0800</pubDate><guid>https://blog.sigsegv.top/posts/cross-compiling-dropbear-android-api23/</guid><description>&lt;p>Compiling a random Linux compatible cli tool for Android can be painful. The Android, although with a Linux kernel, has a very different experience. It has bionic as its C library and linker, the SELinux can be troublesome, lacks common library support out of box (e.g. openssl), no common cli tools, different init system and file system hierarchy. I found precompiled binaries not suitable for my situation (and really we should not trust binaries from internet), so I decided to compile on my own.&lt;/p>
&lt;h1 id="compiling">Compiling&lt;/h1>
&lt;p>Download and set up &lt;a href="https://developer.android.com/ndk/downloads">Android NDK&lt;/a>. Clone the dropbear source code.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/opt/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone -b DROPBEAR_2024.85 -d &lt;span class="m">1&lt;/span> https://github.com/mkj/dropbear/tree/DROPBEAR_2024.85
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Before building the program, let&amp;rsquo;s do some quick hacks:&lt;/p>
&lt;p>&lt;code>src/default_options.h&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// change the default port
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DROPBEAR_DEFPORT &amp;#34;2022&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// where /etc lays may be mounted as ro
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// change the following file path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DSS_PRIV_FILENAME &amp;#34;/data/dropbear/dropbear_dss_host_key&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define RSA_PRIV_FILENAME &amp;#34;/data/dropbear/dropbear_rsa_host_key&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define ECDSA_PRIV_FILENAME &amp;#34;/data/dropbear/dropbear_ecdsa_host_key&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define ED25519_PRIV_FILENAME &amp;#34;/data/dropbear/dropbear_ed25519_host_key&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define DROPBEAR_PIDFILE &amp;#34;/data/dropbear/dropbear.pid&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// android root may has the home folder as /
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// change the cli auth key path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DROPBEAR_DEFAULT_CLI_AUTHKEY &amp;#34;/data/dropbear/.ssh/id_dropbear&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// commonly android does not has a sftp-server out of box
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DROPBEAR_SFTPSERVER 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// define the default path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DEFAULT_ROOT_PATH &amp;#34;/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>src/svr-authpubkey.c&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// change authorized_keys path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">checkpubkey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">keyalgo&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">keyalgolen&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">keyblob&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">unsigned&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">keybloblen&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// filename = m_malloc(len + 22);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// snprintf(filename, len + 22, &amp;#34;%s/.ssh/authorized_keys&amp;#34;, ses.authstate.pw_dir);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">authkeys_filename&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;/data/dropbear/authorized_keys&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">m_malloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">authkeys_filename&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">strcpy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">authkeys_filename&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// do not check public key perms
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// as the home for root is /
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">checkpubkeyperms&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">DROPBEAR_SUCCESS&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>localoptions.h&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Copyright © 2020-2022 Matt Robinson
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> *
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * SPDX-License-Identifier: MIT
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Disable server password auth as crypt() isn&amp;#39;t available under Android
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DROPBEAR_SVR_PASSWORD_AUTH 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Disable client password auth as getpass() isn&amp;#39;t available under Android
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DROPBEAR_CLI_PASSWORD_AUTH 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Speed up symmetrical ciphers and hashes at the expense of larger binaries
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DROPBEAR_SMALL_CODE 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Build all but the most verbose level of trace messages into the binaries
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define DEBUG_TRACE 4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Change the fallback list of shells to the non-standard Android shell path
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define COMPAT_USER_SHELLS &amp;#34;/system/bin/sh&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Configure the build:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">./configure &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --host&lt;span class="o">=&lt;/span>armv7a-linux-androideabi &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-utmp &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-wtmp &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-utmpx &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-zlib &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --disable-syslog &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">AR&lt;/span>&lt;span class="o">=&lt;/span>llvm-ar &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">CC&lt;/span>&lt;span class="o">=&lt;/span>armv7a-linux-androideabi23-clang &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">RANLIB&lt;/span>&lt;span class="o">=&lt;/span>llvm-ranlib &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">STRIP&lt;/span>&lt;span class="o">=&lt;/span>llvm-sstrip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">make &lt;span class="nv">PROGRAMS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;dropbear dbclient dropbearkey dropbearconvert scp&amp;#34;&lt;/span> &lt;span class="nv">SCPPROGRESS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="set-up-on-android">Set up on Android&lt;/h1>
&lt;p>In my case, the device SELinux status is permissive, which often is NOT the case for production builds, so the following may not work for your device.&lt;/p>
&lt;p>Check if dropbear works with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">./dropbear -F -R
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Auto run needs a new init.rc file in &lt;code>/etc/init&lt;/code>, in my case &lt;code>/system&lt;/code> is a ext4 partition.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mount -t ext4 -o rw,remount /system
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cat &amp;gt; /system/etc/init/dropbear.rc &lt;span class="s">&amp;lt;&amp;lt; EOF
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">service sshd /system/xbin/dropbear
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> seclabel u:r:init:s0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> user root
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> group root
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> oneshot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> disabled
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">on property:sys.boot_completed=1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s"> start sshd
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">chmod &lt;span class="m">644&lt;/span> /system/etc/init/dropbear.rc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Refs:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://nerdoftheherd.com/articles/cross-compiling-dropbear-rsync-android/">Cross-compiling Dropbear and rsync for Android&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Set up NumPy dev env with VSCode debugging</title><link>https://blog.sigsegv.top/posts/numpy-debug-setup/</link><pubDate>Tue, 23 Jul 2024 14:19:00 +0800</pubDate><guid>https://blog.sigsegv.top/posts/numpy-debug-setup/</guid><description>&lt;h1 id="compile-python-with-debug-build">Compile Python with debug build&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># setup the source path for later debugging&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PYTHON_BUILD_BUILD_PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$HOME&lt;/span>/pythondev/builds/3.12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># build a debug version of Python&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv install 3.12 -g
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="set-up-numpy-dev-env">Set up NumPy dev env&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># install build deps, see https://numpy.org/doc/stable/building/index.html&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install -y gcc g++ gfortran libopenblas-dev liblapack-dev pkg-config python3-pip python3-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># clone numpy sources&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git clone https://github.com/numpy/numpy.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> numpy
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git submodule update --init
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># automatically use the python debug build in cwd&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pyenv &lt;span class="nb">local&lt;/span> 3.12
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># in case you don&amp;#39;t want to pollute the global env&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python -m pip install --user virtualenv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">virtualenv venv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># build numpy without optimization&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">spin build --clean -- -Dbuildtype&lt;span class="o">=&lt;/span>debug -Ddisable-optimization&lt;span class="o">=&lt;/span>&lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="debugging-c-code">Debugging C code&lt;/h1>
&lt;p>I found that manually setting everything needed is pretty straightforward, and we can stick to the &lt;code>spin&lt;/code> workflow.&lt;/p>
&lt;p>Add a gdb launch conf to your &lt;code>launch.json&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#34;configurations&amp;#34;: [
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;program&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;YOUR_WORKING_DIR/venv/bin/python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;args&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Don&amp;#39;t prepend current dir to `sys.path`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// See https://docs.python.org/3/using/cmdline.html#cmdoption-P
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="s2">&amp;#34;-P&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;whatever_you_wanna_debug.py&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;environment&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// instead manually set `PYTHONPATH`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;PYTHONPATH&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;YOUR_WORKING_DIR/build-install/usr/lib/python3.12/site-packages&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="debugging-python-code">Debugging Python code&lt;/h1>
&lt;p>For env variables, &lt;a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSAFEPATH">&lt;code>PYTHONSAFEPATH&lt;/code>&lt;/a> does not work but &lt;code>PYTHONPATH&lt;/code> does, so I just set python launch args to &lt;code>-P&lt;/code>.&lt;/p>
&lt;p>This time, create a Python debugger launch profile:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// &amp;#34;configurations&amp;#34;: [
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;python&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;YOUR_WORKING_DIR/venv/bin/python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;pythonArgs&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;-P&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;program&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;whatever.py&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// note the inconsistent env config style
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;PYTHONPATH&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;YOUR_WORKING_DIR/build-install/usr/lib/python3.12/site-packages&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;justMyCode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Dll Proxying</title><link>https://blog.sigsegv.top/posts/dll-proxying/</link><pubDate>Fri, 15 Mar 2024 13:41:27 +0800</pubDate><guid>https://blog.sigsegv.top/posts/dll-proxying/</guid><description>&lt;p>通过让 exe 优先加载 proxy dll 来注入进程，或者拦截某个原 dll 中的函数。&lt;br>
DLL 的搜索路径： &lt;a href="https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order">Dynamic-link library search order&lt;/a>&lt;br>
注意 KnownDLLs 中的 dll 必须放在 System32 中才会生效。&lt;/p>
&lt;p>程序 (a.exe, main.c) ：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// cc main.c -lwininet -O2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;Windows.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;Wininet.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">some_string&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">InternetGetConnectedState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">flags&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Connected to the internet&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Not connected to the internet&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">some_string&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DLL (wininet.dll, dll.c) :&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// gcc -shared -static -static-libgcc -O2 -o wininet.dll dll.c m.def
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define WIN32_LEAN_AND_MEAN
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;Windows.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;Psapi.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;wininet.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;processthreadsapi.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memoryapi.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdlib.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">orig_str&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">new_str&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Goodbye World&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">__declspec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dllexport&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BOOL&lt;/span> &lt;span class="kr">__stdcall&lt;/span> &lt;span class="nf">_InternetGetConnectedState&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_Out_&lt;/span> &lt;span class="n">LPDWORD&lt;/span> &lt;span class="n">lpdwFlags&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_Reserved_&lt;/span> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">dwReserved&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">FALSE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nf">memmem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">size_t&lt;/span> &lt;span class="n">len&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">ss&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">size_t&lt;/span> &lt;span class="n">ssLen&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">ss&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">len&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">ssLen&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">h&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">len&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="n">ssLen&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">--&lt;/span>&lt;span class="n">len&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">memcmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ss&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ssLen&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">PatchMemory&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">HANDLE&lt;/span> &lt;span class="n">process&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">GetCurrentProcess&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">HMODULE&lt;/span> &lt;span class="n">module&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">GetModuleHandleW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MODULEINFO&lt;/span> &lt;span class="n">info&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">GetModuleInformation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">module&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">info&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">info&lt;/span>&lt;span class="p">)))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">OutputDebugStringW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;GetModuleInformation failed&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BYTE&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">procImage&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">BYTE&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="nf">malloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BYTE&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SizeOfImage&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SIZE_T&lt;/span> &lt;span class="n">bytesRead&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ReadProcessMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">EntryPoint&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">procImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SizeOfImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">bytesRead&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// expect error 299 here, ReadProcessMemory returns 0, just check bytesRead
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">bytesRead&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">OutputDebugStringW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;ReadProcessMemory failed&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">procImage&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">pos&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">memmem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">procImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bytesRead&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">orig_str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">ARRAYSIZE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">orig_str&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">pos&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">OutputDebugStringW&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;Patch not found&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">procImage&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">realAddr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">)((&lt;/span>&lt;span class="kt">uintptr_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">EntryPoint&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="kt">uintptr_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">pos&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uintptr_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">procImage&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">oldProtect&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">VirtualProtectEx&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">realAddr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">ARRAYSIZE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_str&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">PAGE_EXECUTE_READWRITE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">oldProtect&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">WriteProcessMemory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">realAddr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_str&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">ARRAYSIZE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_str&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">VirtualProtectEx&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">process&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">realAddr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">ARRAYSIZE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">new_str&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">oldProtect&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">procImage&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BOOL&lt;/span> &lt;span class="n">WINAPI&lt;/span> &lt;span class="nf">DllMain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HINSTANCE&lt;/span> &lt;span class="n">hinstDLL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">DWORD&lt;/span> &lt;span class="n">fdwReason&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">LPVOID&lt;/span> &lt;span class="n">lpReserved&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">fdwReason&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">DLL_PROCESS_ATTACH&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">PatchMemory&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">DLL_THREAD_ATTACH&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">DLL_THREAD_DETACH&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">DLL_PROCESS_DETACH&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">TRUE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>生成 module def :&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pefile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dll_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;wininet&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dll&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">pefile&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">PE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dll_name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">f&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;module.def&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;w&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;EXPORTS&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">sym&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">dll&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">DIRECTORY_ENTRY_EXPORT&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">symbols&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s1">&amp;#39; &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">=&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">dll_name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1">.&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s1"> @&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sym&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ordinal&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>DLL (wininet.dll, m.def) :&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">EXPORTS
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AppCacheCheckManifest=&amp;#34;C:\Windows\System32\WININET.AppCacheCheckManifest&amp;#34; @113
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AppCacheCloseHandle=&amp;#34;C:\Windows\System32\WININET.AppCacheCloseHandle&amp;#34; @114
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> AppCacheCreateAndCommitFile=&amp;#34;C:\Windows\System32\WININET.AppCacheCreateAndCommitFile&amp;#34; @115
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> (...)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> InternetGetConnectedState=_InternetGetConnectedState @294
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关于Module-Definition File: &lt;a href="https://learn.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170">MSVC&lt;/a>, &lt;a href="https://sourceware.org/binutils/docs/ld/WIN32.html">ld win32&lt;/a>&lt;br>
有些 dll 中的函数只会通过 ordinal 导出，而没有名称。在 MSVC 中可以通过 &lt;code>some_func=other_module.#42&lt;/code> 来代理，而 mingw 的 ld 不支持这种写法。&lt;br>
另外，代理系统 DLL 需要把路径写全，否则会报 0xc000007b （合理）。&lt;/p>
&lt;p>运行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">PS C:\Users\Azuk\inet-test&amp;gt; .\a.exe
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Connected to the internet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello, World!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(Build the proxy dll)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PS C:\Users\Azuk\inet-test&amp;gt; .\a.exe
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Not connected to the internet
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Hello, World!
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>对 Windows TSF 的一些想法</title><link>https://blog.sigsegv.top/posts/things-about-tsf/</link><pubDate>Wed, 13 Mar 2024 14:07:50 +0800</pubDate><guid>https://blog.sigsegv.top/posts/things-about-tsf/</guid><description>&lt;p>在 windows-rs 里暴露出了几个有意思的 GUID :&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://docs.rs/windows-sys/latest/windows_sys/Win32/UI/TextServices/constant.GUID_TFCAT_TIPCAP_LOCALSERVER.html">&lt;code>GUID_TFCAT_TIPCAP_LOCALSERVER&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/TextServices/constant.GUID_TFCAT_TIPCAP_TSF3.html">&lt;code>GUID_TFCAT_TIPCAP_TSF3&lt;/code>&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>windows-rs 是 winmd 生成的，虽然最近才发现它们已经在 win32 metadata 的源里躺了很长时间了，但之前就从几个地方对这些东西有所了解。&lt;/p>
&lt;p>先说说 LocalServer ：这里的 LocalServer 指的就是 COM 的 LocalServer 了，一个以 exe 方式运行的 COM server 。与之相对的就是现在各类 TSF TIP 实现常用的方法 InProcServer ，即作为 DLL 加载进目标进程中。目前只有微软的输入法实现了 LocalServer 的运行模式，因为 TSF 的文档里根本就没说这件事。 TSF 文档水平实在很烂，还不更新，也没什么资料，基本靠自己瞎猜和看别人怎么写。&lt;/p>
&lt;p>用 LocalServer 有几个好处，首先不被加载进别的进程有一定的安全性，同时也不会因为输入法的故障而使应用程序出问题。微软在 Win8 时的文档里，想规定 TIP 不能和本地的其他程序进行通信，只能用 web 的方式去进行一些网络通信，比如实现云输入法的功能。但这样本地词库、皮肤等各种自定义功能不也就废了么？所以微软虽然文档上装了个大B，但实际上口子放得很大：常见的通信方式是在 TIP 和输入法后端间架一条 Named Pipe ，但由于 Pipe 只是方便单向的请求，所以像 mozc 还会让 TIP 创建一个不显示的窗口，通过窗口的信息来实现双向的通信。如果换用 LocalServer ，就完全没有这样的问题，反正我已经跟目标程序毫无关系了，你还管我干嘛吗？&lt;/p>
&lt;p>这样说来，是不是 &lt;code>GUID_TFCAT_TIPCAP_COMLESS&lt;/code> 也没有意义了呢？既然没有 DLL 加载进目标程序，系统的 TSF 框架来和我通信，加不加载 COM 也不会影响到目标程序，该 obsolete 了。&lt;/p>
&lt;p>再说说 TSF3 ： Edge 的某个设计文档里提了一嘴这个内容，另一方面，通过对 &lt;code>ChsIME.exe&lt;/code> 的符号进行观察，可以发现它用了很多 &lt;code>ITsf*&lt;/code> 的 COM 对象，而不是 &lt;code>ITf*&lt;/code> 开头的对象，难道等 TSF3 出来的时候又要重写一遍输入法？具体改动了什么就不好说了，对 COM 的逆向一窍不通，只能简简单单看函数的名字和一些简单的函数在干啥，来猜一猜。&lt;/p>
&lt;p>另一件值得提的是， &lt;a href="https://github.com/google/mozc/issues/819">mozc#819&lt;/a> 指出微软的输入法现在只通过异步的方式处理 EditSession ，是不是新版的 TSF3 里会对异步支持比较好呢？不过 TSF3 还是要向下兼容 TSF 的，那么面对那些头疼的 TSF Aware App ，是不是又多了一层类似 Cicero 的东西要处理？真的好想看一下微软输入法的源码啊。&lt;/p></description></item><item><title>Edge B站卡死</title><link>https://blog.sigsegv.top/posts/edge-hangs-hevc/</link><pubDate>Wed, 20 Dec 2023 19:41:37 +0800</pubDate><guid>https://blog.sigsegv.top/posts/edge-hangs-hevc/</guid><description>&lt;p>TL;DR: 装了 HEVC 视频扩展的锅，仅在 Edge 下出现问题。删掉扩展解决，或用下面的 UserScript 缓解。&lt;/p>
&lt;p>B 站打开视频大约 5s 左右卡死，首页预览视频的时候也会较随机地卡死。开了 F12 的 Profiling ,看到大部分时间花在 &lt;code>canPlayType&lt;/code> 上。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/edge-hangs-hevc/1.webp" alt="profiling">&lt;/p>
&lt;p>出问题的代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">getBrowserCodecInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;video&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">,&lt;/span> &lt;span class="nx">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;av01&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;av01.0.10M.08&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;av01.0.10M.10&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;av01.0.10M.12&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;av01.1.31M.08&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;av01.2.31H.12&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;hev1.1.6.L93.B0&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;video/mp4; codecs=&amp;#34;vp09.00.50.08&amp;#34;&amp;#39;&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">forEach&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">r&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">o&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">canPlayType&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MediaSource&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">MediaSource&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isTypeSupported&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">t&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">r&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;;&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">t&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>修改跑一下发现是 &lt;code>e.canPlayType('video/mp4; codecs=&amp;quot;hev1.1.6.L93.B0&amp;quot;')&lt;/code> 的问题，且在其他浏览器里不复现，怀疑是 HEVC 视频编码软件包的问题，删掉果然就不卡了。&lt;/p>
&lt;p>如果还需要这个软件包的话，可以做一下缓存当作缓解措施：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ==UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @name Bilibili Edge HEVC fix
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @namespace https://azuk.top/proj/edge-hevc-fix
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @version 2023-12-20
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @description Fix edge hanging in HEVC detection
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @author Azuk 443
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @match *://*.bilibili.com/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @icon https://www.google.com/s2/favicons?sz=64&amp;amp;domain=bilibili.com
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @run-at document-start
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// @grant unsafeWindow
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ==/UserScript==
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;use strict&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">videoEl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createElement&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;video&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">videoEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">__proto__&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">canPlayType2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">videoEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">__proto__&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">canPlayType&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">videoEl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">__proto__&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">canPlayType&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">unsafeWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;__bilifix_canPlay_&amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">canPlayType2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">unsafeWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;__bilifix_canPlay_&amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="nx">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">result&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Ubuntu 将 /tmp 挂载为 tmpfs</title><link>https://blog.sigsegv.top/posts/ubuntu-enable-tmpfs-on-tmp/</link><pubDate>Sat, 04 Nov 2023 12:30:31 +0800</pubDate><guid>https://blog.sigsegv.top/posts/ubuntu-enable-tmpfs-on-tmp/</guid><description>&lt;p>ref: &lt;a href="https://askubuntu.com/questions/1232004/mounting-tmp-as-tmpfs-on-ubuntu-20-04">https://askubuntu.com/questions/1232004/mounting-tmp-as-tmpfs-on-ubuntu-20-04&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo cp -v /usr/share/systemd/tmp.mount /etc/systemd/system/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> tmp.mount
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>or&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo systemctl &lt;span class="nb">enable&lt;/span> /usr/share/systemd/tmp.mount
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>C++ dtor 和虚表指针的一个讨论</title><link>https://blog.sigsegv.top/posts/cpp-dtor-vtable/</link><pubDate>Wed, 11 Oct 2023 15:30:49 +0800</pubDate><guid>https://blog.sigsegv.top/posts/cpp-dtor-vtable/</guid><description>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdio&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Base&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">vb&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">Base&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Base::~Base&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Base::test&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// error!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// virtual void test(int) = 0;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Derived&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Base&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">vd&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">Derived&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Derived::~Derived&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Derived::test&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Base&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Derived&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;~&lt;/span>&lt;span class="n">Base&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">Derived::test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Derived::~Derived
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Derived::test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Base::~Base
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Base::test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Base::test
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Base 析构的时候调用 test 的流程：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Derived&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">Derived&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Derived&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vtable_for_Derived&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// *(void *)this = offset_vtable;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// actual user defined Derived::~Derived
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Base&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Base&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Base&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">Base&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Base&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vtable_for_Base&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// actual user defined Base::~Base
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>vptr 是在 dtor 开始的时候设置的，所以 &lt;code>Base::test(int)&lt;/code> 不能是纯虚函数。&lt;/p>
&lt;p>多重继承的情况类似，假设 &lt;code>Derived&lt;/code> 继承 &lt;code>Base1&lt;/code>, &lt;code>Base2&lt;/code>, &lt;code>Base3&lt;/code> ，&lt;code>Base*&lt;/code> 类中有一个 &lt;code>int64_t&lt;/code> 成员变量：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">Derived 内存布局:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+-------+------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| Base1 | vptr |0|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| +------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | var |1|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+-------+------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| Base2 | vptr |2|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| +------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | var |3|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+-------+------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| Base3 | vptr |4|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| +------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| | var |5|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+-------+------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">|vptr |6|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+--------------+-+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">|var |7|
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+--------------+-+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Derived&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">Derived&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Derived&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// vptr_Base1 = 0;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// vptr_Base2 = 2 * 8;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// vptr_Base3 = 4 * 8;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vptr_Base1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vtable_for_Derived&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vptr_Base2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vtable_for_Derived_thunk_1&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vptr_Base3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vtable_for_Derived_thunk_2&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// actual user defined Derived::~Derived
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Base3&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">Base3&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset_vptr_Base3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Base2&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">Base2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset_vptr_Base2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Base1&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">Base1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset_vptr_Base1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>Base2&lt;/code>, &lt;code>Base3&lt;/code> 对应的 vptr 指向各自的 virtual thunk ，其中的函数只是实际函数的跳板：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">.data:3C48 public _ZTV7Derived ; weak
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C48 ; `vtable for&amp;#39;Derived
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C48 _ZTV7Derived dq 0 ; offset to this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C50 dq offset _ZTI7Derived ; `typeinfo for&amp;#39;Derived
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C58 off_3C58 dq offset _ZN7DerivedD2Ev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C58 ; DATA XREF: Derived::~Derived()+C↑o
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C58 ; Derived::Derived(void)+38↑o
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C58 ; Derived::~Derived()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C60 dq offset _ZN7DerivedD0Ev ; Derived::~Derived()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C68 dq offset _ZN7Derived4testEi ; Derived::test(int)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C70 dq -16 ; offset to this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C78 dq offset _ZTI7Derived ; `typeinfo for&amp;#39;Derived
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C80 off_3C80 dq offset _ZThn16_N7DerivedD1Ev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C80 ; DATA XREF: Derived::~Derived()+1A↑o
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C80 ; Derived::Derived(void)+46↑o
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C80 ; `non-virtual thunk to&amp;#39;Derived::~Derived()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C88 dq offset _ZThn16_N7DerivedD0Ev ; `non-virtual thunk to&amp;#39;Derived::~Derived()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C90 dq offset _ZThn16_N7Derived4testEi ; `non-virtual thunk to&amp;#39;Derived::test(int)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3C98 dq -32 ; offset to this
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CA0 dq offset _ZTI7Derived ; `typeinfo for&amp;#39;Derived
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CA8 off_3CA8 dq offset _ZThn32_N7DerivedD1Ev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CA8 ; DATA XREF: Derived::~Derived()+29↑o
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CA8 ; Derived::Derived(void)+55↑o
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CA8 ; `non-virtual thunk to&amp;#39;Derived::~Derived()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CB0 dq offset _ZThn32_N7DerivedD0Ev ; `non-virtual thunk to&amp;#39;Derived::~Derived()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">.data:3CB8 dq offset _ZThn32_N7Derived4testEi ; `non-virtual thunk to&amp;#39;Derived::test(int)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>MathType 因字体名称过长而崩溃的 Bug</title><link>https://blog.sigsegv.top/posts/mathtype-crash-font/</link><pubDate>Fri, 21 Jul 2023 14:02:00 +0800</pubDate><guid>https://blog.sigsegv.top/posts/mathtype-crash-font/</guid><description>&lt;p>MathType 版本：7.4.2.480 （从 6.9 到最新的 7.6 都有这个 bug ）&lt;/p>
&lt;p>问题：在安装“阿里巴巴普惠体”后， MathType 启动时崩溃退出，无错误信息显示。&lt;/p>
&lt;p>附加调试器，错误为 &lt;code>STATUS_STACK_BUFFER_OVERRUN&lt;/code> ，调用栈如下：&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/mathtype-crash-font/tb.webp" alt="call stack">&lt;/p>
&lt;p>&lt;code>0050CE6D&lt;/code> 是栈安全检查，出错位置在 &lt;code>00457D94&lt;/code> 。根据调用分析可知函数是 &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-enumfontfamiliesexa">&lt;code>EnumFontFamiliesExA&lt;/code>&lt;/a> 的回调函数。IDA 快速分析一下：&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/mathtype-crash-font/ida.webp" alt="ida analyze">&lt;/p>
&lt;p>字符串复制时依赖了 &amp;lsquo;\0&amp;rsquo; ，但根据微软文档，字符串如果长度超出最大长度，那么末尾就会被截断，所以可能不存在 &amp;lsquo;\0&amp;rsquo; （这里微软文档写得不清楚）：&lt;/p>
&lt;blockquote>
&lt;p>&lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-logfonta">LOGFONTA (wingdi.h)&lt;/a>&lt;/p>
&lt;p>lfFaceName[LF_FACESIZE]&lt;/p>
&lt;p>A null-terminated string that specifies the typeface name of the font. The length of this string must not exceed 32 TCHAR values, including the terminating &lt;strong>NULL&lt;/strong>. The EnumFontFamiliesEx function can be used to enumerate the typeface names of all currently available fonts. If lfFaceName is an empty string, GDI uses the first font that matches the other specified attributes.&lt;/p>
&lt;/blockquote>
&lt;p>阿里巴巴普惠体中最长的字体名是 &lt;code>阿里巴巴普惠体 2.0 95 ExtraBold&lt;/code> ，利用 python 计算 &lt;code>len(s).encode('gbk')&lt;/code> 为 31 ，加上 &amp;lsquo;\0&amp;rsquo; 正好 32 ，和 LF_FACESIZE 一样大，为什么还会出现这样的问题？&lt;/p>
&lt;p>在调试器中打条件断点：&lt;/p>
&lt;p>字体本身是正常的：&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/mathtype-crash-font/dump1.png" alt="dump 1">&lt;/p>
&lt;p>但是对于东亚字体，自动产生一个竖排字体，其名称前面多一个 &amp;lsquo;@&amp;rsquo; ，所以刚好超出了字体的长度限制，导致发生崩溃。。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/mathtype-crash-font/dump2.png" alt="dump 2">&lt;/p>
&lt;p>修复：&lt;/p>
&lt;p>找一块内存区域写代码，手动在变量最后填上 &amp;lsquo;\0&amp;rsquo; ，这里选了上一个函数末尾由于对齐留下的空间：&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/mathtype-crash-font/code.png" alt="code fix">&lt;/p>
&lt;p>最好的方法还是做一下 dll proxy ，否则会破坏数字签名。&lt;/p></description></item><item><title>让微信崩溃的二维码</title><link>https://blog.sigsegv.top/posts/crash-wechat-qrcode/</link><pubDate>Mon, 24 Apr 2023 14:37:39 +0800</pubDate><guid>https://blog.sigsegv.top/posts/crash-wechat-qrcode/</guid><description>&lt;p>&lt;strong>更新：漏洞编号 &lt;a href="https://www.cve.org/CVERecord?id=CVE-2023-2617">CVE-2023-2617&lt;/a>&lt;/strong>&lt;/p>
&lt;hr>
&lt;p>早上看见了那张说是能让微信和QQ扫码闪退的二维码，试验了下果然是这样。这里放张自制的&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/crash-wechat-qrcode/1.jpg" alt="invalid qrcode">&lt;/p>
&lt;p>有一些人就开始上手机调之类的，手机上的那一坨C++编译出来以后鬼能看得懂啊，而且微信的扫码代码是开源的，就在 &lt;a href="https://github.com/opencv/opencv_contrib/blob/4.x/modules/wechat_qrcode/">https://github.com/opencv/opencv_contrib/blob/4.x/modules/wechat_qrcode/&lt;/a> ，直接看就好了。&lt;/p>
&lt;p>配置环境，上代码调试一下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// .......
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">R&lt;/span>&lt;span class="s">&amp;#34;(C:\Users\Azuk\Documents\code\qrtest&lt;/span>&lt;span class="se">\b&lt;/span>&lt;span class="s">uild\Debug&lt;/span>&lt;span class="se">\a&lt;/span>&lt;span class="s">.png)&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">img&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">imread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ......
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">detector&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">detectAndDecode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">img&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">points&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ......
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>调试器一挂马上看见问题，在 wechat_qrcode/src/zxing/qrcode/decoder/decoded_bit_stream_parser.cpp:198 行，有&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// try to repair count data if count data is invalid
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">count&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">8&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">available&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">available&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">7&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ArrayRef&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">char&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">bytes_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">readBytes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">bytes_&lt;/span>&lt;span class="p">)[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// CRASH HERE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">count&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// readBytes[i] = (char) bits.readBits(8);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">readBits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">available&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">8&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="nl">available&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">readBytes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">char&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">bits&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">readBits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">readBits&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">err_handler&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>count 为 0，这个时候还在分配 readBytes 读了它第 1 个元素，所以崩溃了。简单修复方法就是 count &amp;lt;= 0 那么 return 掉。&lt;/p>
&lt;p>为什么会这样呢？二维码中数据分为不同区段（segment），其中有一个比较灵活的字节段（byte segment）。
在二维码原始信息中字节段这样储存：&lt;/p>
&lt;p>Version 1-9:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Mode&lt;/th>
&lt;th>Char Count&lt;/th>
&lt;th>Data&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0100 (Byte)&lt;/td>
&lt;td>00000001 (8 bits in Byte or Kanji Mode)&lt;/td>
&lt;td>(&amp;hellip;)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>所以当我们构建一个这样的二维码：&lt;/p>
&lt;p>（正常段）+（字节模式指示位 4 + 长度位 00000001（8位）） ，并让这段数据刚好占用完二维码的所有数据容量，这样扫码的程序看到字符数量应该有1，会来读这个段，但之后又没有数据，从而造成了上面的错误。&lt;/p>
&lt;p>为什么要用二维码的字节段呢？因为字节段的长度位正好是8位，其实日语段用的也是8位，同样可以。数字模式是10位、字符数字段是9位，触发不了这个 bug （但没看有没有其他的 bug）。&lt;/p>
&lt;p>在 opencv_contrib 那提交了个简单的 pr : &lt;a href="https://github.com/opencv/opencv_contrib/pull/3479">https://github.com/opencv/opencv_contrib/pull/3479&lt;/a>&lt;/p>
&lt;p>刚看了一下自己手速很快啊，其他人在后面也交了 pr ，没再看他们怎么搞的了，感觉够用&lt;/p>
&lt;p>&lt;strong>恶意二维码 POC:&lt;/strong>&lt;/p>
&lt;p>用到了一些 &lt;a href="https://github.com/skip2/go-qrcode">github.com/skip2/go-qrcode&lt;/a> 的私有代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">qrcode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">GenEvil&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;qr.png&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">GenEvil&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">size&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">filename&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">q&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">QRCode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">encoder&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">dataEncoder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">encoded&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">bitset&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Bitset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">chosenVersion&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">qrCodeVersion&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoder&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">dataEncoder&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">minVersion&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">maxVersion&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">numericModeIndicator&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">bitset&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">New&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b1&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">alphanumericModeIndicator&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">bitset&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">New&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b0&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">byteModeIndicator&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">bitset&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">New&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">b0&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">numNumericCharCountBits&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">numAlphanumericCharCountBits&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">9&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">numByteCharCountBits&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoded&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">bitset&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">New&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// padding
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">encoded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">encoder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">modeIndicator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dataModeByte&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">l&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="mi">16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">AppendUint32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">uint32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">l&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">encoder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">charCountBits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dataModeByte&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">l&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">AppendByte&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">byte&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// evil
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">encoded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">encoder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">modeIndicator&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dataModeByte&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoded&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">AppendUint32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">uint32&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nx">encoder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">charCountBits&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dataModeByte&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">//encoded.AppendByte(byte(0), 8)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">chosenVersion&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">qrCodeVersion&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">version&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">level&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">dataEncoderType&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">block&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="nx">block&lt;/span>&lt;span class="p">{{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">26&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">19&lt;/span>&lt;span class="p">}},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">numRemainderBits&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%+v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">chosenVersion&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">q&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">QRCode&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Content&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Level&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">Low&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">VersionNumber&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">chosenVersion&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">version&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">ForegroundColor&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Black&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">BackgroundColor&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">color&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">White&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">encoder&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">encoder&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">data&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">encoded&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">version&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">chosenVersion&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">q&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">WriteFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">filename&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Conda 多用户配置</title><link>https://blog.sigsegv.top/posts/conda-multiple-user/</link><pubDate>Fri, 21 Apr 2023 17:46:59 +0800</pubDate><guid>https://blog.sigsegv.top/posts/conda-multiple-user/</guid><description>&lt;p>先安装 conda ，这里用了 root 用户，安装后的文件都是 &lt;code>root:root&lt;/code> 用户/组，保证文件不会被随便修改。&lt;/p>
&lt;p>把 &lt;code>conda init&lt;/code> 生成的代码粘贴到 &lt;code>/etc/profile.d/conda.sh&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;gt;&amp;gt;&amp;gt; conda initialize &amp;gt;&amp;gt;&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># !! Contents within this block are managed by &amp;#39;conda init&amp;#39; !!&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">__conda_setup&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="s1">&amp;#39;/data/opt/miniconda3/bin/conda&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;shell.bash&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;hook&amp;#39;&lt;/span> 2&amp;gt; /dev/null&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> &lt;span class="nv">$?&lt;/span> -eq &lt;span class="m">0&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$__conda_setup&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -f &lt;span class="s2">&amp;#34;/data/opt/miniconda3/etc/profile.d/conda.sh&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> . &lt;span class="s2">&amp;#34;/data/opt/miniconda3/etc/profile.d/conda.sh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/data/opt/miniconda3/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">unset&lt;/span> __conda_setup
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;lt;&amp;lt;&amp;lt; conda initialize &amp;lt;&amp;lt;&amp;lt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>编辑 &lt;code>/etc/bash.bashrc&lt;/code> ，加入：&lt;/p>
&lt;pre tabindex="0">&lt;code>source /etc/profile.d/conda.sh
&lt;/code>&lt;/pre>&lt;p>这样 non-login shell 也能正常运行 conda 。&lt;/p>
&lt;p>先关掉 &lt;code>auto_activate_base&lt;/code> ，防止有人不小心往 base 里装包（然后发现装不上）。&lt;/p>
&lt;pre tabindex="0">&lt;code>root:~# conda config --system --set auto_activate_base false
root:~# conda config --show-sources
==&amp;gt; /data/opt/miniconda3/.condarc &amp;lt;==
auto_activate_base: False
&lt;/code>&lt;/pre>&lt;p>下面的设置需要在所有人都受信任的环境下做！硬盘够了就建议不设置，让每个人的环境都放自己目录里面。
安装 &lt;code>acl&lt;/code> 包，设置文件夹权限&lt;/p>
&lt;pre tabindex="0">&lt;code># 设置 envs 文件夹权限
root:~# sudo chgrp $YOURGROUP /data/opt/miniconda3/envs
root:~# sudo chmod 775 /data/opt/miniconda3/envs
# 让 conda 优先在 conda root 下的 envs 文件夹里建包，需要设置一个 magic file 的写权限
# https://github.com/conda/conda/blob/7ec97d67e05f63260628c33647c68cf455dfbc40/conda/base/context.py#L644
root:~# sudo chgrp $YOURGROUP /data/opt/miniconda3/conda-meta/history
root:~# sudo chmod 775 /data/opt/miniconda3/conda-meta/history
# 设置 pkgs 文件夹权限，包括下载的包、缓存
root:~# sudo chmod g+s pkgs
root:~# sudo setfacl -d -R -m g::rwx /data/opt/miniconda3/pkgs
root:~# sudo setfacl -d -R -m o::rx /data/opt/miniconda3/pkgs
&lt;/code>&lt;/pre></description></item><item><title>将 native library 打包进 jar 里</title><link>https://blog.sigsegv.top/posts/jar-with-so/</link><pubDate>Tue, 11 Apr 2023 22:15:47 +0800</pubDate><guid>https://blog.sigsegv.top/posts/jar-with-so/</guid><description>&lt;p>有一个 Java 写的程序，需要在 Spark 上跑，所以运行之前没法设置环境变量，也没办法设置 Java 运行参数（不知道是否真的不能设置，但甲方这么说，无奈啊），需要在运行的时候把 native library 加载起来。之前没有怎么写过 Java ，琢磨了一下反正也写出来了。&lt;/p>
&lt;h1 id="tensorflow-java-1140-的-bug">Tensorflow Java 1.14.0 的 bug&lt;/h1>
&lt;p>Tensorflow 在运行的时候， Linux 报：&lt;/p>
&lt;pre tabindex="0">&lt;code>Exception in thread &amp;#39;main&amp;#39; java.lang.UnsatisfiedLinkError: /tmp/tensorflow_native_libraries-1680337452521-0/libtensorflow_jni.so: libtensorflow_framework.so.1: 无法打开共享文件：没有那个文件或目录
at ...
at java.lang.System.load(System.java:1100)
at org.tensorflow.NativeLibrary.load(NativeLibrary.java:101)
at ...
&lt;/code>&lt;/pre>&lt;p>看这个 PR &lt;a href="https://github.com/tensorflow/tensorflow/pull/32829">tensorflow/tensorflow#32829&lt;/a> ， tensorflow 解压了 &lt;code>libtensorflow_framework.so&lt;/code> ，但 &lt;code>libtensorflow_jni.so&lt;/code> 链接到的是 &lt;code>libtensorflow_framework.so.1&lt;/code> 这个文件。&lt;/p>
&lt;p>在这个版本的话，要想修复这个问题有很多种办法，比如用 maven-plugin-shade 在打包的时候把这个类替换掉。但是甲方还是不同意～另一个简单的方法就是抢先在 tensorflow 初始化前先把 libtensorflow_framework.so 加载进来，同 tf 加载的套路一样，从资源文件 jar 里把 so 解压出来，然后调用 &lt;code>System.load&lt;/code> 加载就 OK 了。&lt;/p>
&lt;h1 id="jscip-的编译和打包">JSCIP 的编译和打包&lt;/h1>
&lt;p>之所以之前那么加载是 OK 的，是因为如果一个动态链接库已经被 Java 加载了，那么依赖它的动态链接库就可以直接用内存中的这个库，而不会再重新从文件系统中再加载。这就意味着不能直接用 conda-forge 打包的 SCIP ，或者是直接用 conda-forge 的工具链去编译。（倒不是说 conda-forge 的工具链不能编译，而是 conda-forge 里包的依赖关系做得太烂，很多 native 的包依赖高版本 glibc ，而没有把 sysroot 写进依赖里。）&lt;/p>
&lt;p>目标机器是 CentOS 7 ，为了简单本地搭了一下环境：&lt;/p>
&lt;ul>
&lt;li>GCC 9 从 centos scl 里来，虽然最后 C++ 遇到了 C++11 ABI 不兼容的问题，但貌似问题不大（为啥啊？）&lt;/li>
&lt;li>CMake 3 从 EPEL 里来&lt;/li>
&lt;li>Boost 从 Boost 网站上来&lt;/li>
&lt;li>TBB 从 GitHub 上来&lt;/li>
&lt;li>其他一些依赖也从 EPEL 里来&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cmake .. -DTBB_DIR&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">pwd&lt;/span>&lt;span class="k">)&lt;/span>&lt;span class="s2">/../../tbb2019_20190320oss&amp;#34;&lt;/span> -DBOOST_INCLUDEDIR&lt;span class="o">=&lt;/span>/home/azuk/sciptest/scip2/boost_1_81_0 -DBOOST_LIBRARYDIR&lt;span class="o">=&lt;/span>/home/azuk/sciptest/scip2/boost_1_81_0/libs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>编译 papilo 报错了，但是不想研究为啥，因为 libscip 编译出来了。但是 SCIP 不应该依赖 SoPlex 吗，我不理解啊。。&lt;/p>
&lt;p>之后就是 ldd 把依赖找出来，打进一个 jar 包里，等需要用的时候解压出来就行了。&lt;/p>
&lt;p>参考了 tensorflow 的 NativeLibrary.java 瞎编的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">java.io.File&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">java.io.FileOutputStream&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">java.io.IOException&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kn">import&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nn">java.io.InputStream&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">NativeLibrary&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">boolean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">loaded&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">load&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">loaded&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;classpath: %s&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getProperty&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;java.class.path&amp;#34;&lt;/span>&lt;span class="p">)));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">switch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;windows&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;tbb.dll&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libscip.dll&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;jscip.dll&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;linux&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libtinfo.so.5&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libz.so.1&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libpord-5.3.so&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libquadmath.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libgfortran.so.3&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libgmp.so.10&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libgmpxx.so.4&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libblas.so.3&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libbz2.so.1&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libdl.so.2&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libreadline.so.6&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libtbb.so.2&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libgomp.so.1&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libscotcherr.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libscotch.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libmetis.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libmpiseq-5.3.so&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libesmumps.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libscotchmetis.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libopenblas.so.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libmumps_common-5.3.so&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libdmumps-5.3.so&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libipopt.so.3&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libscip.so.8.0&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;libjscip.so&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">loaded&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">err&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">println&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;NativeLibrary: &amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">loadLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">libName&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jniLibName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">libName&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Load library %s&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jniLibName&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resourceName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">makeResourceName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jniLibName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">InputStream&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jniResource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NativeLibrary&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResourceAsStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resourceName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jniResource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">jniResource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NativeLibrary&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getClass&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">getResourceAsStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resourceName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jniResource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UnsatisfiedLinkError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Native library not found&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">libPath&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tempPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">==&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">tempPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">createTemporaryDirectory&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">tempPath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">deleteOnExit&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempDirectory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempPath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getCanonicalPath&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">libPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">extractResource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jniResource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jniLibName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempDirectory&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UnsatisfiedLinkError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Unable to extract native library.&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libPath&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">os&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;os.name&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">toLowerCase&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">contains&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;linux&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;linux&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">contains&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;os x&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">contains&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;darwin&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;darwin&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">contains&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;windows&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;windows&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">replaceAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\\s&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">architecture&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">arch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;os.arch&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">toLowerCase&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;amd64&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;x86_64&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">arch&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">makeResourceName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">baseName&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/%s-%s/&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">architecture&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">baseName&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">extractResource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">InputStream&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resourceName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">extractToDirectory&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extractToDirectory&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">resourceName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">deleteOnExit&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dstPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">toString&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nbytes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">copy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">resource&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dstPath&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">copy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">InputStream&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dstFile&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">FileOutputStream&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileOutputStream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dstFile&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="o">[&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">20&lt;/span>&lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 1MB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ret&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">read&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">ret&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ret&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">finally&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">close&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">close&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">createTemporaryDirectory&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">baseDirectory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getProperty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;java.io.tmpdir&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">directoryName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;jscip-&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">currentTimeMillis&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;-&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attempt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attempt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1000&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attempt&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">temporaryDirectory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">baseDirectory&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">directoryName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">attempt&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">temporaryDirectory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mkdir&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">temporaryDirectory&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IllegalStateException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Could not create a temporary directory (tried to make &amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">directoryName&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;*) to extract TensorFlow native libraries.&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="报错日志">报错日志&lt;/h1>
&lt;p>最后是编译时的报错日志，反正能用不研究了：&lt;/p>
&lt;pre tabindex="0">&lt;code>[ 15%] Linking CXX executable ../../../bin/soplex_c_testing
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::Matchers::Floating::WithinUlpsMatcher::describe() const&amp;#39;:
TestMain.cpp:(.text+0xc155): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::ReusableStringStream::~ReusableStringStream()&amp;#39;:
TestMain.cpp:(.text+0x192c2): undefined reference to `std::basic_ostringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_ostringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::ReusableStringStream::ReusableStringStream()&amp;#39;:
TestMain.cpp:(.text+0x194b1): undefined reference to `std::basic_ostringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_ostringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: TestMain.cpp:(.text+0x19553): undefined reference to `std::basic_ostringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_ostringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: TestMain.cpp:(.text+0x195c2): undefined reference to `std::basic_ostringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_ostringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::clara::detail::BasicResult&amp;lt;Catch::clara::detail::ParseResultType&amp;gt; Catch::clara::detail::convertInto&amp;lt;unsigned int&amp;gt;(std::string const&amp;amp;, unsigned int&amp;amp;)&amp;#39;:
TestMain.cpp:(.text._ZN5Catch5clara6detail11convertIntoIjEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_[_ZN5Catch5clara6detail11convertIntoIjEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_]+0x20): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::clara::detail::BasicResult&amp;lt;Catch::clara::detail::ParseResultType&amp;gt; Catch::clara::detail::convertInto&amp;lt;long&amp;gt;(std::string const&amp;amp;, long&amp;amp;)&amp;#39;:
TestMain.cpp:(.text._ZN5Catch5clara6detail11convertIntoIlEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_[_ZN5Catch5clara6detail11convertIntoIlEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_]+0x20): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::clara::detail::BasicResult&amp;lt;Catch::clara::detail::ParseResultType&amp;gt; Catch::clara::detail::convertInto&amp;lt;double&amp;gt;(std::string const&amp;amp;, double&amp;amp;)&amp;#39;:
TestMain.cpp:(.text._ZN5Catch5clara6detail11convertIntoIdEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_[_ZN5Catch5clara6detail11convertIntoIdEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_]+0x20): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/TestMain.cpp.o: in function `Catch::clara::detail::BasicResult&amp;lt;Catch::clara::detail::ParseResultType&amp;gt; Catch::clara::detail::convertInto&amp;lt;int&amp;gt;(std::string const&amp;amp;, int&amp;amp;)&amp;#39;:
TestMain.cpp:(.text._ZN5Catch5clara6detail11convertIntoIiEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_[_ZN5Catch5clara6detail11convertIntoIiEENS1_11BasicResultINS1_15ParseResultTypeEEERKSsRT_]+0x20): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/papilo/core/PresolveTest.cpp.o: in function `papilo::ParallelColDetection&amp;lt;double&amp;gt;::execute(papilo::Problem&amp;lt;double&amp;gt; const&amp;amp;, papilo::ProblemUpdate&amp;lt;double&amp;gt; const&amp;amp;, papilo::Num&amp;lt;double&amp;gt; const&amp;amp;, papilo::Reductions&amp;lt;double&amp;gt;&amp;amp;, papilo::Timer const&amp;amp;)&amp;#39;:
PresolveTest.cpp:(.text._ZN6papilo20ParallelColDetectionIdE7executeERKNS_7ProblemIdEERKNS_13ProblemUpdateIdEERKNS_3NumIdEERNS_10ReductionsIdEERKNS_5TimerE[_ZN6papilo20ParallelColDetectionIdE7executeERKNS_7ProblemIdEERKNS_13ProblemUpdateIdEERKNS_3NumIdEERNS_10ReductionsIdEERKNS_5TimerE]+0x374): undefined reference to `__cxa_throw_bad_array_new_length&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/unit_test.dir/papilo/core/PresolveTest.cpp.o: in function `papilo::ParallelRowDetection&amp;lt;double&amp;gt;::execute(papilo::Problem&amp;lt;double&amp;gt; const&amp;amp;, papilo::ProblemUpdate&amp;lt;double&amp;gt; const&amp;amp;, papilo::Num&amp;lt;double&amp;gt; const&amp;amp;, papilo::Reductions&amp;lt;double&amp;gt;&amp;amp;, papilo::Timer const&amp;amp;)&amp;#39;:
PresolveTest.cpp:(.text._ZN6papilo20ParallelRowDetectionIdE7executeERKNS_7ProblemIdEERKNS_13ProblemUpdateIdEERKNS_3NumIdEERNS_10ReductionsIdEERKNS_5TimerE[_ZN6papilo20ParallelRowDetectionIdE7executeERKNS_7ProblemIdEERKNS_13ProblemUpdateIdEERKNS_3NumIdEERNS_10ReductionsIdEERKNS_5TimerE]+0x5ff): undefined reference to `__cxa_throw_bad_array_new_length&amp;#39;
collect2: error: ld returned 1 exit status
make[2]: *** [papilo/test/CMakeFiles/unit_test.dir/build.make:443: papilo/test/unit_test] Error 1
make[1]: *** [CMakeFiles/Makefile2:1329: papilo/test/CMakeFiles/unit_test.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `operator delete(void*, unsigned long)&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `std::overflow_error::overflow_error(char const*)&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `__cxa_throw_bad_array_new_length&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `std::__throw_out_of_range_fmt(char const*, ...)&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `std::runtime_error::runtime_error(char const*)&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `std::invalid_argument::invalid_argument(char const*)&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `operator delete[](void*, unsigned long)&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: ../../../lib/libsoplexshared.so.6.0.2.0: undefined reference to `std::domain_error::domain_error(char const*)&amp;#39;
collect2: error: ld returned 1 exit status
make[2]: *** [soplex/tests/c_interface/CMakeFiles/soplex_c_testing.dir/build.make:112: bin/soplex_c_testing] Error 1
make[1]: *** [CMakeFiles/Makefile2:1553: soplex/tests/c_interface/CMakeFiles/soplex_c_testing.dir/all] Error 2
[ 15%] Building C object scip/src/CMakeFiles/libscip.dir/scip/scipgithash.c.o
[ 15%] Building C object scip/src/CMakeFiles/scip.dir/scip/scipgithash.c.o
[ 15%] Linking CXX shared library ../../lib/libscip.so
[ 15%] Linking CXX executable ../../bin/scip
[ 44%] Built target libscip
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/scip.dir/lpi/lpi_spx2.cpp.o: in function `soplex::SPxBasisBase&amp;lt;double&amp;gt;::readBasis(std::istream&amp;amp;, soplex::NameSet const*, soplex::NameSet const*)&amp;#39;:
lpi_spx2.cpp:(.text._ZN6soplex12SPxBasisBaseIdE9readBasisERSiPKNS_7NameSetES5_[_ZN6soplex12SPxBasisBaseIdE9readBasisERSiPKNS_7NameSetES5_]+0x606): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: lpi_spx2.cpp:(.text._ZN6soplex12SPxBasisBaseIdE9readBasisERSiPKNS_7NameSetES5_[_ZN6soplex12SPxBasisBaseIdE9readBasisERSiPKNS_7NameSetES5_]+0x743): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: CMakeFiles/scip.dir/lpi/lpi_spx2.cpp.o: in function `soplex::SPxSolverBase&amp;lt;double&amp;gt;::factorize()&amp;#39;:
lpi_spx2.cpp:(.text._ZN6soplex13SPxSolverBaseIdE9factorizeEv[_ZN6soplex13SPxSolverBaseIdE9factorizeEv]+0x352): undefined reference to `std::basic_stringstream&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;::basic_stringstream()&amp;#39;
collect2: error: ld returned 1 exit status
make[2]: *** [scip/src/CMakeFiles/scip.dir/build.make:5864: bin/scip] Error 1
make[1]: *** [CMakeFiles/Makefile2:2505: scip/src/CMakeFiles/scip.dir/all] Error 2
^Cmake[2]: *** [soplex/src/CMakeFiles/soplex.dir/build.make:83: soplex/src/CMakeFiles/soplex.dir/soplexmain.cpp.o] Interrupt
make[1]: *** [CMakeFiles/Makefile2:1496: soplex/src/CMakeFiles/soplex.dir/all] Interrupt
make: *** [Makefile:183: all] Interrupt
&lt;/code>&lt;/pre></description></item><item><title>某软件 2.5 破解版中问题的修复</title><link>https://blog.sigsegv.top/posts/somesoftware-v25-hack-fix/</link><pubDate>Sat, 01 Apr 2023 22:13:28 +0800</pubDate><guid>https://blog.sigsegv.top/posts/somesoftware-v25-hack-fix/</guid><description>&lt;p>写这篇文章仅供学习软件技术，不放任何链接。&lt;/p>
&lt;p>某软件在网络上比较流行的 v2.5 破解版中有个小问题，比如在输入多行公式的时候，撤销公式反而变成了复制公式。这个功能影响还是比较大的，网络上有大神已经研究过并进行了相应的处理： j29778fcdg&lt;/p>
&lt;p>但想一想这个事情比较蹊跷的，一个破解把正常的功能给破坏掉了，说明原先的破解就有问题。能不能找一下原来的补丁存在什么问题，直接在原来的补丁上动手脚呢？&lt;/p>
&lt;p>对比一下看到只有一个函数不一样（所以还是没弄懂原作者的思路，只看到部分段里的数据不一样了）：&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/somesoftware-v25-hack-fix/1.jpg" alt="func matching">&lt;/p>
&lt;p>简单对比一下，其实就是把 do-while 循环给打开了&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/somesoftware-v25-hack-fix/2.jpg" alt="func comparasion">&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/somesoftware-v25-hack-fix/3.jpg" alt="disassemble result">&lt;/p>
&lt;p>还原回去，发现内存越界访问。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/somesoftware-v25-hack-fix/4.jpg" alt="access violation">&lt;/p>
&lt;p>在这里打个断点，发现撤销时也用到了这个函数，猜测这个函数是一个通用的链表清理函数。因为没有完全清理完链表中的内容，发生了撤销时却复制内容的奇怪现象。&lt;/p>
&lt;p>由于此时 EAX 为 0 ，推测数据结构中最后一个元素此位置为0。把 operator delete 这个对内存影响较小的函数忽略掉，腾出点位置专门给 EAX 写一个判断。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/somesoftware-v25-hack-fix/5.jpg" alt="fix asm">&lt;/p>
&lt;p>重新运行程序，问题消失，破解程序没有其他问题了，这个修复方法要比 B 站文章里提到的简单一些。&lt;/p></description></item><item><title>MSVC 在简体中文 Windows 下的编码问题</title><link>https://blog.sigsegv.top/posts/msvc-encoding/</link><pubDate>Mon, 13 Feb 2023 23:05:39 +0800</pubDate><guid>https://blog.sigsegv.top/posts/msvc-encoding/</guid><description>&lt;p>前几天有人遇到了这样的问题：在编译 C 程序时，更改 &lt;code>extern&lt;/code> 修饰的变量没有起作用，源文件大概如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// main.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdint.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">f&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// f.c
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">extern&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 此时这个语句将会如何执行？编译器会报错吗？链接器会报错吗？
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>具体表现为程序运行输出 1 。我用 gcc 试了下，没有复现这个问题。继续追问，是在 MSVC 编译程序时出现了这个问题。我一试还真是这样！&lt;/p>
&lt;p>为了研究哪里出了问题，我把程序整理成类似如上的最小单元，这个问题又神奇地消失了， amazing 。仔细看编译日志，发现了这样一行：&lt;/p>
&lt;pre tabindex="0">&lt;code>D:\a&amp;gt;cl f.c main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31935 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
f.c
f.c(1): warning C4819: The file contains a character that cannot be represented in the current code page (936). Save the file in Unicode format to prevent data loss
main.c
Generating Code...
Microsoft (R) Incremental Linker Version 14.34.31935.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:f.exe
f.obj
main.obj
&lt;/code>&lt;/pre>&lt;p>快速在 cmd 里验证一下：&lt;/p>
&lt;pre tabindex="0">&lt;code>D:\a&amp;gt;type f.c
// f.c
extern int x;
void f()
{
// 姝ゆ椂杩欎釜璇彞灏嗕細濡備綍鎵ц锛熺紪璇戝櫒浼氭姤閿欏悧锛熼摼鎺ュ櫒浼氭姤閿欏悧锛? x = -0.0;
}
&lt;/code>&lt;/pre>&lt;p>果然是编码出了问题。从 Linux 下复制过来的时候换行符只有 LF ，这个时候在 CP936 里换行就消失了。与此对应的是， GB18030(CP54936) 和 UTF-8 (CP65001) 下都能正常工作。&lt;/p>
&lt;blockquote>
&lt;p>CP936 是一个稍微混乱的东西，一般认为它对应的是 GBK 。详细可看 &lt;a href="https://en.wikipedia.org/wiki/Code_page_936_(Microsoft_Windows)">Code page 936 (Microsoft Windows)&lt;/a> 。&lt;/p>
&lt;/blockquote>
&lt;p>解决方法：给 cl 打开 /utf-8 参数&lt;/p></description></item><item><title>Fixing Mamba for Multiple User Environment</title><link>https://blog.sigsegv.top/posts/fixing-mamba-for-multi-user/</link><pubDate>Mon, 19 Dec 2022 22:01:33 +0800</pubDate><guid>https://blog.sigsegv.top/posts/fixing-mamba-for-multi-user/</guid><description>&lt;p>When it comes to python environment management, I would not hesitate to suggest &lt;em>conda&lt;/em> as a sysadmin. It just works most of the time for python packages, python itself, and other native stuff. The problem is, conda works, but it&amp;rsquo;s too slow. Conda finally went beyond my endurance solving a maybe complex environment for a full hour, so I chose &lt;em>mamba&lt;/em>, a reimplementation of conda package manager written in C++.&lt;/p>
&lt;p>Mamba is great, it&amp;rsquo;s fast and solving the packages the right way. But as mamba being a reimplementation, we cannot expect it works like an alias. When I tried to set up a multi-user conda environment on my server, a strange error happened:&lt;/p>
&lt;pre tabindex="0">&lt;code>(base) ~$ mamba install ipython
Looking for: [&amp;#39;ipython&amp;#39;]
filesystem error: cannot set file time: Operation not permitted [/opt/mambaforge/pkgs/cache/09cdf8bf.json]
# &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; ERROR REPORT &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;
Traceback (most recent call last):
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/conda/exceptions.py&amp;#34;, line 1118, in __call__
return func(*args, **kwargs)
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/mamba.py&amp;#34;, line 936, in exception_converter
raise e
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/mamba.py&amp;#34;, line 929, in exception_converter
exit_code = _wrapped_main(*args, **kwargs)
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/mamba.py&amp;#34;, line 887, in _wrapped_main
result = do_call(parsed_args, p)
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/mamba.py&amp;#34;, line 750, in do_call
exit_code = install(args, parser, &amp;#34;install&amp;#34;)
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/mamba.py&amp;#34;, line 497, in install
index = load_channels(pool, channels, repos)
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/utils.py&amp;#34;, line 129, in load_channels
index = get_index(
File &amp;#34;/opt/mambaforge/lib/python3.10/site-packages/mamba/utils.py&amp;#34;, line 110, in get_index
is_downloaded = dlist.download(api.MAMBA_DOWNLOAD_FAILFAST)
RuntimeError: filesystem error: cannot set file time: Operation not permitted [/opt/mambaforge/pkgs/cache/09cdf8bf.json]
&lt;/code>&lt;/pre>&lt;p>\(Only newer versions of mamba shows the "cannot set file time" prompt.\)&lt;/p>
&lt;p>Mamba failed to update the package index. After some googling, I found the two issues in mamba-org/mamba: &lt;a href="https://github.com/mamba-org/mamba/issues/488">#488 RuntimeError on cache in multi-users case&lt;/a>, &lt;a href="https://github.com/mamba-org/mamba/issues/1123">#1123 Resolving from cache fails when cache dir rights are applied with ACL&amp;rsquo;s&lt;/a>. It seems the current work around is to delete package index manually before installing new packages, but why? After some tests, conda seems to work without this bug.&lt;/p>
&lt;p>I&amp;rsquo;m not familiar with C++ STL, so I decided to build it locally and debug. &lt;code>dlist&lt;/code> is an instance of &lt;code>api.DownloadTargetList&lt;/code>, defined in C++ source &lt;code>libmambapy&lt;/code> pointing to class &lt;code>MultiDownloadTarget&lt;/code> in &lt;code>fetch.cpp&lt;/code>. A quick scan of the source was of no help, I decided to debug it with gdb.&lt;/p>
&lt;p>Build and install &lt;code>libmamba&lt;/code>, &lt;code>libmambapy&lt;/code> and &lt;code>mamba&lt;/code> for debug:&lt;/p>
&lt;pre tabindex="0">&lt;code>(base) $ # install packages from environment-dev.yml
(mdev) $ cmake -B build/ \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_LIBMAMBA=ON \
-DBUILD_SHARED=ON \
-DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \
-DCMAKE_PREFIX_PATH=$CONDA_PREFIX \
-DBUILD_LIBMAMBAPY=ON
(mdev) $ make install -C build/ -j $(($(nproc) + 1))
(mdev) $ pip install -e libmambapy/ --no-deps
(mdev) $ pip install -e mamba/ --no-deps
&lt;/code>&lt;/pre>&lt;p>The compiled mamba in different environment use a different path for cache, mamba will print the path in verbose mode. \(base for \$CONDA_PREFIX/pkgs/cache, others for \$CONDA_PREFIX/env/ENV_NAME/pkgs/cache\) According to Anaconda documentation for &lt;a href="https://docs.anaconda.com/anaconda/install/multi-user/">Installing for multiple users&lt;/a>, set up the environment:&lt;/p>
&lt;pre tabindex="0">&lt;code>$ # create a new group and user
$ sudo groupadd testg
$ sudo useradd -m testu
$ sudo usermod -aG testg testu
$ sudo usermod -aG testg $USER
$ # trigger the creation of index cache
$ # e.g. mamba install ipython
$ # change the ownership
$ sudo chgrp -R /opt/mambaforge
$ sudo chmod 770 -R /opt/mambaforge
$ # oh we are not the files&amp;#39; owner
$ sudo chown testu /opt/mambaforge/pkgs/cache/*
$ # make the cache expired
$ sudo find /opt/mambaforge/pkgs/cache -exec touch -d &amp;#34;7 days ago&amp;#34; {} +
&lt;/code>&lt;/pre>&lt;p>Conda use a magic hash for each channel index cache json. So after creating the cache file, setting the group write bit on the cache, everyone in the group can &lt;strong>update&lt;/strong> the index themselves. Mamba does so, but likely it use a different hash function, the files are different from which conda uses.&lt;/p>
&lt;p>Attach gdb to the process:&lt;/p>
&lt;pre tabindex="0">&lt;code>(mdev) ~$ gdb -q --args python -m mamba.mamba install ipython
gef➤ catch throw
Catchpoint 1 (throw)
gef➤ r
&lt;/code>&lt;/pre>&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/fixing-mamba/1.jpg" alt="backtrace">&lt;/p>
&lt;p>When mamba got 304 Not Modified from HTTP server, it just updates the timestamps. But &lt;code>std::filesystem::last_write_time&lt;/code> led to the problem. On Linux, th underlying &lt;code>utimensat()&lt;/code> updates the timestamps of a file, but if you are not the file owner, or you have no appropriate privilege, the timestamps cannot be modified to a time except now, even if you have write access to the file. A quick &lt;code>strace&lt;/code> verified my thought.&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/fixing-mamba/2.jpg" alt="strace result">&lt;/p>
&lt;p>&lt;code>std::filesystem::last_write_time&lt;/code> is not a perfect function. It works in most scenes, but it&amp;rsquo;s also limited. To fix the problem, simply substitute the function with OS specific functions with appropriate parameters, like &lt;code>utimensat(dirfd, pathname, NULL, flags)&lt;/code> on POSIX systems. We got a library function to avoid OS specific syscalls, but in the end we fix the bug by re-introducing the syscalls, oops. Taking &lt;a href="https://github.com/boostorg/filesystem/blob/141727b568fad2fecb77b2233a407cf4c00638b8/src/operations.cpp#L3843">&lt;code>boost::filesystem::last_write_time&lt;/code>&lt;/a> as a reference, a hack maybe applied to the &lt;code>std::time_t new_time&lt;/code> parameter to touch the file, but since it&amp;rsquo;s an undocumented behavior we cannot guarantee that in different platforms or libc it works.&lt;/p>
&lt;p>BTW, conda works because it directly calls &lt;code>os.utime&lt;/code> &lt;a href="https://github.com/conda/conda/blob/82fa24baedcb42a9504912f6f851aeeae2fc478f/conda/gateways/disk/update.py#L122">\[source\]&lt;/a>. Yes, &lt;code>os.time&lt;/code> is available on Windows! &lt;a href="https://github.com/python/cpython/blob/a87c46eab3c306b1c5b8a072b7b30ac2c50651c0/Modules/posixmodule.c#L5404">\[source\]&lt;/a> Python beats C++ again, LOL. Another reference is GNULib &lt;a href="https://github.com/coreutils/gnulib/blob/08ba9aaebff69a02cbb794c6213314fd09dd5ec5/lib/utimens.c">lib/utimens.c&lt;/a>. It handles utimensat on each unix platform correctly, and a Windows shim was provided for the POSIX API.&lt;/p>
&lt;p>A quick dirty fix for this: hooking it&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;fcntl.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;sys/stat.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stddef.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define __USE_GNU
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;dlfcn.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">utimensat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">dirfd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">pathname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">timespec&lt;/span> &lt;span class="n">times&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">outimensat&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">timespec&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">dlsym&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RTLD_NEXT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;utimensat&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nf">outimensat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dirfd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pathname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">timespec&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compile and load it when using mamba:&lt;/p>
&lt;pre tabindex="0">&lt;code>$ cc fixmamba.c -fPIC -shared -o fixmamba.so -ldl
$ LD_PRELOAD=&amp;#34;$(pwd)/fixmamba.so&amp;#34; mamba install ipython
&lt;/code>&lt;/pre></description></item><item><title>Arch Linux 下将 Hostapd 仅当作 Radius 服务器使用</title><link>https://blog.sigsegv.top/posts/arch-hostapd-as-radius-server/</link><pubDate>Fri, 11 Nov 2022 16:24:52 +0800</pubDate><guid>https://blog.sigsegv.top/posts/arch-hostapd-as-radius-server/</guid><description>&lt;p>从 Arch Linux 获取软件源代码&lt;/p>
&lt;pre tabindex="0">&lt;code>asp export hostapd
&lt;/code>&lt;/pre>&lt;p>允许 hostapd 以纯 radius 服务器运行&lt;/p>
&lt;pre tabindex="0">&lt;code># 编辑 config ，取消注释
CONFIG_DRIVER_NONE=y
&lt;/code>&lt;/pre>&lt;p>安装软件包&lt;/p>
&lt;pre tabindex="0">&lt;code>makepkg -si --skippgpcheck
&lt;/code>&lt;/pre>&lt;p>编辑 &lt;code>/etc/pacman.conf&lt;/code> ，防止升级时 hostapd 被替换&lt;/p>
&lt;pre tabindex="0">&lt;code>IgnorePkg = hostapd
&lt;/code>&lt;/pre></description></item><item><title>华为昇腾的 NPU 真的不好用</title><link>https://blog.sigsegv.top/posts/hiascend-npu-oops/</link><pubDate>Sat, 29 Oct 2022 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/hiascend-npu-oops/</guid><description>&lt;p>今天稚晖君在智能基座高校活动上又一次安利了昇腾生态，加上他本身也是昇腾的人员，我又一次尝试了 NPU 的生态。其实我之前也用过 NPU ，当时就很怀疑人生，但实在想不起来应该吐槽些什么了。活动上本来希望其他人能提些有用的问题，结果整个一狂粉见面会，有些同学恨不得连人家工位在哪个位置都要问出来，不愧是三本。。&lt;/p>
&lt;p>由于遇到的问题很多，之后也许还会遇到其他问题，打算把坑集中记录在这里。&lt;/p>
&lt;h1 id="npu-生态初探">NPU 生态初探&lt;/h1>
&lt;p>华为的 NPU 生态对标的主要是 NVIDIA 计算卡的生态：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>HUAWEI&lt;/th>
&lt;th>NVIDIA&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>昇腾推理卡/训练卡&lt;/td>
&lt;td>计算卡&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NPU firmware&lt;/td>
&lt;td>nvidia driver&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CANN&lt;/td>
&lt;td>CUDA&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MindSpore/AscendPytorch/Ascend Tensorflow&lt;/td>
&lt;td>Pytorch/Tensorflow&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>智能基座活动：“我们华为生态的优势就是所有产品都是我们自己做的。”&lt;/p>
&lt;/blockquote>
&lt;h1 id="产品分商业版和社区版">产品分商业版和社区版&lt;/h1>
&lt;p>以 CANN 为例，官方这么说明：&lt;/p>
&lt;blockquote>
&lt;p>社区版：快速提供新特性的体验版，供开发者提前试用
商业版：满足商用标准的稳定版本&lt;/p>
&lt;/blockquote>
&lt;p>忘了在哪里还曾经看到社区版是不能用于商业环境的，只有商业版可以。现在我找不到相关的说明了，暂且当它不存在。但商业版仍然不能让用户直接下载，需要申请华为的许可才能下载！也就是说，不跟华子谈好，你用的是一个“不保证稳定”的版本。到底需不需要花钱才能拿到我不知道，因为之前 NPU 的固件、驱动是只有商业版本的，你就算买了服务器、硬件，也下载不了驱动，需要跟经销商去要。&lt;/p>
&lt;p>对比 CUDA ，开发和部署用的就是一套东西，也没有什么商业、社区的区别。这 CANN 除了华子自己的 NPU 也没玩意能用，结果交了一次硬件的钱还要再交一次软件的钱。&lt;/p>
&lt;h1 id="版本混乱">版本混乱&lt;/h1>
&lt;h2 id="1-大版本号乱变">1. 大版本号乱变&lt;/h2>
&lt;p>让我们来看看 &lt;a href="https://www.hiascend.com/zh/software/cann/notice">《版本号变更通告》&lt;/a> ，这次改版本号时，直接把 20.1.alpha001 改到 3.1.0.alpha001 。&lt;/p>
&lt;p>在装 mindspore 1.9.0 时（文章撰写时的最新版），我能在官网上下载到的 CANN 版本是 6.0.0.alpha001 。此时 mindspore 无法正常工作，根据提 issue 后官网的修改，它需要 Ascend Data Center Solution 22.0.RC3 才能运行。&lt;/p>
&lt;p>不觉得这很酷吗？很符合我对乱刷版本号的印象。&lt;/p>
&lt;h2 id="2-rc-版与正式版间产生大幅度改动">2. RC 版与正式版间产生大幅度改动&lt;/h2>
&lt;p>6.0.RC1.alpha003 与 6.0.0.alpha001 这两个版本间库文件放置的路径都不一样，导致 mindspore 出错找不到文件。我理解 RC 是 Release Candidate 的意思，别人的 RC 可能和正式版都没什么区别了，这不仅候选了三个版本，还要有破坏性变动放在里面。&lt;/p>
&lt;p>你的下一个大版本号，何必是大版本号。&lt;/p>
&lt;h2 id="3-mindspore-发布的正式版依赖了还没有发布的-cann-版本">3. mindspore 发布的正式版依赖了还没有发布的 CANN 版本&lt;/h2>
&lt;p>mindspore 1.9.0 ，目前官网文档还在那挂着没有下载链接呢。让我们问一下华子怎么说：&lt;/p>
&lt;blockquote>
&lt;p>智能基座活动：“我们华为生态的优势就是所有产品都是我们自己做的。”&lt;/p>
&lt;/blockquote>
&lt;p>优势是需要某个东西直接给对应团队 oncall 要二进制文件？然后让用户挂在这里。&lt;/p>
&lt;h2 id="4-为了使用不同的框架你需要安装一堆-cann-甚至是-rc-版">4. 为了使用不同的框架，你需要安装一堆 CANN ，甚至是 RC 版&lt;/h2>
&lt;p>昇子魔改的 pytorch 依赖于 RC 版的 CANN ， RC 和 正式版间有大幅度变动，你不用 RC 可能就跑不起来了。&lt;/p>
&lt;p>有人说用 cuda 的 pytorch 不也依赖于某个 cuda 版本吗？让我们来看看怎么安装 pytorch&lt;/p>
&lt;pre tabindex="0">&lt;code>conda install pytorch torchvision torchaudio pytorch-cuda=11.6 -c pytorch -c nvidia
&lt;/code>&lt;/pre>&lt;p>啊这，一行命令就安装好指定版本了。让我们来看看怎么安装 mindspore ：&lt;/p>
&lt;blockquote>
&lt;p>昇腾软件包提供商用版和社区版两种下载途径：&lt;/p>
&lt;ul>
&lt;li>商用版下载需要申请权限，下载链接与安装方式请参考[Ascend Data Center Solution 22.0.RC3安装指引文档]。&lt;/li>
&lt;/ul>
&lt;p>安装包默认安装路径为/usr/local/Ascend。安装后确认当前用户有权限访问昇腾AI处理器配套软件包的安装路径，若无权限，需要root用户将当前用户添加到/usr/local/Ascend所在的用户组。&lt;/p>
&lt;p>安装昇腾AI处理器配套软件包提供的whl包，whl包随配套软件包发布。如果之前安装过昇腾AI处理器配套软件包，需要先使用如下命令卸载相应的包。&lt;/p>
&lt;p>pip uninstall te topi hccl -y&lt;/p>
&lt;p>默认安装路径使用以下指令安装。如果安装路径不是默认路径，需要将命令中的路径替换为安装路径。&lt;/p>
&lt;p>pip install sympy
pip install /usr/local/Ascend/ascend-toolkit/latest/lib64/topi-&lt;em>-py3-none-any.whl
pip install /usr/local/Ascend/ascend-toolkit/latest/lib64/te-&lt;/em>-py3-none-any.whl
pip install /usr/local/Ascend/ascend-toolkit/latest/lib64/hccl-*-py3-none-any.whl&lt;/p>
&lt;/blockquote>
&lt;p>别忘了使用的时候还要 source 环境变量文件哦，要先 source CANN 的，再 source mindspore 的。而且这些配置文件里默认都写的 latest ，需要自己改路径。&lt;/p>
&lt;h1 id="mindspore-软件质量差">Mindspore 软件质量差&lt;/h1>
&lt;p>See &lt;a href="https://gitee.com/mindspore/mindspore/issues/I5YCC4#note_14021343">https://gitee.com/mindspore/mindspore/issues/I5YCC4#note_14021343&lt;/a>&lt;/p>
&lt;p>&amp;lsquo;/fwkacllib/lib64&amp;rsquo; &amp;lsquo;/add-ons&amp;rsquo; 已经不用了，但报警还有。因为文档差，也查不到怎么回事，留下用户一脸蒙蔽。&lt;/p>
&lt;p>真正的启动检查报错（让程序无法运行那种）也是 warning ，嗯。&lt;/p>
&lt;h1 id="pytorch-使用麻烦">pytorch 使用麻烦&lt;/h1>
&lt;p>需要自己编译，编译之后我还没跑通测试。现在提了 issue ，等昇子跟进。&lt;/p>
&lt;p>已经两天了，pytorch 这边工作人员比 mindspore 那的懒很多。&lt;/p>
&lt;p>对比一下人家 intel ：[https://github.com/intel/intel-extension-for-pytorch][https://github.com/intel/intel-extension-for-pytorch] ，真的华子你能不能学学人家，他做得比你晚，还做得比你好。&lt;/p>
&lt;p>而且 pytorch 只魔改了几个版本，魔改后的 pytorch 自己的代码很多地方都要改。&lt;/p>
&lt;p>bug 很多，之前跑三层的 LSTM ，用 CPU 跑能出结果，用 NPU 跑返回 None ，错也不报。&lt;/p>
&lt;p>UPDATE:&lt;/p>
&lt;p>虽然已经在各个分支里写上有 pytorch 1.11 的支持，但实际上没有支持。呃。。&lt;/p>
&lt;p>&lt;a href="https://gitee.com/ascend/pytorch/issues/I5YE0Y">https://gitee.com/ascend/pytorch/issues/I5YE0Y&lt;/a>&lt;/p>
&lt;h1 id="文档稀碎">文档稀碎&lt;/h1>
&lt;p>莫名其妙的报错，莫名其妙的错误号，一搜网上全是新闻，一点文档找不到啊。&lt;/p>
&lt;p>同样在等 issue 回复。&lt;/p>
&lt;p>UPDATE:&lt;/p>
&lt;p>建议放弃幻想。&lt;/p>
&lt;h1 id="设备贵">设备贵&lt;/h1>
&lt;p>Atlas 800 (Model 3010) ，系统配置：&lt;/p>
&lt;ul>
&lt;li>Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz x 2&lt;/li>
&lt;li>64 GiB DDR4 Memory&lt;/li>
&lt;li>Ascend 910A&lt;/li>
&lt;/ul>
&lt;p>总价 13w ，这价格你完全可以去买（甚至是涨价后的） A100 。别忘了，910A 是推理卡，你可能还需要一张训练卡。&lt;/p></description></item><item><title>OpenWrt 用 hostapd 作为 Radius 服务器配置 WPAx-EAP 认证</title><link>https://blog.sigsegv.top/posts/openwrt-hostapd-radius-server/</link><pubDate>Fri, 02 Sep 2022 13:11:19 +0800</pubDate><guid>https://blog.sigsegv.top/posts/openwrt-hostapd-radius-server/</guid><description>&lt;p>&lt;a href="https://w1.fi/hostapd/">hostapd&lt;/a> 代码中有一个集成的 Radius 服务器，占用资源比较少。对于想简单尝试一下 EAP 认证的个人用户来说已经完全够用，配置起来也要比 freeradius3 等软件方便。然而 OpenWrt 的相关软件包在编译的时候并没有开启相关的编译选项，所以需要手动更改该软件包的参数进行编译。&lt;/p>
&lt;h1 id="相关软件编译">相关软件编译&lt;/h1>
&lt;p>我的 OpenWrt 是从源码编译的，所以直接配置好了各项工具。对于使用官方镜像或镜像生成器的用户，可以使用官方提供的 SDK 设置好编译环境。&lt;/p>
&lt;p>OpenWrt 同时使用 hostapd 与 wpa_supplicant ，并对代码打了补丁使得这两个程序编译成一个 multicall binary ，即 wpad 。根据自己的需要，勾选 wpad-openssl 或 hostapd-openssl 其中一项。不要使用 wolfssl 的版本，有 bug 会导致 hostapd 无法正常处理数据。&lt;/p>
&lt;p>编辑 &lt;code>package/network/services/hostapd/files/hostapd-full.config&lt;/code> ，启用如下编译选项：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">CONFIG_DRIVER_NONE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">y&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">CONFIG_RADIUS_SERVER&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">y&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>编译软件包：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">make package/hostapd/compile -j &lt;span class="k">$($(&lt;/span>nproc&lt;span class="k">)&lt;/span>+1&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>根据自己的情况可以选择直接用 opkg 覆盖该软件包，或整合入镜像文件中。&lt;/p>
&lt;h1 id="radius-服务器配置">Radius 服务器配置&lt;/h1>
&lt;p>这里以 EAP-TLS 为例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">mkdir -p /etc/hostapd/certs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">touch /etc/hostapd/hostapd.&lt;span class="o">{&lt;/span>conf,eap_user,radius_clients&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>/etc/hostapd/hostapd.conf&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">driver&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">none&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">logger_syslog&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">127&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">logger_syslog_level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">logger_stdout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">127&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">logger_stdout_level&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">eap_server&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">eap_user_file&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">/etc/hostapd/hostapd.eap_user&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">server_cert&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">/etc/hostapd/certs/eap.crt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">private_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">/etc/hostapd/certs/eap.key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">ca_cert&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">/etc/hostapd/certs/ca_chain.pem&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">check_crl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">radius_server_clients&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">/etc/hostapd/hostapd.radius_clients&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">radius_server_auth_port&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">1812&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>/etc/hostapd/hostapd.eap_user&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">* TLS
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>/etc/hostapd/hostapd.radius_clients&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">127.0.0.1 your_radius_key
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">192.168.1.0/24 if_you_wanna_test_it
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>尝试启动 hostapd ，查看是否有报错：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">/usr/sbin/hostapd -s /etc/hostapd/hostapd.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>设置最好不要把 radius 服务器绑到某个 &lt;code>/etc/config/wireless&lt;/code> 配置下，这样太过 hack ，我们新建一个系统服务：&lt;/p>
&lt;p>&lt;code>/etc/init.d/hostapd-radius&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/sh /etc/rc.common
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">START&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">90&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">USE_PROCD&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">NAME&lt;/span>&lt;span class="o">=&lt;/span>hostapd-radius
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">start_service&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -x &lt;span class="s2">&amp;#34;/usr/sbin/hostapd&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_open_instance hostapd-radius
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_set_param &lt;span class="nb">command&lt;/span> /usr/sbin/hostapd -s /etc/hostapd/hostapd.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_set_param respawn &lt;span class="m">3600&lt;/span> &lt;span class="m">1&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span> -x /sbin/ujail -a -e /etc/capabilities/wpad.json &lt;span class="o">]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_add_jail hostapd-radius
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_set_param capabilities /etc/capabilities/wpad.json
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_set_param user network
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_set_param group network
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_set_param no_new_privs &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> procd_close_instance
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启用并启动服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">/etc/init.d/hostapd-radius &lt;span class="nb">enable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/init.d/hostapd-radius start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="配置网络使用-radius-服务器">配置网络使用 Radius 服务器&lt;/h1>
&lt;p>可以在 LuCI 里设置，也可以通过命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">uci &lt;span class="nb">set&lt;/span> wireless.@wifi-iface&lt;span class="o">[&lt;/span>0&lt;span class="o">]&lt;/span>.encryption&lt;span class="o">=&lt;/span>wpa2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uci &lt;span class="nb">set&lt;/span> wireless.@wifi-iface&lt;span class="o">[&lt;/span>0&lt;span class="o">]&lt;/span>.auth_server&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;127.0.0.1&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uci &lt;span class="nb">set&lt;/span> wireless.@wifi-iface&lt;span class="o">[&lt;/span>0&lt;span class="o">]&lt;/span>.auth_secret&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;your_radius_key&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">uci commit wireless
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wifi reload
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>暂时不要用 WPA3 或 WPA2/3 Mixed 的 EAP ，因为客户端的支持并不好。 Mixed 下 Windows 正常连接为 WPA2-EAP ， iOS 无法正常连接。 WPA3 下 Windows 无法正常识别加密类型， iOS 显示无法连接。这个问题比较难排查，就这样吧。&lt;/p>
&lt;p>最后吐槽下 OpenWrt 文档很老旧，和现在的情况一点也不一样。&lt;/p></description></item><item><title>LDAP+SSSD 部署</title><link>https://blog.sigsegv.top/posts/ldap-sssd-deployment/</link><pubDate>Mon, 22 Aug 2022 17:32:02 +0800</pubDate><guid>https://blog.sigsegv.top/posts/ldap-sssd-deployment/</guid><description>&lt;h1 id="选择-openldap-与-sssd-的理由">选择 openldap 与 sssd 的理由&lt;/h1>
&lt;p>最近有在不同 Linux 节点（非集群）上统一管理用户的需求，这个时候开始觉得 Windows AD 确实很方便。其实 Linux 机器也可以加入 AD 域， samba 也有相关的功能，暂时还没有探索多少。&lt;/p>
&lt;p>NIS 也是一个轻量的目录服务协议，但配置起来相对死板麻烦。值得注意的是， Ubuntu 22.04 中将 nis 版本更新到了 4 ，配置方式和之前有很大不同，在配置时踩了很多坑。供参考的资料大多也不适用于现在的情况，加上不太能搞懂组件间的联系等，最后放弃了这条道路。&lt;/p>
&lt;p>FreeIPA 是 RH 牵头搞的、全家桶型的身份管理系统。它不仅内置了 bind 来配置 DNS ，有 acme server 来签发证书，支持 kerberos 认证，还有一个 Web 界面来调整设置，非常方便。可以说自己配一套配置到最后，也是需要配好这一坨东西。但由于 bind 的版本依赖问题， Ubuntu 竟然从 20.04 开始就没打包 &lt;code>freeipa-server&lt;/code> 这个包了。 Docker 版本也试了下，第一次启动很慢，运行时占用资源也稍多，启动中间还出了很多错误，暂时也没有精力去排查，更没有换 rh 系发行版的打算，只能搁置了。另外 Docker 封装里有 systemd ，这也是个小问题。&lt;/p>
&lt;p>由于需求其实比较简单，服务器区域准备设置防火墙并没有公开服务，也没有必要去搞得很复杂。所以最后决定自己搭一套 LDAP 。&lt;/p>
&lt;p>最开始尝试的是 ApacheDS ，这是一个用 Java 写的 LDAP 和 Kerberos 服务器。 Ubuntu 里有 ApacheDS 这个包，和 Debian 上游的完全一样，官方源里面这个包可谓非常之坑，我不懂 Java ，它对我来说就更坑了：一个是假如存密码用了 CRYPT 系列的编码，那么 ApacheDS 就完全登录不了了，提示 NoClassDefFoundError 。看了下是缺少 &lt;code>libcommons-codec-java&lt;/code> 这个包的库，但它已经作为依赖被安装了。在我手动把这个库的 jar 复制到 ApacheDS 所在的 lib 目录后，这个问题解决了。但当设置 TLS 的时候，又出现了 &lt;code>No Cipher Suites in Common&lt;/code> 的问题，搜了下网上的意思，都是说应该是别的地方出问题，这下我没有魔改的思路了。但这是发行版打包的问题，因为如果安装并使用的是 ApacheDS 自己打包的 deb ，那就完全不会出现这些问题。他们自己打包的 deb 没有 systemd 整合，其实这还是小问题。关键是他没有源，需要三天两头去检查升级。发行版都有 security 更新，直接更新就好了，这自己去检查可太累了。再加上之前 log4j 爆出问题，谁知道这么一坨没有时常更新的服务什么时候会炸。浅瞄一眼没有合适的 Docker 封装，算了算了。&lt;/p>
&lt;p>最后尝试了最经典的 openldap ，还是它好用啊，一路下来基本没出现过啥问题，妙啊。&lt;/p>
&lt;p>SSSD 可以缓存登录凭据，这样认证服务器挂了（在我的场景下）问题也不算还大，暂时没看 pam-ldap 是否有同样的选项。据说 sssd 还有其他高级功能，没研究。&lt;/p>
&lt;h1 id="openldap-server-的配置">openldap server 的配置&lt;/h1>
&lt;h2 id="设置-hostname-和-fqdn">设置 hostname 和 FQDN&lt;/h2>
&lt;p>首先设置好 hostname 和 FQDN 。因为 sssd 需要加密，加密需要证书。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo hostnamectl hostname ldap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo vim /etc/hosts
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre tabindex="0">&lt;code>...
127.0.0.1 localhost.localdomain localhost
127.0.0.1 ldap.example.com ldap
...
&lt;/code>&lt;/pre>&lt;p>检查设置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ hostname
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ldap
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ hostname -f
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ldap.example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="设置-openldap-server">设置 openldap server&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo apt update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo apt install slapd ldap-utils
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装中可以跳过 openldap 服务器设置，安装完后再进行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo dpkg-reconfigure slapd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Omit OpenLDAP server configuration? &lt;strong>No&lt;/strong>&lt;/li>
&lt;li>DNS domain name: &lt;strong>example.com&lt;/strong>&lt;/li>
&lt;li>Organization name: &lt;strong>Example Organization&lt;/strong>&lt;/li>
&lt;li>Password&lt;/li>
&lt;li>Do you want the database to be removed when slapd is purged? &lt;strong>No&lt;/strong> ，这里根据需要&lt;/li>
&lt;li>Move old database? &lt;strong>Yes&lt;/strong> ，防手滑&lt;/li>
&lt;/ul>
&lt;h2 id="创建证书">创建证书&lt;/h2>
&lt;p>如果有证书就可以跳过自签证书的步骤，直接放好证书给服务用就好了。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo apt install gnutls-bin ssl-cert
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建证书模板：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo mkdir /etc/ssl/templates
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>/etc/ssl/templates/ca_server.conf&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code>cn = LDAP Service CA
ca
cert_signing_key
expiration_days = 3650
&lt;/code>&lt;/pre>&lt;p>&lt;code>/etc/ssl/templates/ldap_server.conf&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code>organization = &amp;#34;Example Organization&amp;#34;
cn = ldap.example.com
tls_www_server
encryption_key
signing_key
expiration_days = 3650
&lt;/code>&lt;/pre>&lt;p>创建 CA 私钥：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo certtool -p --outfile /etc/ssl/private/ca_server.key
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建 CA 证书：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo certtool -s --load-privkey /etc/ssl/private/ca_server.key --template /etc/ssl/templates/ca_server.conf --outfile /etc/ssl/certs/ca_server.pem
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>域名的私钥：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo certtool -p --sec-param high --outfile /etc/ssl/private/ldap_server.key
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>域名的证书：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo certtool -c --load-privkey /etc/ssl/private/ldap_server.key --load-ca-certificate /etc/ssl/certs/ca_server.pem --load-ca-privkey /etc/ssl/private/ca_server.key --template /etc/ssl/templates/ldap_server.conf --outfile /etc/ssl/certs/ldap_server.pem
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>让服务有权限访问这个证书：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo mkdir /etc/ssl/slapd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo cp /etc/ssl/private/ldap_server.key /etc/ssl/slapd/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo chown -R openldap /etc/ssl/slapd/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo chmod &lt;span class="m">710&lt;/span> /etc/ssl/slapd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="配置-openldap-server-使用证书">配置 openldap server 使用证书&lt;/h2>
&lt;p>创建 &lt;code>addcerts.ldif&lt;/code> ：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-ldif" data-lang="ldif">dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/ca_server.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/ldap_server.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/ldap_server.key
&lt;/code>&lt;/pre>&lt;p>顺序必须是： CA 证书、服务证书、服务私钥，不然报实现自定义错误。证书权限忘调整也报错。&lt;/p>
&lt;p>应用更改：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo ldapmodify -H ldapi:// -Y EXTERNAL -f addcerts.ldif
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ sudo systemctl restart slapd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="openldap-客户机配置">openldap 客户机配置&lt;/h1>
&lt;p>如果是自签发证书，需要信任自己的 CA 证书：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo cp /etc/ssl/certs/ca_server.pem /etc/ldap/ca_certs.pem
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>编辑 &lt;code>/etc/ldap/ldap.conf&lt;/code> ，调整 CA 证书文件：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-conf" data-lang="conf">TLS_CACERT /etc/ldap/ca_certs.pem
&lt;/code>&lt;/pre>&lt;p>测试 STARTTLS 通信：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ ldapwhoami -H ldap:// -x -ZZ
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">anonymous
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于 &lt;code>ldaps://&lt;/code> 已经过时，不应该再使用。&lt;/p>
&lt;h1 id="配置-sssd-认证登录">配置 sssd 认证登录&lt;/h1>
&lt;p>安装软件包：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo apt-get install sssd-ldap ldap-utils
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建 &lt;code>/etc/sssd/sssd.conf&lt;/code> ，权限 &lt;em>0600&lt;/em> ，所有权 &lt;em>root:root&lt;/em> ：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[sssd]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">config_file_version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">domains&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">example.com&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[domain/example.com]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">id_provider&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">ldap&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">auth_provider&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">ldap&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">chpass_provider&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">ldap&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">ldap_uri&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">ldap://ldap.example.com&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">cache_credentials&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">ldap_search_base&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">dc=example,dc=com&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[pam]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">offline_credentials_expiration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">60000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>启动 sssd 服务：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo systemctl start sssd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>自动创建家目录：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">$ sudo pam-auth-update --enable mkhomedir
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="在-ldap-上创建用户并测试">在 LDAP 上创建用户并测试&lt;/h1>
&lt;p>推荐 Apache Directory Studio 作为 GUI 工具，不用也没啥。&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-ldif" data-lang="ldif">dn: dc=example,dc=com
objectClass: dcObject
objectClass: organization
objectClass: top
dc: example
o: example.com
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: People
dn: ou=Group,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: Group
dn: uid=usera,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
cn: User A
gidNumber: 10001
homeDirectory: /home/usera
sn: usera
uid: usera
uidNumber: 10001
loginShell: /bin/bash
userPassword: wow
dn: cn=usera,ou=Group,dc=example,dc=com
objectClass: posixGroup
cn: usera
gidNumber: 10001
&lt;/code>&lt;/pre>&lt;p>导入后，在 ldap 客户机上应该能查询到相关用户，并且以该用户身份登录：&lt;/p>
&lt;pre tabindex="0">&lt;code>$ getent passwd usera
usera:*:10001:10001:User A:/home/usera:/bin/bash
$ id usera
uid=10001(usera) gid=10001(usera) groups=10001(usera)
&lt;/code>&lt;/pre>&lt;h1 id="ssh-配置">SSH 配置&lt;/h1>
&lt;h2 id="ldap-server">LDAP Server&lt;/h2>
&lt;p>修改 LDAP 的 schema ，加上用户的公钥字段：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-ldif" data-lang="ldif">dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME &amp;#39;sshPublicKey&amp;#39;
DESC &amp;#39;MANDATORY: OpenSSH Public key&amp;#39;
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME &amp;#39;ldapPublicKey&amp;#39; SUP top AUXILIARY
DESC &amp;#39;MANDATORY: OpenSSH LPK objectclass&amp;#39;
MAY ( sshPublicKey $ uid )
)
&lt;/code>&lt;/pre>&lt;h2 id="client">Client&lt;/h2>
&lt;p>新建 &lt;code>/usr/local/bin/ssh-keyldap&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># vim: set ts=4:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This script finds and prints authorized SSH public keys in LDAP for the&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># username specified as the first argument.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The program must be owned by root and not writable by group or others.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># It expects configuration file /etc/ssh/ldap.conf in format of ldap.conf(5).&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># sshd_config for OpenSSH 6.2+:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># AuthorizedKeysCommand /usr/local/bin/ssh-keyldap&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># AuthorizedKeysCommandUser nobody&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">set&lt;/span> -eu
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">LDAPCONF&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;/etc/ssh/ldap.conf&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">log&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> logger -s -t sshd -p &lt;span class="s2">&amp;#34;auth.&lt;/span>&lt;span class="nv">$1&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$2&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">uid&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$1&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">export&lt;/span> LDAPCONF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> ! -r &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$LDAPCONF&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> log err &lt;span class="s2">&amp;#34;file &lt;/span>&lt;span class="nv">$LDAPCONF&lt;/span>&lt;span class="s2"> does not exist or not readable&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">exit&lt;/span> &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> ! expr &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$uid&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> : &lt;span class="s1">&amp;#39;[a-zA-Z0-9._-]*$&amp;#39;&lt;/span> 1&amp;gt;/dev/null&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> log err &lt;span class="s2">&amp;#34;bad characters in username: &lt;/span>&lt;span class="nv">$uid&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">exit&lt;/span> &lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">keys&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>ldapsearch -x -LLL -ZZ -o ldif-wrap&lt;span class="o">=&lt;/span>no &lt;span class="s2">&amp;#34;(&amp;amp;(uid=&lt;/span>&lt;span class="nv">$uid&lt;/span>&lt;span class="s2">)(sshPublicKey=*))&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="s1">&amp;#39;sshPublicKey&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> sed -n &lt;span class="s1">&amp;#39;s/^sshPublicKey:\s*\(.*\)$/\1/p&amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">keys_count&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$keys&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep &lt;span class="s1">&amp;#39;^ssh&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> wc -l&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">log info &lt;span class="s2">&amp;#34;Loaded &lt;/span>&lt;span class="nv">$keys_count&lt;/span>&lt;span class="s2"> SSH public key(s) from LDAP for user: &lt;/span>&lt;span class="nv">$uid&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$keys&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>新建 &lt;code>/etc/ssh/ldap.conf&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-conf" data-lang="conf"># /etc/ssh/ldap.conf
# See ldap.conf(5) for details
# This file should be world readable but not world writable.
BASE ou=people,dc=itszzz,dc=top
URI ldap://hn00.itszzz.top
&lt;/code>&lt;/pre>&lt;p>编辑 &lt;code>/etc/ssh/sshd_config&lt;/code> ，加入：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-conf" data-lang="conf">AuthorizedKeysCommand /usr/local/bin/ssh-keyldap
AuthorizedKeysCommandUser nobody
&lt;/code>&lt;/pre></description></item><item><title>Linux 向局域网内设备分享网络</title><link>https://blog.sigsegv.top/posts/linux-share-network/</link><pubDate>Sat, 23 Jul 2022 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/linux-share-network/</guid><description>&lt;p>UPDATE 2022-09-03:&lt;/p>
&lt;p>添加了在装有 ufw 时的配置。&lt;/p>
&lt;p>======&lt;/p>
&lt;p>UPDATE 2022-07-24:&lt;/p>
&lt;p>将使用 firewalld 的方法添加到了 &lt;a href="https://wiki.archlinux.org/title/Internet_sharing#With_firewalld">Arch Wiki - Internet sharing&lt;/a> 上。&lt;/p>
&lt;p>======&lt;/p>
&lt;p>现某台 Linux 服务器 a 能访问网络 A 、 B 与 C ，希望能通过 a 使 C 下的其他设备访问网络 A 、 B 。具体配置如下：&lt;/p>
&lt;ul>
&lt;li>网络 C ： 192.168.44.0/24&lt;/li>
&lt;li>C 中路由器： 192.168.44.1&lt;/li>
&lt;li>a 在 C 中的 IP ： 192.168.44.2&lt;/li>
&lt;li>a 中的网络 A 、 B 与 C 对应的网卡： eno1, eno2, eno3&lt;/li>
&lt;/ul>
&lt;h1 id="服务器-a-上的设置">服务器 a 上的设置&lt;/h1>
&lt;h2 id="使用-nftables">使用 nftables&lt;/h2>
&lt;p>首先允许网络转发：&lt;/p>
&lt;pre tabindex="0">&lt;code># sysctl net.ipv4.ip_forward=1
&lt;/code>&lt;/pre>&lt;p>再使用 nftables 建立一个新表，设置转发规则：&lt;/p>
&lt;pre tabindex="0">&lt;code># nft add table inet nat
# nft &amp;#39;add chain inet nat postrouting { type nat hook postrouting priority 100 ; }&amp;#39;
# nft add rule inet nat postrouting ip saddr 192.168.44.0/24 masquerade
&lt;/code>&lt;/pre>&lt;p>值得一提的是， Arch Wiki 上的&lt;a href="https://wiki.archlinux.org/title/Internet_sharing#With_nftables">操作&lt;/a>是指定 &lt;code>oifname&lt;/code> ，但没有指定 &lt;code>ip saddr&lt;/code> 。我的理解是：&lt;/p>
&lt;ol>
&lt;li>不指定 source address 留下了安全风险&lt;/li>
&lt;li>使用 oifname 假定了只有一个需要做 NAT 的网络区域。即使是只有一个区域，一般情况下机器在这个网络区域里也只有一个 IP 地址，这种情况下应该去设置 snat 而不是 masquerade 以获得更好的性能。&lt;/li>
&lt;/ol>
&lt;h3 id="如果服务器上还装了-ufw">如果服务器上还装了 ufw&lt;/h3>
&lt;p>ufw 默认把所有 IP Forward 都 block 掉。网络上的大部分方法都是把默认策略改成 ACCEPT 。。我们装防火墙是为了防止攻击的，这种引入其他风险的策略听上去就有很大问题。 ufw 的用户界面应该是没有提供相关选项，我们需要手工写 iptables 策略（是的， ufw 用 iptables ，导致我们同时写了 nftables 规则和 iptables 规则）&lt;/p>
&lt;p>编辑 &lt;code>/etc/ufw/before.rules&lt;/code> ，添加以下内容：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">-A ufw-before-forward -s 192.168.44.0/24 -j ACCEPT
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="使用-firewalld">使用 firewalld&lt;/h2>
&lt;p>firewalld 的 external 区域应该是默认开启 masquerade ，意味着 firewalld 也会为我们开启网络转发，所以聚焦于 firewalld 的设置就好。&lt;/p>
&lt;p>设置网口区域并检查设置：&lt;/p>
&lt;pre tabindex="0">&lt;code># firewall-cmd --zone=external --change-interface=eno1 --permanent
# firewall-cmd --zone=external --change-interface=eno2 --permanent
# firewall-cmd --zone=internal --change-interface=eno3 --permanent
# firewall-cmd --zone=internal --set-target=ACCEPT --permanent
# firewall-cmd --reload
# firewall-cmd --get-active-zones
&lt;/code>&lt;/pre>&lt;p>添加配置规则：&lt;/p>
&lt;pre tabindex="0">&lt;code># firewall-cmd --zone=internal --permanent --add-rich-rule=&amp;#39;rule family=&amp;#34;ipv4&amp;#34; source address=&amp;#34;192.168.44.0/24&amp;#34; masquerade&amp;#39;
# firewall-cmd --reload
&lt;/code>&lt;/pre>&lt;h1 id="网络-c-路由器上的配置">网络 C 路由器上的配置&lt;/h1>
&lt;p>路由网关指向该服务器即可。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/linux-share-network/routeros.png" alt="Router OS Route Config">&lt;/p>
&lt;h1 id="后记">后记&lt;/h1>
&lt;p>因为最开始忘记了网络出口的问题，导致折腾了很长时间也没明白出了什么问题。&lt;/p>
&lt;p>firewalld 生成 nftables 表时，内容非常冗杂，但都有规则可循。但是前一段时间折腾 docker （仍然使用 iptables 后端配置网络，对 ipv6 支持还是很差）与 firewalld 的共存时留下了心理阴影，遂开始手工编写 nftables chain ，并在规则中加入 log 来排查问题，具体步骤可以参考 &lt;a href="https://wiki.nftables.org/wiki-nftables/index.php/Logging_traffic">nftables wiki - Loggin traffic&lt;/a> 。&lt;/p></description></item><item><title>Python Interactive 中 __del__ 的奇怪行为</title><link>https://blog.sigsegv.top/posts/python-interactive-with-del/</link><pubDate>Tue, 21 Dec 2021 20:31:41 +0800</pubDate><guid>https://blog.sigsegv.top/posts/python-interactive-with-del/</guid><description>&lt;h1 id="一个简单的问题">一个简单的问题&lt;/h1>
&lt;p>今天在某群里看见一个 Python 很怪异的行为，最后总结到可以复现的最小例子如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">PS C:\Users\azuk&amp;gt; python3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Python 3.8.9 (default, Apr 13 2021, 15:54:59) [GCC 10.2.0 64 bit (AMD64)] on win32
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Type &amp;#34;help&amp;#34;, &amp;#34;copyright&amp;#34;, &amp;#34;credits&amp;#34; or &amp;#34;license&amp;#34; for more information.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; class X:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">... def __del__(self):
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">... print(&amp;#39;die&amp;#39;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; a = X()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; del a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">die
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; a = X()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;__main__.X object at 0x0000022eb60f9430&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; del a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; X
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">die
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;class &amp;#39;__main__.X&amp;#39;&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当在 Python Interactive 运行 &lt;code>a&lt;/code>，再删除这个对象时，并没有马上执行 &lt;code>a.__del__&lt;/code> 的操作，而是等运行了下一条语句时才打印出了 &amp;lsquo;die&amp;rsquo;。如果把它做成一个简单的 Python 脚本来运行，就不会发生这种奇怪的现象。&lt;/p>
&lt;p>&lt;strong>TLDR:&lt;/strong> 因为 Python Interactive 会把上一次运算结果的值保存在 &lt;code>__builtins__._&lt;/code> 里，所以引用计数器没归零。&lt;/p>
&lt;h1 id="一番困难的研究">一番困难的研究&lt;/h1>
&lt;p>最近也做了不少 Python 的工作，正好借这个机会来梳理一下整个过程。&lt;/p>
&lt;h2 id="减少引用计数的过程">减少引用计数的过程&lt;/h2>
&lt;p>首先要弄清楚的是，为什么执行完 &lt;code>del a&lt;/code> 后应该执行 &lt;code>a.__del__&lt;/code>。在 Python 里简单 dis 一下这段话看看结果：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; from dis import dis
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; del a; f = sys._getframe(0)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">die
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; dis(f.f_code)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 0 DELETE_NAME 0 (a)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 LOAD_NAME 1 (sys)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 4 LOAD_METHOD 2 (_getframe)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 6 LOAD_CONST 0 (0)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 8 CALL_METHOD 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 10 STORE_NAME 3 (f)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 12 LOAD_CONST 1 (None)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 14 RETURN_VALUE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>直接 &lt;code>dis 'del a'&lt;/code> 可能与实际执行有差异，这里直接获取到 frame object 的 &lt;code>f_code&lt;/code> 。这里 &lt;code>del a&lt;/code> 实际上对应了 bytecode &lt;code>DELETE_NAME(a)&lt;/code>。 而 DELETE_NAME 在 ceval.c 中的定义是：&lt;/p>
&lt;p>Python/ceval.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">case&lt;/span> &lt;span class="nf">TARGET&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DELETE_NAME&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">GETITEM&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">names&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">oparg&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">f_locals&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">err&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ns&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">_PyErr_Format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tstate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PyExc_SystemError&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;no locals when deleting %R&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">error&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">err&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">PyObject_DelItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ns&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">format_exc_check_arg&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tstate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PyExc_NameError&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">NAME_ERROR_MSG&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">error&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DISPATCH&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>DELETE_NAME&lt;/code> 首先获取参数的名字，再获取当前 frame object 的 f_locals ，最后在 &lt;code>PyObject_DelItem&lt;/code> 中删除该变量。&lt;/p>
&lt;p>Objects/abstract.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">PyObject_DelItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PyMappingMethods&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">Py_TYPE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">tp_as_mapping&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">mp_ass_subscript&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="nf">mp_ass_subscript&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">PyObject&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">assert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">_Py_CheckSlotResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;__delitem__&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>注意到 locals() 是一个 dict 对象，所以这里用了从集合类型中删除元素的函数进行操作。&lt;code>mp_ass_subscript&lt;/code> 不为空说明可以对元素执行下标操作（包括 set 和 del）。在 dictobject.c 中，定义有：&lt;/p>
&lt;p>Objects/dictobject.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="n">PyMappingMethods&lt;/span> &lt;span class="n">dict_as_mapping&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">objobjargproc&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dict_ass_sub&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="cm">/*mp_ass_subscript*/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>之后由 &lt;code>dict_ass_sub&lt;/code> -&amp;gt; &lt;code>PyDict_DelItem&lt;/code> -&amp;gt; &lt;code>_PyDict_DelItem_KnownHash&lt;/code> -&amp;gt; &lt;code>delitem_common&lt;/code> 一路调用过来，最终对 Value 执行了一次 &lt;code>Py_DECREF&lt;/code>，对值的引用计数减 1 。&lt;/p>
&lt;p>要注意的是，不是所有情况下执行 &lt;code>del&lt;/code> 都会产生 &lt;code>DELETE_NAME&lt;/code>。我个人认为 CPython 里带 NAME 的 opcode 都有一种“我也不知道在哪里，你运行的时候自己找找”的感觉。假如有如下函数：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">X&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">del&lt;/span> &lt;span class="n">a&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>那么它会生成 &lt;code>STORE_FAST&lt;/code> 和 &lt;code>DELETE_FAST&lt;/code> 。就像它名字表示的一样，它真的很快。值放在了一个数组上（数组是 locals 和 stack），删除也是对数组进行操作（以直接将值设置为 NULL 的方式），不在这里讨论的范围内。&lt;/p>
&lt;h2 id="调用-__del__-的过程">调用 &lt;code>__del__&lt;/code> 的过程&lt;/h2>
&lt;p>首先追踪一下 &lt;code>Py_DECREF&lt;/code>：&lt;/p>
&lt;p>Python/ceval.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp"># define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1">// 这里简化了许多用于调试的预编译指令
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="kr">inline&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">_Py_DECREF&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ob_refcnt&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">_Py_Dealloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里的逻辑比较简单，当引用计数归零时，就去调用 &lt;code>_Py_Dealloc&lt;/code>。&lt;/p>
&lt;p>Objects/object.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">_Py_Dealloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">destructor&lt;/span> &lt;span class="n">dealloc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">Py_TYPE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">tp_dealloc&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">dealloc&lt;/span>&lt;span class="p">)(&lt;/span>&lt;span class="n">op&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里面直接去调了 &lt;code>PyTypeObject&lt;/code> 的 &lt;code>tp_dealloc&lt;/code> 函数。Python 在 C 中定义的类型 (type) 都是这样的：&lt;/p>
&lt;ol>
&lt;li>创建一个和 &lt;code>PyObject&lt;/code> 二进制兼容的结构，一般命名为 &lt;code>Py{Name}Object&lt;/code> ，用来 object 的相关信息&lt;/li>
&lt;li>另一方面要创建一个 &lt;code>PyTypeObject&lt;/code> 的结构体，让 &lt;code>PyObject&lt;/code> 的 &lt;code>ob_type&lt;/code> 指向它，里面包含了 object 的相关行为函数；我们的 class X 也对应了某个 &lt;code>PyTypeObject&lt;/code> 。&lt;/li>
&lt;/ol>
&lt;p>Python 中类的创建机制比较复杂，文中先略过不谈。最终我们调用到了作为 &lt;code>tp_dealloc&lt;/code> 出现的 &lt;code>subtype_dealloc&lt;/code>，通过检查是否有 Finalizer ， 从 &lt;code>PyObject_CallFinalizerFromDealloc&lt;/code> -&amp;gt; &lt;code>PyObject_CallFinalizer&lt;/code> -&amp;gt; &lt;code>tp_finalize&lt;/code> 最终调用到了我们写的 Python 层面上的 &lt;code>__del__&lt;/code> 。&lt;/p>
&lt;p>这就和我们在 Python 官方文档上看到的说明一样了：调用 &lt;code>del&lt;/code> 未必触发 &lt;code>__del__&lt;/code>。&lt;/p>
&lt;blockquote>
&lt;p>&lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__del__">The Python Language Reference &amp;raquo; Data model # object.__del__&lt;/a>&lt;/p>
&lt;p>Note &lt;code>del x&lt;/code> doesn’t directly call &lt;code>x.__del__()&lt;/code> — the former decrements the reference count for x by one, and the latter is only called when x’s reference count reaches zero.&lt;/p>
&lt;/blockquote>
&lt;h2 id="从引用计数下手">从引用计数下手&lt;/h2>
&lt;p>现在我们实锤了：既然 &lt;code>__del__&lt;/code> 没有被调用，说明变量引用计数根本没有归零。做一个简单的实验：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; a = X()
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;lt;__main__.X object at 0x000001c3fab693a0&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; sys.getrefcount(a)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; sys.getrefcount(a)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; sys.getrefcount(a)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>惊了，引用计数突然就减少了 1 ！此时我的第一想法就是 Python Interactive 会保存上一次执行的结果，马上去搜索了一番（实在不想看代码找问题了）。还真是这样，stackoverflow 上有人说 Python Interactive 会调用 sys.displayhook 把上一次的结果保存到 &lt;code>__builtins__._&lt;/code> 里。&lt;/p>
&lt;p>Python 官方文档里是这样说的：&lt;/p>
&lt;blockquote>
&lt;p>&lt;a href="https://docs.python.org/3/library/sys.html#sys.displayhook">The Python Standard Library &amp;raquo; Python Runtime Services &amp;raquo; sys — System-specific parameters and functions # sys.displayhook&lt;/a>&lt;/p>
&lt;p>sys.displayhook is called on the result of evaluating an expression entered in an interactive Python session. The display of these values can be customized by assigning another one-argument function to sys.displayhook.&lt;/p>
&lt;p>Pseudo-code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">displayhook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Set &amp;#39;_&amp;#39; to None to avoid recursion&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">builtins&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">repr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">UnicodeEncodeError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">bytes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encoding&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;backslashreplace&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nb">hasattr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;buffer&amp;#39;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">bytes&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">bytes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">encoding&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;strict&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stdout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">builtins&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">value&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;p>但无论是 stackoverflow 还是 Python 文档都没说这个 &lt;code>sys.displayhook&lt;/code> 到底是哪来的，为什么只在 interactive session 里生效。&lt;/p>
&lt;h2 id="探寻-sysdisplayhook">探寻 sys.displayhook&lt;/h2>
&lt;p>没有办法，继续从源码里寻找答案。&lt;code>sys&lt;/code> 是一个库，直接去对应的 module 里找：&lt;/p>
&lt;p>Python/sysmodule.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">sys_displayhook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">module&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="cm">/* Print value except if None */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* After printing, also assign to &amp;#39;_&amp;#39; */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* Before, set &amp;#39;_&amp;#39; to None to avoid recursion */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Py_None&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Py_RETURN_NONE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">_PyObject_SetAttrId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">builtins&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">PyId__&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Py_None&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">outf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">sys_get_object_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tstate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">PyId_stdout&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">outf&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">outf&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">Py_None&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">_PyErr_SetString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tstate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PyExc_RuntimeError&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;lost sys.stdout&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">PyFile_WriteObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">newline&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">newline&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">PyUnicode_FromString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">newline&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">PyFile_WriteObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">newline&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Py_PRINT_RAW&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">_PyObject_SetAttrId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">builtins&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">PyId__&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">o&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Py_RETURN_NONE&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这正是 Python Interactive 交互的表现！通过 &lt;code>dis&lt;/code> 的结果来看，当我们“运行”一条变量，它是这样的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">&amp;gt;&amp;gt;&amp;gt; dis(&amp;#39;a&amp;#39;)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1 0 LOAD_NAME 0 (a)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2 RETURN_VALUE
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>返回值就是 &lt;code>a&lt;/code> ，所以在屏幕上也打印了它的值。&lt;/p>
&lt;p>但问题是，为什么只有在 interactive 的情况下，才会去触发 &lt;code>sys.displayhook&lt;/code> 呢？换言之，是谁在调用 &lt;code>sys.displayhook&lt;/code>？&lt;/p>
&lt;h2 id="水落石出一条特殊的-opcode">水落石出：一条特殊的 opcode&lt;/h2>
&lt;p>直接运行 &lt;code>python&lt;/code> ，就可以进入它的交互模式。所以这次从 &lt;code>main&lt;/code> 一路跟进，直到这里：&lt;/p>
&lt;p>&lt;code>run_mod&lt;/code> -&amp;gt; &lt;code>_PyAST_Compile&lt;/code> -&amp;gt; &lt;code>compiler_mod&lt;/code>:&lt;/p>
&lt;p>Python/compile.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="n">PyCodeObject&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">compiler_mod&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">compiler&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mod_ty&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">case&lt;/span> &lt;span class="nl">Interactive_kind&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">find_ann&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">mod&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Interactive&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ADDOP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">SETUP_ANNOTATIONS&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">c_interactive&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">VISIT_SEQ_IN_SCOPE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stmt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mod&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Interactive&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里给 compiler 设置了一个 &lt;code>c_interactive&lt;/code> 选项，于是在后面：&lt;/p>
&lt;p>Python/compile.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">compiler_visit_stmt_expr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">compiler&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">expr_ty&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">c_interactive&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">c_nestlevel&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">VISIT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">expr&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ADDOP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PRINT_EXPR&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里填加了一个 opcode &lt;code>PRINT_EXPR&lt;/code>，而它的作用就是：&lt;/p>
&lt;p>Python/ceval.c&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">case&lt;/span> &lt;span class="nf">TARGET&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PRINT_EXPR&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">_Py_IDENTIFIER&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">displayhook&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">POP&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">hook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">_PySys_GetObjectId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">PyId_displayhook&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PyObject&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">hook&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">_PyErr_SetString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tstate&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">PyExc_RuntimeError&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;lost sys.displayhook&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">Py_DECREF&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">goto&lt;/span> &lt;span class="n">error&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>就是把当前栈上的值 &lt;code>value&lt;/code> 弹出来，然后调用 &lt;code>sys.displayhook(value)&lt;/code> 把它打印，再保存进 &lt;code>__builtins__._&lt;/code> 里。&lt;/p>
&lt;h1 id="结语">结语&lt;/h1>
&lt;p>Python 的源码量真的很庞大，初看很容易摸不到头脑。相比之下 Go 的源码里注释就写得特别好，很多地方逻辑也很简单，很容易上手（当然也因为 Go 又简单又屎）。&lt;/p></description></item><item><title>CH558 OS Switch</title><link>https://blog.sigsegv.top/posts/ch558-os-switch/</link><pubDate>Sat, 10 Jul 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/ch558-os-switch/</guid><description>&lt;p>在 Hackaday 上看到了 &lt;a href="https://hackaday.io/project/179539-hardware-boot-selection-switch">Hardware boot selection switch&lt;/a> 这个项目，作者用 STM32 模拟出一个 U 盘，通过读取硬件开关的状态返回不同的内容，从而让 grub2 启动不同的系统。&lt;/p>
&lt;p>最近 STM32 涨价涨得太厉害，一片 F103 也要几十块钱。有同学推荐了国产的 &lt;a href="http://www.wch.cn/product/CH558.html">CH558&lt;/a> ，价格只需要两三块钱，于是入手了 CH558T 来尝试一下。手边有很多没用掉的 SSOP20 转接板，所以买了这种封装的芯片。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/ch558-os-switch/1.jpg" alt="转接板上的 CH558">&lt;/p>
&lt;p>国产单片机的最大劣势就是生态不好。厂商能提供一份 Datasheet 和几个糊出来的示例程序，剩下的资料就要靠各种论坛上搜了，不过也不一定能搜得到。arm 架构的也许有 CMSIS 用？像这种 51 改造的 8 位单片机，基本就是直接操作寄存器了。 CH558 和 CH559 的硬件规格大致相同，网络上有 CH559 模拟 U 盘的程序下载，但那份程序里状态机貌似写的有些问题，在 Windows 下对一些错误的容忍度比较高，不过在我的电脑开机时， BIOS 读取信息时就会卡死，还是需要动手改造一下再用。&lt;/p>
&lt;p>USB 协议比较好的参考资料就是 usb.org 上的各种文档。由于很多细节涉及到硬件上的操作，还是建议结合他人的程序深入理解。首先把 usb 和电脑的基础沟通部分做好，让 endpoint 能够正常通信，再去把 Mass Storage Class 的状态机写出来。我使用的是 Bulk-Only Transport + SCSI Transparent Command ，要写好 BBB 的操作和 SCSI 命令的处理。最后把文件系统的相应函数写好，可以用 FAT12 这样简单的文件系统减少负担。一次写一层，每一层都用相应的工具进行检验调试。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/ch558-os-switch/2.png" alt="成果">&lt;/p>
&lt;p>读取 GPIO 是非常简单的，因为只在开机时读取一次 GPIO 状态，所以也完全不用考虑滤波的问题。&lt;/p>
&lt;p>最后还是要吐槽一下，如果让我重新来过，我就去用 rpi pico 或 esp32 做。 Pico SDK 里面的 raw usb device 示例中，都有一堆库函数在做底层的操作，如果想偷懒还可以直接用 TinyUSB 写好的 MSC 代码。 ESP32 部分型号不支持 USB 功能，用 risc-v 处理器的可能示例比较少，也需要再留意一下。如果非要用 CH558 ，建议用 LQFP48 封装的版本。 SSOP20 的版本有些蛮重要的引脚被砍掉了，比如 UART0 。 UART1 没被砍，但操作起来竟然和 UART0 是不一样的。&lt;/p></description></item><item><title>macOS hook Objective-C 程序</title><link>https://blog.sigsegv.top/posts/macos-objc-hook/</link><pubDate>Sun, 09 May 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/macos-objc-hook/</guid><description>&lt;p>摸了，改天再记一下。
主要是记一下不是类成员的 objc 函数和 c 函数的 hook&lt;/p></description></item><item><title>macOS 更改程序的设置文件</title><link>https://blog.sigsegv.top/posts/nsuserdefault-on-macos/</link><pubDate>Sun, 09 May 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/nsuserdefault-on-macos/</guid><description>&lt;p>原因： &lt;code>cfprefsd&lt;/code> 这个进程在后台运行，可能在维护着一层缓冲结构。&lt;/p>
&lt;p>三种解决方法：&lt;/p>
&lt;ol>
&lt;li>调用 &lt;code>defaults&lt;/code> 程序改设置&lt;/li>
&lt;li>&lt;code>killall cfprefsd&lt;/code>&lt;/li>
&lt;li>注入这个程序，写代码改掉。&lt;/li>
&lt;/ol>
&lt;p>App 设置文件存放的路径：&lt;/p>
&lt;ul>
&lt;li>sandboxed: &lt;code>~/Library/Containers/(bundle-identifier)/Data/Library/Preferences&lt;/code>&lt;/li>
&lt;li>non-sandboxed: &lt;code>~/Library/Preferences&lt;/code>&lt;/li>
&lt;/ul></description></item><item><title>HUAWEI Watch 1代国际版固件装国行商店</title><link>https://blog.sigsegv.top/posts/huawei-watch-appstore/</link><pubDate>Wed, 05 May 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/huawei-watch-appstore/</guid><description>&lt;p>我在 Android Wear 2.0 的时候借机会把国行的 watch 刷成了国际版，因为国行能玩的东西太少了。不过现在看来，国行商店里的 app 貌似更好用，于是就研究着怎么给它同时装一个国行商店。&lt;/p>
&lt;p>首先在 Android SDK 官方镜像里提取一个国行商店的旧版 apk ，直接 adb 装入。打开时闪退，开 logcat 看日志：&lt;/p>
&lt;pre tabindex="0">&lt;code>E DatabaseUtils: java.lang.SecurityException: com.mobvoi.ticwear.aw.appstore was not granted this permission: android.permission.WRITE_SETTINGS.
...
E AndroidRuntime: java.lang.RuntimeException: Unable to create application com.mobvoi.ticwear.aw.appstore.AppStoreApplication: java.lang.SecurityException: com.mobvoi.ticwear.aw.appstore was not granted this permission: android.permission.WRITE_SETTINGS.
...
&lt;/code>&lt;/pre>&lt;p>大概查了下资料，尝试手改 &lt;code>/etc/permissions/platform.xml&lt;/code> 失败，因为 SELinux 的标志位总设置不对。又看到如果是系统 app ，即放在 &lt;code>/system/app&lt;/code> 或 &lt;code>/system/priv-app&lt;/code> 里也可以，于是想办法把它转换一下。&lt;/p>
&lt;p>装 Magisk 21.4（22版有 bug ，难道是被 Google 招安后不想认真做了 2333），调整手表分辨率，再安装 Lucky Patcher 把它转换为系统 app 就可以了。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/3/1.png" alt="商店正常安装">&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/3/2.png" alt="程序正常安装">&lt;/p>
&lt;p>惊喜的是，旧版的商店后来自己更新成了新版。可惜这个手表已经老了，很多程序同时运行会非常卡，电池也不太够用，最后还是只装了几个常用的 app 。&lt;/p></description></item><item><title>OMEN Command Center 逆向</title><link>https://blog.sigsegv.top/posts/omen-command-center-light/</link><pubDate>Sat, 24 Apr 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/omen-command-center-light/</guid><description>&lt;p>先在 98 发一下，改天挪过来&lt;/p>
&lt;ol>
&lt;li>RPC Patch&lt;/li>
&lt;li>ACPI WMI&lt;/li>
&lt;li>WMI in DSDT&lt;/li>
&lt;/ol></description></item><item><title>阿里云国内 ECS acme.sh 更新证书出错</title><link>https://blog.sigsegv.top/posts/aliyun-acmesh-failed/</link><pubDate>Sun, 04 Apr 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/aliyun-acmesh-failed/</guid><description>&lt;p>最近几天半夜总收到阿里云的域名解析删除通知邮件，感觉 HTTPS 证书更新应该有点问题，上号看一下。因为: acme.sh 现在用被墙的 CloudFlare 和 Google 的 DoH 服务器检查 DNS 是否被设置正确，但是因为这两个服务在国内都被墙了，所以更新证书的程序运行失败了。&lt;/p>
&lt;p>在 acme.sh 的 Github Repo 中可以看到：&lt;a href="https://github.com/acmesh-official/acme.sh/blob/c33e5bc40fa89e9f10b9c667428e08c1fbf237ce/acme.sh#L3921">acmesh-official/acme.sh, acme.sh&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#domain, type&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">_ns_lookup&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -z &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DOH_USE&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> _debug &lt;span class="s2">&amp;#34;Detect dns server first.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> _ns_is_available_cf&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> _debug &lt;span class="s2">&amp;#34;Use cloudflare doh server&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">export&lt;/span> &lt;span class="nv">DOH_USE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$DOH_CLOUDFLARE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> _debug &lt;span class="s2">&amp;#34;Use google doh server&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">export&lt;/span> &lt;span class="nv">DOH_USE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nv">$DOH_GOOGLE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DOH_USE&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DOH_CLOUDFLARE&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="o">[&lt;/span> -z &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DOH_USE&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> _ns_lookup_cf &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$@&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> _ns_lookup_google &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$@&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>开发者没有留自定义 DoH 服务器的接口，貌似也没留其他查 DNS 的方式。解决倒是很容易：&lt;/p>
&lt;ol>
&lt;li>直接在 acme.sh 的参数后加 &amp;ndash;dnssleep 300 ，直接假定 DNS 设置正确。当然 300s 很长，可以调小一些。阿里云的 DNS 解析服务调个 20s 左右就可以了。&lt;/li>
&lt;li>把 acme.sh 里 DoH 服务器的地址换掉，如阿里 DoH ：&lt;code>https://dns.alidns.com/dns-query&lt;/code> 或 DNSPod DoH &lt;code>https://doh.pub/dns-query&lt;/code> 。&lt;/li>
&lt;/ol></description></item><item><title>钉钉回放的 m3u8 报 403 错误</title><link>https://blog.sigsegv.top/posts/dingtalk-video-download/</link><pubDate>Sun, 04 Apr 2021 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/dingtalk-video-download/</guid><description>&lt;p>因为现在 m3u8 文件只能访问一次。&lt;/p>
&lt;p>最开始的时候网络上还没有钉钉下载回放的相关文章，现在随便搜一下就全都是，所以想着要搞这一波吧。不过也是偷个懒，设置了这样的缓解措施。&lt;/p></description></item><item><title>为天猫精灵方糖调整MTU设置</title><link>https://blog.sigsegv.top/posts/mtu-tuning-for-tmall-genie/</link><pubDate>Sat, 22 Aug 2020 10:09:33 +0800</pubDate><guid>https://blog.sigsegv.top/posts/mtu-tuning-for-tmall-genie/</guid><description>&lt;p>昨天翻出很久没有用过的天猫精灵，恰好最近买了路由器，联网很方便，就把它拿出来玩一玩。开机、连接Wi-Fi、蓝牙连接手机都很顺利，但它貌似没有连接上淘宝的服务器。每次用天猫精灵语音指令时，它都会响应超时，说一句“一走神，连您的吩咐都听岔了”。&lt;/p>
&lt;blockquote>
&lt;p>MTU(Maximum Transmission Unit)是数据链路层上所能通过的最大数据包大小。如果数据包过大，就要求在 IP 层进行分片传输。&lt;/p>
&lt;/blockquote>
&lt;p>校园网使用 L2TP。L2TP 有 20 (IP Header) + 8 (UDP Header) + 12 (L2TP Header) = 40 bytes 的额外数据需要传输，所以要将 MTU 下调至 1460 。&lt;/p></description></item><item><title>UWP应用：Repacks &amp; Hacks</title><link>https://blog.sigsegv.top/posts/uwp-repacks-hacks/</link><pubDate>Tue, 12 May 2020 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/uwp-repacks-hacks/</guid><description>&lt;p>今天遇到了需要修改 UWP 程序的情况，步骤比较繁杂，在此记录一下。&lt;/p>
&lt;h1 id="repacks">Repacks&lt;/h1>
&lt;h2 id="获取程序文件">获取程序文件&lt;/h2>
&lt;ol>
&lt;li>appx 文件可以通过抓包的方式直接获取下载地址。我在搜索过程中发现了很多相关的教程，都比较老了，有些地方变动比较大。&lt;/li>
&lt;/ol>
&lt;p>首先 Microsoft Store 会产生一个到&lt;code>http://dl.delivery.mp.microsoft.com/filestreamingservice/files/{GUID}/piecehash&lt;/code>的请求，确定程序版本、哈希、分段等信息。然后在&lt;code>http://tlu.dl.delivery.mp.microsoft.com/filestreamingservice/files/{GUID}?P1={}&amp;amp;P2={}&amp;amp;P3={}&amp;amp;P4={}&lt;/code>分段下载文件。只要访问 URL ，就可以下载到完整的 appx 。&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/1/2.jpg" alt="Microsoft Store 下载请求">&lt;/p>
&lt;ol start="2">
&lt;li>直接打开应用的安装目录（在&lt;code>C:\Program Files\WindowsApps\&lt;/code>下），把所有文件复制出来。如果不能直接打开这个文件夹，可能需要调整文件夹的高级安全设置，加入当前用户的访问权限。事实证明，对于我测试的一个程序来说，在打包之后它确实能够运行，所以这也可以算是一种可行方法吧。&lt;/li>
&lt;/ol>
&lt;h2 id="解包">解包&lt;/h2>
&lt;p>使用&lt;code>makeappx.exe unpack /p foo.appx /d unpacked /l&lt;/code>对 appx 进行解包，得到的目录结构如下：&lt;/p>
&lt;p>&lt;img src="https://blog.sigsegv.top/img/post/1/1.png" alt="appx 解包后的目录结构">&lt;/p>
&lt;p>如果&lt;code>makeappx&lt;/code>报错，很有可能抓包得到的是 App Bundle 。根据文件头可知其为一个 zip 文件，里面的内容有不同分辨率的资源文件和 x64 、 x86 两个架构的程序文件。Unbundle 命令为&lt;code>makeappx.exe unbundle /p rest.appxbundle /d unpacked&lt;/code>。&lt;/p>
&lt;p>原安装包留下的不必要的结构有：&lt;code>AppxSignature.p7x&lt;/code>文件和&lt;code>AppxBlockMap.xml&lt;/code>文件（指出文件实际位置信息的xml）。直接把这些文件删除就可以。&lt;/p>
&lt;h2 id="重新打包">重新打包&lt;/h2>
&lt;p>为了能正常安装 appx 程序包，我们必须准备一个用于签名的证书。在 Powershell 中，执行&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 制作自签名证书，记录输出的证书指纹&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">New-SelfSignedCertificate&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">Custom&lt;/span> &lt;span class="n">-Subject&lt;/span> &lt;span class="s2">&amp;#34;CN=Contoso Software&amp;#34;&lt;/span> &lt;span class="n">-KeyUsage&lt;/span> &lt;span class="n">DigitalSignature&lt;/span> &lt;span class="n">-FriendlyName&lt;/span> &lt;span class="s2">&amp;#34;Your friendly name goes here&amp;#34;&lt;/span> &lt;span class="n">-CertStoreLocation&lt;/span> &lt;span class="s2">&amp;#34;Cert:\CurrentUser\My&amp;#34;&lt;/span> &lt;span class="n">-TextExtension&lt;/span> &lt;span class="vm">@&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;2.5.29.37={text}1.3.6.1.5.5.7.3.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;2.5.29.19={text}&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 使用密码方式导出&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$password&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">ConvertTo-SecureString&lt;/span> &lt;span class="n">-String&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Your&lt;/span> &lt;span class="n">Password&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">-Force&lt;/span> &lt;span class="n">-AsPlainText&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Export-PfxCertificate&lt;/span> &lt;span class="n">-cert&lt;/span> &lt;span class="s2">&amp;#34;Cert:\CurrentUser\My\&amp;lt;Certificate Thumbprint&amp;gt;&amp;#34;&lt;/span> &lt;span class="n">-FilePath&lt;/span> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">FilePath&lt;/span>&lt;span class="p">&amp;gt;.&lt;/span>&lt;span class="py">pfx&lt;/span> &lt;span class="n">-Password&lt;/span> &lt;span class="nv">$password&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>打开&lt;code>AppxManifest.xml&lt;/code>，将&lt;code>Identity&lt;/code>节点中的&lt;code>Publisher&lt;/code>属性改为刚刚制作的证书中的&lt;code>Subject&lt;/code>值，如&lt;code>CN=Contoso Software&lt;/code>。之后就可以打包、签名了。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmd" data-lang="cmd">&lt;span class="line">&lt;span class="cl">makeappx.exe pack /d unpacked /p repacked.appx /l
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">signtool.exe sign /fd SHA256 /a /f mycert.pfx /p &lt;span class="s2">&amp;#34;mycertpassword&amp;#34;&lt;/span> repacked.appx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>signtool&lt;/code>中指定使用 SHA256 的原因是&lt;code>makeappx&lt;/code>默认使用SHA256。&lt;/p>
&lt;h1 id="hacks">Hacks&lt;/h1>
&lt;p>如何让一个正在试用中的应用认为它已经被购买？对于 dotnet 程序，微软在&lt;code>Windows.Services.Store&lt;/code>中提供了&lt;code>StoreAppLicense&lt;/code>类作为应用可以获取到的返回值类型。在官方示例中，说明了其用法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">StoreAppLicense&lt;/span> &lt;span class="n">license&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">storeContext&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetAppLicenseAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">license&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IsActive&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">license&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IsTrial&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LicenseMode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">&amp;#34;Trial license&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LicenseMode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">&amp;#34;Full license&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LicenseMode&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">&amp;#34;Inactive license&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>StoreAppLicense.IsActive&lt;/code> 和 &lt;code>StoreAppLicense.IsTrial&lt;/code> 都是只读的属性，所以修改它们需要用到反射。但很幸运的是，我要修改的程序中自己封装了一个用来获取许可的类，也提供了相同名称的两个只读属性。在 C# 中，属性的&lt;code>get&lt;/code>和&lt;code>set&lt;/code>都是函数，所以可以直接修改它们的IL代码强制其返回指定的布尔值，达到欺骗程序的目的。&lt;/p>
&lt;p>处理 UWP 程序相对来说比较困难。它不允许直接启动，事实上 Visual Studio 在调试 UWP 程序时，会先将其安装到系统中，目前我还没有找到很好的动态调试的方法。其次 XAML 交互部分的代码中可能存在大量匿名函数，对程序的修改工作制造了一定的阻碍。&lt;/p>
&lt;h1 id="参考">参考&lt;/h1>
&lt;ol>
&lt;li>&lt;a href="https://docs.microsoft.com/en-us/windows/msix/package/create-certificate-package-signing">Create a certificate for package signing&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.microsoft.com/en-us/windows/win32/appxpkg/make-appx-package--makeappx-exe-#to-create-a-package-using-a-mapping-file">App packager (MakeAppx.exe)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://gist.github.com/boodle/96b9b02044e5a396caf68c1c3a026b99">Unpack, edit, repack, and re-sign Windows Phone apps&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.laurierhodes.info/?q=node/80">Repackaging Windows Store Applications&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.microsoft.com/en-us/windows/uwp/monetize/get-license-info-for-apps-and-add-ons">Get license info for apps and add-ons&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>在云服务的对象存储上部署网页</title><link>https://blog.sigsegv.top/posts/deploy-pages-on-oss/</link><pubDate>Wed, 26 Feb 2020 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/deploy-pages-on-oss/</guid><description>&lt;p>因为把博客部署在 VPS 上带宽实在他小了（只有 1M ），访问体验十分不好，于是就迁移到了对象存储 + CDN 的模式。嘿，速度上去了，费用反而下降了。尤其腾讯云还在搞活动，如果只是小量的访问就是不要钱啊~&lt;/p>
&lt;h1 id="迁移经历">迁移经历&lt;/h1>
&lt;p>阿里云和腾讯云大致相同，只不过有些名称和操作细节上不太一样。比如阿里云的对象存储叫 OSS ，腾讯云的叫 COS 。总之在两边都要都要设置对象存储为静态网页模式，把路由搞好。
迁移的时候最好用子账户进行操作，控制权限的同时方便日后自动化部署。&lt;/p>
&lt;h2 id="阿里云">阿里云&lt;/h2>
&lt;p>进入访问控制台新建一个用户，再进 OSS 控制台赋予其全部权限就好了。&lt;/p>
&lt;p>最开始的时候我在用如下的自定义权限策略，比较复杂，现在只配置一点点简单服务的情况下不建议这么做。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Statement&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Action&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;oss:*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Effect&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Allow&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Resource&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;acs:oss:*:*:bucket_name&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;acs:oss:*:*:bucket_name/*&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;Version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="腾讯云">腾讯云&lt;/h2>
&lt;p>进入访问管理控制台新建一个子用户并生成一对 API 密钥，也是进 COS 控制台给它权限。&lt;/p>
&lt;h1 id="上传脚本">上传脚本&lt;/h1>
&lt;p>写了个 nodejs 脚本，每次写完东西直接就上传了，很方便。&lt;/p>
&lt;p>阿里云： &lt;a href="https://github.com/determ1ne/node-aliyun-oss-deployer">https://github.com/determ1ne/node-aliyun-oss-deployer&lt;/a>&lt;/p>
&lt;p>腾讯云： &lt;a href="https://github.com/determ1ne/node-tencent-cos-deployer">https://github.com/determ1ne/node-tencent-cos-deployer&lt;/a>&lt;/p>
&lt;h1 id="部署前端">部署前端&lt;/h1>
&lt;p>如果是静态前端的话，也直接上传就好了。不过之前用 antd pro 时， API 的反向代理就不能用了，因为 OSS 不支持 POST 方法。不知道有没有啥别的解决方法，但是我就不再用代理了，乖乖配了相关的配置，比如 cors 。&lt;/p></description></item><item><title>在 Docker 中运行 ROS Desktop</title><link>https://blog.sigsegv.top/posts/run-ros-desktop-in-docker/</link><pubDate>Sat, 22 Feb 2020 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/run-ros-desktop-in-docker/</guid><description>&lt;p>Dockerfile 下载： &lt;a href="https://github.com/determ1ne/ros-dekstop-nvidia-docker">https://github.com/determ1ne/ros-dekstop-nvidia-docker&lt;/a>&lt;/p>
&lt;p>想配置一个 ros 的环境确实挺麻烦， ros 的开发人员也许想到了这一点，所以在官方 wiki 上就有写了一半弃坑的教程 &lt;a href="http://wiki.ros.org/docker/Tutorials/GUI">Using GUI&amp;rsquo;s with Docker&lt;/a> 和写完的的教程 &lt;a href="http://wiki.ros.org/docker/Tutorials/Hardware%20Acceleration">Using Hardware Acceleration with Docker&lt;/a> 。&lt;/p>
&lt;p>仿照着做的时候，总提示 glvnd 的问题，于是干脆拉了一个 nvidia 的 glvnd 镜像作为 base ，之后也用 nvidia-docker 运行容器，说不定之后改成包含 CUDA 的镜像也很简单。基本方法和 wiki 上的 The isolated way 大致相同，只不过加了一些 nvidia 相关的东西。&lt;/p>
&lt;p>至于 gazebo7 ，首先需要关闭 Qt 的 &lt;a href="https://en.wikipedia.org/wiki/MIT-SHM">MIT-SHM&lt;/a> 才能正常运行： &lt;code>QT_X11_NO_MITSHM=1 gazebo&lt;/code> 。不过由于 gazebo7 还会联网下载模型，这段时间窗口会保持黑屏，我以为是 bug 。。不过 gazebo9 是完全可以正常使用的，就没有再折腾了。&lt;/p>
&lt;p>还有一个比较有意思的事就是如何在 docker 不同阶段构建时保持不同的网络环境。我最后使用了 V2Ray 控制路由，对国内和国外不同服务器的连接使用不同的网络线路，获得最快的构建速度。&lt;/p></description></item><item><title>Linux 下在 USB 固态硬盘上开启 trim</title><link>https://blog.sigsegv.top/posts/enable-trim-on-usb-ssd-linux/</link><pubDate>Thu, 20 Feb 2020 00:00:00 +0000</pubDate><guid>https://blog.sigsegv.top/posts/enable-trim-on-usb-ssd-linux/</guid><description>&lt;h1 id="heading">？&lt;/h1>
&lt;p>因为课程要使用旧版本的 ros ，为了免去不必要的麻烦就在 USB SSD 上装了 Ubuntu 16.04 LTS 。本来没有什么问题，在改 &lt;code>/etc/fstab&lt;/code> 中的 EFI 分区时突然发现这块固态硬盘上没有开 trim ，于是尝试用 &lt;code>fstrim&lt;/code> 手动 trim 一下，结果报了错。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ sudo fstrim -v /run/media/azuk/ssd
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">fstrim: /run/media/telgar/ssd: the discard operation is not supported
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ lsblk --discard /dev/sdb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sdb &lt;span class="m">0&lt;/span> 0B 0B &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─sdb1 &lt;span class="m">0&lt;/span> 0B 0B &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─sdb2 &lt;span class="m">0&lt;/span> 0B 0B &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="heading-1">！&lt;/h1>
&lt;p>我的 NVMe 硬盘盒通过 USB 3.0 接口接到电脑上，是通过 USB Attached SCSI (UAS) 协议完成通信的。 SCSI 中并没有 &lt;code>trim&lt;/code> 这个 ATA 命令，只有与之对应的 SCSI 命令 &lt;code>unmap&lt;/code> 。而看起来我的硬盘盒并不支持通过 SCSI/ATA Translation 将 &lt;code>trim&lt;/code> 翻译为 &lt;code>unmap&lt;/code> ，所以需要在 &lt;code>udev&lt;/code> 里配置一下了。&lt;/p>
&lt;h1 id="heading-2">。&lt;/h1>
&lt;p>首先，用 &lt;code>lsusb&lt;/code> 获得设备的 Vendor ID 和 Product ID 。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ lsusb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Bus &lt;span class="m">004&lt;/span> Device 002: ID 0bda:9210 Realtek Semiconductor Corp. RTL9210
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>创建 &lt;code>/etc/udev/rules.d/50-usb-ssd-trim.rules&lt;/code> ，对应自己设备的 ID 输入一条规则：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-udev-rule" data-lang="udev-rule">ACTION==&amp;#34;add|change&amp;#34;, ATTRS{idVendor}==&amp;#34;0bda&amp;#34;, ATTRS{idProduct}==&amp;#34;9210&amp;#34;, SUBSYSTEM==&amp;#34;scsi_disk&amp;#34;, ATTR{provisioning_mode}=&amp;#34;unmap&amp;#34;
&lt;/code>&lt;/pre>&lt;p>重新插上设备，就能看到创建的规则生效啦！&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">$ lsblk --discard /dev/sdb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sdb &lt;span class="m">0&lt;/span> 512B 4G &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├─sdb1 &lt;span class="m">0&lt;/span> 512B 4G &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─sdb2 &lt;span class="m">0&lt;/span> 512B 4G &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后就像普通的 ssd 一样用 &lt;code>fstrim&lt;/code> 就好了。&lt;/p>
&lt;h1 id="heading-3">＊&lt;/h1>
&lt;p>&lt;strong>References:&lt;/strong>&lt;/p>
&lt;p>&lt;a href="https://www.glump.net/howto/desktop/enable-trim-on-an-external-ssd-on-linux">https://www.glump.net/howto/desktop/enable-trim-on-an-external-ssd-on-linux&lt;/a>
&lt;a href="https://bbs.archlinux.org/viewtopic.php?id=244636">https://bbs.archlinux.org/viewtopic.php?id=244636&lt;/a>
&lt;a href="https://wiki.archlinux.org/index.php/Solid_state_drive#External_SSD_with_TRIM_support">https://wiki.archlinux.org/index.php/Solid_state_drive#External_SSD_with_TRIM_support&lt;/a>
&lt;a href="https://wiki.archlinux.org/index.php/Udev">https://wiki.archlinux.org/index.php/Udev&lt;/a>&lt;/p></description></item></channel></rss>