<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>//*梦-Blog</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2026-04-16T05:21:11.731Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>//*梦</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>2026腾讯游戏安全技术竞赛-安卓客户端安全-初赛 Writeup</title>
    <link href="http://example.com/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/"/>
    <id>http://example.com/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/</id>
    <published>2026-04-16T04:32:00.000Z</published>
    <updated>2026-04-16T05:21:11.731Z</updated>
    
    <content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>拿到题目也是懵逼了，腾子你怎么换引擎了今年，那我复现的往年那么多 UE4 算什么(T_T)</p><p>只能硬着头皮做了。</p><p>担惊受怕两天也是如愿所偿，进决赛了&#x2F;(ㄒoㄒ)&#x2F;~~，希望决赛能好好发挥，佬们轻点打！！</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260416123923252.png" alt="image-20260416123923252"></p><h3 id="gdc-文件解密分析"><a href="#gdc-文件解密分析" class="headerlink" title="*.gdc 文件解密分析"></a><code>*.gdc</code> 文件解密分析</h3><h4 id="寻找核心加密代码"><a href="#寻找核心加密代码" class="headerlink" title="寻找核心加密代码"></a>寻找核心加密代码</h4><p>上来直接解压看到 <code>lib/</code> 中有<code>libgodot_android.so</code> ,确定是 Godot 引擎。</p><p>同时在 <code>assets/</code> 中看到有 <code>.gdc</code> 的 GDScript 字节码，查看他的文件头发现不是标准的 <code>GDSC</code> 开头</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411115813911.png" alt="image-20260411115813911"></p><p>既然如此，那就直接开始在 <code>libgodot_android.so</code> 里找怎么解密这些 <code>.gdc</code> 文件</p><p>网上找了很多，也试了很多常见的字符串搜索，如 <code>open_encrypted</code> 是加密文件打开的方法名、<code>open_encrypted_with_pass</code> 是带密码的加密打开的方法名，但向上对他们进行交叉引用只招到了一些 Godot 方法注册</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411120709058.png" alt="image-20260411120709058"></p><p>最后搜索的是 <code>PackedSourcePCK</code> ，这个是 PCK 包读取器的 RTTI 名，定位到</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411123720759.png" alt="image-20260411123720759"></p><p>前面的15是指字符串 <code>PackedSourcePCK</code> 的长度，往上找交叉引用，</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411124452658.png" alt="image-20260411124452658"></p><p>在这里我们找到了 <code>PackedSourcePCK</code> 类的虚函数表，当进入函数 <code>sub_3804C2C</code> 时，完全可以确定就是这里了</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411124929265.png" alt="image-20260411124929265"></p><p>这里明显都是在做 PCK 文件头校验，这里代码太多太乱了，交给 ai，以下两个函数分析来自 GPT5.4 结合 idapro mcp进行的分析</p><h4 id="sub-3804C2C-函数分析"><a href="#sub-3804C2C-函数分析" class="headerlink" title="sub_3804C2C 函数分析"></a><code>sub_3804C2C</code> 函数分析</h4><h5 id="在-sub-3804C2C-中，0x3804F08-处有一个关键条件判断"><a href="#在-sub-3804C2C-中，0x3804F08-处有一个关键条件判断" class="headerlink" title="在 sub_3804C2C 中，0x3804F08 处有一个关键条件判断"></a>在 <code>sub_3804C2C</code> 中，<code>0x3804F08</code> 处有一个关键条件判断</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">v36 = (*(__int64 (__fastcall **)(_QWORD *))(*a17 + <span class="number">360LL</span>))(a17);</span><br><span class="line"><span class="keyword">if</span> ( (v31 &amp; <span class="number">1</span>) == <span class="number">0</span> )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_71;  <span class="comment">// 不加密，直接读文件列表</span></span><br></pre></td></tr></table></figure><p>当 <code>v31 &amp; 1</code> 为 <strong>1</strong> 时，代码<strong>不会跳转</strong>，而是继续往下执行加密处理路径。这里的 <code>v31</code> 来自 PCK 文件头中的 flags 字段，<code>bit 0</code> 标志”是否加密”。</p><h5 id="加密分支的关键代码段-0x3804F10-0x3804F78"><a href="#加密分支的关键代码段-0x3804F10-0x3804F78" class="headerlink" title="加密分支的关键代码段 (0x3804F10 ~ 0x3804F78)"></a>加密分支的关键代码段 (0x3804F10 ~ 0x3804F78)</h5><p>当进入加密分支后，汇编如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">; --- 0x3804F10: 保存上下文，准备分配密钥缓冲 ---</span><br><span class="line">3804F10  STR   X26, [SP, #0x10]</span><br><span class="line">3804F14  STUR  XZR, [X29, #-0x28]        ; 初始化 FileAccessEncrypted 指针为 NULL</span><br><span class="line">3804F18  BL    sub_37DAC48               ; 创建 FileAccessEncrypted 对象</span><br><span class="line"></span><br><span class="line">; --- 0x3804F28: 分配 32 字节缓冲区 ---</span><br><span class="line">3804F28  MOV   W1, #0x20                 ; 0x20 = 32，要求 32 字节</span><br><span class="line">3804F34  BL    sub_107CBA4               ; 内存分配/扩容函数</span><br><span class="line"></span><br><span class="line">; --- 0x3804F40 ~ 0x3804F74: 逐字节复制密钥！ ---</span><br><span class="line">3804F40  ADRP  X26, #0x4004000           ; 加载页地址</span><br><span class="line">3804F44  MOV   X27, XZR                  ; 循环计数器 i = 0</span><br><span class="line">3804F48  LDR   X26, [X26, #0x488]        ; 从 GOT 加载实际地址 → X26 = 0x400EDF0</span><br><span class="line"></span><br><span class="line">; 循环开始：</span><br><span class="line">3804F4C  LDUR  X1, [X8, #-8]             ; 读取缓冲区当前大小</span><br><span class="line">3804F50  CMP   X1, X27                   ; if (i &gt;= size) break</span><br><span class="line">3804F54  B.LE  loc_3804F78               ;</span><br><span class="line"></span><br><span class="line">3804F58  LDRB  W28, [X26, X27]           ; ★ W28 = byte_400EDF0[i] ← 读取密钥字节！</span><br><span class="line">3804F5C  ADD   X0, X25, #8</span><br><span class="line">3804F60  BL    sub_107CBA4               ; 确保缓冲区大小足够</span><br><span class="line">3804F64  LDUR  X8, [X29, #-0x10]</span><br><span class="line">3804F68  STRB  W28, [X8, X27]            ; ★ key_buf[i] = 密钥字节</span><br><span class="line">3804F6C  ADD   X27, X27, #1              ; i++</span><br><span class="line">3804F70  LDUR  X8, [X29, #-0x10]</span><br><span class="line">3804F74  CBNZ  X8, loc_3804F4C           ; 继续循环</span><br></pre></td></tr></table></figure><h5 id="关键发现：密钥地址-0x400EDF0"><a href="#关键发现：密钥地址-0x400EDF0" class="headerlink" title="关键发现：密钥地址 0x400EDF0"></a>关键发现：密钥地址 0x400EDF0</h5><p>上面的汇编可以清晰看到：</p><ul><li><p><code>ADRP X26, #0x4004000</code> + <code>LDR X26, [X26, #0x488]</code>：通过 GOT 间接加载，实际读取的是地址 <code>0x4004488</code> 处存储的指针。</p></li><li><p><strong>GOT 表 <code>0x4004488</code> 处存储的值</strong>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0x4004488: F0 ED 00 04 00 00 00 00</span><br></pre></td></tr></table></figure><p>即 <code>0x000000000400EDF0</code>，确认 X26 最终指向 <code>0x400EDF0</code>。</p></li><li><p><code>LDRB W28, [X26, X27]</code>：从 <code>0x400EDF0 + i</code> 读一个字节。</p></li><li><p><code>STRB W28, [X8, X27]</code>：写入密钥缓冲区 <code>key_buf[i]</code>。</p></li><li><p>循环 32 次（因为前面分配了 32 字节）。</p></li></ul><p>在 IDA 中直接提取密钥，导出十六进制：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CE 4D F8 75 3B 59 A5 A3 9A DE 58 AC 07 EF 94 7A</span><br><span class="line">3D A3 9F 2A F7 5E 32 84 D5 12 17 C0 4D 49 A0 61</span><br></pre></td></tr></table></figure><p>即密钥十六进制表示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ce4df8753b59a5a39ade58ac07ef947a3da39f2af75e3284d51217c04d49a061</span><br></pre></td></tr></table></figure><h5 id="为什么可以确认这是-AES-密钥而不是其他数据"><a href="#为什么可以确认这是-AES-密钥而不是其他数据" class="headerlink" title="为什么可以确认这是 AES 密钥而不是其他数据"></a>为什么可以确认这是 AES 密钥而不是其他数据</h5><p><strong>证据链</strong>：</p><p>(1) <strong>上下文</strong>：这段代码在 <code>sub_3804C2C</code>（PCK 包打开函数）中，且只在”加密标志为 1”时才执行。</p><p>(2) <strong>长度</strong>：恰好 32 字节 &#x3D; 256 bit，这是 AES-256 的标准密钥长度。</p><p>(3) <strong>去向</strong>：复制完成后，密钥缓冲传给了 <code>sub_3801410</code>（见下一节），而 <code>sub_3801410</code> 会验证密钥长度 <strong>必须是 32</strong>：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// sub_3801410 @ 0x3801450:</span></span><br><span class="line">v16 = *(_QWORD *)(a3 + <span class="number">8</span>);</span><br><span class="line"><span class="keyword">if</span> ( !v16 || *(_QWORD *)(v16 - <span class="number">8</span>) != <span class="number">32LL</span> )</span><br><span class="line">    <span class="keyword">goto</span> LABEL_41;  <span class="comment">// 报错返回</span></span><br></pre></td></tr></table></figure><p>(4) <strong>复用</strong>：同一个 <code>byte_400EDF0</code> 在另一条路径 <code>sub_3805EDC</code>（<code>0x38061B0</code>）中也被使用，用途相同——传给 <code>sub_3801410</code> 做加密文件初始化。</p><p>(5) <strong>最终</strong>：<code>sub_3801410</code> 内部会将这 32 字节传给 AES key schedule 函数 <code>sub_197C210</code>，而 <code>sub_197C210</code> 在 <code>a3 == 256</code> 时设置 <code>*a1 = 14</code>（AES-256 的 14 轮），进一步确认这是 AES-256 密钥。</p><h4 id="0x3801410-函数分析"><a href="#0x3801410-函数分析" class="headerlink" title="0x3801410 函数分析"></a><code>0x3801410</code> 函数分析</h4><p>在 <code>sub_3804C2C</code> 的 <code>0x3804FB0</code> 处：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v42 = sub_3801410(v41, &amp;a15, &amp;a18, <span class="number">0LL</span>, <span class="number">0LL</span>, &amp;a13);</span><br></pre></td></tr></table></figure><p>其中：</p><ul><li><code>v41</code> &#x3D; FileAccessEncrypted 对象</li><li><code>&amp;a15</code> &#x3D; 底层文件访问对象指针（用于读取原始加密文件）</li><li><code>&amp;a18</code> &#x3D; 密钥缓冲区</li></ul><p><code>sub_3801410</code> 就是 <strong>FileAccessEncrypted 的初始化&#x2F;打开函数</strong>。</p><p>反编译 sub_3801410 的关键段</p><p>在 <code>sub_3801410</code> 中，当 <code>a4 == 0</code>（打开模式 &#x3D; 读取）时，执行以下步骤：</p><h5 id="步骤-1：验证密钥长度"><a href="#步骤-1：验证密钥长度" class="headerlink" title="步骤 1：验证密钥长度"></a>步骤 1：验证密钥长度</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @ 0x3801450</span></span><br><span class="line">v16 = *(_QWORD *)(a3 + <span class="number">8</span>);                    <span class="comment">// 取密钥缓冲区指针</span></span><br><span class="line"><span class="keyword">if</span> ( !v16 || *(_QWORD *)(v16 - <span class="number">8</span>) != <span class="number">32LL</span> )   <span class="comment">// 检查长度是否为 32</span></span><br><span class="line">    <span class="keyword">goto</span> LABEL_41;                              <span class="comment">// 不是 32 则报错</span></span><br></pre></td></tr></table></figure><h5 id="步骤-2：（可选）检查-GDEC-magic"><a href="#步骤-2：（可选）检查-GDEC-magic" class="headerlink" title="步骤 2：（可选）检查 GDEC magic"></a>步骤 2：（可选）检查 <code>GDEC</code> magic</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @ 0x380170C</span></span><br><span class="line"><span class="keyword">if</span> ( *(_BYTE *)(a1 + <span class="number">417</span>) &amp;&amp;                   <span class="comment">// 如果设置了加密标志</span></span><br><span class="line">     (vf_360)(a2) != <span class="number">1128612935</span> )               <span class="comment">// 读取 4 字节 magic</span></span><br></pre></td></tr></table></figure><p><code>1128612935 = 0x43454447</code>，字节序转换为 ASCII &#x3D; <code>&quot;GDEC&quot;</code>。</p><blockquote><p><strong>注意</strong>：这个 <code>GDEC</code> magic 检查只在某些模式下触发。在本题样本中，加密文件<strong>直接以 MD5 开头</strong>，没有 <code>GDEC</code> 前缀——也就是说我们分析的文件走的是不带 <code>GDEC</code> magic 的那条路径。</p></blockquote><h5 id="步骤-3：读取文件头字段"><a href="#步骤-3：读取文件头字段" class="headerlink" title="步骤 3：读取文件头字段"></a>步骤 3：读取文件头字段</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @ 0x3801724</span></span><br><span class="line">(vf_408)(a2, &amp;a14, <span class="number">16</span>);                        <span class="comment">// ★ 读取 16 字节 → MD5 摘要</span></span><br><span class="line">v42 = (vf_368)(a2);                             <span class="comment">// ★ 读取 8 字节 → 明文长度（uint64 小端）</span></span><br><span class="line">*(_QWORD *)(a1 + <span class="number">384</span>) = v42;                   <span class="comment">// 保存到对象的 plain_len 字段</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 紧接着读 IV：</span></span><br><span class="line"><span class="comment">// 读取 16 字节到 a1+336 所指的缓冲区</span></span><br><span class="line">(vf_408)(v44, *(_QWORD *)(a1 + <span class="number">336</span>), <span class="number">16</span>);      <span class="comment">// ★ 读取 16 字节 → IV</span></span><br></pre></td></tr></table></figure><h5 id="步骤-4：读取密文"><a href="#步骤-4：读取密文" class="headerlink" title="步骤 4：读取密文"></a>步骤 4：读取密文</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @ 0x38017C0 ~ 0x38017D4</span></span><br><span class="line">v47 = *(_QWORD *)(a1 + <span class="number">384</span>);                   <span class="comment">// plain_len</span></span><br><span class="line">v49 = v47 + <span class="number">15</span>;</span><br><span class="line">v50 = v49 &amp; <span class="number">0xFFFFFFFFFFFFFFF0L</span>L;              <span class="comment">// ★ align_up(plain_len, 16)</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">(vf_408)(v53, *(_QWORD *)(a1 + <span class="number">400</span>), v50);     <span class="comment">// 读取 v50 字节密文</span></span><br></pre></td></tr></table></figure><h5 id="步骤-5：设置-AES-上下文-解密"><a href="#步骤-5：设置-AES-上下文-解密" class="headerlink" title="步骤 5：设置 AES 上下文 &amp; 解密"></a>步骤 5：设置 AES 上下文 &amp; 解密</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sub_376EDA0(&amp;a12);                              <span class="comment">// AES 上下文结构体初始化（分配 0x120 字节）</span></span><br><span class="line">sub_376EDFC(&amp;a12, key, <span class="number">256</span>);                    <span class="comment">// AES key schedule 设置（256 = AES-256）</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">sub_376EF68(&amp;a12, v50, iv, ciphertext, output); <span class="comment">// ★ 解密！（调用 sub_197DE18 with a2=0）</span></span><br><span class="line">sub_376EDD0(&amp;a12);                              <span class="comment">// AES 上下文清理/释放</span></span><br></pre></td></tr></table></figure><p>其中 <code>sub_376EDFC</code> 内部调用了 <code>sub_197C210</code>（key expansion），<code>sub_376EF68</code> 内部调用了 <code>sub_197DE18</code>（核心解密循环），参数 <code>W1 = 0</code> 表示解密模式。</p><h5 id="步骤-6：MD5-校验"><a href="#步骤-6：MD5-校验" class="headerlink" title="步骤 6：MD5 校验"></a>步骤 6：MD5 校验</h5><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// @ 0x3801A2C ~ 0x3801A54</span></span><br><span class="line">sub_3CB453C(&amp;a11, &amp;a12);                        <span class="comment">// 计算解密后明文的 MD5</span></span><br><span class="line">sub_3CB453C(&amp;a10, &amp;a14);                        <span class="comment">// 包装文件头中的 MD5</span></span><br><span class="line">v68 = sub_3CADE58(&amp;a11, &amp;a10);                  <span class="comment">// 比较两个 MD5</span></span><br><span class="line"><span class="keyword">if</span> ( (v68 &amp; <span class="number">1</span>) != <span class="number">0</span> )                           <span class="comment">// 不匹配</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">16LL</span>;                                <span class="comment">// 返回错误码 16</span></span><br></pre></td></tr></table></figure><p>还原出的文件头结构</p><p>综合以上分析，加密资源文件的格式为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">偏移    大小    字段            说明</span><br><span class="line">0x00    16      md5[16]        明文的 MD5 摘要</span><br><span class="line">0x10    8       plain_len      明文长度（uint64，小端序）</span><br><span class="line">0x18    16      iv[16]         AES-CFB 变种的初始化向量</span><br><span class="line">0x28    N       ciphertext     密文（N = align_up(plain_len, 16)）</span><br></pre></td></tr></table></figure><p>用 token.gdc 验证格式</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">文件: preliminary/assets/token.gdc</span><br><span class="line">大小: 680 字节</span><br><span class="line"></span><br><span class="line">头部解析:</span><br><span class="line">  md5       = 9590a928a742f1f0536581d451f252b5</span><br><span class="line">  plain_len = 0x0000000000000271 = 625</span><br><span class="line">  iv        = c5b1680b09a2f12db436fcb1ddb69c0f</span><br><span class="line"></span><br><span class="line">密文长度 = align_up(625, 16) = 640</span><br><span class="line">文件总长 = 40(头部) + 640(密文) = 680 ✓ 完全吻合</span><br></pre></td></tr></table></figure><p>这两个函数分析完，我获取了密文，密钥，iv，以及原文件md5值，这些所有内容，但是当我尝试通过 <a href="https://github.com/GDRETools/gdsdecomp">gdre_tools</a> 对 <code>.gdc</code> 文件进行解密和反编译时，发现始终无法成功。</p><p>这里了卡了我很久，我一开始以为是文件格式还有我不会使用工具的原因，后来我在网络上找到一些帖子，才考虑到是不是加密算法被魔改了。</p><p>以下对算法的深入分析同样来自 GPT5.4 结合 idapro mcp 给出</p><h4 id="为什么”标准-AES-CFB”解不出正确结果"><a href="#为什么”标准-AES-CFB”解不出正确结果" class="headerlink" title="为什么”标准 AES-CFB”解不出正确结果"></a>为什么”标准 AES-CFB”解不出正确结果</h4><h5 id="最直觉的假设"><a href="#最直觉的假设" class="headerlink" title="最直觉的假设"></a>最直觉的假设</h5><p>到这一步，我们已经知道：</p><ul><li>密钥：32 字节（AES-256）</li><li>IV：16 字节</li><li>文件头有 MD5 校验</li></ul><p>这样做出来的 MD5 <strong>不匹配</strong>。</p><h4 id="深入-0x197DE18：找到真正的逐字节解密逻辑"><a href="#深入-0x197DE18：找到真正的逐字节解密逻辑" class="headerlink" title="深入 0x197DE18：找到真正的逐字节解密逻辑"></a>深入 0x197DE18：找到真正的逐字节解密逻辑</h4><h5 id="调用链追踪"><a href="#调用链追踪" class="headerlink" title="调用链追踪"></a>调用链追踪</h5><p>从 <code>sub_3801410</code> 中的解密调用出发：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sub_376EF68(ctx, length, iv_buf, ciphertext, output)</span><br><span class="line">    ↓</span><br><span class="line">    LDR X0, [X0]           ; 取 AES 上下文指针</span><br><span class="line">    MOV W1, WZR            ; ★ W1 = 0 → 解密模式</span><br><span class="line">    BL sub_197DE18          ; 核心处理函数</span><br></pre></td></tr></table></figure><p>对比另一个封装函数 <code>sub_376EE9C</code>（加密方向）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sub_376EE9C:</span><br><span class="line">    MOV W1, #1              ; ★ W1 = 1 → 加密模式</span><br><span class="line">    BL sub_197DE18</span><br></pre></td></tr></table></figure><p>所以 <code>sub_197DE18</code> 是 <strong>统一的 CFB 处理函数</strong>，参数 <code>a2</code> 决定方向：</p><ul><li><code>a2 == 0</code>：解密</li><li><code>a2 == 1</code>：加密</li></ul><h5 id="反编译-sub-197DE18"><a href="#反编译-sub-197DE18" class="headerlink" title="反编译 sub_197DE18"></a>反编译 sub_197DE18</h5><p>完整伪代码（IDA <code>F5</code>）如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">__int64 <span class="title function_">sub_197DE18</span><span class="params">(</span></span><br><span class="line"><span class="params">    __int64 aes_ctx,     <span class="comment">// a1: AES 上下文（含 key schedule）</span></span></span><br><span class="line"><span class="params">    <span class="type">unsigned</span> <span class="type">int</span> mode,    <span class="comment">// a2: 0=解密, 1=加密</span></span></span><br><span class="line"><span class="params">    __int64 length,       <span class="comment">// a3: 数据长度</span></span></span><br><span class="line"><span class="params">    __int64 *offset_ptr,  <span class="comment">// a4: 当前块内偏移的指针</span></span></span><br><span class="line"><span class="params">    __int64 state_buf,    <span class="comment">// a5: 16字节状态块（初始值 = IV）</span></span></span><br><span class="line"><span class="params">    <span class="type">char</span> *input,          <span class="comment">// a6: 输入数据</span></span></span><br><span class="line"><span class="params">    _BYTE *output,        <span class="comment">// a7: 输出数据</span></span></span><br><span class="line"><span class="params">    ...</span></span><br><span class="line"><span class="params">)</span></span><br></pre></td></tr></table></figure><p>函数首先检查 <code>mode &gt; 1</code> 则返回错误，然后检查 <code>*offset_ptr &gt; 15</code> 也返回错误。</p><h5 id="解密分支的汇编（mode-0，即-a2-0）"><a href="#解密分支的汇编（mode-0，即-a2-0）" class="headerlink" title="解密分支的汇编（mode == 0，即 a2 == 0）"></a>解密分支的汇编（<code>mode == 0</code>，即 <code>a2 == 0</code>）</h5><p>从 <code>0x197DF04</code> 开始的解密循环：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">; 循环入口：检查是否需要刷新 keystream</span><br><span class="line">197DF54  CBNZ  X25, loc_197DF2C           ; if (offset != 0) 跳过 AES-ECB</span><br><span class="line"></span><br><span class="line">; offset == 0，需要重新加密状态块生成 keystream</span><br><span class="line">197DF70  MOV   X0, X24                     ; aes_ctx</span><br><span class="line">197DF74  MOV   X1, X22                     ; state_buf (输入)</span><br><span class="line">197DF78  MOV   X2, X22                     ; state_buf (输出, in-place)</span><br><span class="line">197DF7C  BL    sub_197CDB8                 ; ★ AES_ECB_Encrypt(state_buf) → state_buf</span><br><span class="line"></span><br><span class="line">; 逐字节处理：</span><br><span class="line">197DF2C  LDRB  W8, [X21], #1              ; W8 = *input++  （读取一个密文字节）</span><br><span class="line">197DF30  LDRB  W9, [X22, X25]             ; W9 = state[offset]  （读取 keystream 字节）</span><br><span class="line">197DF34  ADD   W10, W25, #1               ; W10 = offset + 1</span><br><span class="line">197DF38  SUB   X23, X23, #1               ; length--</span><br><span class="line"></span><br><span class="line">197DF3C  EOR   W8, W8, W25               ; ★★ W8 = ciphertext_byte ^ offset</span><br><span class="line">197DF40  EOR   W9, W9, W8                ; ★★ W9 = state[offset] ^ (ciphertext_byte ^ offset) = 明文</span><br><span class="line">197DF44  STRB  W9, [X20], #1             ; *output++ = plaintext_byte</span><br><span class="line">197DF48  STRB  W8, [X22, X25]            ; ★★ state[offset] = ciphertext_byte ^ offset  （反馈）</span><br><span class="line"></span><br><span class="line">197DF4C  AND   X25, X10, #0xF            ; offset = (offset + 1) &amp; 0xF</span><br><span class="line">197DF50  CBZ   X23, loc_197DF84           ; if (length == 0) 跳出</span><br></pre></td></tr></table></figure><h5 id="逐指令翻译为伪代码"><a href="#逐指令翻译为伪代码" class="headerlink" title="逐指令翻译为伪代码"></a>逐指令翻译为伪代码</h5><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">state = <span class="built_in">bytearray</span>(iv)    <span class="comment"># 16 字节状态块，初始值为 IV</span></span><br><span class="line">offset = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> cipher_byte <span class="keyword">in</span> ciphertext:</span><br><span class="line">    <span class="keyword">if</span> offset == <span class="number">0</span>:</span><br><span class="line">        state[:] = AES_ECB_Encrypt(key, state)   <span class="comment"># 每处理完 16 字节，刷新 keystream</span></span><br><span class="line"></span><br><span class="line">    mixed = cipher_byte ^ offset                  <span class="comment"># ★ 关键：密文字节与块内偏移异或</span></span><br><span class="line">    plain = state[offset] ^ mixed                 <span class="comment"># 用 keystream 解密</span></span><br><span class="line">    state[offset] = mixed                         <span class="comment"># ★ 关键：反馈的是 mixed，不是原始密文</span></span><br><span class="line"></span><br><span class="line">    output.append(plain)</span><br><span class="line">    offset = (offset + <span class="number">1</span>) &amp; <span class="number">0x0F</span></span><br></pre></td></tr></table></figure><h5 id="与标准-AES-CFB-的对比"><a href="#与标准-AES-CFB-的对比" class="headerlink" title="与标准 AES-CFB 的对比"></a>与标准 AES-CFB 的对比</h5><table><thead><tr><th>步骤</th><th>标准 AES-CFB128</th><th>Godot 变种</th></tr></thead><tbody><tr><td>读入密文字节 <code>c</code></td><td><code>c</code></td><td><code>c</code></td></tr><tr><td>混合操作</td><td>无</td><td><code>mixed = c ^ offset</code></td></tr><tr><td>生成明文</td><td><code>p = keystream[offset] ^ c</code></td><td><code>p = keystream[offset] ^ mixed</code></td></tr><tr><td>反馈到状态块</td><td><code>state[offset] = c</code></td><td><code>state[offset] = mixed</code></td></tr></tbody></table><p><strong>差异只有一个地方</strong>：Godot 在每个字节处理时额外混入了 <code>offset</code>（块内位置 <code>0~15</code>）。</p><h5 id="加密分支的反向验证"><a href="#加密分支的反向验证" class="headerlink" title="加密分支的反向验证"></a>加密分支的反向验证</h5><p>对比加密分支（<code>mode == 1</code>，在 <code>0x197DEAC</code> 开始的循环）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">197DEAC  LDRB  W8, [X21], #1              ; W8 = *input++  （明文字节）</span><br><span class="line">197DEB8  EOR   W9, W9, W8                 ; W9 = state[offset] ^ plaintext</span><br><span class="line">197DEC0  STRB  W9, [X22, X25]             ; state[offset] = state[offset] ^ plaintext</span><br><span class="line">197DEC4  EOR   W8, W9, W25               ; W8 = (state[offset] ^ plaintext) ^ offset</span><br><span class="line">197DECC  STRB  W8, [X20], #1             ; *output++ = 密文字节</span><br></pre></td></tr></table></figure><p>加密伪代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mixed = state[offset] ^ plain_byte         <span class="comment"># 先与 keystream 异或</span></span><br><span class="line">state[offset] = mixed                       <span class="comment"># 反馈 mixed</span></span><br><span class="line">cipher_byte = mixed ^ offset                <span class="comment"># 再与 offset 异或生成密文输出</span></span><br></pre></td></tr></table></figure><p>可以验证：解密是加密的精确逆过程 ✓</p><h5 id="确认-sub-197CDB8-是-AES-ECB"><a href="#确认-sub-197CDB8-是-AES-ECB" class="headerlink" title="确认 sub_197CDB8 是 AES-ECB"></a>确认 sub_197CDB8 是 AES-ECB</h5><p><code>sub_197CDB8</code> 的反编译结果中可以看到：</p><ul><li>它读取 <code>a1</code> 指向的结构体来获取轮数和 round keys。</li><li>使用了 <code>dword_4046118</code>、<code>dword_4046918</code> 等查找表——这些是标准 AES 实现中的 T-table（Te0~Te3 的变体）。</li><li>使用了 <code>byte_4044F18</code>——这是 AES 的 S-Box。</li><li>函数接受 16 字节输入（<code>a2</code>），输出 16 字节（<code>a3</code>），是典型的 AES 单块加密。</li><li>key schedule 函数 <code>sub_197C210</code> 中，当传入 <code>a3 = 256</code> 时设置 <code>*a1 = 14</code>（14 轮 &#x3D; AES-256）。</li></ul><p>因此 <strong><code>sub_197CDB8</code> 就是 AES-256-ECB 单块加密函数</strong>。</p><p>既然如此，在ai的配合下就很容易的写出解密代码如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"><span class="keyword">import</span> hashlib, struct</span><br><span class="line"></span><br><span class="line">KEY_HEX = <span class="string">&quot;ce4df8753b59a5a39ade58ac07ef947a3da39f2af75e3284d51217c04d49a061&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">godot_cfb_xor_decrypt</span>(<span class="params">key: <span class="built_in">bytes</span>, iv: <span class="built_in">bytes</span>, ciphertext: <span class="built_in">bytes</span></span>) -&gt; <span class="built_in">bytes</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    Godot 变种 CFB 解密。</span></span><br><span class="line"><span class="string">    与标准 CFB 的唯一区别：每个密文字节在参与 keystream 异或和状态反馈前，</span></span><br><span class="line"><span class="string">    先与块内偏移 offset (0~15) 做一次异或。</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    ecb = AES.new(key, AES.MODE_ECB)</span><br><span class="line">    state = <span class="built_in">bytearray</span>(iv)</span><br><span class="line">    out = <span class="built_in">bytearray</span>()</span><br><span class="line">    offset = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> enc_byte <span class="keyword">in</span> ciphertext:</span><br><span class="line">        <span class="keyword">if</span> offset == <span class="number">0</span>:</span><br><span class="line">            <span class="comment"># 每处理完 16 字节，用 AES-ECB 加密当前状态块生成新的 keystream</span></span><br><span class="line">            state[:] = ecb.encrypt(<span class="built_in">bytes</span>(state))</span><br><span class="line"></span><br><span class="line">        mixed = enc_byte ^ offset           <span class="comment"># Godot 变种的关键一步</span></span><br><span class="line">        plain = state[offset] ^ mixed       <span class="comment"># 用 keystream 解密</span></span><br><span class="line">        state[offset] = mixed               <span class="comment"># 反馈到状态块</span></span><br><span class="line"></span><br><span class="line">        out.append(plain)</span><br><span class="line">        offset = (offset + <span class="number">1</span>) &amp; <span class="number">0x0F</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(out)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">decrypt_file</span>(<span class="params">filepath: <span class="built_in">str</span>, key: <span class="built_in">bytes</span></span>) -&gt; <span class="built_in">bytes</span>:</span><br><span class="line">    data = <span class="built_in">open</span>(filepath, <span class="string">&quot;rb&quot;</span>).read()</span><br><span class="line"></span><br><span class="line">    digest = data[<span class="number">0</span>:<span class="number">16</span>]                                    <span class="comment"># MD5 摘要</span></span><br><span class="line">    plain_len = struct.unpack(<span class="string">&quot;&lt;Q&quot;</span>, data[<span class="number">16</span>:<span class="number">24</span>])[<span class="number">0</span>]        <span class="comment"># 明文长度</span></span><br><span class="line">    iv = data[<span class="number">24</span>:<span class="number">40</span>]                                       <span class="comment"># IV</span></span><br><span class="line">    ciphertext = data[<span class="number">40</span>:]                                 <span class="comment"># 密文</span></span><br><span class="line"></span><br><span class="line">    plaintext = godot_cfb_xor_decrypt(key, iv, ciphertext)[:plain_len]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 验证 MD5</span></span><br><span class="line">    <span class="keyword">if</span> hashlib.md5(plaintext).digest() != digest:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;MD5 mismatch! 密钥或算法有误&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Right!&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> plaintext</span><br><span class="line"></span><br><span class="line">key = <span class="built_in">bytes</span>.fromhex(KEY_HEX)</span><br><span class="line">pt = decrypt_file(<span class="string">&quot;assets/token.gdc&quot;</span>, key)</span><br></pre></td></tr></table></figure><p>由于没有多少个文件，我就直接解密了，不写批量解密代码了</p><p>此时查看解密后的 <code>.gdc</code> 文件，可以看到已经将文件头恢复成功了</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411140730854.png" alt="image-20260411140730854"></p><p>此时使用 gdre_tools 就可以正常反编译了</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411141412349.png" alt="image-20260411141412349"></p><h3 id="flag游戏内验证"><a href="#flag游戏内验证" class="headerlink" title="flag游戏内验证"></a>flag游戏内验证</h3><h4 id="gd-文件分析"><a href="#gd-文件分析" class="headerlink" title="*.gd 文件分析"></a><code>*.gd</code> 文件分析</h4><p>首先是 <code>token.gd</code> </p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411141919890.png" alt="image-20260411141919890"></p><p>这里是 <code>token</code> 的生成逻辑，可以看到是随机生成的</p><p>最关键的代码在 <code>trigger.gd</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">extends Area3D</span><br><span class="line"></span><br><span class="line">signal collided_with(name)</span><br><span class="line">var flag1Triggered: = false</span><br><span class="line">var flag2Triggered: = false</span><br><span class="line">var t: = 0.0</span><br><span class="line">var cnt: = 0</span><br><span class="line"></span><br><span class="line">const FLAG_PREFIX: = &quot;sec2026_PART1_&quot;</span><br><span class="line">const ROUNDS: = 8</span><br><span class="line">const TOKEN_BYTES: = 4</span><br><span class="line">var obj</span><br><span class="line"></span><br><span class="line">func _ready() -&gt; void :</span><br><span class="line">    obj = GameExtension.new()</span><br><span class="line"></span><br><span class="line">func xor_enc(plain: String) -&gt; PackedByteArray:</span><br><span class="line"></span><br><span class="line">    var out_buf = plain.to_utf8_buffer()</span><br><span class="line">    if out_buf.size() &lt; 8:</span><br><span class="line">        out_buf.resize(8)</span><br><span class="line"></span><br><span class="line">    var result = out_buf.slice(0, 8)</span><br><span class="line">    for i in range(7):</span><br><span class="line">        result[i] = result[i] ^ result[i + 1]</span><br><span class="line">    result[7] = result[7] ^ result[0]</span><br><span class="line">    return result</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">func _process(delta):</span><br><span class="line">    t += delta * 2.0</span><br><span class="line">    if $MeshInstance3D != null and monitoring:</span><br><span class="line"></span><br><span class="line">        $MeshInstance3D.rotation.y += delta * 1.0</span><br><span class="line"></span><br><span class="line">        var height = sin(t) * 0.2</span><br><span class="line">        $MeshInstance3D.position.y = height</span><br><span class="line"></span><br><span class="line">        var pulse = 1.0 + sin(t * 3.0) * 0.1</span><br><span class="line">        $MeshInstance3D.scale = Vector3(pulse, pulse, pulse)</span><br><span class="line"></span><br><span class="line">        var body = get_overlapping_bodies()</span><br><span class="line">        if body.size() &gt; 0:</span><br><span class="line">            if str(get_path()) == &quot;/root/TownScene/Trigger1&quot;:</span><br><span class="line">                var label = get_node(&quot;/root/TownScene/Label2&quot;)</span><br><span class="line">                label.text = &quot;flag&#123;sec2026_PART0_example&#125;&quot;</span><br><span class="line"></span><br><span class="line">            elif str(get_path()) == &quot;/root/TownScene/Trigger2&quot;:</span><br><span class="line">                var label = get_node(&quot;/root/TownScene/Label2&quot;)</span><br><span class="line">                var label1 = get_node(&quot;/root/TownScene/Label&quot;)</span><br><span class="line"></span><br><span class="line">                var flag1 = obj.Process(xor_enc(str(label1.text).substr(7)))</span><br><span class="line">                label.text = &quot;flag&#123;&quot; + FLAG_PREFIX + flag1 + &quot;&#125;  &quot;</span><br></pre></td></tr></table></figure><p>结合 ai 给出分析如下</p><p>这个脚本继承自 <code>Area3D</code>，也就是一个 3D 区域检测节点，常用于碰撞检测、触发器。</p><p>如果碰到 <code>Trigger1</code>，直接显示测试 flag</p><p>如果碰到 <code>Trigger2</code> 则绑定了 flag 生成逻辑：</p><ul><li>获取 Label 文本，截取 <code>substr(7)</code> 去掉 <code>&quot;Token: &quot;</code> 前缀，得到 8 位 hex token。</li><li>对 token 调用 <code>xor_enc()</code> 做一轮链式异或变换（7 次相邻异或 + 尾首异或），得到 8 字节 <code>PackedByteArray</code>。</li><li>将结果传给 <strong><code>GameExtension.Process()</code></strong>——这是 native 扩展 <code>libsec2026.so</code> 提供的方法。</li><li>native 层返回的字符串拼接为最终 flag：<code>flag{sec2026_PART1_&lt;native返回值&gt;}</code>。</li></ul><p>说明接下来我们该去分析 <code>libsec2026.so</code> 了</p><h4 id="获得flag"><a href="#获得flag" class="headerlink" title="获得flag"></a>获得flag</h4><p>因为 Godot 4 引擎的 String 内部以 char32_t (UTF-32LE) 存储, 分配在堆上。而 GDScript 编译后的常量池中, 字符串字面量作为 String 对象存在。</p><p>因此可以直接修改堆上的 char32_t 数据:</p><p>将 elif 条件中的 “&#x2F;Trigger2” → “&#x2F;Trigger1”  (使其匹配 Trigger1 节点)，同时将 if   条件中的 “&#x2F;Trigger1” → “&#x2F;TriggerX”  (使其不再匹配任何节点)，这样当我们在游戏内碰触 Trigger1 时, 代码走 elif 分支, 执行真实 flag 计算。</p><p>如下图，我碰到了下面的黄色方块，但得到了另一个 flag</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411200409717.png" alt="image-20260411200409717"></p><p>Frida 代码如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">patchTriggersUTF32</span>(<span class="params"></span>) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> p1 = <span class="string">&#x27;54 00 00 00 72 00 00 00 69 00 00 00 67 00 00 00 &#x27;</span> +</span><br><span class="line">             <span class="string">&#x27;67 00 00 00 65 00 00 00 72 00 00 00 31 00 00 00&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> p2 = <span class="string">&#x27;54 00 00 00 72 00 00 00 69 00 00 00 67 00 00 00 &#x27;</span> +</span><br><span class="line">             <span class="string">&#x27;67 00 00 00 65 00 00 00 72 00 00 00 32 00 00 00&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> addrs1 = []; <span class="comment">// &quot;/Trigger1&quot; 的 &#x27;T&#x27; 位置</span></span><br><span class="line">    <span class="keyword">var</span> addrs2 = []; <span class="comment">// &quot;/Trigger2&quot; 的 &#x27;T&#x27; 位置</span></span><br><span class="line"></span><br><span class="line">    <span class="title class_">Process</span>.<span class="title function_">enumerateRanges</span>(<span class="string">&#x27;rw-&#x27;</span>).<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">range</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (range.<span class="property">size</span> &lt; <span class="number">256</span> || range.<span class="property">size</span> &gt; <span class="number">0x80000000</span>) <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="title class_">Memory</span>.<span class="title function_">scanSync</span>(range.<span class="property">base</span>, range.<span class="property">size</span>, p1).<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">m</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 检查前一个 char32 是否为 &#x27;/&#x27; (0x0000002F)</span></span><br><span class="line">                    <span class="keyword">if</span> (m.<span class="property">address</span>.<span class="title function_">sub</span>(<span class="number">4</span>).<span class="title function_">readU32</span>() === <span class="number">0x2F</span>)</span><br><span class="line">                        addrs1.<span class="title function_">push</span>(m.<span class="property">address</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (e) &#123;&#125;</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="title class_">Memory</span>.<span class="title function_">scanSync</span>(range.<span class="property">base</span>, range.<span class="property">size</span>, p2).<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">m</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (m.<span class="property">address</span>.<span class="title function_">sub</span>(<span class="number">4</span>).<span class="title function_">readU32</span>() === <span class="number">0x2F</span>)</span><br><span class="line">                        addrs2.<span class="title function_">push</span>(m.<span class="property">address</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (e) &#123;&#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;&#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;    [UTF-32] &quot;/Trigger1&quot;: &#x27;</span> + addrs1.<span class="property">length</span> + <span class="string">&#x27; 处&#x27;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;    [UTF-32] &quot;/Trigger2&quot;: &#x27;</span> + addrs2.<span class="property">length</span> + <span class="string">&#x27; 处&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (addrs1.<span class="property">length</span> === <span class="number">0</span> &amp;&amp; addrs2.<span class="property">length</span> === <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关键: 先收集再修改, 避免 Trigger2→1 后被误识为原始 Trigger1</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// Step 1: 将 elif 中的 &quot;Trigger2&quot; → &quot;Trigger1&quot;</span></span><br><span class="line">    <span class="comment">//         使其匹配 Trigger1 节点的实际路径</span></span><br><span class="line">    addrs2.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">addr</span>) &#123;</span><br><span class="line">        <span class="comment">// &#x27;2&#x27; 在 &quot;Trigger2&quot; 的第 8 个字符 (index 7), 偏移 = 7 * 4 = 28</span></span><br><span class="line">        addr.<span class="title function_">add</span>(<span class="number">28</span>).<span class="title function_">writeU32</span>(<span class="number">0x31</span>); <span class="comment">// &#x27;2&#x27; (0x32) → &#x27;1&#x27; (0x31)</span></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;    [+] &quot;/Trigger2&quot; → &quot;/Trigger1&quot; @ &#x27;</span> + addr);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Step 2: 将 if 中的 &quot;Trigger1&quot; → &quot;TriggerX&quot;</span></span><br><span class="line">    <span class="comment">//         使其不再匹配任何节点, 跳过示例 flag</span></span><br><span class="line">    addrs1.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">addr</span>) &#123;</span><br><span class="line">        addr.<span class="title function_">add</span>(<span class="number">28</span>).<span class="title function_">writeU32</span>(<span class="number">0x58</span>); <span class="comment">// &#x27;1&#x27; (0x31) → &#x27;X&#x27; (0x58)</span></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;    [+] &quot;/Trigger1&quot; → &quot;/TriggerX&quot; @ &#x27;</span> + addr);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">doPatch</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*] 开始搜索并补丁 GDScript 字符串...&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_">patchTriggersUTF32</span>()) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*] UTF-32LE 补丁成功!&#x27;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> patchDone = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">runOnce</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 1. 补丁</span></span><br><span class="line">    patchDone = <span class="title function_">doPatch</span>();</span><br><span class="line">    <span class="keyword">if</span> (patchDone) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;\n[*] ✓ 补丁已生效!&#x27;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*]   在游戏中碰触 Trigger1 (黄色旋转物体) 即可看到真实 flag\n&#x27;</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;\n[!] 补丁失败, 场景可能尚未加载, 将自动重试...\n&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> patchDone;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 启动: 延迟 5 秒等待 Godot 引擎 + 场景加载</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*] 等待游戏加载 (5秒)...&#x27;</span>);</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_">runOnce</span>()) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 自动重试, 每 3 秒一次, 最多 10 次</span></span><br><span class="line">    <span class="keyword">var</span> attempt = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">var</span> timer = <span class="built_in">setInterval</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        attempt++;</span><br><span class="line">        <span class="keyword">if</span> (attempt &gt; <span class="number">10</span>) &#123;</span><br><span class="line">            <span class="built_in">clearInterval</span>(timer);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[!] 达到最大重试次数 (10)。请确认游戏已进入主场景。&#x27;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[!] 可手动调用 rpc.exports.patch() 重试&#x27;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*] 重试 #&#x27;</span> + attempt + <span class="string">&#x27;...&#x27;</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_">doPatch</span>()) &#123;</span><br><span class="line">            patchDone = <span class="literal">true</span>;</span><br><span class="line">            <span class="built_in">clearInterval</span>(timer);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*] ✓ 补丁成功! 碰触 Trigger1 查看 flag&#x27;</span>);</span><br><span class="line">            <span class="keyword">var</span> toks = <span class="title function_">discoverToken</span>();</span><br><span class="line">            toks.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">tok</span>) &#123;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;[*] Token=&#x27;</span> + tok + <span class="string">&#x27; → &#x27;</span> + <span class="title function_">computeFlag</span>(tok));</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="number">3000</span>);</span><br><span class="line">&#125;, <span class="number">5000</span>);</span><br></pre></td></tr></table></figure><p>运行效果如下图所示</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260411201137316.png" alt="image-20260411201137316"></p><h3 id="flag生成逻辑分析"><a href="#flag生成逻辑分析" class="headerlink" title="flag生成逻辑分析"></a>flag生成逻辑分析</h3><p><code>libsec2026.so</code> 分析</p><p>首先打开看到 <code>start</code> 函数</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260412152120648.png" alt="image-20260412152120648"></p><p>函数通过 <code>openat</code> + <code>read</code> 读取 <code>/proc/self/auxv</code>，获取 <code>AT_PAGESZ</code>（页大小）</p><p><code>sub_69984</code> 是一个解压函数，将代码段的压缩数据解压，之后通过 <code>memfd_create</code> + <code>write</code> + <code>mmap</code> 将解压后的代码映射到内存，通过 <code>BR X14</code> 跳转到映射的代码中执行。</p><p>现在需要想办法得到已经被解压之后的代码，这里通过frida动态dump的方式获取，这里我直接hook <code>dlopen</code> 函数，只要 <code>dlopen</code> 返回，说明进程中已经存在了完整脱壳的 so 文件。</p><p>同时已知的一些字符串特征可以用来辅助dump</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="variable constant_">TARGET_LIB</span>  = <span class="string">&quot;libsec2026.so&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">RAW_NAME</span>    = <span class="string">&quot;libsec2026_dump.bin&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">RELOC_NAME</span>  = <span class="string">&quot;libsec2026_dump_reloc.bin&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自动检测可写目录</span></span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">DUMP_DIR</span> = (<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 通过 /proc/self/cmdline 获取包名，拼出应用数据目录</span></span><br><span class="line">    <span class="keyword">var</span> candidates = [];</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> cmdline = <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;/proc/self/cmdline&quot;</span>, <span class="string">&quot;r&quot;</span>).<span class="title function_">readLine</span>();</span><br><span class="line">        <span class="comment">// cmdline 以 &#x27;\0&#x27; 结尾，取第一个字段即包名</span></span><br><span class="line">        <span class="keyword">var</span> pkgName = cmdline.<span class="title function_">split</span>(<span class="string">&quot;\0&quot;</span>)[<span class="number">0</span>].<span class="title function_">trim</span>();</span><br><span class="line">        <span class="keyword">if</span> (pkgName.<span class="title function_">indexOf</span>(<span class="string">&quot;.&quot;</span>) !== -<span class="number">1</span>) &#123;</span><br><span class="line">            candidates.<span class="title function_">push</span>(<span class="string">&quot;/data/data/&quot;</span> + pkgName + <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">    candidates.<span class="title function_">push</span>(<span class="string">&quot;/data/local/tmp/&quot;</span>);</span><br><span class="line">    candidates.<span class="title function_">push</span>(<span class="string">&quot;/sdcard/Download/&quot;</span>);</span><br><span class="line">    candidates.<span class="title function_">push</span>(<span class="string">&quot;/sdcard/&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; candidates.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">var</span> testPath = candidates[i] + <span class="string">&quot;.frida_write_test&quot;</span>;</span><br><span class="line">            <span class="keyword">var</span> f = <span class="keyword">new</span> <span class="title class_">File</span>(testPath, <span class="string">&quot;wb&quot;</span>);</span><br><span class="line">            f.<span class="title function_">write</span>(<span class="keyword">new</span> <span class="title class_">ArrayBuffer</span>(<span class="number">1</span>));</span><br><span class="line">            f.<span class="title function_">flush</span>();</span><br><span class="line">            f.<span class="title function_">close</span>();</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 可写目录: &quot;</span> + candidates[i]);</span><br><span class="line">            <span class="keyword">return</span> candidates[i];</span><br><span class="line">        &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 警告: 未找到可写目录，默认使用 /data/local/tmp/&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;/data/local/tmp/&quot;</span>;</span><br><span class="line">&#125;)();</span><br><span class="line"><span class="comment">// 脱壳后的 ELF 虚拟地址范围约 0x0 ~ 0xF4000（999KB），用于筛选候选区域</span></span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">MIN_ELF_SIZE</span> = <span class="number">0x80000</span>;   <span class="comment">// 最少 512KB 才认为是目标</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 已知特征：修改后的 ChaCha20 常量，用于二次验证</span></span><br><span class="line"><span class="keyword">var</span> <span class="variable constant_">CHACHA_MAGIC</span> = [<span class="number">0x66</span>, <span class="number">0x78</span>, <span class="number">0x70</span>, <span class="number">0x61</span>, <span class="number">0x6f</span>, <span class="number">0x64</span>, <span class="number">0x20</span>, <span class="number">0x33</span>,</span><br><span class="line">                    <span class="number">0x31</span>, <span class="number">0x2d</span>, <span class="number">0x62</span>, <span class="number">0x79</span>, <span class="number">0x73</span>, <span class="number">0x65</span>, <span class="number">0x20</span>, <span class="number">0x6b</span>];</span><br><span class="line"><span class="comment">// &quot;fxpaod 31-byse k&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ========================== 辅助函数 ============================</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在内存中搜索已知字节序列</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">containsMagic</span>(<span class="params">base, size, pattern</span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> result = <span class="title class_">Memory</span>.<span class="title function_">scanSync</span>(base, size, <span class="title function_">patternToHex</span>(pattern));</span><br><span class="line">        <span class="keyword">return</span> result.<span class="property">length</span> &gt; <span class="number">0</span>;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">patternToHex</span>(<span class="params">arr</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> arr.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">b</span>) &#123; <span class="keyword">return</span> (<span class="string">&#x27;0&#x27;</span> + b.<span class="title function_">toString</span>(<span class="number">16</span>)).<span class="title function_">slice</span>(-<span class="number">2</span>); &#125;).<span class="title function_">join</span>(<span class="string">&#x27; &#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将 ArrayBuffer 写入文件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">writeFile</span>(<span class="params">path, buf</span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> f = <span class="keyword">new</span> <span class="title class_">File</span>(path, <span class="string">&quot;wb&quot;</span>);</span><br><span class="line">        f.<span class="title function_">write</span>(buf);</span><br><span class="line">        f.<span class="title function_">flush</span>();</span><br><span class="line">        f.<span class="title function_">close</span>();</span><br><span class="line">    &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">        <span class="comment">// 如果目标路径写失败，回退到 /sdcard/</span></span><br><span class="line">        <span class="keyword">var</span> fallback = <span class="string">&quot;/sdcard/&quot;</span> + path.<span class="title function_">split</span>(<span class="string">&quot;/&quot;</span>).<span class="title function_">pop</span>();</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 写入 &quot;</span> + path + <span class="string">&quot; 失败: &quot;</span> + e);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 回退写入 &quot;</span> + fallback);</span><br><span class="line">        <span class="keyword">var</span> f2 = <span class="keyword">new</span> <span class="title class_">File</span>(fallback, <span class="string">&quot;wb&quot;</span>);</span><br><span class="line">        f2.<span class="title function_">write</span>(buf);</span><br><span class="line">        f2.<span class="title function_">flush</span>();</span><br><span class="line">        f2.<span class="title function_">close</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="comment">/**</span></span><br><span class="line"><span class="comment"> * 从 ELF header 解析所有 PT_LOAD 段，计算虚拟地址范围</span></span><br><span class="line"><span class="comment"> * 返回: &#123; totalSize: number, loads: [&#123;vaddr, memsz, filesz, offset, flags&#125;] &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">parseElfLoads</span>(<span class="params">base</span>) &#123;</span><br><span class="line">    <span class="comment">// Elf64_Ehdr</span></span><br><span class="line">    <span class="keyword">var</span> e_phoff     = <span class="built_in">parseInt</span>(base.<span class="title function_">add</span>(<span class="number">32</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">    <span class="keyword">var</span> e_phentsize = base.<span class="title function_">add</span>(<span class="number">54</span>).<span class="title function_">readU16</span>();</span><br><span class="line">    <span class="keyword">var</span> e_phnum     = base.<span class="title function_">add</span>(<span class="number">56</span>).<span class="title function_">readU16</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> loads = [];</span><br><span class="line">    <span class="keyword">var</span> maxEnd = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; e_phnum; i++) &#123;</span><br><span class="line">        <span class="keyword">var</span> phdr = base.<span class="title function_">add</span>(e_phoff + i * e_phentsize);</span><br><span class="line">        <span class="keyword">var</span> p_type  = phdr.<span class="title function_">readU32</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (p_type === <span class="number">1</span>) &#123;  <span class="comment">// PT_LOAD</span></span><br><span class="line">            <span class="keyword">var</span> p_offset = <span class="built_in">parseInt</span>(phdr.<span class="title function_">add</span>(<span class="number">8</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">            <span class="keyword">var</span> p_vaddr  = <span class="built_in">parseInt</span>(phdr.<span class="title function_">add</span>(<span class="number">16</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">            <span class="keyword">var</span> p_filesz = <span class="built_in">parseInt</span>(phdr.<span class="title function_">add</span>(<span class="number">32</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">            <span class="keyword">var</span> p_memsz  = <span class="built_in">parseInt</span>(phdr.<span class="title function_">add</span>(<span class="number">40</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">            <span class="keyword">var</span> p_flags  = phdr.<span class="title function_">add</span>(<span class="number">4</span>).<span class="title function_">readU32</span>();</span><br><span class="line"></span><br><span class="line">            loads.<span class="title function_">push</span>(&#123;</span><br><span class="line">                <span class="attr">vaddr</span>:  p_vaddr,</span><br><span class="line">                <span class="attr">memsz</span>:  p_memsz,</span><br><span class="line">                <span class="attr">filesz</span>: p_filesz,</span><br><span class="line">                <span class="attr">offset</span>: p_offset,</span><br><span class="line">                <span class="attr">flags</span>:  p_flags</span><br><span class="line">            &#125;);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">var</span> end = p_vaddr + p_memsz;</span><br><span class="line">            <span class="keyword">if</span> (end &gt; maxEnd) maxEnd = end;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> &#123; <span class="attr">totalSize</span>: maxEnd, <span class="attr">loads</span>: loads &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 解析 PT_DYNAMIC 段，获取 DT_RELA / DT_RELASZ</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">findRelaDynamic</span>(<span class="params">base</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> e_phoff     = <span class="built_in">parseInt</span>(base.<span class="title function_">add</span>(<span class="number">32</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">    <span class="keyword">var</span> e_phentsize = base.<span class="title function_">add</span>(<span class="number">54</span>).<span class="title function_">readU16</span>();</span><br><span class="line">    <span class="keyword">var</span> e_phnum     = base.<span class="title function_">add</span>(<span class="number">56</span>).<span class="title function_">readU16</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; e_phnum; i++) &#123;</span><br><span class="line">        <span class="keyword">var</span> phdr = base.<span class="title function_">add</span>(e_phoff + i * e_phentsize);</span><br><span class="line">        <span class="keyword">if</span> (phdr.<span class="title function_">readU32</span>() !== <span class="number">2</span>) <span class="keyword">continue</span>;  <span class="comment">// PT_DYNAMIC</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> p_vaddr = <span class="built_in">parseInt</span>(phdr.<span class="title function_">add</span>(<span class="number">16</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">        <span class="keyword">var</span> p_memsz = <span class="built_in">parseInt</span>(phdr.<span class="title function_">add</span>(<span class="number">40</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> relaAddr = <span class="number">0</span>, relaSize = <span class="number">0</span>, relaEnt = <span class="number">24</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j + <span class="number">16</span> &lt;= p_memsz; j += <span class="number">16</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> d_tag = <span class="built_in">parseInt</span>(base.<span class="title function_">add</span>(p_vaddr + j).<span class="title function_">readS64</span>().<span class="title function_">toString</span>());</span><br><span class="line">            <span class="keyword">var</span> d_val = <span class="built_in">parseInt</span>(base.<span class="title function_">add</span>(p_vaddr + j + <span class="number">8</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (d_tag === <span class="number">0</span>) <span class="keyword">break</span>;          <span class="comment">// DT_NULL</span></span><br><span class="line">            <span class="keyword">if</span> (d_tag === <span class="number">7</span>) relaAddr = d_val; <span class="comment">// DT_RELA</span></span><br><span class="line">            <span class="keyword">if</span> (d_tag === <span class="number">8</span>) relaSize = d_val; <span class="comment">// DT_RELASZ</span></span><br><span class="line">            <span class="keyword">if</span> (d_tag === <span class="number">9</span>) relaEnt  = d_val; <span class="comment">// DT_RELAENT</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123; <span class="attr">relaAddr</span>: relaAddr, <span class="attr">relaSize</span>: relaSize, <span class="attr">relaEnt</span>: relaEnt &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ====================== 核心 dump 逻辑 ==========================</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 扫描进程内存，查找脱壳后的 ELF 镜像</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 判断标准：</span></span><br><span class="line"><span class="comment"> *   1. 内存区域 ≥ MIN_ELF_SIZE</span></span><br><span class="line"><span class="comment"> *   2. 起始地址为 ELF64 magic (7f 45 4c 46 02)</span></span><br><span class="line"><span class="comment"> *   3. 不属于原始 libsec2026.so 的模块范围</span></span><br><span class="line"><span class="comment"> *   4. 不属于系统库</span></span><br><span class="line"><span class="comment"> *   5. （可选）包含 &quot;fxpaod 31-byse k&quot; 特征串</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">findUnpackedElf</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 扫描内存，查找脱壳后的 ELF 镜像...&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> origMod  = <span class="title class_">Process</span>.<span class="title function_">findModuleByName</span>(<span class="variable constant_">TARGET_LIB</span>);</span><br><span class="line">    <span class="keyword">var</span> origBase = origMod ? origMod.<span class="property">base</span> : <span class="title function_">ptr</span>(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">var</span> origEnd  = origMod ? origMod.<span class="property">base</span>.<span class="title function_">add</span>(origMod.<span class="property">size</span>) : <span class="title function_">ptr</span>(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> candidates = [];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历所有可读区域（ELF header 所在页至少是 r--）</span></span><br><span class="line">    <span class="keyword">var</span> allRanges = <span class="title class_">Process</span>.<span class="title function_">enumerateRangesSync</span>(<span class="string">&#x27;r--&#x27;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> ri = <span class="number">0</span>; ri &lt; allRanges.<span class="property">length</span>; ri++) &#123;</span><br><span class="line">        <span class="keyword">var</span> range = allRanges[ri];</span><br><span class="line">        <span class="keyword">if</span> (range.<span class="property">size</span> &lt; <span class="variable constant_">MIN_ELF_SIZE</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 排除原始 SO 模块</span></span><br><span class="line">        <span class="keyword">if</span> (origMod &amp;&amp;</span><br><span class="line">            range.<span class="property">base</span>.<span class="title function_">compare</span>(origBase) &gt;= <span class="number">0</span> &amp;&amp;</span><br><span class="line">            range.<span class="property">base</span>.<span class="title function_">compare</span>(origEnd) &lt; <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 排除系统路径</span></span><br><span class="line">        <span class="keyword">if</span> (range.<span class="property">file</span> &amp;&amp; range.<span class="property">file</span>.<span class="property">path</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> p = range.<span class="property">file</span>.<span class="property">path</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;/system/&#x27;</span>)  !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;/vendor/&#x27;</span>)  !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;/apex/&#x27;</span>)    !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;/dev/&#x27;</span>)     !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;dalvik&#x27;</span>)    !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;base.apk&#x27;</span>)  !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;libgodot_android&#x27;</span>) !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;libc++_shared&#x27;</span>)    !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;libc.so&#x27;</span>)          !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span> (p.<span class="title function_">indexOf</span>(<span class="string">&#x27;gralloc&#x27;</span>)   !== -<span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 检查 ELF64 magic</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">var</span> magic = range.<span class="property">base</span>.<span class="title function_">readU32</span>();</span><br><span class="line">            <span class="keyword">if</span> (magic !== <span class="number">0x464c457f</span>) <span class="keyword">continue</span>;         <span class="comment">// \x7fELF</span></span><br><span class="line">            <span class="keyword">if</span> (range.<span class="property">base</span>.<span class="title function_">add</span>(<span class="number">4</span>).<span class="title function_">readU8</span>() !== <span class="number">2</span>) <span class="keyword">continue</span>;  <span class="comment">// ELFCLASS64</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> path = (range.<span class="property">file</span> &amp;&amp; range.<span class="property">file</span>.<span class="property">path</span>) ? range.<span class="property">file</span>.<span class="property">path</span> : <span class="string">&quot;(anonymous/memfd)&quot;</span>;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] 候选 ELF64:&quot;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      基址: &quot;</span> + range.<span class="property">base</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      区域大小: 0x&quot;</span> + range.<span class="property">size</span>.<span class="title function_">toString</span>(<span class="number">16</span>));</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      权限: &quot;</span> + range.<span class="property">protection</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      来源: &quot;</span> + path);</span><br><span class="line"></span><br><span class="line">        candidates.<span class="title function_">push</span>(range);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> candidates;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 逐区域读取，组装完整 ELF 镜像</span></span><br><span class="line"><span class="comment"> * 对于不可读的空洞区域自动填零</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">readElfImage</span>(<span class="params">base, totalSize</span>) &#123;</span><br><span class="line">    <span class="comment">// 先尝试直接一次性读取</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> base.<span class="title function_">readByteArray</span>(totalSize);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 单次读取失败，改用分区域拼接...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 收集 base ~ base+totalSize 范围内所有可读区域（r--, r-x, rw-, rwx 等）</span></span><br><span class="line">    <span class="keyword">var</span> buf  = <span class="keyword">new</span> <span class="title class_">ArrayBuffer</span>(totalSize);</span><br><span class="line">    <span class="keyword">var</span> dest = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(buf);</span><br><span class="line">    <span class="keyword">var</span> end  = base.<span class="title function_">add</span>(totalSize);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// &#x27;r--&#x27; 在 Frida 中匹配所有 readable 区域（包括 r-x, rw-, rwx）</span></span><br><span class="line">    <span class="comment">// 但为确保兼容，分别搜集多种权限</span></span><br><span class="line">    <span class="keyword">var</span> perms = [<span class="string">&#x27;r--&#x27;</span>, <span class="string">&#x27;r-x&#x27;</span>, <span class="string">&#x27;rw-&#x27;</span>, <span class="string">&#x27;rwx&#x27;</span>];</span><br><span class="line">    <span class="keyword">var</span> seen  = &#123;&#125;;  <span class="comment">// 避免重复区域</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> ranges = <span class="title class_">Process</span>.<span class="title function_">enumerateRangesSync</span>(<span class="string">&#x27;r--&#x27;</span>);</span><br><span class="line">    <span class="comment">// 补充其他权限的区域（避免遗漏纯 r-x 段）</span></span><br><span class="line">    <span class="keyword">var</span> extraPerms = [<span class="string">&#x27;r-x&#x27;</span>, <span class="string">&#x27;rw-&#x27;</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> pi = <span class="number">0</span>; pi &lt; extraPerms.<span class="property">length</span>; pi++) &#123;</span><br><span class="line">        <span class="keyword">var</span> extra = <span class="title class_">Process</span>.<span class="title function_">enumerateRangesSync</span>(extraPerms[pi]);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> ei = <span class="number">0</span>; ei &lt; extra.<span class="property">length</span>; ei++) &#123;</span><br><span class="line">            ranges.<span class="title function_">push</span>(extra[ei]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 去重（按基址）</span></span><br><span class="line">    <span class="keyword">var</span> seen = &#123;&#125;;</span><br><span class="line">    <span class="keyword">var</span> uniqueRanges = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> ui = <span class="number">0</span>; ui &lt; ranges.<span class="property">length</span>; ui++) &#123;</span><br><span class="line">        <span class="keyword">var</span> key = ranges[ui].<span class="property">base</span>.<span class="title function_">toString</span>();</span><br><span class="line">        <span class="keyword">if</span> (!seen[key]) &#123;</span><br><span class="line">            seen[key] = <span class="literal">true</span>;</span><br><span class="line">            uniqueRanges.<span class="title function_">push</span>(ranges[ui]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ranges = uniqueRanges;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> ri = <span class="number">0</span>; ri &lt; ranges.<span class="property">length</span>; ri++) &#123;</span><br><span class="line">        <span class="keyword">var</span> r = ranges[ri];</span><br><span class="line">        <span class="keyword">var</span> rStart = r.<span class="property">base</span>;</span><br><span class="line">        <span class="keyword">var</span> rEnd   = rStart.<span class="title function_">add</span>(r.<span class="property">size</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 区域是否与目标范围重叠？</span></span><br><span class="line">        <span class="keyword">if</span> (rEnd.<span class="title function_">compare</span>(base) &lt;= <span class="number">0</span> || rStart.<span class="title function_">compare</span>(end) &gt;= <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 计算重叠部分</span></span><br><span class="line">        <span class="keyword">var</span> copyStart  = rStart.<span class="title function_">compare</span>(base) &gt; <span class="number">0</span> ? rStart : base;</span><br><span class="line">        <span class="keyword">var</span> copyEnd    = rEnd.<span class="title function_">compare</span>(end) &lt; <span class="number">0</span> ? rEnd : end;</span><br><span class="line">        <span class="keyword">var</span> offsetInBuf = <span class="built_in">parseInt</span>(copyStart.<span class="title function_">sub</span>(base).<span class="title function_">toString</span>());</span><br><span class="line">        <span class="keyword">var</span> copySize    = <span class="built_in">parseInt</span>(copyEnd.<span class="title function_">sub</span>(copyStart).<span class="title function_">toString</span>());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (copySize &lt;= <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">var</span> rawBytes = copyStart.<span class="title function_">readByteArray</span>(copySize);</span><br><span class="line">            <span class="keyword">if</span> (rawBytes) &#123;</span><br><span class="line">                <span class="keyword">var</span> chunk = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(rawBytes);</span><br><span class="line">                dest.<span class="title function_">set</span>(chunk, offsetInBuf);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;    [!] 读取失败 @ &quot;</span> + copyStart + <span class="string">&quot; (size 0x&quot;</span> +</span><br><span class="line">                        copySize.<span class="title function_">toString</span>(<span class="number">16</span>) + <span class="string">&quot;): &quot;</span> + e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> buf;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对 dump 出的 ArrayBuffer 应用 R_AARCH64_RELATIVE 重定位</span></span><br><span class="line"><span class="comment"> * 重定位后 RTTI / vtable / GOT 区域将填充正确的指针值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">applyRelocations</span>(<span class="params">buf, base</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> rela = <span class="title function_">findRelaDynamic</span>(base);</span><br><span class="line">    <span class="keyword">if</span> (!rela || rela.<span class="property">relaAddr</span> === <span class="number">0</span> || rela.<span class="property">relaSize</span> === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 未找到 RELA 段，跳过重定位&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> nEntries = <span class="title class_">Math</span>.<span class="title function_">floor</span>(rela.<span class="property">relaSize</span> / rela.<span class="property">relaEnt</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 应用重定位: DT_RELA @ 0x&quot;</span> + rela.<span class="property">relaAddr</span>.<span class="title function_">toString</span>(<span class="number">16</span>) +</span><br><span class="line">                <span class="string">&quot;, &quot;</span> + nEntries + <span class="string">&quot; 条目&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> view    = <span class="keyword">new</span> <span class="title class_">DataView</span>(buf);</span><br><span class="line">    <span class="keyword">var</span> applied = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; nEntries; i++) &#123;</span><br><span class="line">        <span class="keyword">var</span> entryBase = base.<span class="title function_">add</span>(rela.<span class="property">relaAddr</span> + i * rela.<span class="property">relaEnt</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> r_offset = <span class="built_in">parseInt</span>(entryBase.<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">        <span class="keyword">var</span> r_info   = <span class="built_in">parseInt</span>(entryBase.<span class="title function_">add</span>(<span class="number">8</span>).<span class="title function_">readU64</span>().<span class="title function_">toString</span>());</span><br><span class="line">        <span class="keyword">var</span> r_addend = <span class="built_in">parseInt</span>(entryBase.<span class="title function_">add</span>(<span class="number">16</span>).<span class="title function_">readS64</span>().<span class="title function_">toString</span>());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> r_type = r_info &amp; <span class="number">0xFFFFFFFF</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (r_type !== <span class="number">0x403</span>) <span class="keyword">continue</span>;  <span class="comment">// 只处理 R_AARCH64_RELATIVE</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (r_offset + <span class="number">7</span> &gt;= buf.<span class="property">byteLength</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// *r_offset = base_addr(0) + r_addend</span></span><br><span class="line">        <span class="keyword">var</span> lo = r_addend &amp; <span class="number">0xFFFFFFFF</span>;</span><br><span class="line">        <span class="keyword">var</span> hi = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (r_addend &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            hi = <span class="title class_">Math</span>.<span class="title function_">floor</span>(r_addend / <span class="number">0x100000000</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 负 addend（极少见，但安全处理）</span></span><br><span class="line">            <span class="keyword">var</span> unsigned = r_addend + <span class="number">0x10000000000000000</span>;</span><br><span class="line">            lo = unsigned &amp; <span class="number">0xFFFFFFFF</span>;</span><br><span class="line">            hi = <span class="title class_">Math</span>.<span class="title function_">floor</span>(unsigned / <span class="number">0x100000000</span>) &amp; <span class="number">0xFFFFFFFF</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        view.<span class="title function_">setUint32</span>(r_offset,     lo, <span class="literal">true</span>);</span><br><span class="line">        view.<span class="title function_">setUint32</span>(r_offset + <span class="number">4</span>, hi, <span class="literal">true</span>);</span><br><span class="line">        applied++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] 已应用 &quot;</span> + applied + <span class="string">&quot; 条 R_AARCH64_RELATIVE 重定位&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ========================= 主入口 ===============================</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">doDump</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// ========== 策略 1：直接从模块基址 dump ==========</span></span><br><span class="line">    <span class="comment">// init_array 执行后，.text 已被原地解密（mprotect RW → 解密 → mprotect RX）</span></span><br><span class="line">    <span class="comment">// 模块基址就是解密后的 ELF，无需扫描匿名区域</span></span><br><span class="line">    <span class="keyword">var</span> origMod = <span class="title class_">Process</span>.<span class="title function_">findModuleByName</span>(<span class="variable constant_">TARGET_LIB</span>);</span><br><span class="line">    <span class="keyword">if</span> (origMod) &#123;</span><br><span class="line">        <span class="keyword">var</span> base = origMod.<span class="property">base</span>;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 模块基址: &quot;</span> + base + <span class="string">&quot; (size 0x&quot;</span> + origMod.<span class="property">size</span>.<span class="title function_">toString</span>(<span class="number">16</span>) + <span class="string">&quot;)&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 确认是 ELF64</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (base.<span class="title function_">readU32</span>() !== <span class="number">0x464c457f</span> || base.<span class="title function_">add</span>(<span class="number">4</span>).<span class="title function_">readU8</span>() !== <span class="number">2</span>) &#123;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 模块基址不是 ELF64 header，跳过策略 1&quot;</span>);</span><br><span class="line">                <span class="title function_">doDumpScan</span>();</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 无法读取模块基址: &quot;</span> + e);</span><br><span class="line">            <span class="title function_">doDumpScan</span>();</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 解析 LOAD 段</span></span><br><span class="line">        <span class="keyword">var</span> elfInfo;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            elfInfo = <span class="title function_">parseElfLoads</span>(base);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 解析 ELF header 失败: &quot;</span> + e + <span class="string">&quot;, 用模块大小替代&quot;</span>);</span><br><span class="line">            elfInfo = &#123; <span class="attr">totalSize</span>: origMod.<span class="property">size</span>, <span class="attr">loads</span>: [] &#125;;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> totalSize = elfInfo.<span class="property">totalSize</span>;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] ELF 虚拟范围: 0x0 ~ 0x&quot;</span> + totalSize.<span class="title function_">toString</span>(<span class="number">16</span>) +</span><br><span class="line">                    <span class="string">&quot; (&quot;</span> + totalSize + <span class="string">&quot; bytes)&quot;</span>);</span><br><span class="line"></span><br><span class="line">        elfInfo.<span class="property">loads</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">seg, i</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> flagStr = ((seg.<span class="property">flags</span> &amp; <span class="number">4</span>) ? <span class="string">&quot;R&quot;</span> : <span class="string">&quot;-&quot;</span>) +</span><br><span class="line">                          ((seg.<span class="property">flags</span> &amp; <span class="number">2</span>) ? <span class="string">&quot;W&quot;</span> : <span class="string">&quot;-&quot;</span>) +</span><br><span class="line">                          ((seg.<span class="property">flags</span> &amp; <span class="number">1</span>) ? <span class="string">&quot;X&quot;</span> : <span class="string">&quot;-&quot;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;    LOAD[&quot;</span> + i + <span class="string">&quot;] vaddr=0x&quot;</span> + seg.<span class="property">vaddr</span>.<span class="title function_">toString</span>(<span class="number">16</span>) +</span><br><span class="line">                        <span class="string">&quot; memsz=0x&quot;</span> + seg.<span class="property">memsz</span>.<span class="title function_">toString</span>(<span class="number">16</span>) +</span><br><span class="line">                        <span class="string">&quot; filesz=0x&quot;</span> + seg.<span class="property">filesz</span>.<span class="title function_">toString</span>(<span class="number">16</span>) +</span><br><span class="line">                        <span class="string">&quot; &quot;</span> + flagStr);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 验证特征</span></span><br><span class="line">        <span class="keyword">var</span> verified = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            verified = <span class="title function_">containsMagic</span>(base, <span class="title class_">Math</span>.<span class="title function_">min</span>(origMod.<span class="property">size</span>, totalSize), <span class="variable constant_">CHACHA_MAGIC</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">        <span class="comment">// 也搜索后续 LOAD 段</span></span><br><span class="line">        <span class="keyword">if</span> (!verified) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">var</span> si = <span class="number">0</span>; si &lt; elfInfo.<span class="property">loads</span>.<span class="property">length</span> &amp;&amp; !verified; si++) &#123;</span><br><span class="line">                <span class="keyword">var</span> seg = elfInfo.<span class="property">loads</span>[si];</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    verified = <span class="title function_">containsMagic</span>(base.<span class="title function_">add</span>(seg.<span class="property">vaddr</span>),</span><br><span class="line">                                             <span class="title class_">Math</span>.<span class="title function_">min</span>(seg.<span class="property">memsz</span>, seg.<span class="property">filesz</span>),</span><br><span class="line">                                             <span class="variable constant_">CHACHA_MAGIC</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (verified) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] 特征验证: 找到 \&quot;fxpaod 31-byse k\&quot; ✓&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 特征验证: 未找到 ChaCha 特征串（可能在运行时生成）&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 检查 extension_init 区域是否已解密（应该是有效 ARM64 指令）</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// extension_init 在 vaddr 0x56D50</span></span><br><span class="line">            <span class="keyword">var</span> extInit = base.<span class="title function_">add</span>(<span class="number">0x56D50</span>);</span><br><span class="line">            <span class="keyword">var</span> insn0 = extInit.<span class="title function_">readU32</span>();</span><br><span class="line">            <span class="keyword">var</span> insn1 = extInit.<span class="title function_">add</span>(<span class="number">4</span>).<span class="title function_">readU32</span>();</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] extension_init @ &quot;</span> + extInit + <span class="string">&quot;:&quot;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      insn[0] = 0x&quot;</span> + (<span class="string">&quot;00000000&quot;</span> + insn0.<span class="title function_">toString</span>(<span class="number">16</span>)).<span class="title function_">slice</span>(-<span class="number">8</span>));</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      insn[1] = 0x&quot;</span> + (<span class="string">&quot;00000000&quot;</span> + insn1.<span class="title function_">toString</span>(<span class="number">16</span>)).<span class="title function_">slice</span>(-<span class="number">8</span>));</span><br><span class="line">            <span class="comment">// NOP = 0xd503201f, STP/SUB 常见开头</span></span><br><span class="line">            <span class="keyword">if</span> (insn0 === <span class="number">0xd503201f</span> || (insn0 &gt;&gt;&gt; <span class="number">22</span>) === <span class="number">0x2d2</span> ||</span><br><span class="line">                (insn0 &gt;&gt;&gt; <span class="number">23</span>) === <span class="number">0x1a9</span> || (insn0 &gt;&gt;&gt; <span class="number">24</span>) === <span class="number">0xd1</span>) &#123;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      → 有效 ARM64 指令 ✓ (.text 已解密)&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;      → 疑似仍为加密数据（但继续 dump）&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 无法读取 extension_init 区域: &quot;</span> + e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="title function_">doDumpFromBase</span>(base, totalSize, elfInfo);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ========== 策略 2：扫描匿名区域  ==========</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 未找到模块 &quot;</span> + <span class="variable constant_">TARGET_LIB</span> + <span class="string">&quot;，改用扫描模式...&quot;</span>);</span><br><span class="line">    <span class="title function_">doDumpScan</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 从已知基址 dump</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">doDumpFromBase</span>(<span class="params">base, totalSize, elfInfo</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 读取 ELF 镜像...&quot;</span>);</span><br><span class="line">    <span class="keyword">var</span> rawBuf = <span class="title function_">readElfImage</span>(base, totalSize);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 写入原始 dump</span></span><br><span class="line">    <span class="keyword">var</span> rawPath = <span class="variable constant_">DUMP_DIR</span> + <span class="variable constant_">RAW_NAME</span>;</span><br><span class="line">    <span class="title function_">writeFile</span>(rawPath, rawBuf);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] 原始 dump: &quot;</span> + rawPath + <span class="string">&quot; (&quot;</span> + totalSize + <span class="string">&quot; bytes)&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 应用重定位</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> relocBuf = rawBuf.<span class="title function_">slice</span>(<span class="number">0</span>);</span><br><span class="line">        <span class="title function_">applyRelocations</span>(relocBuf, base);</span><br><span class="line">        <span class="keyword">var</span> relocPath = <span class="variable constant_">DUMP_DIR</span> + <span class="variable constant_">RELOC_NAME</span>;</span><br><span class="line">        <span class="title function_">writeFile</span>(relocPath, relocBuf);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] 重定位 dump: &quot;</span> + relocPath);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 重定位处理失败: &quot;</span> + e);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_">printDone</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 扫描模式（fallback）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">doDumpScan</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> candidates = <span class="title function_">findUnpackedElf</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 先以特征串筛选</span></span><br><span class="line">    <span class="keyword">var</span> verified = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> ci = <span class="number">0</span>; ci &lt; candidates.<span class="property">length</span>; ci++) &#123;</span><br><span class="line">        <span class="keyword">var</span> c = candidates[ci];</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="title function_">containsMagic</span>(c.<span class="property">base</span>, c.<span class="property">size</span>, <span class="variable constant_">CHACHA_MAGIC</span>)) &#123;</span><br><span class="line">                verified.<span class="title function_">push</span>(c);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (verified.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        candidates = verified;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] 通过特征验证的候选: &quot;</span> + verified.<span class="property">length</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (candidates.<span class="property">length</span> === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[-] 未找到任何候选 ELF。&quot;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] dump 所有大型匿名可执行区域作为参考...&quot;</span>);</span><br><span class="line">        <span class="keyword">var</span> idx = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">var</span> rxRanges = <span class="title class_">Process</span>.<span class="title function_">enumerateRangesSync</span>(<span class="string">&#x27;r-x&#x27;</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> ri = <span class="number">0</span>; ri &lt; rxRanges.<span class="property">length</span>; ri++) &#123;</span><br><span class="line">            <span class="keyword">var</span> r = rxRanges[ri];</span><br><span class="line">            <span class="keyword">if</span> (r.<span class="property">size</span> &gt;= <span class="number">0x10000</span> &amp;&amp;</span><br><span class="line">                (!r.<span class="property">file</span> || !r.<span class="property">file</span>.<span class="property">path</span> || r.<span class="property">file</span>.<span class="property">path</span> === <span class="string">&quot;&quot;</span>)) &#123;</span><br><span class="line">                <span class="keyword">var</span> outPath = <span class="variable constant_">DUMP_DIR</span> + <span class="string">&quot;anon_rx_&quot;</span> + idx + <span class="string">&quot;.bin&quot;</span>;</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  dump &quot;</span> + r.<span class="property">base</span> + <span class="string">&quot; size=0x&quot;</span> + r.<span class="property">size</span>.<span class="title function_">toString</span>(<span class="number">16</span>));</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="title function_">writeFile</span>(outPath, r.<span class="property">base</span>.<span class="title function_">readByteArray</span>(r.<span class="property">size</span>));</span><br><span class="line">                    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  → &quot;</span> + outPath);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">                    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  失败: &quot;</span> + e);</span><br><span class="line">                &#125;</span><br><span class="line">                idx++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</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">    candidates.<span class="title function_">sort</span>(<span class="keyword">function</span> (<span class="params">a, b</span>) &#123; <span class="keyword">return</span> b.<span class="property">size</span> - a.<span class="property">size</span>; &#125;);</span><br><span class="line">    <span class="keyword">var</span> target = candidates[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">var</span> base = target.<span class="property">base</span>;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\n[*] 选中目标: &quot;</span> + base);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> elfInfo;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        elfInfo = <span class="title function_">parseElfLoads</span>(base);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">        elfInfo = &#123; <span class="attr">totalSize</span>: target.<span class="property">size</span>, <span class="attr">loads</span>: [] &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_">doDumpFromBase</span>(base, elfInfo.<span class="property">totalSize</span>, elfInfo);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">printDone</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\n======================================&quot;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  DUMP 完成!&quot;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;======================================&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ====================== 时机控制 ================================</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;╔═══════════════════════════════════════╗&quot;</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;║  libsec2026.so 内存 Dump 工具         ║&quot;</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;╚═══════════════════════════════════════╝&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查目标是否已加载（attach 模式）</span></span><br><span class="line"><span class="keyword">var</span> mod = <span class="title class_">Process</span>.<span class="title function_">findModuleByName</span>(<span class="variable constant_">TARGET_LIB</span>);</span><br><span class="line"><span class="keyword">if</span> (mod) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] &quot;</span> + <span class="variable constant_">TARGET_LIB</span> + <span class="string">&quot; 已加载 @ &quot;</span> + mod.<span class="property">base</span> +</span><br><span class="line">                <span class="string">&quot; (size 0x&quot;</span> + mod.<span class="property">size</span>.<span class="title function_">toString</span>(<span class="number">16</span>) + <span class="string">&quot;)&quot;</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] init_array 已执行完毕，直接开始 dump...\n&quot;</span>);</span><br><span class="line">    <span class="title function_">doDump</span>();</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// spawn 模式：hook dlopen，等待目标 SO 加载</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] &quot;</span> + <span class="variable constant_">TARGET_LIB</span> + <span class="string">&quot; 尚未加载，hook dlopen 等待...\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> dlopenNames = [<span class="string">&quot;android_dlopen_ext&quot;</span>, <span class="string">&quot;dlopen&quot;</span>];</span><br><span class="line">    <span class="keyword">var</span> hooked = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    dlopenNames.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">name</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> addr = <span class="title class_">Module</span>.<span class="title function_">findExportByName</span>(<span class="literal">null</span>, name);</span><br><span class="line">        <span class="keyword">if</span> (!addr || hooked) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="title class_">Interceptor</span>.<span class="title function_">attach</span>(addr, &#123;</span><br><span class="line">            <span class="attr">onEnter</span>: <span class="keyword">function</span> (<span class="params">args</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (args[<span class="number">0</span>].<span class="title function_">isNull</span>()) &#123;</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">libpath</span> = <span class="literal">null</span>;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">libpath</span> = args[<span class="number">0</span>].<span class="title function_">readUtf8String</span>();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">libpath</span> = <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="attr">onReturn</span>: <span class="keyword">function</span> (<span class="params">retval</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="property">libpath</span>) <span class="keyword">return</span>;</span><br><span class="line">                <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">libpath</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;libsec2026&quot;</span>) === -<span class="number">1</span>) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] &quot;</span> + name + <span class="string">&quot;(\&quot;&quot;</span> + <span class="variable language_">this</span>.<span class="property">libpath</span> + <span class="string">&quot;\&quot;) 返回&quot;</span>);</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] .init_array 已执行 → 脱壳完成\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 延迟一小段时间确保所有初始化完成</span></span><br><span class="line">                <span class="built_in">setTimeout</span>(doDump, <span class="number">100</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 language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 已 hook &quot;</span> + name + <span class="string">&quot; @ &quot;</span> + addr);</span><br><span class="line">        hooked = <span class="literal">true</span>;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!hooked) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[!] 未找到 dlopen，尝试轮询检测...&quot;</span>);</span><br><span class="line">        <span class="keyword">var</span> pollId = <span class="built_in">setInterval</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> m = <span class="title class_">Process</span>.<span class="title function_">findModuleByName</span>(<span class="variable constant_">TARGET_LIB</span>);</span><br><span class="line">            <span class="keyword">if</span> (m) &#123;</span><br><span class="line">                <span class="built_in">clearInterval</span>(pollId);</span><br><span class="line">                <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 检测到 &quot;</span> + <span class="variable constant_">TARGET_LIB</span> + <span class="string">&quot; 已加载\n&quot;</span>);</span><br><span class="line">                <span class="built_in">setTimeout</span>(doDump, <span class="number">200</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="number">500</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="dump-so-分析"><a href="#dump-so-分析" class="headerlink" title="dump.so 分析"></a><code>dump.so</code> 分析</h4><p>由于代码太过于混乱，实在不好分析，只好交给ai</p><h5 id="障碍：跳板混淆"><a href="#障碍：跳板混淆" class="headerlink" title="障碍：跳板混淆"></a>障碍：跳板混淆</h5><p>所有函数调用都通过<strong>间接跳板 (trampoline)</strong> 实现。每个跳板是一段固定模式的代码：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ADR   X8, loc_XXXX        ; 加载一个地址</span><br><span class="line">MVN   W9, W8              ; 计算 ~X8</span><br><span class="line">AND   X10, X8, #0x20      ; X8 &amp; 0x20</span><br><span class="line">ORR   X9, X9, #~0x20      ; X9 |= 0xFFFFFFFFFFFFFFDF</span><br><span class="line">ADD   X10, X8, X10         ; X10 = X8 + (X8 &amp; 0x20)</span><br><span class="line">ADD   X9, X10, X9          ; X9 = X10 + X9</span><br><span class="line">ADD   X9, X9, #0x21        ; X9 += 0x21</span><br><span class="line">...</span><br><span class="line">BR    X9                   ; 跳转到计算出的地址</span><br></pre></td></tr></table></figure><p><strong>规律</strong>：无论中间计算如何，最终跳转目标始终为 <strong>ADR 操作数 + 0x20</strong>。</p><p>例如：</p><ul><li><code>ADR X8, 0x5B7F8</code> → 实际跳转到 <code>0x5B818</code>（ChaCha20 init）</li><li><code>ADR X8, 0x5BB34</code> → 实际跳转到 <code>0x5BB54</code>（quarter_round）</li><li><code>ADR X8, 0x5B930</code> → 实际跳转到 <code>0x5B950</code>（encrypt）</li></ul><p><strong>应对方法</strong>：在逆向时，每次遇到 <code>ADR X8, addr; ... BR</code> 模式，直接去看 <code>addr + 0x20</code> 处的代码。</p><h5 id="字符串搜索-—-找到突破口"><a href="#字符串搜索-—-找到突破口" class="headerlink" title="字符串搜索 — 找到突破口"></a>字符串搜索 — 找到突破口</h5><p>在 IDA 中 <code>Shift+F12</code> 打开 Strings 窗口搜索：</p><table><thead><tr><th>地址</th><th>字符串</th><th>意义</th></tr></thead><tbody><tr><td>0x19ED8</td><td><code>fxpaod 31-byse k</code></td><td>★ 魔改的 ChaCha20 常量</td></tr><tr><td>0x19EC8</td><td><code>GameExtension</code></td><td>RTTI 类名</td></tr><tr><td>0xD48</td><td><code>extension_init</code></td><td>GDExtension 初始化函数</td></tr></tbody></table><p><strong>疑点 ④</strong>：<code>&quot;fxpaod 31-byse k&quot;</code> 与标准 ChaCha20 常量 <code>&quot;expand 32-byte k&quot;</code> 高度相似，仅若干字符不同。这是识别算法的关键线索。</p><h5 id="常量对比-—-确认-ChaCha20"><a href="#常量对比-—-确认-ChaCha20" class="headerlink" title="常量对比 — 确认 ChaCha20"></a>常量对比 — 确认 ChaCha20</h5><p>将两个 16 字节常量按小端序 u32 解析：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">标准: &quot;expand 32-byte k&quot;</span><br><span class="line">       0x61707865  0x3320646e  0x79622d32  0x6b206574</span><br><span class="line"></span><br><span class="line">魔改: &quot;fxpaod 31-byse k&quot;</span><br><span class="line">       0x61707866  0x3320646f  0x79622d31  0x6b206573</span><br></pre></td></tr></table></figure><p>差异模式：末尾字节分别为 <code>5→6, e→f, 2→1, 4→3</code>。这不影响算法结构，只影响初始状态。</p><h5 id="从常量出发交叉引用"><a href="#从常量出发交叉引用" class="headerlink" title="从常量出发交叉引用"></a>从常量出发交叉引用</h5><p>对 <code>byte_19ED8</code> 按 <code>X</code> 查找交叉引用 → 被 <code>sub_5B818</code> 引用。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">; sub_5B818 — ChaCha20 初始化函数</span><br><span class="line">ADR   X2, byte_19ED8       ; 常量 &quot;fxpaod 31-byse k&quot;</span><br><span class="line">MOV   X1, #-1</span><br><span class="line">MOV   W3, #0x10            ; 16 字节</span><br><span class="line">ADR   X8, sub_5B7C4        ; → 跳转到 memcpy 型函数</span><br><span class="line">BLR   X8                   ; 将常量复制到 state[0..3]</span><br></pre></td></tr></table></figure><p>然后连续 8 次调用 <code>off_EBEF8</code>（实际为 <code>LDR W0, [X0]</code>——即小端读 u32）将密钥写入 <code>state[4..11]</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">BLR   X23                  ; read_le_u32(key+0)</span><br><span class="line">STR   W0, [X19, #0x10]     ; state[4] = key_word[0]</span><br><span class="line">...                         ; 重复 8 次</span><br><span class="line">STP   W0, W21, [X19,#0x2C] ; state[11]=key[7], state[12]=counter</span><br></pre></td></tr></table></figure><p>接着 3 次读 Nonce 写入 <code>state[13..15]</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">STR   W0, [X19, #0x34]     ; state[13] = nonce_word[0]</span><br><span class="line">STR   W0, [X19, #0x38]     ; state[14] = nonce_word[1]</span><br><span class="line">STR   W0, [X19, #0x3C]     ; state[15] = nonce_word[2]</span><br></pre></td></tr></table></figure><p>最后设置 <code>[X19, #0x80] = 0x40</code>（内部缓冲区用完标记，强制首次调用时生成新 keystream block）。</p><p><strong>标准 ChaCha20 状态矩阵得到确认</strong>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">state[ 0.. 3] = 常量 (&quot;fxpaod 31-byse k&quot;)</span><br><span class="line">state[ 4..11] = 密钥 (32 字节, 8 个 u32 LE)</span><br><span class="line">state[12]     = 计数器 (初始 0)</span><br><span class="line">state[13..15] = Nonce  (12 字节, 3 个 u32 LE)</span><br></pre></td></tr></table></figure><h5 id="quarter-round-sub-5BB54"><a href="#quarter-round-sub-5BB54" class="headerlink" title="quarter_round (sub_5BB54)"></a>quarter_round (sub_5BB54)</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">; 参数: X0=&amp;state[a], X1=&amp;state[b], X2=&amp;state[c], X3=&amp;state[d]</span><br><span class="line"></span><br><span class="line">; a += b; d ^= a; d = rotl(d, 16)</span><br><span class="line">LDR   W8, [X1]             ; W8 = b</span><br><span class="line">LDR   W9, [X0]             ; W9 = a</span><br><span class="line">ADD   W8, W9, W8           ; a = a + b</span><br><span class="line">STR   W8, [X0]</span><br><span class="line">LDR   W9, [X3]</span><br><span class="line">EOR   W0, W9, W8           ; d = d ^ a</span><br><span class="line">STR   W0, [X3]</span><br><span class="line">BLR   X23  ; rotl(W0, 16)  ; 调用 rotl32 函数</span><br><span class="line">STR   W0, [X3]             ; d = rotl(d, 16)</span><br><span class="line"></span><br><span class="line">; c += d; b ^= c; b = rotl(b, 12)</span><br><span class="line">...MOV W1, #0xC...          ; 旋转量 12</span><br><span class="line"></span><br><span class="line">; a += b; d ^= a; d = rotl(d, 8)</span><br><span class="line">...MOV W1, #8...            ; 旋转量 8</span><br><span class="line"></span><br><span class="line">; c += d; b ^= c; b = rotl(b, 7)</span><br><span class="line">...MOV W1, #7...            ; 旋转量 7</span><br></pre></td></tr></table></figure><p><strong>旋转量为 16, 12, 8, 7 — 完全标准。</strong></p><h5 id="rotl32-sub-5B648-→-0x5B764"><a href="#rotl32-sub-5B648-→-0x5B764" class="headerlink" title="rotl32 (sub_5B648 → 0x5B764)"></a>rotl32 (sub_5B648 → 0x5B764)</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">; W0 = value, W1 = shift_amount</span><br><span class="line">NEG   W8, W1               ; W8 = -shift = (32 - shift) mod 32</span><br><span class="line">NEG   W9, W1, LSL#1        ; W9 = -(shift*2)</span><br><span class="line">EOR   W8, W8, #0x20        ; W8 ^= 32</span><br><span class="line">AND   W9, W9, #0x40        ; W9 &amp;= 64</span><br><span class="line">ADD   W8, W9, W8           ; W8 = right_shift_amount</span><br><span class="line">LSL   W9, W0, W1           ; W9 = value &lt;&lt; shift</span><br><span class="line">LSR   W8, W0, W8           ; W8 = value &gt;&gt; right_shift</span><br><span class="line">ORR   W0, W8, W9           ; result = (value &gt;&gt; rs) | (value &lt;&lt; shift)</span><br><span class="line">RET</span><br></pre></td></tr></table></figure><p>这是一个带混淆的 <code>rotl32</code> 实现，数学上等价于 <code>(x &lt;&lt; n) | (x &gt;&gt; (32-n))</code>。</p><h5 id="column-round-diagonal-round"><a href="#column-round-diagonal-round" class="headerlink" title="column_round &#x2F; diagonal_round"></a>column_round &#x2F; diagonal_round</h5><p><strong>column_round (sub_5BF6C)</strong>：</p><table><thead><tr><th>调用</th><th>参数偏移 (×4&#x3D;索引)</th><th>实际索引</th></tr></thead><tbody><tr><td>第 1 次</td><td>+0x00, +0x10, +0x20, +0x30</td><td>QR(0, 4, 8, 12)</td></tr><tr><td>第 2 次</td><td>+0x04, +0x14, +0x24, +0x34</td><td>QR(1, 5, 9, 13)</td></tr><tr><td>第 3 次</td><td>+0x08, +0x18, +0x28, +0x38</td><td>QR(2, 6, 10, 14)</td></tr><tr><td>第 4 次</td><td>+0x0C, +0x1C, +0x2C, +0x3C</td><td>QR(3, 7, 11, 15)</td></tr></tbody></table><p><strong>diagonal_round (sub_5BC48)</strong>：</p><table><thead><tr><th>调用</th><th>参数偏移</th><th>实际索引</th></tr></thead><tbody><tr><td>第 1 次</td><td>+0x00, +0x14, +0x28, +0x3C</td><td>QR(0, 5, 10, 15)</td></tr><tr><td>第 2 次</td><td>+0x04, +0x18, +0x2C, +0x30</td><td>QR(1, 6, 11, 12)</td></tr><tr><td>第 3 次</td><td>+0x08, +0x1C, +0x20, +0x34</td><td>QR(2, 7, 8, 13)</td></tr><tr><td>第 4 次</td><td>+0x0C, +0x10, +0x24, +0x38</td><td>QR(3, 4, 9, 14)</td></tr></tbody></table><p><strong>完全标准的 ChaCha20 column + diagonal round 索引。</strong></p><h5 id="block-函数-sub-5BCF0"><a href="#block-函数-sub-5BCF0" class="headerlink" title="block 函数 (sub_5BCF0)"></a>block 函数 (sub_5BCF0)</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">; 1. 复制 state → working (栈上 64 字节)</span><br><span class="line">MOV   X0, SP               ; dst = working (栈)</span><br><span class="line">MOV   W1, #0x40             ; 64 字节</span><br><span class="line">MOV   X2, X19               ; src = state</span><br><span class="line">MOV   W3, #0x40</span><br><span class="line">BLR   sub_5B7C4             ; memcpy(working, state, 64)</span><br><span class="line"></span><br><span class="line">; 2. 执行 10 次 double-round</span><br><span class="line">BLR   X21  ; column_round    ─┐</span><br><span class="line">BLR   X22  ; diagonal_round  │ × 10 次</span><br><span class="line">;          (重复 10 对)      ─┘</span><br><span class="line"></span><br><span class="line">; 3. NEON 向量加法: output = working + state</span><br><span class="line">LDP   Q0, Q1, [X19]         ; state[0..7]</span><br><span class="line">LDP   Q2, Q3, [SP]          ; working[0..7]</span><br><span class="line">ADD   V0.4S, V2.4S, V0.4S   ; output[0..3] = working[0..3] + state[0..3]</span><br><span class="line">ADD   V1.4S, V3.4S, V1.4S   ; output[4..7] = working[4..7] + state[4..7]</span><br><span class="line">; ... 对 state[8..15] 同理</span><br><span class="line"></span><br><span class="line">; 4. 将 16 个 u32 以小端序写回输出 (off_EBF00 = STR W1,[X0])</span><br><span class="line">STR   → [X19+0x40]          ; output[0]</span><br><span class="line">STR   → [X19+0x44]          ; output[1]</span><br><span class="line">...                          ; 共 16 个</span><br><span class="line"></span><br><span class="line">; 5. 递增计数器</span><br><span class="line">LDR   W8, [X19, #0x30]      ; counter</span><br><span class="line">ADD   W8, W8, #1</span><br><span class="line">STR   W8, [X19, #0x30]</span><br></pre></td></tr></table></figure><p><strong>标准 ChaCha20 block function：copy → 20 rounds → add → write → counter++</strong></p><h5 id="encrypt-函数-sub-5B950"><a href="#encrypt-函数-sub-5B950" class="headerlink" title="encrypt 函数 (sub_5B950)"></a>encrypt 函数 (sub_5B950)</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">; 参数: X0=ctx, X1=input, X2=output, X3=length</span><br><span class="line">; 核心 XOR 循环:</span><br><span class="line">LDRB  W13, [X12], #1       ; 读 input 字节</span><br><span class="line">LDRB  W14, [X8], #1        ; 读 keystream 字节</span><br><span class="line">EOR   W13, W14, W13         ; 异或</span><br><span class="line">STRB  W13, [X11], #1       ; 写 output 字节</span><br></pre></td></tr></table></figure><p>对于 ≥32 字节的数据，使用 NEON 向量化 XOR 加速：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">LDP   Q0, Q1, [X13, #-0x10]  ; 读 32 字节 input</span><br><span class="line">LDP   Q2, Q3, [X12, #-0x10]  ; 读 32 字节 keystream</span><br><span class="line">EOR   V0.16B, V2.16B, V0.16B ; 向量异或</span><br><span class="line">EOR   V1.16B, V3.16B, V1.16B</span><br><span class="line">STP   Q0, Q1, [X11, #-0x10]  ; 写 32 字节 output</span><br></pre></td></tr></table></figure><h5 id="Process-函数-sub-4E548"><a href="#Process-函数-sub-4E548" class="headerlink" title="Process 函数 (sub_4E548)"></a>Process 函数 (sub_4E548)</h5><p>这是 <code>GameExtension.Process()</code> 的 native 实现：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">; 1. 获取互斥锁</span><br><span class="line">ADR   X0, unk_ED7C0</span><br><span class="line">BLR   [off_EBD68]           ; lock</span><br><span class="line"></span><br><span class="line">; 2. 分配 ChaCha20 上下文 (0x88 字节)</span><br><span class="line">BLR   [off_EBD78]           ; alloc_state() → X21</span><br><span class="line"></span><br><span class="line">; 3. 初始化 ChaCha20</span><br><span class="line">ADR   X1, unk_ED5D2          ; KEY   (32 字节)</span><br><span class="line">ADR   X2, unk_ED5F3          ; NONCE (12 字节)</span><br><span class="line">MOV   W3, WZR                ; counter = 0</span><br><span class="line">BLR   [off_EBD80]           ; chacha20_init(ctx, key, nonce, 0)</span><br><span class="line"></span><br><span class="line">; 4. 获取 PackedByteArray 数据指针</span><br><span class="line">MOV   X0, X20                ; PackedByteArray 参数</span><br><span class="line">BLR   [off_EBD88]           ; get_data_ptr → X22 (数据指针)</span><br><span class="line"></span><br><span class="line">; 5. 初始化空字符串</span><br><span class="line">ADR   X1, byte_11DC6         ; &quot;&quot; (空字符串)</span><br><span class="line">BLR   [off_EBCD0]           ; String::init(result, &quot;&quot;)</span><br><span class="line"></span><br><span class="line">; 6. 分配输出缓冲区</span><br><span class="line">BL    0x88C80                ; PackedByteArray::size() → 8</span><br><span class="line">; ... chacha20_encrypt(ctx, input, output, 8) ...</span><br><span class="line"></span><br><span class="line">; 7. 循环格式化为十六进制</span><br><span class="line">loop:</span><br><span class="line">  LDR   X8, [qword_ED7E8]   ; 输出缓冲区指针</span><br><span class="line">  LDRB  W4, [X21, X8]       ; 读一个密文字节</span><br><span class="line">  ; 调用 sprintf(buf, &quot;%02X&quot;, byte)</span><br><span class="line">  ADR   X4, unk_ED600       ; 格式串 &quot;%02X&quot;</span><br><span class="line">  BL    sub_4C6D4            ; snprintf</span><br><span class="line">  BL    sub_64DFC            ; String::append</span><br><span class="line">  ADD   X21, X21, #1</span><br><span class="line">  B     loop</span><br><span class="line"></span><br><span class="line">; 8. 返回十六进制字符串</span><br></pre></td></tr></table></figure><p><strong>关键确认</strong>：</p><ul><li>密钥地址：<code>0xED5D2</code></li><li>Nonce 地址：<code>0xED5F3</code></li><li>格式化：<code>sprintf(buf, &quot;%02X&quot;, byte)</code> — <strong>大写十六进制</strong></li></ul><p>密钥为<code>Th1s ls n0t a rea1 key!!@sec2026</code></p><p>Nonce为 <code>012345678901</code></p><h3 id="疑惑的点"><a href="#疑惑的点" class="headerlink" title="疑惑的点"></a>疑惑的点</h3><p>到这里其实已经分析完了，但这里有一个疑惑的点，我dump出的so文件在地址 <code>0xED5D2</code> 可以看到这里的值是正常的</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260412165244767.png" alt="image-20260412165244767"></p><p>而在ida中看到的值却是不正常的</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260412165357602.png" alt="image-20260412165357602"></p><p>这里卡了我很久，直到我在010中查看的时候才发现密钥是不一样的</p><p>同时在 <code>libsec2026.so</code> 中，可以看到这里的值是乱码，猜测是在解压的代码中有覆盖操作</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260412165537936.png" alt="image-20260412165537936"></p><p>我抱着求证的心态尝试去hook <code>chacha20_init</code> </p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// chacha20_init(ChaCha20State *state, uint8_t *key, uint8_t *nonce, uint32_t counter)</span></span><br><span class="line"><span class="comment">// 位于 libsec2026.so 偏移 0x5B818</span></span><br><span class="line"><span class="comment">// ARM64 ABI: X0=state, X1=key(32B), X2=nonce(12B), W3=counter</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="title class_">CHACHA20</span>_INIT_OFFSET = <span class="number">0x5B818</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">hookChacha20Init</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> mod = <span class="title class_">Process</span>.<span class="title function_">findModuleByName</span>(<span class="string">&quot;libsec2026.so&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!mod) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[-] libsec2026.so 未加载&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> funcAddr = mod.<span class="property">base</span>.<span class="title function_">add</span>(<span class="title class_">CHACHA20</span>_INIT_OFFSET);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] chacha20_init @ &quot;</span> + funcAddr);</span><br><span class="line"></span><br><span class="line">    <span class="title class_">Interceptor</span>.<span class="title function_">attach</span>(funcAddr, &#123;</span><br><span class="line">        <span class="attr">onEnter</span>: <span class="keyword">function</span>(<span class="params">args</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> state   = args[<span class="number">0</span>];</span><br><span class="line">            <span class="keyword">var</span> key     = args[<span class="number">1</span>];</span><br><span class="line">            <span class="keyword">var</span> nonce   = args[<span class="number">2</span>];</span><br><span class="line">            <span class="keyword">var</span> counter = args[<span class="number">3</span>].<span class="title function_">toInt32</span>();</span><br><span class="line"></span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\n════════ chacha20_init 被调用 ════════&quot;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  state   = &quot;</span> + state);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  key     = &quot;</span> + key);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  nonce   = &quot;</span> + nonce);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  counter = &quot;</span> + counter);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Dump key (32 bytes)</span></span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\n  [Key - 32 bytes]&quot;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">hexdump</span>(key, &#123; <span class="attr">length</span>: <span class="number">32</span>, <span class="attr">ansi</span>: <span class="literal">true</span> &#125;));</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">var</span> keyBytes = key.<span class="title function_">readByteArray</span>(<span class="number">32</span>);</span><br><span class="line">                <span class="keyword">var</span> ka = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(keyBytes);</span><br><span class="line">                <span class="keyword">var</span> keyPrintable = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">var</span> keyStr = <span class="string">&quot;&quot;</span>;</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; ka.<span class="property">length</span>; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (ka[i] &lt; <span class="number">0x20</span> || ka[i] &gt;= <span class="number">0x7f</span>) keyPrintable = <span class="literal">false</span>;</span><br><span class="line">                    keyStr += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(ka[i]);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (keyPrintable) &#123;</span><br><span class="line">                    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  Key ASCII: &quot;</span> + keyStr);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Dump nonce (12 bytes)</span></span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\n  [Nonce - 12 bytes]&quot;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">hexdump</span>(nonce, &#123; <span class="attr">length</span>: <span class="number">12</span>, <span class="attr">ansi</span>: <span class="literal">true</span> &#125;));</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">var</span> nonceBytes = nonce.<span class="title function_">readByteArray</span>(<span class="number">12</span>);</span><br><span class="line">                <span class="keyword">var</span> na = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(nonceBytes);</span><br><span class="line">                <span class="keyword">var</span> noncePrintable = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">var</span> nonceStr = <span class="string">&quot;&quot;</span>;</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; na.<span class="property">length</span>; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (na[i] &lt; <span class="number">0x20</span> || na[i] &gt;= <span class="number">0x7f</span>) noncePrintable = <span class="literal">false</span>;</span><br><span class="line">                    nonceStr += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(na[i]);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (noncePrintable) &#123;</span><br><span class="line">                    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  Nonce ASCII: &quot;</span> + nonceStr);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span>(e) &#123;&#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印调用栈</span></span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;\n  [Backtrace]&quot;</span>);</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;  &quot;</span> + <span class="title class_">Thread</span>.<span class="title function_">backtrace</span>(<span class="variable language_">this</span>.<span class="property">context</span>, <span class="title class_">Backtracer</span>.<span class="property">FUZZY</span>)</span><br><span class="line">                .<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">addr</span>) &#123;</span><br><span class="line">                    <span class="keyword">var</span> m = <span class="title class_">Process</span>.<span class="title function_">findModuleByAddress</span>(addr);</span><br><span class="line">                    <span class="keyword">if</span> (m) &#123;</span><br><span class="line">                        <span class="keyword">return</span> m.<span class="property">name</span> + <span class="string">&quot;!0x&quot;</span> + addr.<span class="title function_">sub</span>(m.<span class="property">base</span>).<span class="title function_">toString</span>(<span class="number">16</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">return</span> addr.<span class="title function_">toString</span>();</span><br><span class="line">                &#125;).<span class="title function_">join</span>(<span class="string">&quot;\n  &quot;</span>));</span><br><span class="line"></span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;══════════════════════════════════════\n&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[+] chacha20_init hook 已安装&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> mod = <span class="title class_">Process</span>.<span class="title function_">findModuleByName</span>(<span class="string">&quot;libsec2026.so&quot;</span>);</span><br><span class="line"><span class="keyword">if</span> (mod) &#123;</span><br><span class="line">    <span class="title function_">hookChacha20Init</span>();</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> names = [<span class="string">&quot;android_dlopen_ext&quot;</span>, <span class="string">&quot;dlopen&quot;</span>];</span><br><span class="line">    <span class="keyword">var</span> hooked = <span class="literal">false</span>;</span><br><span class="line">    names.<span class="title function_">forEach</span>(<span class="keyword">function</span>(<span class="params">name</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> addr = <span class="title class_">Module</span>.<span class="title function_">findExportByName</span>(<span class="literal">null</span>, name);</span><br><span class="line">        <span class="keyword">if</span> (!addr || hooked) <span class="keyword">return</span>;</span><br><span class="line">        <span class="title class_">Interceptor</span>.<span class="title function_">attach</span>(addr, &#123;</span><br><span class="line">            <span class="attr">onEnter</span>: <span class="keyword">function</span>(<span class="params">args</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123; <span class="variable language_">this</span>.<span class="property">lib</span> = args[<span class="number">0</span>].<span class="title function_">readUtf8String</span>(); &#125; <span class="keyword">catch</span>(e) &#123; <span class="variable language_">this</span>.<span class="property">lib</span> = <span class="literal">null</span>; &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="attr">onReturn</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">lib</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">lib</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;libsec2026&quot;</span>) !== -<span class="number">1</span>) &#123;</span><br><span class="line">                    <span class="built_in">setTimeout</span>(hookChacha20Init, <span class="number">200</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        hooked = <span class="literal">true</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] 等待 libsec2026.so 加载...&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>注意这个脚本需要在先跑起上获得flag的js代码后再 attach 上去，然后操控小车撞上黄色方块即可</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260412170655288.png" alt="image-20260412170655288"></p><p>果然传入的是这个 key 和 nonce</p><h3 id="算法实现"><a href="#算法实现" class="headerlink" title="算法实现"></a>算法实现</h3><p>flag生成算法与生成逆算法python实现如下</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> struct</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line"><span class="comment"># Modified constants: &quot;fxpaod 31-byse k&quot; (instead of &quot;expand 32-byte k&quot;)</span></span><br><span class="line">CONSTANTS = [<span class="number">0x61707866</span>, <span class="number">0x3320646f</span>, <span class="number">0x79622d31</span>, <span class="number">0x6b206573</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Key: 32 bytes at runtime vaddr 0xed5d2 (overwritten by outer SO packer)</span></span><br><span class="line"><span class="comment"># Original .data had 1a367ed4... but at runtime it&#x27;s &quot;Th1s ls n0t a rea1 key!!@sec2026&quot;</span></span><br><span class="line">KEY_BYTES = <span class="string">b&quot;Th1s ls n0t a rea1 key!!@sec2026&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Nonce: 12 bytes at runtime vaddr 0xed5f3 (overwritten by outer SO packer)</span></span><br><span class="line"><span class="comment"># Original .data had e485e46a... but at runtime it&#x27;s &quot;012345678901&quot;</span></span><br><span class="line">NONCE_BYTES = <span class="string">b&quot;012345678901&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">u32</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> x &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rotl32</span>(<span class="params">x, n</span>):</span><br><span class="line">    <span class="keyword">return</span> u32((x &lt;&lt; n) | (x &gt;&gt; (<span class="number">32</span> - n)))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">quarter_round</span>(<span class="params">state, a, b, c, d</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Standard ChaCha20 quarter round with rotations 16, 12, 8, 7&quot;&quot;&quot;</span></span><br><span class="line">    state[a] = u32(state[a] + state[b]); state[d] ^= state[a]; state[d] = rotl32(state[d], <span class="number">16</span>)</span><br><span class="line">    state[c] = u32(state[c] + state[d]); state[b] ^= state[c]; state[b] = rotl32(state[b], <span class="number">12</span>)</span><br><span class="line">    state[a] = u32(state[a] + state[b]); state[d] ^= state[a]; state[d] = rotl32(state[d], <span class="number">8</span>)</span><br><span class="line">    state[c] = u32(state[c] + state[d]); state[b] ^= state[c]; state[b] = rotl32(state[b], <span class="number">7</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">chacha20_block</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Generate one 64-byte keystream block&quot;&quot;&quot;</span></span><br><span class="line">    working = <span class="built_in">list</span>(state)</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):  <span class="comment"># 20 rounds = 10 double-rounds</span></span><br><span class="line">        <span class="comment"># Column rounds</span></span><br><span class="line">        quarter_round(working, <span class="number">0</span>, <span class="number">4</span>, <span class="number">8</span>, <span class="number">12</span>)</span><br><span class="line">        quarter_round(working, <span class="number">1</span>, <span class="number">5</span>, <span class="number">9</span>, <span class="number">13</span>)</span><br><span class="line">        quarter_round(working, <span class="number">2</span>, <span class="number">6</span>, <span class="number">10</span>, <span class="number">14</span>)</span><br><span class="line">        quarter_round(working, <span class="number">3</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">15</span>)</span><br><span class="line">        <span class="comment"># Diagonal rounds</span></span><br><span class="line">        quarter_round(working, <span class="number">0</span>, <span class="number">5</span>, <span class="number">10</span>, <span class="number">15</span>)</span><br><span class="line">        quarter_round(working, <span class="number">1</span>, <span class="number">6</span>, <span class="number">11</span>, <span class="number">12</span>)</span><br><span class="line">        quarter_round(working, <span class="number">2</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">13</span>)</span><br><span class="line">        quarter_round(working, <span class="number">3</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">14</span>)</span><br><span class="line">    <span class="comment"># Add original state</span></span><br><span class="line">    output = [u32(working[i] + state[i]) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">16</span>)]</span><br><span class="line">    <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">chacha20_init</span>(<span class="params">key, nonce, counter=<span class="number">0</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Initialize ChaCha20 state&quot;&quot;&quot;</span></span><br><span class="line">    state = <span class="built_in">list</span>(CONSTANTS)</span><br><span class="line">    <span class="comment"># Key: 8 x u32 LE</span></span><br><span class="line">    state.extend(struct.unpack(<span class="string">&#x27;&lt;8I&#x27;</span>, key))</span><br><span class="line">    <span class="comment"># Counter</span></span><br><span class="line">    state.append(counter)</span><br><span class="line">    <span class="comment"># Nonce: 3 x u32 LE</span></span><br><span class="line">    state.extend(struct.unpack(<span class="string">&#x27;&lt;3I&#x27;</span>, nonce))</span><br><span class="line">    <span class="keyword">return</span> state</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">chacha20_encrypt</span>(<span class="params">key, nonce, plaintext, counter=<span class="number">0</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Encrypt/decrypt with modified ChaCha20&quot;&quot;&quot;</span></span><br><span class="line">    state = chacha20_init(key, nonce, counter)</span><br><span class="line">    result = <span class="built_in">bytearray</span>()</span><br><span class="line">    offset = <span class="number">0</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> offset &lt; <span class="built_in">len</span>(plaintext):</span><br><span class="line">        block = chacha20_block(state)</span><br><span class="line">        keystream = <span class="string">b&#x27;&#x27;</span>.join(struct.pack(<span class="string">&#x27;&lt;I&#x27;</span>, w) <span class="keyword">for</span> w <span class="keyword">in</span> block)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># XOR plaintext with keystream</span></span><br><span class="line">        chunk_size = <span class="built_in">min</span>(<span class="number">64</span>, <span class="built_in">len</span>(plaintext) - offset)</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(chunk_size):</span><br><span class="line">            result.append(plaintext[offset + i] ^ keystream[i])</span><br><span class="line">        </span><br><span class="line">        offset += chunk_size</span><br><span class="line">        state[<span class="number">12</span>] = u32(state[<span class="number">12</span>] + <span class="number">1</span>)  <span class="comment"># Increment counter</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(result)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xor_enc</span>(<span class="params">plain: <span class="built_in">str</span></span>) -&gt; <span class="built_in">bytes</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;GDScript xor_enc: chain XOR on 8-byte UTF-8 buffer (trigger.gd)</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    for i in range(7): result[i] ^= result[i+1]</span></span><br><span class="line"><span class="string">    result[7] ^= result[0]   # uses already-modified result[0]</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    buf = <span class="built_in">bytearray</span>(plain.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(buf) &lt; <span class="number">8</span>:</span><br><span class="line">        buf.extend(<span class="string">b&#x27;\x00&#x27;</span> * (<span class="number">8</span> - <span class="built_in">len</span>(buf)))</span><br><span class="line">    result = <span class="built_in">bytearray</span>(buf[:<span class="number">8</span>])</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">7</span>):</span><br><span class="line">        result[i] ^= result[i + <span class="number">1</span>]</span><br><span class="line">    result[<span class="number">7</span>] ^= result[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(result)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xor_dec</span>(<span class="params">enc_bytes: <span class="built_in">bytes</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Inverse of xor_enc: recover original 8-char token from XOR&#x27;d bytes&quot;&quot;&quot;</span></span><br><span class="line">    result = <span class="built_in">bytearray</span>(enc_bytes[:<span class="number">8</span>])</span><br><span class="line">    <span class="comment"># Reverse: undo result[7] ^= result[0], then undo i=6..0</span></span><br><span class="line">    result[<span class="number">7</span>] ^= result[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line">        result[i] ^= result[i + <span class="number">1</span>]</span><br><span class="line">    <span class="keyword">return</span> result.decode(<span class="string">&#x27;utf-8&#x27;</span>, errors=<span class="string">&#x27;replace&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">token_to_flag</span>(<span class="params">token: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Convert 8-char token to flag (16 hex chars)</span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    Matches game flow: xor_enc(token) -&gt; chacha20_encrypt -&gt; hex_upper</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    preprocessed = xor_enc(token)</span><br><span class="line">    encrypted = chacha20_encrypt(KEY_BYTES, NONCE_BYTES, preprocessed)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;&#x27;</span>.join(<span class="string">f&#x27;<span class="subst">&#123;b:02X&#125;</span>&#x27;</span> <span class="keyword">for</span> b <span class="keyword">in</span> encrypted)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">flag_to_token</span>(<span class="params">flag: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Convert flag (16 hex chars) back to token&quot;&quot;&quot;</span></span><br><span class="line">    flag_bytes = <span class="built_in">bytes</span>.fromhex(flag)</span><br><span class="line">    decrypted = chacha20_encrypt(KEY_BYTES, NONCE_BYTES, flag_bytes)</span><br><span class="line">    <span class="keyword">return</span> xor_dec(decrypted)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Test</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=== Modified ChaCha20 Flag Generator ===&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Constants: <span class="subst">&#123;<span class="string">&#x27; &#x27;</span>.join(<span class="string">f&#x27;0x<span class="subst">&#123;c:08x&#125;</span>&#x27;</span> <span class="keyword">for</span> c <span class="keyword">in</span> CONSTANTS)&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Key: <span class="subst">&#123;KEY_BYTES.<span class="built_in">hex</span>()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Nonce: <span class="subst">&#123;NONCE_BYTES.<span class="built_in">hex</span>()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Show initial state</span></span><br><span class="line">    state = chacha20_init(KEY_BYTES, NONCE_BYTES, <span class="number">0</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Initial state:&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  [<span class="subst">&#123;i*<span class="number">4</span>:2d&#125;</span>-<span class="subst">&#123;i*<span class="number">4</span>+<span class="number">3</span>:2d&#125;</span>]: <span class="subst">&#123;<span class="string">&#x27; &#x27;</span>.join(<span class="string">f&#x27;<span class="subst">&#123;state[i*<span class="number">4</span>+j]:08x&#125;</span>&#x27;</span> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>))&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Generate one keystream block for verification</span></span><br><span class="line">    block = chacha20_block(state)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;First keystream block:&quot;</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  [<span class="subst">&#123;i*<span class="number">4</span>:2d&#125;</span>-<span class="subst">&#123;i*<span class="number">4</span>+<span class="number">3</span>:2d&#125;</span>]: <span class="subst">&#123;<span class="string">&#x27; &#x27;</span>.join(<span class="string">f&#x27;<span class="subst">&#123;block[i*<span class="number">4</span>+j]:08x&#125;</span>&#x27;</span> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>))&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    keystream = <span class="string">b&#x27;&#x27;</span>.join(struct.pack(<span class="string">&#x27;&lt;I&#x27;</span>, w) <span class="keyword">for</span> w <span class="keyword">in</span> block)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\nKeystream bytes: <span class="subst">&#123;keystream[:<span class="number">16</span>].<span class="built_in">hex</span>()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Test with sample tokens</span></span><br><span class="line">    test_tokens = [<span class="string">&quot;4b37a5c4&quot;</span>, <span class="string">&quot;f54b25f4&quot;</span>, <span class="string">&quot;testtest&quot;</span>, <span class="string">&quot;aabbccdd&quot;</span>]//在这里改输入的token</span><br><span class="line">    <span class="keyword">for</span> token <span class="keyword">in</span> test_tokens:</span><br><span class="line">        enc = xor_enc(token)</span><br><span class="line">        flag = token_to_flag(token)</span><br><span class="line">        recovered = flag_to_token(flag)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Token: <span class="subst">&#123;token&#125;</span> -&gt; xor_enc: <span class="subst">&#123;enc.<span class="built_in">hex</span>()&#125;</span> -&gt; Flag: <span class="subst">&#123;flag&#125;</span> -&gt; Recovered: <span class="subst">&#123;recovered&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># Interactive mode</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(sys.argv) &gt; <span class="number">1</span>:</span><br><span class="line">        <span class="keyword">if</span> sys.argv[<span class="number">1</span>] == <span class="string">&#x27;-t&#x27;</span> <span class="keyword">and</span> <span class="built_in">len</span>(sys.argv) &gt; <span class="number">2</span>:</span><br><span class="line">            <span class="comment"># Token to flag</span></span><br><span class="line">            token = sys.argv[<span class="number">2</span>]</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;Flag: <span class="subst">&#123;token_to_flag(token)&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">elif</span> sys.argv[<span class="number">1</span>] == <span class="string">&#x27;-f&#x27;</span> <span class="keyword">and</span> <span class="built_in">len</span>(sys.argv) &gt; <span class="number">2</span>:</span><br><span class="line">            <span class="comment"># Flag to token</span></span><br><span class="line">            flag = sys.argv[<span class="number">2</span>]</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;Token: <span class="subst">&#123;flag_to_token(flag)&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;Usage: <span class="subst">&#123;sys.argv[<span class="number">0</span>]&#125;</span> -t &lt;token&gt;  or  -f &lt;flag&gt;&quot;</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>C语言实现如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CHACHA_BLOCK_SIZE 64</span></span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">uint32_t</span> CONSTANTS[<span class="number">4</span>] = &#123;</span><br><span class="line">    <span class="number">0x61707866</span>, <span class="number">0x3320646f</span>, <span class="number">0x79622d31</span>, <span class="number">0x6b206573</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">uint8_t</span> KEY_BYTES[<span class="number">32</span>] = &#123;</span><br><span class="line">    <span class="string">&#x27;T&#x27;</span>,<span class="string">&#x27;h&#x27;</span>,<span class="string">&#x27;1&#x27;</span>,<span class="string">&#x27;s&#x27;</span>,<span class="string">&#x27; &#x27;</span>,<span class="string">&#x27;l&#x27;</span>,<span class="string">&#x27;s&#x27;</span>,<span class="string">&#x27; &#x27;</span>,<span class="string">&#x27;n&#x27;</span>,<span class="string">&#x27;0&#x27;</span>,<span class="string">&#x27;t&#x27;</span>,<span class="string">&#x27; &#x27;</span>,<span class="string">&#x27;a&#x27;</span>,<span class="string">&#x27; &#x27;</span>,<span class="string">&#x27;r&#x27;</span>,<span class="string">&#x27;e&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;a&#x27;</span>,<span class="string">&#x27;1&#x27;</span>,<span class="string">&#x27; &#x27;</span>,<span class="string">&#x27;k&#x27;</span>,<span class="string">&#x27;e&#x27;</span>,<span class="string">&#x27;y&#x27;</span>,<span class="string">&#x27;!&#x27;</span>,<span class="string">&#x27;!&#x27;</span>,<span class="string">&#x27;@&#x27;</span>,<span class="string">&#x27;s&#x27;</span>,<span class="string">&#x27;e&#x27;</span>,<span class="string">&#x27;c&#x27;</span>,<span class="string">&#x27;2&#x27;</span>,<span class="string">&#x27;0&#x27;</span>,<span class="string">&#x27;2&#x27;</span>,<span class="string">&#x27;6&#x27;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="type">uint8_t</span> NONCE_BYTES[<span class="number">12</span>] = &#123;</span><br><span class="line">    <span class="string">&#x27;0&#x27;</span>,<span class="string">&#x27;1&#x27;</span>,<span class="string">&#x27;2&#x27;</span>,<span class="string">&#x27;3&#x27;</span>,<span class="string">&#x27;4&#x27;</span>,<span class="string">&#x27;5&#x27;</span>,<span class="string">&#x27;6&#x27;</span>,<span class="string">&#x27;7&#x27;</span>,<span class="string">&#x27;8&#x27;</span>,<span class="string">&#x27;9&#x27;</span>,<span class="string">&#x27;0&#x27;</span>,<span class="string">&#x27;1&#x27;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">uint32_t</span> <span class="title function_">rotl32</span><span class="params">(<span class="type">uint32_t</span> x, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> (x &lt;&lt; n) | (x &gt;&gt; (<span class="number">32</span> - n));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">uint32_t</span> <span class="title function_">load32_le</span><span class="params">(<span class="type">const</span> <span class="type">uint8_t</span> *p)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> ((<span class="type">uint32_t</span>)p[<span class="number">0</span>]) |</span><br><span class="line">           ((<span class="type">uint32_t</span>)p[<span class="number">1</span>] &lt;&lt; <span class="number">8</span>) |</span><br><span class="line">           ((<span class="type">uint32_t</span>)p[<span class="number">2</span>] &lt;&lt; <span class="number">16</span>) |</span><br><span class="line">           ((<span class="type">uint32_t</span>)p[<span class="number">3</span>] &lt;&lt; <span class="number">24</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">store32_le</span><span class="params">(<span class="type">uint8_t</span> *p, <span class="type">uint32_t</span> v)</span> &#123;</span><br><span class="line">    p[<span class="number">0</span>] = (<span class="type">uint8_t</span>)(v &amp; <span class="number">0xff</span>);</span><br><span class="line">    p[<span class="number">1</span>] = (<span class="type">uint8_t</span>)((v &gt;&gt; <span class="number">8</span>) &amp; <span class="number">0xff</span>);</span><br><span class="line">    p[<span class="number">2</span>] = (<span class="type">uint8_t</span>)((v &gt;&gt; <span class="number">16</span>) &amp; <span class="number">0xff</span>);</span><br><span class="line">    p[<span class="number">3</span>] = (<span class="type">uint8_t</span>)((v &gt;&gt; <span class="number">24</span>) &amp; <span class="number">0xff</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">quarter_round</span><span class="params">(<span class="type">uint32_t</span> state[<span class="number">16</span>], <span class="type">int</span> a, <span class="type">int</span> b, <span class="type">int</span> c, <span class="type">int</span> d)</span> &#123;</span><br><span class="line">    state[a] += state[b]; state[d] ^= state[a]; state[d] = rotl32(state[d], <span class="number">16</span>);</span><br><span class="line">    state[c] += state[d]; state[b] ^= state[c]; state[b] = rotl32(state[b], <span class="number">12</span>);</span><br><span class="line">    state[a] += state[b]; state[d] ^= state[a]; state[d] = rotl32(state[d], <span class="number">8</span>);</span><br><span class="line">    state[c] += state[d]; state[b] ^= state[c]; state[b] = rotl32(state[b], <span class="number">7</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">chacha20_init</span><span class="params">(<span class="type">uint32_t</span> state[<span class="number">16</span>], <span class="type">const</span> <span class="type">uint8_t</span> key[<span class="number">32</span>], <span class="type">const</span> <span class="type">uint8_t</span> nonce[<span class="number">12</span>], <span class="type">uint32_t</span> counter)</span> &#123;</span><br><span class="line">    state[<span class="number">0</span>] = CONSTANTS[<span class="number">0</span>];</span><br><span class="line">    state[<span class="number">1</span>] = CONSTANTS[<span class="number">1</span>];</span><br><span class="line">    state[<span class="number">2</span>] = CONSTANTS[<span class="number">2</span>];</span><br><span class="line">    state[<span class="number">3</span>] = CONSTANTS[<span class="number">3</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">8</span>; i++) &#123;</span><br><span class="line">        state[<span class="number">4</span> + i] = load32_le(key + i * <span class="number">4</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    state[<span class="number">12</span>] = counter;</span><br><span class="line">    state[<span class="number">13</span>] = load32_le(nonce + <span class="number">0</span>);</span><br><span class="line">    state[<span class="number">14</span>] = load32_le(nonce + <span class="number">4</span>);</span><br><span class="line">    state[<span class="number">15</span>] = load32_le(nonce + <span class="number">8</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">chacha20_block</span><span class="params">(<span class="type">const</span> <span class="type">uint32_t</span> input[<span class="number">16</span>], <span class="type">uint32_t</span> output[<span class="number">16</span>])</span> &#123;</span><br><span class="line">    <span class="type">uint32_t</span> working[<span class="number">16</span>];</span><br><span class="line">    <span class="built_in">memcpy</span>(working, input, <span class="keyword">sizeof</span>(working));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">        quarter_round(working, <span class="number">0</span>, <span class="number">4</span>, <span class="number">8</span>, <span class="number">12</span>);</span><br><span class="line">        quarter_round(working, <span class="number">1</span>, <span class="number">5</span>, <span class="number">9</span>, <span class="number">13</span>);</span><br><span class="line">        quarter_round(working, <span class="number">2</span>, <span class="number">6</span>, <span class="number">10</span>, <span class="number">14</span>);</span><br><span class="line">        quarter_round(working, <span class="number">3</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">15</span>);</span><br><span class="line"></span><br><span class="line">        quarter_round(working, <span class="number">0</span>, <span class="number">5</span>, <span class="number">10</span>, <span class="number">15</span>);</span><br><span class="line">        quarter_round(working, <span class="number">1</span>, <span class="number">6</span>, <span class="number">11</span>, <span class="number">12</span>);</span><br><span class="line">        quarter_round(working, <span class="number">2</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">13</span>);</span><br><span class="line">        quarter_round(working, <span class="number">3</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">14</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++) &#123;</span><br><span class="line">        output[i] = working[i] + input[i];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">chacha20_encrypt</span><span class="params">(</span></span><br><span class="line"><span class="params">    <span class="type">const</span> <span class="type">uint8_t</span> key[<span class="number">32</span>],</span></span><br><span class="line"><span class="params">    <span class="type">const</span> <span class="type">uint8_t</span> nonce[<span class="number">12</span>],</span></span><br><span class="line"><span class="params">    <span class="type">const</span> <span class="type">uint8_t</span> *plaintext,</span></span><br><span class="line"><span class="params">    <span class="type">size_t</span> plaintext_len,</span></span><br><span class="line"><span class="params">    <span class="type">uint8_t</span> *ciphertext,</span></span><br><span class="line"><span class="params">    <span class="type">uint32_t</span> counter</span></span><br><span class="line"><span class="params">)</span> &#123;</span><br><span class="line">    <span class="type">uint32_t</span> state[<span class="number">16</span>];</span><br><span class="line">    chacha20_init(state, key, nonce, counter);</span><br><span class="line"></span><br><span class="line">    <span class="type">size_t</span> offset = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (offset &lt; plaintext_len) &#123;</span><br><span class="line">        <span class="type">uint32_t</span> block_words[<span class="number">16</span>];</span><br><span class="line">        <span class="type">uint8_t</span> keystream[CHACHA_BLOCK_SIZE];</span><br><span class="line"></span><br><span class="line">        chacha20_block(state, block_words);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++) &#123;</span><br><span class="line">            store32_le(keystream + i * <span class="number">4</span>, block_words[i]);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">size_t</span> chunk_size = (plaintext_len - offset &lt; CHACHA_BLOCK_SIZE)</span><br><span class="line">            ? (plaintext_len - offset)</span><br><span class="line">            : CHACHA_BLOCK_SIZE;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; chunk_size; i++) &#123;</span><br><span class="line">            ciphertext[offset + i] = plaintext[offset + i] ^ keystream[i];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        offset += chunk_size;</span><br><span class="line">        state[<span class="number">12</span>] += <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">xor_enc</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *plain, <span class="type">uint8_t</span> out[<span class="number">8</span>])</span> &#123;</span><br><span class="line">    <span class="type">size_t</span> len = <span class="built_in">strlen</span>(plain);</span><br><span class="line">    <span class="built_in">memset</span>(out, <span class="number">0</span>, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (len &gt; <span class="number">8</span>) len = <span class="number">8</span>;</span><br><span class="line">    <span class="built_in">memcpy</span>(out, plain, len);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">7</span>; i++) &#123;</span><br><span class="line">        out[i] ^= out[i + <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    out[<span class="number">7</span>] ^= out[<span class="number">0</span>];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">xor_dec</span><span class="params">(<span class="type">const</span> <span class="type">uint8_t</span> enc[<span class="number">8</span>], <span class="type">char</span> out[<span class="number">9</span>])</span> &#123;</span><br><span class="line">    <span class="type">uint8_t</span> buf[<span class="number">8</span>];</span><br><span class="line">    <span class="built_in">memcpy</span>(buf, enc, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">    buf[<span class="number">7</span>] ^= buf[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">6</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        buf[i] ^= buf[i + <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">memcpy</span>(out, buf, <span class="number">8</span>);</span><br><span class="line">    out[<span class="number">8</span>] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">bytes_to_hex_upper</span><span class="params">(<span class="type">const</span> <span class="type">uint8_t</span> *in, <span class="type">size_t</span> len, <span class="type">char</span> *out)</span> &#123;</span><br><span class="line">    <span class="type">static</span> <span class="type">const</span> <span class="type">char</span> HEX[] = <span class="string">&quot;0123456789ABCDEF&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;</span><br><span class="line">        out[i * <span class="number">2</span>]     = HEX[(in[i] &gt;&gt; <span class="number">4</span>) &amp; <span class="number">0xF</span>];</span><br><span class="line">        out[i * <span class="number">2</span> + <span class="number">1</span>] = HEX[in[i] &amp; <span class="number">0xF</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    out[len * <span class="number">2</span>] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">hex_value</span><span class="params">(<span class="type">char</span> c)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;0&#x27;</span> &lt;= c &amp;&amp; c &lt;= <span class="string">&#x27;9&#x27;</span>) <span class="keyword">return</span> c - <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;a&#x27;</span> &lt;= c &amp;&amp; c &lt;= <span class="string">&#x27;f&#x27;</span>) <span class="keyword">return</span> c - <span class="string">&#x27;a&#x27;</span> + <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&#x27;A&#x27;</span> &lt;= c &amp;&amp; c &lt;= <span class="string">&#x27;F&#x27;</span>) <span class="keyword">return</span> c - <span class="string">&#x27;A&#x27;</span> + <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">hex_to_bytes</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *hex, <span class="type">uint8_t</span> *out, <span class="type">size_t</span> out_len)</span> &#123;</span><br><span class="line">    <span class="type">size_t</span> hex_len = <span class="built_in">strlen</span>(hex);</span><br><span class="line">    <span class="keyword">if</span> (hex_len != out_len * <span class="number">2</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; out_len; i++) &#123;</span><br><span class="line">        <span class="type">int</span> hi = hex_value(hex[i * <span class="number">2</span>]);</span><br><span class="line">        <span class="type">int</span> lo = hex_value(hex[i * <span class="number">2</span> + <span class="number">1</span>]);</span><br><span class="line">        <span class="keyword">if</span> (hi &lt; <span class="number">0</span> || lo &lt; <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        out[i] = (<span class="type">uint8_t</span>)((hi &lt;&lt; <span class="number">4</span>) | lo);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">token_to_flag</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *token, <span class="type">char</span> flag_hex[<span class="number">17</span>])</span> &#123;</span><br><span class="line">    <span class="type">uint8_t</span> preprocessed[<span class="number">8</span>];</span><br><span class="line">    <span class="type">uint8_t</span> encrypted[<span class="number">8</span>];</span><br><span class="line"></span><br><span class="line">    xor_enc(token, preprocessed);</span><br><span class="line">    chacha20_encrypt(KEY_BYTES, NONCE_BYTES, preprocessed, <span class="number">8</span>, encrypted, <span class="number">0</span>);</span><br><span class="line">    bytes_to_hex_upper(encrypted, <span class="number">8</span>, flag_hex);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">flag_to_token</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *flag_hex, <span class="type">char</span> token[<span class="number">9</span>])</span> &#123;</span><br><span class="line">    <span class="type">uint8_t</span> flag_bytes[<span class="number">8</span>];</span><br><span class="line">    <span class="type">uint8_t</span> decrypted[<span class="number">8</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (hex_to_bytes(flag_hex, flag_bytes, <span class="number">8</span>) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    chacha20_encrypt(KEY_BYTES, NONCE_BYTES, flag_bytes, <span class="number">8</span>, decrypted, <span class="number">0</span>);</span><br><span class="line">    xor_dec(decrypted, token);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (argc != <span class="number">3</span>) &#123;</span><br><span class="line">        <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;Usage: %s -t &lt;token&gt; | -f &lt;flag&gt;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(argv[<span class="number">1</span>], <span class="string">&quot;-t&quot;</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">char</span> flag[<span class="number">17</span>];</span><br><span class="line">        token_to_flag(argv[<span class="number">2</span>], flag);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, flag);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(argv[<span class="number">1</span>], <span class="string">&quot;-f&quot;</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">char</span> token[<span class="number">9</span>];</span><br><span class="line">        <span class="keyword">if</span> (flag_to_token(argv[<span class="number">2</span>], token) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;Invalid flag hex.\n&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, token);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;Usage: %s -t &lt;token&gt; | -f &lt;flag&gt;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用如图所示</p><p><img src="/2026/04/16/2026%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E5%AE%89%E5%8D%93%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E5%85%A8-%E5%88%9D%E8%B5%9B%20Writeup/image-20260412163034661.png" alt="image-20260412163034661"></p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>学到很多新知识，感谢腾讯给的学习机会，不过考了太多加解密知识，让我有点措手不及，幸好今年初赛题目似乎比往年简单，或许是换了引擎的原因吧。希望能进决赛，继续加油吧！</p>]]></content>
    
    
    <summary type="html">感激腾讯给的学习机会   ┭┮﹏┭┮</summary>
    
    
    
    
    <category term="Android" scheme="http://example.com/tags/Android/"/>
    
    <category term="Reverse" scheme="http://example.com/tags/Reverse/"/>
    
    <category term="2026腾讯游戏安全技术竞赛" scheme="http://example.com/tags/2026%E8%85%BE%E8%AE%AF%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B/"/>
    
    <category term="Godot" scheme="http://example.com/tags/Godot/"/>
    
  </entry>
  
  <entry>
    <title>第三届数信杯数据安全大赛初赛reverse wp</title>
    <link href="http://example.com/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/"/>
    <id>http://example.com/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/</id>
    <published>2026-02-28T07:28:00.000Z</published>
    <updated>2026-03-03T08:48:10.573Z</updated>
    
    <content type="html"><![CDATA[<h4 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h4><p>看到决赛通知我才想起来，初赛当时还有道题没做完，当时时间太紧了，压根没时间认真做啊🤣</p><p>题目如下：</p><p>工程师小王认识到前面开发的程序并不能保证对数据的安全存储，现在对处理程序进行了改进，这次能行吗?分析程序功能，解密文件获取原始数据，提交第8行第2列数据。</p><h4 id="花指令去除"><a href="#花指令去除" class="headerlink" title="花指令去除"></a>花指令去除</h4><p>一开始跟进 <code>main</code> 函数就能发现 ida 没识别成功</p><p>在 0x4016F1 处能看到第一处</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230005626.png" alt="image-20260228230005626"></p><p>这样即可</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230144549.png" alt="image-20260228230144549"></p><p>第二处在 0x4012A5 处</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230319167.png" alt="image-20260228230319167"></p><p>同上处理</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230255697.png" alt="image-20260228230255697"></p><p>第三处在 0x4014E6 处</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230344695.png" alt="image-20260228230344695"></p><p>许久不见 call-ret 型的花指令了，这里真正的代码是从 0x4014F6 开始的，还原后如下</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230623090.png" alt="image-20260228230623090"></p><p>重新识别函数之后就能开心的读代码了</p><h4 id="加密分析"><a href="#加密分析" class="headerlink" title="加密分析"></a>加密分析</h4><p>三个加密逻辑都在函数 <code>sub_40127A</code> 中</p><h5 id="循环左移-rol1"><a href="#循环左移-rol1" class="headerlink" title="循环左移 rol1"></a>循环左移 rol1</h5><p><code>dest</code> 数组就是从文件中读进来的明文数据</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228230858628.png" alt="image-20260228230858628"></p><h5 id="shellcode-自解密"><a href="#shellcode-自解密" class="headerlink" title="shellcode 自解密"></a>shellcode 自解密</h5><p>注意他从明文的 0x339 开始取了两字节的数据作为 key </p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228231338709.png" alt="image-20260228231338709"></p><p>shellcode 数据</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228231455723.png" alt="image-20260228231455723"></p><h5 id="xor加密"><a href="#xor加密" class="headerlink" title="xor加密"></a>xor加密</h5><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228231730611.png" alt="image-20260228231730611"></p><h4 id="shellcode-解密"><a href="#shellcode-解密" class="headerlink" title="shellcode 解密"></a>shellcode 解密</h4><p>很显然题目的重点就在第二重加密了，既然 shellcode 是要执行的，那就先把 shellcode 全部转为代码看看吧</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260228232152184.png" alt="image-20260228232152184"></p><h5 id="入口部分"><a href="#入口部分" class="headerlink" title="入口部分"></a>入口部分</h5><p>可以看到前几字节是正常的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">41 54                push r12</span><br><span class="line">41 53                push r11</span><br><span class="line">e8 45 00 00 00       call next</span><br></pre></td></tr></table></figure><p>作用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">push r12</span><br><span class="line">push r11</span><br><span class="line">call +0x45</span><br></pre></td></tr></table></figure><p>这个 <code>call</code> 会：</p><ul><li>把下一条指令地址压栈</li><li>跳到后面 0x45 字节处</li></ul><h5 id="数据区"><a href="#数据区" class="headerlink" title="数据区"></a>数据区</h5><p>前三条指令都是比较正常的，但后面就变的杂乱无章了，说明他们压根就不是正确的代码，他们很可能是需要解密的数据</p><h5 id="call-落点"><a href="#call-落点" class="headerlink" title="call 落点"></a>call 落点</h5><p>call 跳到这里：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">41 5b              pop r11</span><br><span class="line">4d 31 e4           xor r12, r12</span><br><span class="line">45 31 c0           xor r8d, r8d</span><br></pre></td></tr></table></figure><p> <code>pop r11</code> 拿到 call 返回地址。</p><p>也就是数据区首地址，所以 <code>r11 = &amp;encrypted_data</code></p><h5 id="主循环"><a href="#主循环" class="headerlink" title="主循环"></a>主循环</h5><p>取一个加密字节</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">movzx eax, byte ptr [r11 + r12]</span><br></pre></td></tr></table></figure><p> 取 key</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mov bl, [rsi + r8]</span><br></pre></td></tr></table></figure><p>因为 r8 只能是 0 或 1 ，所以显然这里是取两字节的 key</p><p>异或解密</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xor al, bl</span><br></pre></td></tr></table></figure><p>所以最后的逻辑还原大概是这样</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">0x42</span>; i++) &#123;</span><br><span class="line">    data[i] ^= key[i % <span class="number">2</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="爆破密钥"><a href="#爆破密钥" class="headerlink" title="爆破密钥"></a>爆破密钥</h5><p>由于这段 shellcode 需要的 key 是从明文里来的，也就是说这个程序只能用来加密那个对应的文件（小王这么写是要准备提桶跑路吗🤣），所以只能进行爆破</p><p>要注意的一点是，数据区只有 0x42 的长度，在 0x4040EC 还有一段shellcode执行完之后的代码，注意到有两个 <code>pop r11</code> 的操作，但上面只有一个 <code>push r11</code> 的操作，所以解密之后的数据肯定有压栈的操作，这样可以缩小范围（虽然我是做出来之后看到了才想到的）</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260301000604816.png" alt="image-20260301000604816"></p><p>爆破代码如下：</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> capstone <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># 原始 Shellcode 数据</span></span><br><span class="line">cipher_text = <span class="built_in">bytes</span>([</span><br><span class="line">    <span class="number">0x33</span>, <span class="number">0x3A</span>, <span class="number">0x33</span>, <span class="number">0x3D</span>, <span class="number">0x3A</span>, <span class="number">0x91</span>, <span class="number">0xBD</span>, <span class="number">0x26</span>, <span class="number">0x8D</span>, <span class="number">0xA1</span>,</span><br><span class="line">    <span class="number">0x3A</span>, <span class="number">0x91</span>, <span class="number">0xBD</span>, <span class="number">0x27</span>, <span class="number">0xFB</span>, <span class="number">0x92</span>, <span class="number">0x3B</span>, <span class="number">0xEF</span>, <span class="number">0x96</span>, <span class="number">0x91</span>,</span><br><span class="line">    <span class="number">0x72</span>, <span class="number">0x6E</span>, <span class="number">0x72</span>, <span class="number">0x27</span>, <span class="number">0xF1</span>, <span class="number">0x8A</span>, <span class="number">0x7D</span>, <span class="number">0x27</span>, <span class="number">0xFB</span>, <span class="number">0x95</span>,</span><br><span class="line">    <span class="number">0x3B</span>, <span class="number">0xEF</span>, <span class="number">0x91</span>, <span class="number">0x91</span>, <span class="number">0x72</span>, <span class="number">0x6E</span>, <span class="number">0x72</span>, <span class="number">0x27</span>, <span class="number">0xB3</span>, <span class="number">0x95</span>,</span><br><span class="line">    <span class="number">0x76</span>, <span class="number">0x27</span>, <span class="number">0xF1</span>, <span class="number">0x8D</span>, <span class="number">0x7D</span>, <span class="number">0x27</span>, <span class="number">0xB3</span>, <span class="number">0x8A</span>, <span class="number">0x76</span>, <span class="number">0x23</span>,</span><br><span class="line">    <span class="number">0x7B</span>, <span class="number">0x8D</span>, <span class="number">0x3E</span>, <span class="number">0xE7</span>, <span class="number">0xAA</span>, <span class="number">0x5A</span>, <span class="number">0x61</span>, <span class="number">0x6A</span>, <span class="number">0x76</span>, <span class="number">0x90</span>,</span><br><span class="line">    <span class="number">0xB2</span>, <span class="number">0x90</span>, <span class="number">0xB2</span>, <span class="number">0x2F</span>, <span class="number">0x29</span>, <span class="number">0x2F</span>, <span class="number">0x2E</span></span><br><span class="line">])</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xor_decrypt</span>(<span class="params">data, k1, k2</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;双字节循环异或解密&quot;&quot;&quot;</span></span><br><span class="line">    dec = <span class="built_in">bytearray</span>()</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(data)):</span><br><span class="line">        <span class="keyword">if</span> i % <span class="number">2</span> == <span class="number">0</span>:</span><br><span class="line">            dec.append(data[i] ^ k1)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            dec.append(data[i] ^ k2)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(dec)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">brute_force</span>():</span><br><span class="line">    <span class="comment"># 初始化 Capstone x86-64 模式</span></span><br><span class="line">    md = Cs(CS_ARCH_X86, CS_MODE_64)</span><br><span class="line">    <span class="comment"># 开启详细模式以过滤无效指令</span></span><br><span class="line">    md.detail = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    results = []</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[*] Starting brute force... this may take a minute.&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> k1 <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">        <span class="keyword">for</span> k2 <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">            decrypted = xor_decrypt(cipher_text, k1, k2)</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 尝试反汇编</span></span><br><span class="line">            insns = <span class="built_in">list</span>(md.disasm(decrypted, <span class="number">0x1000</span>))</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> insns:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 评估解密结果的质量</span></span><br><span class="line">            score = <span class="number">0</span></span><br><span class="line">            bad_insn = <span class="literal">False</span></span><br><span class="line">            asm_code = []</span><br><span class="line">            </span><br><span class="line">            total_bytes_disassembled = <span class="number">0</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> insns:</span><br><span class="line">                mnemonic = i.mnemonic.lower()</span><br><span class="line">                total_bytes_disassembled += <span class="built_in">len</span>(i.<span class="built_in">bytes</span>)</span><br><span class="line">                </span><br><span class="line">                <span class="comment"># 排除不符合要求的指令（jmp, call, ret等控制流指令）</span></span><br><span class="line">                <span class="keyword">if</span> mnemonic.startswith(<span class="string">&#x27;j&#x27;</span>) <span class="keyword">or</span> mnemonic <span class="keyword">in</span> [<span class="string">&#x27;call&#x27;</span>, <span class="string">&#x27;ret&#x27;</span>, <span class="string">&#x27;int&#x27;</span>, <span class="string">&#x27;syscall&#x27;</span>]:</span><br><span class="line">                    bad_insn = <span class="literal">True</span></span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                </span><br><span class="line">                <span class="comment"># 数据处理指令加分</span></span><br><span class="line">                <span class="keyword">if</span> mnemonic <span class="keyword">in</span> [<span class="string">&#x27;mov&#x27;</span>, <span class="string">&#x27;xor&#x27;</span>, <span class="string">&#x27;add&#x27;</span>, <span class="string">&#x27;sub&#x27;</span>, <span class="string">&#x27;lea&#x27;</span>, <span class="string">&#x27;inc&#x27;</span>, <span class="string">&#x27;dec&#x27;</span>, <span class="string">&#x27;shl&#x27;</span>, <span class="string">&#x27;shr&#x27;</span>, <span class="string">&#x27;rol&#x27;</span>, <span class="string">&#x27;ror&#x27;</span>, <span class="string">&#x27;and&#x27;</span>, <span class="string">&#x27;or&#x27;</span>]:</span><br><span class="line">                    score += <span class="number">1</span></span><br><span class="line">                </span><br><span class="line">                asm_code.append(<span class="string">f&quot;<span class="subst">&#123;i.address:#x&#125;</span>:\t<span class="subst">&#123;i.mnemonic&#125;</span>\t<span class="subst">&#123;i.op_str&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 如果反汇编覆盖率太低（说明中间有很多无法解析的机器码），则忽略</span></span><br><span class="line">            <span class="keyword">if</span> total_bytes_disassembled &lt; <span class="built_in">len</span>(cipher_text) * <span class="number">0.8</span>:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> bad_insn <span class="keyword">and</span> score &gt; <span class="number">2</span>: <span class="comment"># 设定一个基本的指令密度阈值</span></span><br><span class="line">                results.append(&#123;</span><br><span class="line">                    <span class="string">&#x27;key&#x27;</span>: (k1, k2),</span><br><span class="line">                    <span class="string">&#x27;score&#x27;</span>: score,</span><br><span class="line">                    <span class="string">&#x27;asm&#x27;</span>: asm_code</span><br><span class="line">                &#125;)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 按分数排序，输出最可能的解</span></span><br><span class="line">    results.sort(key=<span class="keyword">lambda</span> x: x[<span class="string">&#x27;score&#x27;</span>], reverse=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[*] Found <span class="subst">&#123;<span class="built_in">len</span>(results)&#125;</span> potential candidates.\n&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> top <span class="keyword">in</span> results[:<span class="number">5</span>]: <span class="comment"># 输出前5个最像的</span></span><br><span class="line">        k1, k2 = top[<span class="string">&#x27;key&#x27;</span>]</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;================ Key: 0x<span class="subst">&#123;k1:02X&#125;</span> 0x<span class="subst">&#123;k2:02X&#125;</span> (Score: <span class="subst">&#123;top[<span class="string">&#x27;score&#x27;</span>]&#125;</span>) ================&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span>.join(top[<span class="string">&#x27;asm&#x27;</span>]))</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;-&quot;</span> * <span class="number">50</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    brute_force()</span><br></pre></td></tr></table></figure><p>结果如下：</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260301001013275.png" alt="image-20260301001013275"></p><p>最终的加密逻辑为</p><figure class="highlight mathematica"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">f</span><span class="punctuation">(</span><span class="variable">b</span><span class="punctuation">)</span> <span class="operator">=</span> <span class="punctuation">(</span> <span class="type">nibble_swap</span><span class="punctuation">(</span><span class="variable">b</span><span class="operator">-</span><span class="number">3</span><span class="punctuation">)</span> <span class="operator">^</span> <span class="number">0</span><span class="variable">x13</span> <span class="punctuation">)</span> <span class="operator">+</span> <span class="number">6</span></span><br></pre></td></tr></table></figure><h4 id="解密"><a href="#解密" class="headerlink" title="解密"></a>解密</h4><p>最后写出解密代码如下：</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"></span><br><span class="line">xor_key = <span class="string">b&quot;e6911c24&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;info_81bedab.ori.en&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>) <span class="keyword">as</span> infile:</span><br><span class="line">    cipher_data = infile.read()</span><br><span class="line"></span><br><span class="line">buffer = <span class="built_in">bytearray</span>(cipher_data)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> idx <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(buffer)):</span><br><span class="line">    <span class="comment"># 第一阶段：XOR</span></span><br><span class="line">    buffer[idx] ^= xor_key[idx % <span class="number">8</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 第二阶段：减6 -&gt; XOR 0x13 -&gt; 交换高低4位 -&gt; 加3</span></span><br><span class="line">    buffer[idx] = (buffer[idx] - <span class="number">6</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">    buffer[idx] ^= <span class="number">0x13</span></span><br><span class="line">    buffer[idx] = ((buffer[idx] &amp; <span class="number">0x0F</span>) &lt;&lt; <span class="number">4</span>) | ((buffer[idx] &amp; <span class="number">0xF0</span>) &gt;&gt; <span class="number">4</span>)</span><br><span class="line">    buffer[idx] = (buffer[idx] + <span class="number">3</span>) &amp; <span class="number">0xFF</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 第三阶段：循环右移1位</span></span><br><span class="line">    current = buffer[idx]</span><br><span class="line">    buffer[idx] = ((current &gt;&gt; <span class="number">1</span>) | (current &lt;&lt; <span class="number">7</span>)) &amp; <span class="number">0xFF</span></span><br><span class="line"></span><br><span class="line">plain_data = <span class="built_in">bytes</span>(buffer)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;info_81bedab.ori&#x27;</span>, <span class="string">&#x27;wb&#x27;</span>) <span class="keyword">as</span> outfile:</span><br><span class="line">    outfile.write(plain_data)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;done&quot;</span>)</span><br></pre></td></tr></table></figure><p>解密结果如图：</p><p><img src="/2026/02/28/%E7%AC%AC%E4%B8%89%E5%B1%8A%E6%95%B0%E4%BF%A1%E6%9D%AF%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B%E5%88%9D%E8%B5%9Breverse%20wp/image-20260301001731396.png" alt="image-20260301001731396"></p><h4 id="参考链接："><a href="#参考链接：" class="headerlink" title="参考链接："></a>参考链接：</h4><p><a href="https://www.cnblogs.com/lantern-lab/p/19430548/2025-the-3rd-shuxin-cup-qxcuc#%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A82%E6%B2%A1%E5%81%9A%E5%87%BA%E6%9D%A5">https://www.cnblogs.com/lantern-lab/p/19430548/2025-the-3rd-shuxin-cup-qxcuc#%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A82%E6%B2%A1%E5%81%9A%E5%87%BA%E6%9D%A5</a></p>]]></content>
    
    
    <summary type="html">工程师小王你也不想看着你开发的程序被破解吧？</summary>
    
    
    
    
    <category term="Reverse" scheme="http://example.com/tags/Reverse/"/>
    
    <category term="CTF" scheme="http://example.com/tags/CTF/"/>
    
    <category term="Windows" scheme="http://example.com/tags/Windows/"/>
    
  </entry>
  
  <entry>
    <title>DASCTF2025下半年赛 reverse Writeup</title>
    <link href="http://example.com/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/"/>
    <id>http://example.com/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/</id>
    <published>2025-12-08T07:28:00.000Z</published>
    <updated>2025-12-09T13:33:20.325Z</updated>
    
    <content type="html"><![CDATA[<h2 id="ezmac"><a href="#ezmac" class="headerlink" title="ezmac"></a>ezmac</h2><p>总共就没几个函数，定位到 <code>sub_10000045C</code> 函数，调用 <code>sub_100000634</code> ，传入 a6 参数为 57 </p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209153242037.png" alt="image-20251209153242037"></p><p><code>sub_100000634</code> 只做了异或操作</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209153510536.png" alt="image-20251209153510536"></p><p>找到密文 <code>byte_100004022</code> 即可解密</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209153604845.png" alt="image-20251209153604845"></p><p>exp</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">enc = <span class="string">&quot;7D7B687F69784478722174767522267B7C7E787A2E2D7F2D&quot;</span></span><br><span class="line">a = <span class="number">57</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(enc)//<span class="number">2</span>):</span><br><span class="line">    byte = <span class="built_in">int</span>(enc[<span class="number">2</span>*i:<span class="number">2</span>*i+<span class="number">2</span>], <span class="number">16</span>)</span><br><span class="line">    decoded_byte = byte ^ a</span><br><span class="line">    a = a + <span class="number">1</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">chr</span>(decoded_byte), end=<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    </span><br><span class="line"><span class="comment"># DASCTF&#123;83c720da35436cc0&#125;</span></span><br></pre></td></tr></table></figure><h2 id="androidfff"><a href="#androidfff" class="headerlink" title="androidfff"></a>androidfff</h2><p>解压在 <code>/lib</code> 里就看到 flutter 特征，Blutter 直接解就好了，app 名字叫 untitled3 ，那就去 <code>asm/untitled3/main.dart</code> 里找，根据 ui 里的 incorrect 去定位关键逻辑</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209154302690.png" alt="image-20251209154302690"></p><p>上下翻翻得到关键信息</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209154842096.png" alt="image-20251209154842096"></p><p>现在去恢复 libapp.so 里的符号表，搜索 flagcheck 出来也没几个函数</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209155103805.png" alt="image-20251209155103805"></p><p>在 <code>untitled3_main__FlagCheckerState::ctor_2f7c1c</code> 里找到密文，记得要除2，原因在第一篇博客里解释过了</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209155336633.png" alt="image-20251209155336633"></p><p>在 <code>untitled3_main__FlagCheckerState::_xorEncrypt_29cb18</code> 里找到异或操作</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209155708914.png" alt="image-20251209155708914"></p><p>得到 flag：<code>DASCTF{flutter_is_so_easy}</code></p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209160022079.png" alt="image-20251209160022079"></p><h2 id="androidfile"><a href="#androidfile" class="headerlink" title="androidfile"></a>androidfile</h2><p>jeb 打开，在 <code>MainActivity</code> 找到逻辑，注意到 AES 加密标志</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209161934387.png" alt="image-20251209161934387"></p><p>仔细看这里的字符串和题目txt中给的公钥私钥是一样的     </p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209160716594.png" alt="image-20251209160716594"></p><p>直接进 <code>i0.a</code> 函数看</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209160903699.png" alt="image-20251209160903699"></p><p>看来原来的数据应该是有 enkey 和 eniv 字段的，那看来 AES 是最后一重加密，继续跟进 <code>B</code> </p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209161035701.png" alt="image-20251209161035701"></p><p>回到了 <code>MainActivity</code> ，发现是调用了个 native 函数，继续跟进看看，在 libandroidfile.so 里定位到  <code>Java_com_dasctf_androidfile_MainActivity_a_1p</code></p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209161244608.png" alt="image-20251209161244608"></p><p>可以看出是个 rc4 加密，密钥是 <code>REVERSE</code> ，最后进行 base64 编码</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209161708619.png" alt="image-20251209161708619"></p><p>接下来用 RSA 公私钥解 enkey 和 eniv ，再用他们解 AES 即可</p><p>exp</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"><span class="keyword">from</span> Crypto.PublicKey <span class="keyword">import</span> RSA</span><br><span class="line"><span class="keyword">from</span> Crypto.Util.Padding <span class="keyword">import</span> unpad</span><br><span class="line"><span class="keyword">from</span> base64 <span class="keyword">import</span> b64decode</span><br><span class="line"></span><br><span class="line"><span class="comment"># === 输入 ===</span></span><br><span class="line">enkey_b64 = <span class="string">&#x27;&#x27;&#x27;QMz2qirA80LJiOs30Efl00JsrIv+ZdrM9iB74P/nCWOrzEemEOaq2lN1/V5/rOAoTgBanJO/AcpookhVIOVdsA==&#x27;&#x27;&#x27;</span></span><br><span class="line">eniv_b64  = <span class="string">&#x27;&#x27;&#x27;hKH/M/v8zwVICeWlc652BZk2eA/c2g0cLpBwvWBVlphiwBBasdn9HPWk7sb/IaRh8eppZrToUwz6f1eomFJkEQ==&#x27;&#x27;&#x27;</span></span><br><span class="line">aes_ct_b64 = <span class="string">&#x27;&#x27;&#x27;UBUSWb+1P3Z/aokV67e5xQ7eaHoEj3JAeC0XA1RckTWdWZYCB/+D7qC3Hao74goX&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">private_key_pem = <span class="string">&#x27;&#x27;&#x27;-----BEGIN PRIVATE KEY-----</span></span><br><span class="line"><span class="string">MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAncB8BH4egqfyJBoV</span></span><br><span class="line"><span class="string">PzGNIuQl/64e5fl1If+CwtICWoiRV4AMfHuiREB+XlTawJ7QD/ZJj2wO6sY4sdNh</span></span><br><span class="line"><span class="string">yYcC4QIDAQABAkEAh81Gdg+kcFHoD9AsbkRX/atuUtcwXkYL4gK2LMThpdEFHIO7</span></span><br><span class="line"><span class="string">Scr+SYfwqmm/LMtkbojEGEnNoIfmoLvGfhXaAQIhANDWo8OSMSQFnvh129cFiVfY</span></span><br><span class="line"><span class="string">KlS4ec24ixvFD8fUD4SRAiEAwWBuZ3kox1n21AsTAxom+E3z5KUUOSUjPXvG6tZB</span></span><br><span class="line"><span class="string">gVECIDOP2y0tSi6/qIll6BqFxmxG9eSnC4PMfaQkmonXBOHRAiBmJUPsUGmj8/eX</span></span><br><span class="line"><span class="string">xknCp7vSCYs9SZ3HGcDlp05Jmed8IQIhAJnE1PNe9lC5OazgRYhSG6bGCTbfFHT6</span></span><br><span class="line"><span class="string">OuwCVIxRSx4P</span></span><br><span class="line"><span class="string">-----END PRIVATE KEY-----&#x27;&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ==== Step 1: Base64 decode ====</span></span><br><span class="line">enkey = b64decode(enkey_b64)</span><br><span class="line">eniv  = b64decode(eniv_b64)</span><br><span class="line">aes_ct = b64decode(aes_ct_b64)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==== Step 2: RSA Raw Decrypt ====</span></span><br><span class="line">key = RSA.import_key(private_key_pem)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rsa_raw_decrypt_raw</span>(<span class="params">data</span>):</span><br><span class="line">    decrypted_int = <span class="built_in">pow</span>(<span class="built_in">int</span>.from_bytes(data, <span class="string">&quot;big&quot;</span>), key.d, key.n)</span><br><span class="line">    decrypted = decrypted_int.to_bytes((key.size_in_bits() + <span class="number">7</span>) // <span class="number">8</span>, <span class="string">&quot;big&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> decrypted</span><br><span class="line"></span><br><span class="line"><span class="comment"># 得到原始解密（包含前导 zeros）</span></span><br><span class="line">raw_key = rsa_raw_decrypt_raw(enkey)</span><br><span class="line">raw_iv  = rsa_raw_decrypt_raw(eniv)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==== Step 3: 真正 AES key / IV 是最后 16 bytes ====</span></span><br><span class="line">aes_key = raw_key[-<span class="number">16</span>:]</span><br><span class="line">aes_iv  = raw_iv[-<span class="number">16</span>:]</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] AES Key =&quot;</span>, aes_key)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[+] AES IV  =&quot;</span>, aes_iv)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ==== Step 4: AES-CBC 解密 ====</span></span><br><span class="line">cipher = AES.new(aes_key, AES.MODE_CBC, aes_iv)</span><br><span class="line">pt_padded = cipher.decrypt(aes_ct)</span><br><span class="line">pt = unpad(pt_padded, <span class="number">16</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n[+] Plaintext =&quot;</span>, pt.decode())</span><br><span class="line"></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">[+] AES Key = b&#x27;ElmGJYfKbc2gJh0G&#x27;</span></span><br><span class="line"><span class="string">[+] AES IV  = b&#x27;JZ4tQgwSm3ZZIELJ&#x27;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">[+] Plaintext = DASCTF&#123;android_encrypto_file_and_plains&#125;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><h2 id="login"><a href="#login" class="headerlink" title="login"></a>login</h2><p>先看服务端文件，在 <code>sub_29C0</code> 定位到关键逻辑</p><p>服务段先发送 <code>inp u account</code> ，rc4 加密后发送出去</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209163343244.png" alt="image-20251209163343244"></p><p>接收到客户端信息，会先解一次 rc4 </p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209162925166.png" alt="image-20251209162925166"></p><p>接着和数组 <code>byte_C1A0</code> 进行比较，错误则提示 <code>back door...</code></p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209163352722.png" alt="image-20251209163352722"></p><p>同时还需要验证 key ，逻辑相同，最后比较的数组是 <code>byte_C0A0</code></p><p>这两个数组都是会进行 RSA 解密的，对应的函数都是 <code>sub_73B2</code></p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209164001046.png" alt="image-20251209164001046"></p><p>而这里这么多参数，不用去分析程序逻辑，直接计算验证就能分出，谁是n，谁是d</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209164221447.png" alt="image-20251209164221447"></p><p>别说d了，甚至p和q都给了</p><p>直接解密即可</p><p>exp</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> Crypto.PublicKey <span class="keyword">import</span> RSA</span><br><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> PKCS1_OAEP</span><br><span class="line"><span class="keyword">import</span> binascii</span><br><span class="line"></span><br><span class="line"><span class="comment"># 密钥参数（n, e, d）</span></span><br><span class="line">n = <span class="number">0x9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb1309b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741fad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bbd81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2ccfdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe24d3</span></span><br><span class="line">d = <span class="number">0x28c7df24a5798679db2a44979275f5f3179db180d91335702942fb1b70e985de825da90f2eb65d20ddf8be1d9d4e15bc1d84e95795ff8c0c28ce3c33fde054f6e82a4f4cc22597b350c9c62ccc0188bd4152a701a3601558f22aa9fae8b9fdac6c2bc09b1637f71e0511805e04b203c4fdb2b36ad232fe819b06ed4e57c74f39fd9b72623c16ff2100f148f622bf12876260c4859672360dc0da3da6b45c5c8c6215ccda072765840c213fba11a91d6bf598a8a8065797566c8950a34ea0a072a9ed0c38bdc58662f186ec578ca55d5098443fd566cc722ace9c4e89afc4e302c8a4870e11a003b935f4a102695bfd64bb0fa74dcc372682e2b24ff45a1a69</span></span><br><span class="line">e = <span class="number">0x10001</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 密文 HEX（c1、c2）</span></span><br><span class="line">c1 = <span class="string">&quot;373A2A27B38FD778C716728EBB95BE89A0A057109119A08D5CE49261EBB0E0776D254A40C4D21BD2463E61608771DE401EED13AC6660D996BEA8C8B82BDD0EAF56C38466776EBA31F7B2219230B654A77EC0AF395A01C31C139A4F6B7B8BA845192096165DD7ACD0331E79DBE434ED8C9A66581D26F69E5FAA295F66010076B91A6DD61DB7ABD325F8BD25D928DEBCC02E5555FF81F7AE3E548E3E4659A37F5D3D3C39FBCAD1B583E42FB04FA328EBB77E7841F45B711E77EE23E11989DB2C0E06B8191A456D56BD1A7D42C47FDFDF1179228B57C6EFCA9B9B6A7D22682E5B67C7C46A877FB677F5F317B4823FCDC812F0362BE27C0F5453037148ED30127B26&quot;</span></span><br><span class="line">c2 = <span class="string">&quot;1638E0EB936140B5527033292CBEFCD73B55CFC7FB79DF51AE3768A0DD9C84AE4580E47A5133B425F4C93EAC97E4B1AA0B4CD30589D004F6D0D19FCBC709E86CC2996B433D29F650B69987A466F05BEF7F69945860DCC44742A511F3621385C89FBD4D73153615789634B25CFC3151A4115BC30C96979E5F965290F36A863E3378B5CFC9BA31438C4BAE22B23EF815EDF7CF1771803BD392A5072B468900B75F5A4377D1DAF3D6F7B7B6850D1A4A4134F2F65840EFAA9B83D31083051DF0FC80A786529159484F62BBB9524F68285F48C7AB8E03BDFECA1A6025AAED9F9728B390689C0C963920C728EB5695FCB9413F9F4E06D3B93DB40E26D6275C84E6126A&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 构造 RSA 私钥</span></span><br><span class="line">key = RSA.construct((n, e, d))</span><br><span class="line">cipher = PKCS1_OAEP.new(key)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解密并输出</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    m1 = cipher.decrypt(<span class="built_in">bytes</span>.fromhex(c1))</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] m1 解密成功:&quot;</span>, m1)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> err:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[-] 解密 m1 出错:&quot;</span>, err)</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    m2 = cipher.decrypt(<span class="built_in">bytes</span>.fromhex(c2))</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] m2 解密成功:&quot;</span>, m2)</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> err:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[-] 解密 m2 出错:&quot;</span>, err)</span><br><span class="line"></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">[+] m1 解密成功: b&#x27;qqwweerrttyyuuii&#x27;</span></span><br><span class="line"><span class="string">[+] m2 解密成功: b&#x27;aassddffgghhjjll&#x27;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure><p>得到两个字符串，不知道怎么使用，该去客户端看看了，在关键词 <code>passwd</code> 找到函数 <code>sub_49DD</code> ，各种特征都显示是标准 AES 加密</p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209164718928.png" alt="image-20251209164718928"></p><p>那两个字符串看来一个是 iv ，一个是 key 了，密文就是 server 里的数组 <code>byte_C2A0</code></p><p><img src="/2025/12/08/DASCTF2025%E4%B8%8B%E5%8D%8A%E5%B9%B4%E8%B5%9B%20reverse%20Writeup/image-20251209165132634.png" alt="image-20251209165132634"></p><p>flag ：<code>DASCTF{dqmaxfwkm921kr21m;df1m1dqmlk1d12d1}</code></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>题目难度还算不大，不过用来给我爽玩一个月之后的复健还是够用了(●’◡’●)</p>]]></content>
    
    
    <summary type="html">矩阵博弈，零度突围</summary>
    
    
    
    
    <category term="Android" scheme="http://example.com/tags/Android/"/>
    
    <category term="Reverse" scheme="http://example.com/tags/Reverse/"/>
    
    <category term="CTF" scheme="http://example.com/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>WMCTF2025-Want2BecomeMagicalGirl 复现</title>
    <link href="http://example.com/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/"/>
    <id>http://example.com/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/</id>
    <published>2025-10-15T08:57:59.000Z</published>
    <updated>2025-10-20T14:06:18.968Z</updated>
    
    <content type="html"><![CDATA[<h4 id="作者彩蛋"><a href="#作者彩蛋" class="headerlink" title="作者彩蛋"></a>作者彩蛋</h4><p>将 apk 拖入模拟器，没想到是流氓软件，这就是魔法少女吗&#x2F;(ㄒoㄒ)&#x2F;~~</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012154535340.png" alt="image-20251012154535340"></p><p>作者好中二😄</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012154610487.png" alt="image-20251012154610487"></p><h4 id="魔改-AES-加密"><a href="#魔改-AES-加密" class="headerlink" title="魔改 AES 加密"></a>魔改 AES 加密</h4><p>Jadx 打开找到 <code>MainActivity</code> ，很明显的 flutter 特征，同时还有一个 <code>Magic</code> 类，里面是标准的 xxtea ，最后定位是在 <code>encryptToBase64()</code>，这个加密函数无人调用，说明是在 native 层进行调用的</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012160111136.png" alt="image-20251012160111136"></p><p>先不管 Java 层的加密，去恢复 <code>libapp.so</code> 的符号和 dart 源码等</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012161535131.png" alt="image-20251012161535131"></p><p>在 &#x2F;asm&#x2F;magical_girl 里看到这些文件，看到这里就已经确定是 aes 加密了。</p><h5 id="S-盒"><a href="#S-盒" class="headerlink" title="S 盒"></a>S 盒</h5><p>据 GPT 所说 <code>main.dart</code> 里只有 Flutter 应用的启动过程，而在 <code>aes_crypt_null_safe.dart</code> 中，可以看到 aes 加密代码，但是这些汇编代码太难看了，GPT 也分析不出什么，所以应该到 ida 里去看这些代码。</p><p>而在这里的汇编中，已经标准好了这段代码对应的地址，我们在 <code>libapp.so</code> 可以找到</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251013133150633.png" alt="image-20251013133150633"></p><p>对应到函数 <code>magical_girl_aes_crypt_null_safe__Aes::_subBytes_2901b8()</code></p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251013133605813.png" alt="image-20251013133605813"></p><p>数一下这里的赋值操作，正好有 256 个，那是 S 盒无疑了。</p><h6 id="Dart-在ida中的数值问题"><a href="#Dart-在ida中的数值问题" class="headerlink" title="Dart 在ida中的数值问题"></a>Dart 在ida中的数值问题</h6><p>但是为什么会有超过 255(0xFF) 的呢。</p><p>这是因为虽然 Dart 中一切皆为 <code>Object</code>，但是实际 Dart 在也是有底层优化的。</p><p>在 Dart 中最最常见的就是 int 类型。</p><p>int 分两种类型：</p><p>1.smi: 对于小数值的数字，Dart直接将原数值左移一位。</p><p>2.bigint: 对于大数值，Dart构建对象，其数值在对象偏移+7处。</p><p>这里一个字节的值显然是小数值，所以在底层硬编码时被左移了一位，现在需要右移回来。</p><p>顺便随便搜一些和 aes 有关的东西，搜到了如下，大致确定他是 ecb 模式的 aes 。</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012165927609.png" alt="image-20251012165927609"></p><h5 id="加密逻辑"><a href="#加密逻辑" class="headerlink" title="加密逻辑"></a>加密逻辑</h5><p>既然改了 S 盒，那肯定要检查一下是不是在加密逻辑上也进行了更改，同样定位到对应的代码逻辑，发现果然</p><p>在 <code>magical_girl_aes_crypt_null_safe__Aes::aesEncryptBlock_28d5bc()</code> 中，注意到</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251013163343190.png" alt="image-20251013163343190"></p><p>他先做了轮密钥加，再做列混淆，颠倒了加密顺序。其他加密代码中倒是没有发现别的更改。</p><p>在 <code>EditView.dart</code> 文件中，找到了 check 逻辑，同时还有找到了密文。</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012164621738.png" alt="image-20251012164621738"></p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012164726149.png" alt="image-20251012164726149"></p><p><strong>You have become a magical girl ! !</strong></p><p>这里注意到最后的密文是 Base64 格式的，而我们在 Java 看到的最后一层编码也是 Base64 ，可以猜测先是 native 层做 aes 加密，然后在 Java 层做 xxtea 加密。</p><p>根据 ai 的提示，现在去 <code>libnative_add.so</code> 找密钥，惊喜发现 <code>libnative_add.so</code> 没有抹符号表。</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012170152593.png" alt="image-20251012170152593"></p><p>在 <code>genkey()</code> 中，使用标准 rc4 加密生成的 key ，成功得到密钥</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251012171314845.png" alt="image-20251012171314845"></p><p>aes 此时就成功的分析完了。</p><h4 id="魔改-xxtea-加密"><a href="#魔改-xxtea-加密" class="headerlink" title="魔改 xxtea 加密"></a>魔改 xxtea 加密</h4><p>在 <code>init_proc()</code> 中，看到了非常多的  <code>NEON SIMD</code> 指令处理数据的代码，让 ai 写出字符解密代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> ida_bytes, ida_name</span><br><span class="line"></span><br><span class="line"><span class="comment"># 固定向量，根据你前面提供的数据</span></span><br><span class="line">l = [<span class="number">0xF0</span>] * <span class="number">16</span></span><br><span class="line">shellcode_end = [<span class="number">0x0D</span>, <span class="number">0x0E</span>, <span class="number">0x0F</span>, <span class="number">0x0C</span>, <span class="number">0x0B</span>, <span class="number">0x0A</span>, <span class="number">0x09</span>, <span class="number">0x08</span>,</span><br><span class="line">                 <span class="number">0x06</span>, <span class="number">0x07</span>, <span class="number">0x05</span>, <span class="number">0x04</span>, <span class="number">0x02</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 所有在 init_proc 里出现的表</span></span><br><span class="line">tables = [</span><br><span class="line">    <span class="string">&quot;xmmword_10100&quot;</span>, <span class="string">&quot;xmmword_10110&quot;</span>, <span class="string">&quot;xmmword_10120&quot;</span>, <span class="string">&quot;xmmword_10130&quot;</span>,</span><br><span class="line">    <span class="string">&quot;xmmword_10140&quot;</span>, <span class="string">&quot;xmmword_10150&quot;</span>, <span class="string">&quot;xmmword_10160&quot;</span>, <span class="string">&quot;xmmword_10170&quot;</span>,</span><br><span class="line">    <span class="string">&quot;xmmword_10180&quot;</span>, <span class="string">&quot;xmmword_10190&quot;</span>, <span class="string">&quot;xmmword_101A0&quot;</span>, <span class="string">&quot;xmmword_101B0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;xmmword_101C0&quot;</span>, <span class="string">&quot;xmmword_101D0&quot;</span>, <span class="string">&quot;xmmword_101E0&quot;</span>, <span class="string">&quot;xmmword_101F0&quot;</span>,</span><br><span class="line">    <span class="string">&quot;xmmword_10200&quot;</span>, <span class="string">&quot;xmmword_10210&quot;</span>, <span class="string">&quot;xmmword_10230&quot;</span>, <span class="string">&quot;xmmword_10240&quot;</span>,</span><br><span class="line">    <span class="string">&quot;xmmword_10250&quot;</span>, <span class="string">&quot;xmmword_10260&quot;</span>, <span class="string">&quot;xmmword_10270&quot;</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment"># ---------- 基础函数 ----------</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read_bytes</span>(<span class="params">ea, size=<span class="number">16</span></span>):</span><br><span class="line">    <span class="keyword">return</span> [ida_bytes.get_byte(ea + i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(size)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">veorq_s8</span>(<span class="params">v1, v2</span>):</span><br><span class="line">    <span class="keyword">return</span> [(v1[i] ^ v2[i]) &amp; <span class="number">0xFF</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">16</span>)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">vqtbl1q_s8</span>(<span class="params">table, index</span>):</span><br><span class="line">    <span class="keyword">return</span> [(table[i] <span class="keyword">if</span> i &lt; <span class="built_in">len</span>(table) <span class="keyword">else</span> <span class="number">0</span>) <span class="keyword">for</span> i <span class="keyword">in</span> index]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_vector</span>(<span class="params">label</span>):</span><br><span class="line">    ea = ida_name.get_name_ea(<span class="number">0</span>, label)</span><br><span class="line">    <span class="keyword">if</span> ea == idaapi.BADADDR:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[!] 找不到符号 <span class="subst">&#123;label&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">    <span class="keyword">return</span> read_bytes(ea, <span class="number">16</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">to_ascii</span>(<span class="params">bs</span>):</span><br><span class="line">    out = []</span><br><span class="line">    <span class="keyword">for</span> b <span class="keyword">in</span> bs:</span><br><span class="line">        <span class="keyword">if</span> <span class="number">32</span> &lt;= b &lt; <span class="number">127</span>:</span><br><span class="line">            out.append(<span class="built_in">chr</span>(b))</span><br><span class="line">        <span class="keyword">elif</span> b == <span class="number">0</span>:</span><br><span class="line">            out.append(<span class="string">&#x27;\0&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;&#x27;</span>.join(out)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ---------- 主逻辑 ----------</span></span><br><span class="line">decoded_total = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> lbl <span class="keyword">in</span> tables:</span><br><span class="line">    tbl = get_vector(lbl)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> tbl:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line">    step1 = veorq_s8(l, shellcode_end)</span><br><span class="line">    step2 = vqtbl1q_s8(tbl, shellcode_end)</span><br><span class="line">    final = veorq_s8(step1, step2)</span><br><span class="line">    s = to_ascii(final)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;lbl:15s&#125;</span>: <span class="subst">&#123;s&#125;</span>&quot;</span>)</span><br><span class="line">    decoded_total.extend(final)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ---------- 输出 ----------</span></span><br><span class="line">joined = to_ascii(decoded_total)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;\n======== 合并结果 ========\n&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(joined)</span><br></pre></td></tr></table></figure><p>得到解密结果如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">xmmword_10100  : EEbPNS_9ArtMeooo</span><br><span class="line">xmmword_10110  : _ZN3art9ArtMeooo</span><br><span class="line">xmmword_10120  : Lb0EEEbPNS_9AMMM</span><br><span class="line">xmmword_10130  : PKNS_11Instruiii</span><br><span class="line">xmmword_10140  : er12IsDebuggaeee</span><br><span class="line">xmmword_10150  : _ZN3art11interrr</span><br><span class="line">xmmword_10160  : b</span><br><span class="line">xmmword_10170  : Ev</span><br><span class="line">xmmword_10180  : dEPNS_6ThreadNNN</span><br><span class="line">xmmword_10190  : onEtbPNS_6JVaeee</span><br><span class="line">xmmword_101A0  : dERNS_11Shadorrr</span><br><span class="line">xmmword_101B0  : lueE</span><br><span class="line">xmmword_101C0  : _ZNK3art9OatHddd</span><br><span class="line">xmmword_101D0  : S_11ShadowFraEEE</span><br><span class="line">xmmword_101E0  : ethodEPNS_6Thaaa</span><br><span class="line">xmmword_101F0  : gic.fi</span><br><span class="line">xmmword_10200  : uctionEtPNS_6aaa</span><br><span class="line">xmmword_10210  : d12PrettyMethEEE</span><br><span class="line">xmmword_10230  : ameEPKNS_11Inrrr</span><br><span class="line">xmmword_10240  : gic.toB</span><br><span class="line">xmmword_10250  : libart.so</span><br><span class="line">xmmword_10260  : E</span><br><span class="line">xmmword_10270  : eter6DoCallILEEE</span><br></pre></td></tr></table></figure><p>注意到解密之后有两个特殊的字符串，<code>gic.fi</code> 和 <code>gic.toB</code> ，查他们交叉引用，看到是在 <code>sub_1A51C()</code> 函数里被用到</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251013161352031.png" alt="image-20251013161352031"></p><p>而 <code>sub_1A51C()</code> 在函数 <code>init_proc()</code> 函数里有如下操作</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251013155511706.png" alt="image-20251013155511706"></p><p>我一开始做复现的时候就做到这里了，我到处翻找一直卡在这里，我认为是AES已经完全逆完了，这一段 <code>init_proc()</code> 的内容，大概是跟 Java 层那个没人调用的 <code>encryptToBase64()</code> 有关，那两个特殊字符串应该是提示，但是完全没领会到意思。官方 wp 里作者也没有给详细的解题思路（其实是没认真看官方 wp 里也写了 hook 了 <code>DoCall</code>函数😭） ，只好搁置。</p><p>后来题目作者在自己博客重新发了一篇详解</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251013175736883.png" alt="image-20251013175736883"></p><p>看到这里真是<strong>恍然大悟</strong>，(之前一直想找 <code>ArtMethod::Invoke</code> 函数，因为作者说像抽取壳，难怪卡住了)，现在赶紧去学一学安卓字节码执行的解释模式和 <code>DoCall</code> 函数。</p><p>学习后，按照我的理解如下</p><p>在 Android 的解释执行模式（Interpreter Mode）下，Java 字节码中的 <code>invoke-*</code> 指令最终就是通过 <code>DoCall</code> 函数（及其相关函数）来完成方法调用的执行的。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Docall函数定义</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="type">bool</span> is_range&gt;</span></span><br><span class="line"><span class="function">NO_STACK_PROTECTOR</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DoCall</span><span class="params">(ArtMethod* called_method, <span class="comment">//要被调用的方法（目标方法）</span></span></span></span><br><span class="line"><span class="params"><span class="function">            Thread* self, <span class="comment">// 当前线程对象</span></span></span></span><br><span class="line"><span class="params"><span class="function">            ShadowFrame&amp; shadow_frame, <span class="comment">//当前解释器的 shadow frame（栈帧）</span></span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="type">const</span> Instruction* inst, <span class="comment">//当前字节码指令</span></span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="type">uint16_t</span> inst_data, <span class="comment">//解析指令所需的数据</span></span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="type">bool</span> is_string_init, <span class="comment">//是否是特殊处理的 String.&lt;init&gt; 构造函数</span></span></span></span><br><span class="line"><span class="params"><span class="function">            JValue* result)</span><span class="comment">// 调用结果 </span></span></span><br></pre></td></tr></table></figure><p>而本题通过 hook <code>DoCall</code> 函数去得到内存中的 <strong>dex</strong> 文件，如作者所说</p><blockquote><p><strong>把内存中函数的字节码转为 Instruction* ，就可以利用其中函数读取操作码和操作数。那么通过 inst 拿到的指针就是 dex 的指针，只要把指针向前回溯到 dex 头就可以操作正在运行的整个 dex 文件。</strong></p></blockquote><p>而我们在ida里看到的<code>sub_1A51C()</code> ，正是 <code>DoCall</code> 的 <code>onEnter</code> 处理函数</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251015145610503.png" alt="image-20251015145610503"></p><p>知道了原理，我们很容易就可以看出来，，第一个 <code>gic.fi</code> 分支里的操作是对 <strong>dex</strong> 文件进行修改，而 <code>gic.toB</code> 分支里的操作是恢复 <strong>dex</strong> 文件，而 <code>xmmword_41F60</code> 显然是原始字节码的储存处。</p><p>既然这样，我们作者到题的思路就非常明显且简单了，要么在进入 <code>gic.toB</code> 分支前将 <strong>dex</strong> dump下来，要么去 trace Smali，对比看看他改了什么，当然最简单的思路是作者给出的。</p><blockquote><p><strong>第二次 <code>strstr</code> 后的还原逻辑 <code>NOP</code> 掉</strong></p></blockquote><p>这样，<strong>dex</strong> 文件就不会被改回去了。</p><p>我的做法相当暴力，我给里面的逻辑全 nop 了，如图</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251015150233599.png" alt="image-20251015150233599"></p><p>重新打包签名后，安装到手机上，随便输入一次按回车之后，我们直接使用 frida-dexdump 就能得到 被修改之后的 <strong>dex</strong> 文件了</p><blockquote><p><strong>在这里我使用的是 <a href="https://gitcode.com/open-source-toolkit/a47ab/?utm_source=tools_gitcode&index=top&type=card&uuid_tt_dd=10_37304650070-1748170098365-934690&from_id=141971553&from_link=0021dbc388edb4d70d466438e3dfa1b9">hluda</a> ，并且改了端口，因为不改会闪退，应该有端口检测，我做 Android 已经习惯用 hluda server 了，不知是不是绕过了其他检测</strong></p></blockquote><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251015150356223.png" alt="image-20251015150356223"></p><p>得到的 <strong>dex</strong> 如图</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251015151230114.png" alt="image-20251015151230114"></p><p>最后 xxtea 的 key 就很容易获得了，frida 脚本如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使弹窗可关闭</span></span><br><span class="line"><span class="title class_">Java</span>.<span class="title function_">perform</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> a = <span class="title class_">Java</span>.<span class="title function_">use</span>(<span class="string">&quot;android.widget.Button&quot;</span>);</span><br><span class="line">    a[<span class="string">&quot;setEnabled&quot;</span>].<span class="property">implementation</span> = <span class="keyword">function</span> (<span class="params">enable</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;Button set enable&quot;</span>)</span><br><span class="line">        <span class="variable language_">this</span>[<span class="string">&quot;setEnabled&quot;</span>](<span class="literal">true</span>);</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 以上代码来自作者</span></span><br><span class="line"><span class="title class_">Java</span>.<span class="title function_">perform</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> <span class="title class_">Magic</span> = <span class="title class_">Java</span>.<span class="title function_">use</span>(<span class="string">&quot;work.pangbai.magic.magical_girl.Magic&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="title class_">Magic</span>.<span class="property">encrypt</span>.<span class="title function_">overload</span>(<span class="string">&#x27;[B&#x27;</span>, <span class="string">&#x27;[B&#x27;</span>).<span class="property">implementation</span> = <span class="keyword">function</span> (<span class="params">bArr, bArr2</span>) &#123;</span><br><span class="line">        <span class="comment">// 打印第二个参数（bArr2）</span></span><br><span class="line">        <span class="keyword">var</span> keyBytes = <span class="title class_">Java</span>.<span class="title function_">array</span>(<span class="string">&#x27;byte&#x27;</span>, bArr2);</span><br><span class="line">        <span class="keyword">var</span> keyStr = <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; keyBytes.<span class="property">length</span>; i++) &#123;</span><br><span class="line">            keyStr += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(keyBytes[i] &amp; <span class="number">0xff</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;[*] Hooked Magic.encrypt&quot;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;    [+] 密钥 (bArr2) 原始字节: &quot;</span> + keyBytes);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;    [+] 密钥 (bArr2) 字符串形式: &quot;</span> + keyStr);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用原函数</span></span><br><span class="line">        <span class="keyword">var</span> result = <span class="variable language_">this</span>.<span class="title function_">encrypt</span>(bArr, bArr2);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>如图</p><p><img src="/2025/10/15/WMCTF2025-Want2BecomeMagicalGirl%20%E5%A4%8D%E7%8E%B0/image-20251015151810179.png" alt="image-20251015151810179"></p><p>最后我们的 exp 如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> struct</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">to_uint32_list</span>(<span class="params">data, include_length</span>):</span><br><span class="line">    n = <span class="built_in">len</span>(data) // <span class="number">4</span></span><br><span class="line">    v = <span class="built_in">list</span>(struct.unpack(<span class="string">&#x27;&lt;&#x27;</span> + <span class="string">&#x27;I&#x27;</span> * n, data[:n*<span class="number">4</span>]))  <span class="comment"># 保持你原先的小端</span></span><br><span class="line">    <span class="keyword">if</span> include_length:</span><br><span class="line">        v.append(<span class="built_in">len</span>(data))</span><br><span class="line">    <span class="keyword">return</span> v</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">to_bytes</span>(<span class="params">v, include_length</span>):</span><br><span class="line">    n = <span class="built_in">len</span>(v)</span><br><span class="line">    <span class="keyword">if</span> include_length:</span><br><span class="line">        m = v[-<span class="number">1</span>]</span><br><span class="line">        n = n - <span class="number">1</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        m = n &lt;&lt; <span class="number">2</span></span><br><span class="line">    <span class="keyword">return</span> struct.pack(<span class="string">&#x27;&lt;&#x27;</span> + <span class="string">&#x27;I&#x27;</span> * n, *v)[:m]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">xxtea_decrypt</span>(<span class="params">v, key</span>):</span><br><span class="line">    n = <span class="built_in">len</span>(v)</span><br><span class="line">    <span class="keyword">if</span> n &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span> v</span><br><span class="line">    delta = <span class="number">0x9e3779b9</span></span><br><span class="line">    rounds = <span class="number">6</span> + <span class="number">52</span> * n</span><br><span class="line">    s = (rounds * delta) &amp; <span class="number">0xffffffff</span></span><br><span class="line">    y = v[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">while</span> s:</span><br><span class="line">        e = (s &gt;&gt; <span class="number">2</span>) &amp; <span class="number">3</span></span><br><span class="line">        <span class="keyword">for</span> p <span class="keyword">in</span> <span class="built_in">range</span>(n - <span class="number">1</span>, <span class="number">0</span>, -<span class="number">1</span>):</span><br><span class="line">            z = v[p - <span class="number">1</span>]</span><br><span class="line">            mx = (((z &gt;&gt; <span class="number">5</span>) ^ (y &gt;&gt; <span class="number">2</span>)) + ((y &gt;&gt; <span class="number">3</span>) ^ (z &gt;&gt; <span class="number">4</span>))) ^ ((s ^ y) + (key[(p &amp; <span class="number">3</span>) ^ e] ^ z))</span><br><span class="line">            v[p] = (v[p] - mx) &amp; <span class="number">0xffffffff</span></span><br><span class="line">            y = v[p]</span><br><span class="line">        z = v[-<span class="number">1</span>]</span><br><span class="line">        mx = (((z &gt;&gt; <span class="number">5</span>) ^ (y &gt;&gt; <span class="number">2</span>)) + ((y &gt;&gt; <span class="number">3</span>) ^ (z &gt;&gt; <span class="number">4</span>))) ^ ((s ^ y) + (key[(<span class="number">0</span> &amp; <span class="number">3</span>) ^ e] ^ z))</span><br><span class="line">        v[<span class="number">0</span>] = (v[<span class="number">0</span>] - mx) &amp; <span class="number">0xffffffff</span></span><br><span class="line">        y = v[<span class="number">0</span>]</span><br><span class="line">        s = (s - delta) &amp; <span class="number">0xffffffff</span></span><br><span class="line">    <span class="keyword">return</span> v</span><br><span class="line"></span><br><span class="line">sbox = [ </span><br><span class="line">    <span class="number">0x20</span>,<span class="number">0x7b</span>,<span class="number">0x18</span>,<span class="number">0xa7</span>,<span class="number">0x42</span>,<span class="number">0x44</span>,<span class="number">0xd7</span>,<span class="number">0x4a</span>,<span class="number">0xcd</span>,<span class="number">0x32</span>,<span class="number">0xd1</span>,<span class="number">0xec</span>,<span class="number">0xf3</span>,<span class="number">0x81</span>,<span class="number">0xa5</span>,<span class="number">0x89</span>,</span><br><span class="line">    <span class="number">0x0e</span>,<span class="number">0x91</span>,<span class="number">0x4b</span>,<span class="number">0xf0</span>,<span class="number">0xe9</span>,<span class="number">0x5d</span>,<span class="number">0x8d</span>,<span class="number">0xf5</span>,<span class="number">0x46</span>,<span class="number">0xfc</span>,<span class="number">0x31</span>,<span class="number">0x36</span>,<span class="number">0xb6</span>,<span class="number">0xac</span>,<span class="number">0x9b</span>,<span class="number">0xb9</span>,</span><br><span class="line">    <span class="number">0x26</span>,<span class="number">0x09</span>,<span class="number">0xe6</span>,<span class="number">0x40</span>,<span class="number">0xd4</span>,<span class="number">0xb0</span>,<span class="number">0x51</span>,<span class="number">0x4f</span>,<span class="number">0x9c</span>,<span class="number">0x3e</span>,<span class="number">0xe7</span>,<span class="number">0x79</span>,<span class="number">0x30</span>,<span class="number">0x88</span>,<span class="number">0xb1</span>,<span class="number">0x3c</span>,</span><br><span class="line">    <span class="number">0x7a</span>,<span class="number">0x5c</span>,<span class="number">0xd3</span>,<span class="number">0x14</span>,<span class="number">0x5a</span>,<span class="number">0xab</span>,<span class="number">0x56</span>,<span class="number">0xc0</span>,<span class="number">0x04</span>,<span class="number">0x29</span>,<span class="number">0xd0</span>,<span class="number">0x3b</span>,<span class="number">0x1f</span>,<span class="number">0xf9</span>,<span class="number">0xa3</span>,<span class="number">0x57</span>,</span><br><span class="line">    <span class="number">0x00</span>,<span class="number">0x8a</span>,<span class="number">0x84</span>,<span class="number">0x16</span>,<span class="number">0xf4</span>,<span class="number">0x1a</span>,<span class="number">0xea</span>,<span class="number">0x64</span>,<span class="number">0xa6</span>,<span class="number">0xd6</span>,<span class="number">0x2e</span>,<span class="number">0xbe</span>,<span class="number">0x2f</span>,<span class="number">0x17</span>,<span class="number">0xc4</span>,<span class="number">0xe0</span>,</span><br><span class="line">    <span class="number">0x1e</span>,<span class="number">0x02</span>,<span class="number">0x3a</span>,<span class="number">0x22</span>,<span class="number">0x8f</span>,<span class="number">0x9f</span>,<span class="number">0xcb</span>,<span class="number">0xa8</span>,<span class="number">0x2c</span>,<span class="number">0x67</span>,<span class="number">0x34</span>,<span class="number">0x25</span>,<span class="number">0xd5</span>,<span class="number">0xff</span>,<span class="number">0xef</span>,<span class="number">0xf6</span>,</span><br><span class="line">    <span class="number">0xe2</span>,<span class="number">0xaa</span>,<span class="number">0xd9</span>,<span class="number">0x72</span>,<span class="number">0xfe</span>,<span class="number">0xce</span>,<span class="number">0xa1</span>,<span class="number">0x78</span>,<span class="number">0x85</span>,<span class="number">0x96</span>,<span class="number">0x2a</span>,<span class="number">0x77</span>,<span class="number">0xca</span>,<span class="number">0xc1</span>,<span class="number">0x37</span>,<span class="number">0x74</span>,</span><br><span class="line">    <span class="number">0xa2</span>,<span class="number">0x5e</span>,<span class="number">0x6c</span>,<span class="number">0xfd</span>,<span class="number">0xb8</span>,<span class="number">0x4d</span>,<span class="number">0x7d</span>,<span class="number">0x70</span>,<span class="number">0xb3</span>,<span class="number">0xdd</span>,<span class="number">0xcf</span>,<span class="number">0x71</span>,<span class="number">0x73</span>,<span class="number">0x61</span>,<span class="number">0xf8</span>,<span class="number">0x19</span>,</span><br><span class="line">    <span class="number">0x48</span>,<span class="number">0xe3</span>,<span class="number">0x63</span>,<span class="number">0x33</span>,<span class="number">0x3d</span>,<span class="number">0x15</span>,<span class="number">0xae</span>,<span class="number">0x98</span>,<span class="number">0xe5</span>,<span class="number">0x80</span>,<span class="number">0xbd</span>,<span class="number">0xbc</span>,<span class="number">0x82</span>,<span class="number">0xc6</span>,<span class="number">0x94</span>,<span class="number">0x01</span>,</span><br><span class="line">    <span class="number">0xe4</span>,<span class="number">0xde</span>,<span class="number">0x06</span>,<span class="number">0x50</span>,<span class="number">0x95</span>,<span class="number">0xdf</span>,<span class="number">0x47</span>,<span class="number">0xf7</span>,<span class="number">0x90</span>,<span class="number">0x8b</span>,<span class="number">0x45</span>,<span class="number">0x9a</span>,<span class="number">0x6e</span>,<span class="number">0x07</span>,<span class="number">0xad</span>,<span class="number">0x1c</span>,</span><br><span class="line">    <span class="number">0x35</span>,<span class="number">0x83</span>,<span class="number">0x68</span>,<span class="number">0x03</span>,<span class="number">0x6f</span>,<span class="number">0x5b</span>,<span class="number">0xb7</span>,<span class="number">0xfb</span>,<span class="number">0x1d</span>,<span class="number">0xc5</span>,<span class="number">0x10</span>,<span class="number">0x7c</span>,<span class="number">0xd8</span>,<span class="number">0x6a</span>,<span class="number">0xcc</span>,<span class="number">0x69</span>,</span><br><span class="line">    <span class="number">0x8e</span>,<span class="number">0x24</span>,<span class="number">0x4c</span>,<span class="number">0x39</span>,<span class="number">0xb4</span>,<span class="number">0xa0</span>,<span class="number">0x0b</span>,<span class="number">0x52</span>,<span class="number">0xe8</span>,<span class="number">0xa9</span>,<span class="number">0xb2</span>,<span class="number">0x8c</span>,<span class="number">0x0a</span>,<span class="number">0xbf</span>,<span class="number">0x28</span>,<span class="number">0x86</span>,</span><br><span class="line">    <span class="number">0x6d</span>,<span class="number">0xaf</span>,<span class="number">0xda</span>,<span class="number">0x41</span>,<span class="number">0xfa</span>,<span class="number">0x75</span>,<span class="number">0xb5</span>,<span class="number">0x43</span>,<span class="number">0xc3</span>,<span class="number">0x60</span>,<span class="number">0x62</span>,<span class="number">0x2b</span>,<span class="number">0x55</span>,<span class="number">0xf2</span>,<span class="number">0x9e</span>,<span class="number">0x2d</span>,</span><br><span class="line">    <span class="number">0x12</span>,<span class="number">0x23</span>,<span class="number">0x0d</span>,<span class="number">0xdb</span>,<span class="number">0x6b</span>,<span class="number">0xc7</span>,<span class="number">0x38</span>,<span class="number">0x7f</span>,<span class="number">0x5f</span>,<span class="number">0x97</span>,<span class="number">0x08</span>,<span class="number">0xed</span>,<span class="number">0xe1</span>,<span class="number">0xbb</span>,<span class="number">0xee</span>,<span class="number">0x9d</span>,</span><br><span class="line">    <span class="number">0xd2</span>,<span class="number">0x92</span>,<span class="number">0x49</span>,<span class="number">0x3f</span>,<span class="number">0xdc</span>,<span class="number">0x58</span>,<span class="number">0x87</span>,<span class="number">0xc2</span>,<span class="number">0xba</span>,<span class="number">0x99</span>,<span class="number">0xc9</span>,<span class="number">0x4e</span>,<span class="number">0xf1</span>,<span class="number">0x21</span>,<span class="number">0xeb</span>,<span class="number">0x13</span>,</span><br><span class="line">    <span class="number">0x65</span>,<span class="number">0x59</span>,<span class="number">0x76</span>,<span class="number">0x0c</span>,<span class="number">0xc8</span>,<span class="number">0x05</span>,<span class="number">0xa4</span>,<span class="number">0x54</span>,<span class="number">0x93</span>,<span class="number">0x1b</span>,<span class="number">0x66</span>,<span class="number">0x11</span>,<span class="number">0x27</span>,<span class="number">0x53</span>,<span class="number">0x7e</span>,<span class="number">0x0f</span></span><br><span class="line">]</span><br><span class="line">inv_sbox = [ </span><br><span class="line">    <span class="number">0x40</span>,<span class="number">0x8f</span>,<span class="number">0x51</span>,<span class="number">0xa3</span>,<span class="number">0x38</span>,<span class="number">0xf5</span>,<span class="number">0x92</span>,<span class="number">0x9d</span>,<span class="number">0xda</span>,<span class="number">0x21</span>,<span class="number">0xbc</span>,<span class="number">0xb6</span>,<span class="number">0xf3</span>,<span class="number">0xd2</span>,<span class="number">0x10</span>,<span class="number">0xff</span>,</span><br><span class="line">    <span class="number">0xaa</span>,<span class="number">0xfb</span>,<span class="number">0xd0</span>,<span class="number">0xef</span>,<span class="number">0x33</span>,<span class="number">0x85</span>,<span class="number">0x43</span>,<span class="number">0x4d</span>,<span class="number">0x02</span>,<span class="number">0x7f</span>,<span class="number">0x45</span>,<span class="number">0xf9</span>,<span class="number">0x9f</span>,<span class="number">0xa8</span>,<span class="number">0x50</span>,<span class="number">0x3c</span>,</span><br><span class="line">    <span class="number">0x00</span>,<span class="number">0xed</span>,<span class="number">0x53</span>,<span class="number">0xd1</span>,<span class="number">0xb1</span>,<span class="number">0x5b</span>,<span class="number">0x20</span>,<span class="number">0xfc</span>,<span class="number">0xbe</span>,<span class="number">0x39</span>,<span class="number">0x6a</span>,<span class="number">0xcb</span>,<span class="number">0x58</span>,<span class="number">0xcf</span>,<span class="number">0x4a</span>,<span class="number">0x4c</span>,</span><br><span class="line">    <span class="number">0x2c</span>,<span class="number">0x1a</span>,<span class="number">0x09</span>,<span class="number">0x83</span>,<span class="number">0x5a</span>,<span class="number">0xa0</span>,<span class="number">0x1b</span>,<span class="number">0x6e</span>,<span class="number">0xd6</span>,<span class="number">0xb3</span>,<span class="number">0x52</span>,<span class="number">0x3b</span>,<span class="number">0x2f</span>,<span class="number">0x84</span>,<span class="number">0x29</span>,<span class="number">0xe3</span>,</span><br><span class="line">    <span class="number">0x23</span>,<span class="number">0xc3</span>,<span class="number">0x04</span>,<span class="number">0xc7</span>,<span class="number">0x05</span>,<span class="number">0x9a</span>,<span class="number">0x18</span>,<span class="number">0x96</span>,<span class="number">0x80</span>,<span class="number">0xe2</span>,<span class="number">0x07</span>,<span class="number">0x12</span>,<span class="number">0xb2</span>,<span class="number">0x75</span>,<span class="number">0xeb</span>,<span class="number">0x27</span>,</span><br><span class="line">    <span class="number">0x93</span>,<span class="number">0x26</span>,<span class="number">0xb7</span>,<span class="number">0xfd</span>,<span class="number">0xf7</span>,<span class="number">0xcc</span>,<span class="number">0x36</span>,<span class="number">0x3f</span>,<span class="number">0xe5</span>,<span class="number">0xf1</span>,<span class="number">0x34</span>,<span class="number">0xa5</span>,<span class="number">0x31</span>,<span class="number">0x15</span>,<span class="number">0x71</span>,<span class="number">0xd8</span>,</span><br><span class="line">    <span class="number">0xc9</span>,<span class="number">0x7d</span>,<span class="number">0xca</span>,<span class="number">0x82</span>,<span class="number">0x47</span>,<span class="number">0xf0</span>,<span class="number">0xfa</span>,<span class="number">0x59</span>,<span class="number">0xa2</span>,<span class="number">0xaf</span>,<span class="number">0xad</span>,<span class="number">0xd4</span>,<span class="number">0x72</span>,<span class="number">0xc0</span>,<span class="number">0x9c</span>,<span class="number">0xa4</span>,</span><br><span class="line">    <span class="number">0x77</span>,<span class="number">0x7b</span>,<span class="number">0x63</span>,<span class="number">0x7c</span>,<span class="number">0x6f</span>,<span class="number">0xc5</span>,<span class="number">0xf2</span>,<span class="number">0x6b</span>,<span class="number">0x67</span>,<span class="number">0x2b</span>,<span class="number">0x30</span>,<span class="number">0x01</span>,<span class="number">0xab</span>,<span class="number">0x76</span>,<span class="number">0xfe</span>,<span class="number">0xd7</span>,</span><br><span class="line">    <span class="number">0x89</span>,<span class="number">0x0d</span>,<span class="number">0x8c</span>,<span class="number">0xa1</span>,<span class="number">0x42</span>,<span class="number">0x68</span>,<span class="number">0xbf</span>,<span class="number">0xe6</span>,<span class="number">0x2d</span>,<span class="number">0x0f</span>,<span class="number">0x41</span>,<span class="number">0x99</span>,<span class="number">0xbb</span>,<span class="number">0x16</span>,<span class="number">0xb0</span>,<span class="number">0x54</span>,</span><br><span class="line">    <span class="number">0x98</span>,<span class="number">0x11</span>,<span class="number">0xe1</span>,<span class="number">0xf8</span>,<span class="number">0x8e</span>,<span class="number">0x94</span>,<span class="number">0x69</span>,<span class="number">0xd9</span>,<span class="number">0x87</span>,<span class="number">0xe9</span>,<span class="number">0x9b</span>,<span class="number">0x1e</span>,<span class="number">0x28</span>,<span class="number">0xdf</span>,<span class="number">0xce</span>,<span class="number">0x55</span>,</span><br><span class="line">    <span class="number">0xb5</span>,<span class="number">0x66</span>,<span class="number">0x70</span>,<span class="number">0x3e</span>,<span class="number">0xf6</span>,<span class="number">0x0e</span>,<span class="number">0x48</span>,<span class="number">0x03</span>,<span class="number">0x57</span>,<span class="number">0xb9</span>,<span class="number">0x61</span>,<span class="number">0x35</span>,<span class="number">0x1d</span>,<span class="number">0x9e</span>,<span class="number">0x86</span>,<span class="number">0xc1</span>,</span><br><span class="line">    <span class="number">0x25</span>,<span class="number">0x2e</span>,<span class="number">0xba</span>,<span class="number">0x78</span>,<span class="number">0xb4</span>,<span class="number">0xc6</span>,<span class="number">0x1c</span>,<span class="number">0xa6</span>,<span class="number">0x74</span>,<span class="number">0x1f</span>,<span class="number">0xe8</span>,<span class="number">0xdd</span>,<span class="number">0x8b</span>,<span class="number">0x8a</span>,<span class="number">0x4b</span>,<span class="number">0xbd</span>,</span><br><span class="line">    <span class="number">0x37</span>,<span class="number">0x6d</span>,<span class="number">0xe7</span>,<span class="number">0xc8</span>,<span class="number">0x4e</span>,<span class="number">0xa9</span>,<span class="number">0x8d</span>,<span class="number">0xd5</span>,<span class="number">0xf4</span>,<span class="number">0xea</span>,<span class="number">0x6c</span>,<span class="number">0x56</span>,<span class="number">0xae</span>,<span class="number">0x08</span>,<span class="number">0x65</span>,<span class="number">0x7a</span>,</span><br><span class="line">    <span class="number">0x3a</span>,<span class="number">0x0a</span>,<span class="number">0xe0</span>,<span class="number">0x32</span>,<span class="number">0x24</span>,<span class="number">0x5c</span>,<span class="number">0x49</span>,<span class="number">0x06</span>,<span class="number">0xac</span>,<span class="number">0x62</span>,<span class="number">0xc2</span>,<span class="number">0xd3</span>,<span class="number">0xe4</span>,<span class="number">0x79</span>,<span class="number">0x91</span>,<span class="number">0x95</span>,</span><br><span class="line">    <span class="number">0x4f</span>,<span class="number">0xdc</span>,<span class="number">0x60</span>,<span class="number">0x81</span>,<span class="number">0x90</span>,<span class="number">0x88</span>,<span class="number">0x22</span>,<span class="number">0x2a</span>,<span class="number">0xb8</span>,<span class="number">0x14</span>,<span class="number">0x46</span>,<span class="number">0xee</span>,<span class="number">0x0b</span>,<span class="number">0xdb</span>,<span class="number">0xde</span>,<span class="number">0x5e</span>,</span><br><span class="line">    <span class="number">0x13</span>,<span class="number">0xec</span>,<span class="number">0xcd</span>,<span class="number">0x0c</span>,<span class="number">0x44</span>,<span class="number">0x17</span>,<span class="number">0x5f</span>,<span class="number">0x97</span>,<span class="number">0x7e</span>,<span class="number">0x3d</span>,<span class="number">0xc4</span>,<span class="number">0xa7</span>,<span class="number">0x19</span>,<span class="number">0x73</span>,<span class="number">0x64</span>,<span class="number">0x5d</span></span><br><span class="line">]</span><br><span class="line">rcon_words = [<span class="number">0x01000000</span>,<span class="number">0x02000000</span>,<span class="number">0x04000000</span>,<span class="number">0x08000000</span>,<span class="number">0x10000000</span>,</span><br><span class="line">              <span class="number">0x20000000</span>,<span class="number">0x40000000</span>,<span class="number">0x80000000</span>,<span class="number">0x1B000000</span>,<span class="number">0x36000000</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">LOAD32H</span>(<span class="params">b, off</span>):</span><br><span class="line">    <span class="keyword">return</span> ((b[off] &lt;&lt; <span class="number">24</span>) | (b[off+<span class="number">1</span>] &lt;&lt; <span class="number">16</span>) | (b[off+<span class="number">2</span>] &lt;&lt; <span class="number">8</span>) | b[off+<span class="number">3</span>]) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">BYTE</span>(<span class="params">x, n</span>):  <span class="comment"># 低字节序提取第 n 个字节</span></span><br><span class="line">    <span class="keyword">return</span> (x &gt;&gt; (<span class="number">8</span> * n)) &amp; <span class="number">0xFF</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">MIX</span>(<span class="params">x</span>):</span><br><span class="line">    <span class="keyword">return</span> ((sbox[BYTE(x,<span class="number">2</span>)] &lt;&lt; <span class="number">24</span>) ^ (sbox[BYTE(x,<span class="number">1</span>)] &lt;&lt; <span class="number">16</span>) ^</span><br><span class="line">            (sbox[BYTE(x,<span class="number">0</span>)] &lt;&lt; <span class="number">8</span>) ^ (sbox[BYTE(x,<span class="number">3</span>)])) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">key_expansion_words</span>(<span class="params">key16</span>):</span><br><span class="line">    w = [<span class="number">0</span>]*<span class="number">44</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        w[i] = LOAD32H(key16, <span class="number">4</span>*i)</span><br><span class="line">    <span class="keyword">for</span> round_idx <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">        b = round_idx*<span class="number">4</span></span><br><span class="line">        w[b+<span class="number">4</span>] = (w[b] ^ MIX(w[b+<span class="number">3</span>]) ^ rcon_words[round_idx]) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line">        w[b+<span class="number">5</span>] = (w[b+<span class="number">1</span>] ^ w[b+<span class="number">4</span>]) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line">        w[b+<span class="number">6</span>] = (w[b+<span class="number">2</span>] ^ w[b+<span class="number">5</span>]) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line">        w[b+<span class="number">7</span>] = (w[b+<span class="number">3</span>] ^ w[b+<span class="number">6</span>]) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line">    <span class="keyword">return</span> w  </span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">make_dec_key_words</span>(<span class="params">eK</span>):</span><br><span class="line">    dK = [<span class="number">0</span>]*<span class="number">44</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">11</span>):</span><br><span class="line">        src = (<span class="number">10</span> - j) * <span class="number">4</span></span><br><span class="line">        dst = j * <span class="number">4</span></span><br><span class="line">        dK[dst:dst+<span class="number">4</span>] = eK[src:src+<span class="number">4</span>]</span><br><span class="line">    <span class="keyword">return</span> dK</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">load_state_array</span>(<span class="params">block16</span>):</span><br><span class="line">    state = [[<span class="number">0</span>]*<span class="number">4</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>)]  </span><br><span class="line">    idx = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> col <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        <span class="keyword">for</span> row <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">            state[row][col] = block16[idx]</span><br><span class="line">            idx += <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> state</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">store_state_array</span>(<span class="params">state</span>):</span><br><span class="line">    out = <span class="built_in">bytearray</span>(<span class="number">16</span>)</span><br><span class="line">    idx = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> col <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        <span class="keyword">for</span> row <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">            out[idx] = state[row][col]</span><br><span class="line">            idx += <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(out)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">add_round_key</span>(<span class="params">state, key_words4</span>):</span><br><span class="line">    <span class="keyword">for</span> col <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        w = key_words4[col]</span><br><span class="line">        <span class="keyword">for</span> row <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">            state[row][col] ^= BYTE(w, <span class="number">3</span> - row)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">ROR32</span>(<span class="params">x, n</span>):</span><br><span class="line">    n %= <span class="number">32</span></span><br><span class="line">    <span class="keyword">return</span> ((x &gt;&gt; n) | ((x &lt;&lt; (<span class="number">32</span> - n)) &amp; <span class="number">0xFFFFFFFF</span>)) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inv_shift_rows</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="keyword">for</span> row <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        x = ((state[row][<span class="number">0</span>] &lt;&lt; <span class="number">24</span>) | (state[row][<span class="number">1</span>] &lt;&lt; <span class="number">16</span>) | (state[row][<span class="number">2</span>] &lt;&lt; <span class="number">8</span>) | state[row][<span class="number">3</span>]) &amp; <span class="number">0xFFFFFFFF</span></span><br><span class="line">        x = ROR32(x, <span class="number">8</span> * row)</span><br><span class="line">        state[row][<span class="number">0</span>] = (x &gt;&gt; <span class="number">24</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">        state[row][<span class="number">1</span>] = (x &gt;&gt; <span class="number">16</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">        state[row][<span class="number">2</span>] = (x &gt;&gt; <span class="number">8</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">        state[row][<span class="number">3</span>] = x &amp; <span class="number">0xFF</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inv_sub_bytes</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">            state[i][j] = inv_sbox[state[i][j]]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">GMul</span>(<span class="params">u, v</span>):</span><br><span class="line">    p = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>):</span><br><span class="line">        <span class="keyword">if</span> u &amp; <span class="number">1</span>:</span><br><span class="line">            p ^= v</span><br><span class="line">        flag = v &amp; <span class="number">0x80</span></span><br><span class="line">        v = (v &lt;&lt; <span class="number">1</span>) &amp; <span class="number">0xFF</span></span><br><span class="line">        <span class="keyword">if</span> flag:</span><br><span class="line">            v ^= <span class="number">0x1B</span></span><br><span class="line">        u &gt;&gt;= <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> p</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">inv_mix_columns</span>(<span class="params">state</span>):</span><br><span class="line">    M = [</span><br><span class="line">        [<span class="number">0x0E</span>, <span class="number">0x0B</span>, <span class="number">0x0D</span>, <span class="number">0x09</span>],</span><br><span class="line">        [<span class="number">0x09</span>, <span class="number">0x0E</span>, <span class="number">0x0B</span>, <span class="number">0x0D</span>],</span><br><span class="line">        [<span class="number">0x0D</span>, <span class="number">0x09</span>, <span class="number">0x0E</span>, <span class="number">0x0B</span>],</span><br><span class="line">        [<span class="number">0x0B</span>, <span class="number">0x0D</span>, <span class="number">0x09</span>, <span class="number">0x0E</span>],</span><br><span class="line">    ]</span><br><span class="line">    tmp = [[state[i][j] <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>)] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>)]</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>):</span><br><span class="line">            state[i][j] = GMul(M[i][<span class="number">0</span>], tmp[<span class="number">0</span>][j]) ^ GMul(M[i][<span class="number">1</span>], tmp[<span class="number">1</span>][j]) ^ GMul(M[i][<span class="number">2</span>], tmp[<span class="number">2</span>][j]) ^ GMul(M[i][<span class="number">3</span>], tmp[<span class="number">3</span>][j])</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">aes_ecb_decrypt_all</span>(<span class="params">ciphertext: <span class="built_in">bytes</span>, key16: <span class="built_in">bytes</span></span>) -&gt; <span class="built_in">bytes</span>:</span><br><span class="line">    <span class="keyword">assert</span> <span class="built_in">len</span>(key16) == <span class="number">16</span></span><br><span class="line">    eK = key_expansion_words(key16)</span><br><span class="line">    dK = make_dec_key_words(eK)</span><br><span class="line"></span><br><span class="line">    out = <span class="built_in">bytearray</span>()</span><br><span class="line">    <span class="keyword">for</span> off <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(ciphertext), <span class="number">16</span>):</span><br><span class="line">        block = ciphertext[off:off+<span class="number">16</span>]</span><br><span class="line">        state = load_state_array(block)</span><br><span class="line"></span><br><span class="line">        rk_idx = <span class="number">0</span></span><br><span class="line">        add_round_key(state, dK[rk_idx:rk_idx+<span class="number">4</span>])</span><br><span class="line">        <span class="keyword">for</span> _<span class="built_in">round</span> <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">10</span>):</span><br><span class="line">            rk_idx += <span class="number">4</span></span><br><span class="line">            inv_shift_rows(state)</span><br><span class="line">            inv_sub_bytes(state)</span><br><span class="line">            inv_mix_columns(state)</span><br><span class="line">            add_round_key(state, dK[rk_idx:rk_idx+<span class="number">4</span>])</span><br><span class="line"></span><br><span class="line">        inv_shift_rows(state)</span><br><span class="line">        inv_sub_bytes(state)</span><br><span class="line">        add_round_key(state, dK[rk_idx+<span class="number">4</span>:rk_idx+<span class="number">8</span>])</span><br><span class="line"></span><br><span class="line">        out += store_state_array(state)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">bytes</span>(out)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    ciphertext_b64 = <span class="string">&quot;8sAFX45zT7uc0vSUyFNNly1h/d5zTt89tV3kcVr5P5n7lRKPyYtxg31zYNB2lPV0c5nf/x2/IK94XV9Ufs9XfaDG5IXxMlZy+Z2nE+ZZRFBSpMoKzQXfUq2TSjJJfQxV&quot;</span></span><br><span class="line">    ciphertext = base64.b64decode(ciphertext_b64)</span><br><span class="line">    key_bytes = <span class="string">b&quot;16929&quot;</span></span><br><span class="line">    key_bytes = (key_bytes + <span class="string">b&quot;\0&quot;</span>*<span class="number">16</span>)[:<span class="number">16</span>]</span><br><span class="line">    key = <span class="built_in">list</span>(struct.unpack(<span class="string">&#x27;&lt;4I&#x27;</span>, key_bytes))  </span><br><span class="line">    v = to_uint32_list(ciphertext, <span class="literal">False</span>)</span><br><span class="line">    v = xxtea_decrypt(v, key)</span><br><span class="line">    ciphertext2 = to_bytes(v, <span class="literal">False</span>)</span><br><span class="line">    ciphertext2 = <span class="built_in">bytes</span>.fromhex(ciphertext2.decode())</span><br><span class="line">    <span class="comment"># print(&quot;XXTEA 解密结果：&quot;, ciphertext2.hex())</span></span><br><span class="line">    aes_key = <span class="built_in">bytes</span>.fromhex(<span class="string">&quot;7a25c524c6334c30f362afac3f2303d5&quot;</span>)</span><br><span class="line">    plaintext = aes_ecb_decrypt_all(ciphertext2, aes_key)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;AES 解密后:&quot;</span>, plaintext)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"><span class="comment"># WMCTF&#123;I_R4@11y_w@n7_70 _84c0m4_@_m@gic@1_Gir1&#125;</span></span><br></pre></td></tr></table></figure><h4 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h4><p><a href="https://blog.wm-team.cn/index.php/archives/86/#Want2BecomeMagicalGirl">https://blog.wm-team.cn/index.php/archives/86/#Want2BecomeMagicalGirl</a></p><p><a href="https://pangbai.work/CTF/Reverse/Want2BecomeMagicalGirl/">https://pangbai.work/CTF/Reverse/Want2BecomeMagicalGirl/</a></p><p><a href="https://www.ctfiot.com/261979.html">https://www.ctfiot.com/261979.html</a></p>]]></content>
    
    
    <summary type="html">你也想成为魔法少女吗？</summary>
    
    
    
    
    <category term="Android" scheme="http://example.com/tags/Android/"/>
    
    <category term="CTF" scheme="http://example.com/tags/CTF/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="http://example.com/2025/10/06/hello-world/"/>
    <id>http://example.com/2025/10/06/hello-world/</id>
    <published>2025-10-06T13:42:23.898Z</published>
    <updated>2025-10-08T14:19:35.977Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</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">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</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">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</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">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</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">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
    
    
    <summary type="html">Hello , I am //*梦。</summary>
    
    
    
    <category term="学习笔记" scheme="http://example.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="Hexo" scheme="http://example.com/tags/Hexo/"/>
    
    <category term="博客" scheme="http://example.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
</feed>
