<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>修行的技术阁</title>
  
  
  <link href="https://mrxiuxing.com/atom.xml" rel="self"/>
  
  <link href="https://mrxiuxing.com/"/>
  <updated>2023-02-12T02:14:57.000Z</updated>
  <id>https://mrxiuxing.com/</id>
  
  <author>
    <name>Mr.xiuxing</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>mouyandianying signKey 分析及页面字体反爬</title>
    <link href="https://mrxiuxing.com/posts/11adbe97.html"/>
    <id>https://mrxiuxing.com/posts/11adbe97.html</id>
    <published>2023-02-12T02:14:57.000Z</published>
    <updated>2023-02-12T02:14:57.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Follow your instincts and do what you think right. ——《吸血鬼日记》</center></strong></em><br><em><strong><center>听从自己的直觉，做正确的事。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cubWFveWFuLmNvbS9maWxtcy8xMzY2Njk2</code></pre><p>请求加密<br><img src="/posts/11adbe97/001.png" alt></p><p>字体加密<br><img src="/posts/11adbe97/002.png" alt></p><h2 id="JS-分析">JS 分析</h2><p>  全局搜索大法，搜索关键词 signKey，最终定位到下图所示位置</p><p><img src="/posts/11adbe97/003.png" alt></p><p>  我们看一下这一块需要的参数都是什么。可以看到每次的加密结果一致，参数 u 是：请求方式 + 时间戳 + UA + 请求参数，参数 f 是个字符串（经多次测试为定值）</p><p><img src="/posts/11adbe97/004.png" alt></p><p>  再来详细分析下加密值，这是由数字和字母的32位组合，那么现在有理由怀疑这是 md5，让加密方法对字符串 ‘1’ 进行加密，熟悉 md5 的小伙伴应该已经知道这是什么了，这是标准的 md5，没有经过魔改。</p><p><img src="/posts/11adbe97/005.png" alt></p><p>  好家伙，既然这样的话，都不用扣 js 代码了，nice！😊，不过访问频繁的话会出现滑块验证码</p><p>运行效果<br><img src="/posts/11adbe97/006.png" alt></p><h2 id="字体分析">字体分析</h2><p>  字体文件每次请求都会变动，链接就在同一文件下，如下图：</p><p><img src="/posts/11adbe97/007.png" alt></p><p>  由于字体文件是动态的，所以就不能简单的做映射了，可以根据字体的字形绘制图片，然后使用 ddddocr 识别</p><p>运行效果<br><img src="/posts/11adbe97/009.png" alt></p><p><b>工具站点：</b><br><a href="https://font.qqe2.com">字体在线编辑器</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Follow your instincts and do what you think right. ——《吸血鬼日记》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;听从自己的直觉，做正确的事。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
    <category term="字体反爬" scheme="https://mrxiuxing.com/tags/%E5%AD%97%E4%BD%93%E5%8F%8D%E7%88%AC/"/>
    
  </entry>
  
  <entry>
    <title>mouhongshu Web 端 x-s 逆向分析</title>
    <link href="https://mrxiuxing.com/posts/c43cbcf6.html"/>
    <id>https://mrxiuxing.com/posts/c43cbcf6.html</id>
    <published>2023-02-11T06:34:39.000Z</published>
    <updated>2023-02-11T06:34:39.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>The most ordinary person can change the world. ——《神秘博士》</center></strong></em><br><em><strong><center>平凡之人亦能改变世界。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cueGlhb2hvbmdzaHUuY29tL2V4cGxvcmU=</code></pre><p><img src="/posts/c43cbcf6/001.png" alt></p><p>  可以明显看到有三个参数：分别是 x-b3-traceid（未知）、x-s（未知）、x-t（明显是时间戳）。将链接以 cURL 格式复制出来，经过测试只需要将 x-s 搞定就行了。</p><p><img src="/posts/c43cbcf6/002.png" alt></p><p><img src="/posts/c43cbcf6/003.png" alt></p><h2 id="JS-分析">JS 分析</h2><p>  全局搜索大法，搜索关键词 x-s，可以看到 sign 方法就是我们需要的。挨个看一下需要的参数，主要的两个参数是 t（请求路径）、i（请求参数）</p><p><img src="/posts/c43cbcf6/004.png" alt></p><p><img src="/posts/c43cbcf6/005.png" alt></p><p>  进入 sign 方法内部，在开头打个条件断点，取消掉其他断点，刷新页面，成功断上</p><p><img src="/posts/c43cbcf6/006.png" alt></p><p><img src="/posts/c43cbcf6/007.png" alt></p><p><img src="/posts/c43cbcf6/008.png" alt></p><p>  将 sign 方法完整扣下来，简单观察下发现了控制流平坦化，加密生成的地方在 case “9”:</p><p><img src="/posts/c43cbcf6/009.png" alt></p><p>  还缺几个方法分别是 a0_0x4dee00、a0_0x5c27、a0_0x543e，扣下来放在合适的位置。运行代码时出现错误</p><p><img src="/posts/c43cbcf6/010.png" alt></p><p>  分别在 WebStrom 和浏览器，在报错代码位置打上断点</p><p>WebStrom<br><img src="/posts/c43cbcf6/011.png" alt></p><p>浏览器<br><img src="/posts/c43cbcf6/012.png" alt></p><p>  可以看到参数 s 和 u 的值是一致的，但经过 cr 计算的方法是不一致的。继续跟下去，进入 cr 内部</p><p>WebStrom<br><img src="/posts/c43cbcf6/013.png" alt></p><p>浏览器<br><img src="/posts/c43cbcf6/014.png" alt></p><p>  在 cr 中 e、t、sr 三个参数的值是一致的，继续跟下去，进入到 a0_0x4dee00 内部</p><p>WebStrom<br><img src="/posts/c43cbcf6/015.png" alt></p><p>浏览器<br><img src="/posts/c43cbcf6/016.png" alt></p><p>  参数 e、t 的值是一致的，继续跟下去，进入到 a0_0x5c27 内部</p><p>WebStrom<br><img src="/posts/c43cbcf6/018.png" alt></p><p>浏览器<br><img src="/posts/c43cbcf6/017.png" alt></p><p>  参数 e、t 的值仍然是一致的，到了这里就要返回结果了，还差一个 r，可以看到 r 是由 a0_0x543e 得来的，这个方法已经补过了，我们还是分别看一下 a0_0x543e 在 WebStrom 和浏览器中的值</p><p>WebStrom<br><img src="/posts/c43cbcf6/020.png" alt></p><p>浏览器<br><img src="/posts/c43cbcf6/019.png" alt></p><p>  可以看到，就是这个大数组不一致，我们直接把 WebStrom 中的改为浏览器中的值（经多次测试，这个就是定值），在浏览器控制台可以使用 copy 方法复制下来。重新 debugger，发现结果已经和浏览器一致了</p><p><img src="/posts/c43cbcf6/021.png" alt></p><p>  执行代码，出现新的错误：ReferenceError:__webpack_require__ is not defined。</p><p><img src="/posts/c43cbcf6/022.png" alt></p><p>  浏览器中进行定位在合适的位置打上断点，在控制台执行 __webpack_require__，发现是方法，点击进入。</p><p><img src="/posts/c43cbcf6/023.png" alt></p><p>  一共两百来行，是一个自执行函数，第一个方法是 webpack 的加载器，将整个文件扣下来，放在 WebStrom 文件顶部</p><p><img src="/posts/c43cbcf6/024.png" alt></p><p>  执行代码，出现错误：ReferenceError: self is not defined，将 self 修改为 window，并在文件顶部新增一行 window = global;</p><p><img src="/posts/c43cbcf6/025.png" alt></p><p>  继续执行代码，出现错误：ReferenceError: esm_typeof is not defined，经常搞 js 逆向的小伙伴应该可以看出来，这一块代码是用来监测环境的</p><p><img src="/posts/c43cbcf6/026.png" alt></p><p>  在浏览器中执行查看结果，将浏览器的结果复制过来进行替换</p><p><img src="/posts/c43cbcf6/027.png" alt></p><p>  继续执行代码，出现错误：ReferenceError: esm_typeof is not defined，不过是在 case “7” 中，用上面同样的方法进行替换</p><p><img src="/posts/c43cbcf6/028.png" alt></p><p><img src="/posts/c43cbcf6/029.png" alt></p><p>  继续执行代码，出现错误：ReferenceError: MD5 is not defined，可以看到这里用到了 MD5。我们可以在浏览器控制台执行 MD5(‘1’) 与 在线 MD5(‘1’) 的值进行比较，如果一致的话就说明没有经过魔改，我已经试过了，这里并没有魔改，就不再展示具体过程了。</p><p><img src="/posts/c43cbcf6/030.png" alt></p><p>  在 node 中可以使用 crypto-js，如果需要安装的话可以执行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install crypto-js</span><br></pre></td></tr></table></figure><p>  在 js 文件顶部添加：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">Crypto</span> = <span class="built_in">require</span>(<span class="string">&quot;crypto-js&quot;</span>)</span><br></pre></td></tr></table></figure><p>  并将 case “9” 中的代码修改为如下代码：</p><p><img src="/posts/c43cbcf6/031.png" alt></p><p>  我们试一下到了这一步时，参数的值是否一致。分别在 WebStrom 和浏览器的 case “9” 代码内部打上断点</p><p><img src="/posts/c43cbcf6/032.png" alt></p><p>  可以看到除了参数 yr 以及 dr 的值不一致，其他都一样，参数 yr 是时间戳不用管，dr 是字符串，经过多次测试可以确定 dr 是定值，所以直接替换即可</p><p>WebStrom<br><img src="/posts/c43cbcf6/033.png" alt></p><p>浏览器<br><img src="/posts/c43cbcf6/034.png" alt></p><p>  将 case “8” 中的时间戳修改为与浏览器一致，再次执行代码，可以看到运行的结果与浏览器一致了</p><p><img src="/posts/c43cbcf6/035.png" alt></p><p><img src="/posts/c43cbcf6/036.png" alt></p><p>  使用 Python 执行，成功了！！！nice！！！😊</p><p><img src="/posts/c43cbcf6/037.png" alt></p><h2 id="写在最后">写在最后</h2><p>  总的来说难度不大，有坑点，但不算太坑，只要静下心来去做，肯定是可以搞得定的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;The most ordinary person can change the world. ——《神秘博士》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;平凡之人亦能改变世界。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
    <category term="webpack" scheme="https://mrxiuxing.com/tags/webpack/"/>
    
  </entry>
  
  <entry>
    <title>Wireshark 选择网卡时出错。 The capture session could not be initiated on capture device &quot;en0&quot; (You don&#39;t have permission to capture on that device).</title>
    <link href="https://mrxiuxing.com/posts/729d46f9.html"/>
    <id>https://mrxiuxing.com/posts/729d46f9.html</id>
    <published>2023-02-05T05:13:35.000Z</published>
    <updated>2023-02-05T05:13:35.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Sometimes your whole life boils down to one insane move.——《阿凡达》</center></strong></em><br><em><strong><center>人生的转机有时就得靠一次疯狂的举动。</center></strong></em></p><span id="more"></span><h2 id="前情提要">前情提要</h2><ul><li>系统 MacBook Air M1 2020 macOS Ventora 13.2</li><li>Wireshark 4.0.3</li></ul><p>  打开 Wireshark，在选择网卡时出现了错误：The capture session could not be initiated on capture device “en0” (You don’t have permission to capture on that device).</p><p><img src="/posts/729d46f9/001.png" alt></p><h2 id="解决办法">解决办法</h2><p>  错误提示为没有足够的权限，搜索下，可以用以下命令添加权限来解决</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod o+r /dev/bpf*</span><br></pre></td></tr></table></figure><p><img src="/posts/729d46f9/002.png" alt></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Sometimes your whole life boils down to one insane move.——《阿凡达》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;人生的转机有时就得靠一次疯狂的举动。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Wireshark" scheme="https://mrxiuxing.com/categories/Wireshark/"/>
    
    
    <category term="Wireshark" scheme="https://mrxiuxing.com/tags/Wireshark/"/>
    
  </entry>
  
  <entry>
    <title>当 Selenium 遇上 iframe 内嵌#document</title>
    <link href="https://mrxiuxing.com/posts/b9331965.html"/>
    <id>https://mrxiuxing.com/posts/b9331965.html</id>
    <published>2023-02-03T09:09:51.000Z</published>
    <updated>2023-02-03T09:09:51.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Don’t let anyone stop you from getting what you want. ——《灵书妙探》</center></strong></em><br><em><strong><center>别让任何人阻挡你得到想要的东西。</center></strong></em></p><span id="more"></span><h2 id="前情提要">前情提要</h2><p>  Selenium 在加载页面时，默认情况下的焦点保持在顶部窗口上，顶部窗口包含其他 iframe 和框架集。当你直接使用 Xpath 或其他方式进行定位时，就会找不到元素。</p><p><img src="/posts/b9331965/001.png" alt></p><h2 id="解决方法">解决方法</h2><p>  所以当我们需要与 iframe 中的元素交互时，必须先切换到目标 iframe 元素中，才能执行进一步的操作。我们可以通过三种方式切换到 iframe。</p><h3 id="通过-iframe-的-name-属性">通过 iframe 的 name 属性</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">driver.switch_to.frame(<span class="string">&quot;iframe_name&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="通过-iframe-的-id-属性">通过 iframe 的 id 属性</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">driver.switch_to.frame(<span class="string">&quot;iframe_id&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="通过-iframe-的-索引">通过 iframe 的 索引</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 第一个 iframe</span></span><br><span class="line">driver.switch_to.frame(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># 第二个 iframe</span></span><br><span class="line">driver.switch_to.frame(<span class="number">1</span>)</span><br></pre></td></tr></table></figure><h3 id="切回主框架">切回主框架</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">driver.switch_to.default_content()</span><br></pre></td></tr></table></figure><p>或</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">driver.switch_to.parent_frame()</span><br></pre></td></tr></table></figure><p>  切换 iframe 的更好方法是通过 WebDriverWait 等待预期 iframe 加载完毕。</p><h3 id="通过-iframe-的-id-属性-2">通过 iframe 的 id 属性</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WebDriverWait(driver, <span class="number">20</span>, <span class="number">0.5</span>).until(EC.frame_to_be_available_and_switch_to_it(By.ID, <span class="string">&quot;id_of_iframe&quot;</span>))</span><br></pre></td></tr></table></figure><h3 id="通过-iframe-的-name-属性-2">通过 iframe 的 name 属性</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WebDriverWait(driver, <span class="number">20</span>, <span class="number">0.5</span>).until(EC.frame_to_be_available_and_switch_to_it((By.NAME, <span class="string">&quot;name_of_iframe&quot;</span>)))</span><br></pre></td></tr></table></figure><h3 id="通过-Xpath-定位-iframe">通过 Xpath 定位 iframe</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WebDriverWait(driver, <span class="number">20</span>, <span class="number">0.5</span>).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, <span class="string">&quot;xpath_of_iframe&quot;</span>)))</span><br></pre></td></tr></table></figure><h3 id="通过-CSS-定位-iframe">通过 CSS 定位 iframe</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WebDriverWait(driver, <span class="number">20</span>, <span class="number">0.5</span>).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, <span class="string">&quot;css_of_iframe&quot;</span>)))</span><br></pre></td></tr></table></figure><p><b>参考文章：</b><br><a href="https://stackoverflow.com/questions/53203417/ways-to-deal-with-document-under-iframe/53276478#53276478">Ways to deal with #document under iframe</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Don’t let anyone stop you from getting what you want. ——《灵书妙探》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;别让任何人阻挡你得到想要的东西。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="自动化" scheme="https://mrxiuxing.com/categories/%E8%87%AA%E5%8A%A8%E5%8C%96/"/>
    
    
    <category term="Selenium" scheme="https://mrxiuxing.com/tags/Selenium/"/>
    
    <category term="自动化" scheme="https://mrxiuxing.com/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title>树莓派折腾记（三）-- 远程启动 Windows 电脑</title>
    <link href="https://mrxiuxing.com/posts/2895ed5a.html"/>
    <id>https://mrxiuxing.com/posts/2895ed5a.html</id>
    <published>2023-01-19T06:05:43.000Z</published>
    <updated>2023-01-19T06:05:43.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Trust your own strengths.——《指环王1:护花使者》</center></strong></em><br><em><strong><center>相信自己的力量。</center></strong></em></p><span id="more"></span><p><b>PS: 阅读这篇文章前请确认你的主板支持网络唤醒功能！！！</b></p><h2 id="前言">前言</h2><p>  最近搞了下 Selenium 自动化，需要将代码打包成可执行程序，搜索了一番发现 Pyinstaller 还挺好用，在我电脑上打包执行没什么问题。但是在进一步折腾的过程中发现，这玩意不太跨平台呀！我上班使用的电脑是 Mac M1 的，打包的可执行程序只能在我自己电脑上运行，搜索了一下发现，什么系统及架构打包的程序，只能在同样的系统及架构上运行，这就好尴尬了。</p><h2 id="解决方案">解决方案</h2><p>  研究了一下，发现可以用 etherwake 这个软件，在知晓目标机器的 MAC 地址的情况下，发送魔术封包(Magic Packet)就可以远程唤醒机器。很早之前我的树莓派就通过有公网 IP 的服务器做了内网穿透，家里的 Windows 台式机和树莓派在同一局域网内。那么只要把家中的电脑搞定相关配置，就可以远程通过树莓派唤醒 Windows，然后再由远程控制软件操作 Windows。<br><img src="/posts/2895ed5a/001.png" alt></p><h3 id="Windows-设置">Windows 设置</h3><p>  我的 Windows 台式机主板是某硕的 TUF GAMING B660M-PLUS D4（不同主板的设置不同，具体可度娘），在按下电源键后，狂点 DEL键 或 F2 键进入 BIOS，然后按 F7 进入高级，点击高级，往下滑找到<b>高级电源管理(APM)</b>，点击进入<br><img src="/posts/2895ed5a/002.png" alt></p><p>  将<b>由 PCI-E 设备唤醒</b>设置为 Enabled，然后按 F10 保存重启电脑进入系统</p><p><img src="/posts/2895ed5a/003.png" alt></p><p>  打开设备管理器，找到你的有线网卡，右键点击属性，点击高级</p><p><img src="/posts/2895ed5a/004.png" alt></p><p>  将<b>关机 网络唤醒</b>以及<b>魔术封包唤醒</b>开启</p><p><img src="/posts/2895ed5a/005.png" alt></p><p>  点击电源管理，勾选<b>允许此设备唤醒计算机</b></p><p><img src="/posts/2895ed5a/006.png" alt></p><p>  打开控制面板，找到电源选项，点击<b>选择电源按钮的功能</b></p><p><img src="/posts/2895ed5a/007.png" alt></p><p>  取消勾选<b>启用快速启动(推荐)</b></p><p><img src="/posts/2895ed5a/008.png" alt></p><h3 id="树莓派设置">树莓派设置</h3><p>  树莓派只需安装 etherwake 即可，我的树莓派系统是 Ubuntu，其他系统请自行度娘</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install etherwake</span><br></pre></td></tr></table></figure><p>  命令的格式为：sudo etherwake -i 本机网卡名称(树莓派) 目标MAC地址(Windows)</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo etherwake -i eth0 AA:FF:99:77:AA:66</span><br></pre></td></tr></table></figure><p>  如果以上顺利的话，你就可以通过树莓派远程开启你的电脑了。对了，不要忘记打开电源哦！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Trust your own strengths.——《指环王1:护花使者》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;相信自己的力量。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/tags/Linux/"/>
    
    <category term="Ubuntu" scheme="https://mrxiuxing.com/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>mouchezhijia CSS 伪元素混淆</title>
    <link href="https://mrxiuxing.com/posts/34456143.html"/>
    <id>https://mrxiuxing.com/posts/34456143.html</id>
    <published>2023-01-15T02:16:49.000Z</published>
    <updated>2023-01-15T02:16:49.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Self-improvement is a lifelong process. ——《我爱上的人是奇葩》</center></strong></em><br><em><strong><center>自我提升是一个终生的过程。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aHR0cHM6Ly9jYXIuYXV0b2hvbWUuY29tLmNuL2NvbmZpZy9zcGVjLzU3NjA2Lmh0bWw=</span><br></pre></td></tr></table></figure><p><img src="/posts/34456143/002.png" alt><br><img src="/posts/34456143/001.png" alt></p><p>  从上面两图可以看出，数据并不在标签中，而是在CSS伪元素的content属性中。</p><p><img src="/posts/34456143/003.png" alt></p><p>  看一下网页源码，未经过渲染的数据被包裹在 script 标签中，config 变量中，span 标签的 class 属性均以 hs_kw*_config* 的格式存在，这些并不是固定的，会随着网页刷新而变化，变量 keyLink 以及变量 option 类似，分别是 hs_kw*_baike* 和 hs_kw*_option*。</p><h2 id="JS-分析">JS 分析</h2><p>  全局搜索大法，经过多次测试，搜索 '_config，可以仅搜索到一个结果，这里便是生成属性的地方</p><p><img src="/posts/34456143/004.png" alt></p><p>  将这一行全部复制出来，去掉 script 标签，发现是两个自执行函数，分别是 _config 和 _option 相关的。</p><p><img src="/posts/34456143/005.png" alt></p><p>  将 _config 相关函数提出来，放到浏览器环境，在最开始加上 debugger，一步步调试，找到关键方法。</p><p><img src="/posts/34456143/006.png" alt></p><p><img src="/posts/34456143/007.png" alt></p><p>  通过断点打印出的结果可以明显看出，$InsertRuleRun$ 就是关键方法，17，39 这两个序号是不是很熟悉，回到文章开头，这两个都是 span 的属性值中，hs_kw 后面的数值，控制台上打印的值与页面展示的值一致。</p><p>  通过以上分析，我们可以使用正则将 script 标签包裹的 js 提取出来，然后稍微补一下环境，之后再将上面提到关键入口方法替换修改下，将结果添加到一个全局变量中，再根据变量与 js 的对应关系去替换 span 标签的内容，这样就能达到动态替换的效果。</p><h2 id="最终效果">最终效果</h2><p><img src="/posts/34456143/010.png" alt></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Self-improvement is a lifelong process. ——《我爱上的人是奇葩》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;自我提升是一个终生的过程。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
    <category term="CSS反爬" scheme="https://mrxiuxing.com/tags/CSS%E5%8F%8D%E7%88%AC/"/>
    
    <category term="伪元素混淆" scheme="https://mrxiuxing.com/tags/%E4%BC%AA%E5%85%83%E7%B4%A0%E6%B7%B7%E6%B7%86/"/>
    
  </entry>
  
  <entry>
    <title>mouzhunwang webpack 逆向分析</title>
    <link href="https://mrxiuxing.com/posts/65473a17.html"/>
    <id>https://mrxiuxing.com/posts/65473a17.html</id>
    <published>2022-11-06T07:48:19.000Z</published>
    <updated>2022-11-06T07:48:19.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>You konw more than you think you do. ——《了不起的麦瑟尔夫人》</center></strong></em><br><em><strong><center>你远比自己想象的要出色。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cua2Fuemh1bi5jb20vZmlybS93YWdlLzFuVl8ydGt+Lmh0bWw/a2E9Y29tLXNhbGFyeS1tb2R1bGUtZXhwb3Nl</code></pre><p><img src="/posts/65473a17/001.png" alt></p><p><img src="/posts/65473a17/002.png" alt></p><p>  经过分析可以明显看到该请求的参数以及响应都是加密过的。</p><h2 id="JS-分析">JS 分析</h2><p>  全局搜索大法，搜索关键词 kiv，最终仅有一个文件含有该关键词，进入该文件，再次搜索 kiv，发现共出现15次，好像有点多，继续往下看</p><p><img src="/posts/65473a17/003.png" alt></p><p>  在第二次出现 kiv 的地方发现了 400 和 200，这俩怎么看怎么像响应状态码，但是这里仅有参数 kiv 没有 参数 b，指定不是发请求的地方，继续往下看</p><p><img src="/posts/65473a17/004.png" alt></p><p>  经过多次寻找与测试，最终在1万3千来行附近找到了构造请求参数的地方，同样也是发请求的地方</p><p><img src="/posts/65473a17/005.png" alt></p><p>  为了方便调试，如下图所示打上一个条件断点</p><p><img src="/posts/65473a17/006.png" alt></p><p>  这个站点直接刷新是断不上断点的，需要点击其他板块，再点击需要分析的板块才能成功断上，如下图</p><p><img src="/posts/65473a17/007.png" alt></p><h3 id="参数-b-和-kiv">参数 b 和 kiv</h3><p>  通过上图我们可以看到参数 kiv 就是 o，参数 b 就是 t，接下来我们挨个分析。</p><p><img src="/posts/65473a17/008.png" alt></p><p>  将关键代码扣下来，精简一下</p><p><img src="/posts/65473a17/009.png" alt></p><p>  可以看到 t 的生成需要 o，而 o 是由 P._A() 生成的。在控制台输出测试一下，发现每次执行的结果都不一样，P._A 这个方法很好扣，但是我们需要的不止这一点，因为不管是 o 的生成还是 t 的生成都需要 P，那我们往上找找 P 是怎么来的</p><p><img src="/posts/65473a17/010.png" alt></p><p>  P 是由 n(60591) 生成的，在这个位置打个断点刷新一下，跟进去</p><p><img src="/posts/65473a17/011.png" alt></p><p>   发现进入到了另一个 js 文件，这个文件仅有一百来行，加载器就在这，将其全部扣下里放进编辑器里</p><p><img src="/posts/65473a17/012.png" alt></p><p><img src="/posts/65473a17/013.png" alt></p><p>   将之前第一次分析的 js 文件整个扣下来，放到编辑器另一个文件中，折叠一下代码，有11万行</p><p><img src="/posts/65473a17/014.png" alt></p><p>  掐头去尾将大括号里的所有模块都扣下来，放到加载器中，如下图，一定不要忘了大括号</p><p><img src="/posts/65473a17/015.png" alt></p><p>  接下来我们需要修改下这个自执行函数，因为我们需要在自执行函数外部调用，所以需要声明一个全局变量 get_P，并且自执行函数里的一些方法也都属于 t，所以在函数最后新增一行，在开头添加一个参数 r，并将在函数中声明的一些变量修改，如下图</p><p><img src="/posts/65473a17/022.png" alt></p><p>  运行一下，报错了 self is not defined，定位到错误的位置，将 self 修改为 window，并在最开头声明一个 window</p><p><img src="/posts/65473a17/017.png" alt></p><p><img src="/posts/65473a17/018.png" alt></p><p>  再次运行，报错 P is not defined，利用全局变量 get_P 去声明一下 P</p><p><img src="/posts/65473a17/019.png" alt></p><p><img src="/posts/65473a17/020.png" alt></p><p>  再次运行，打印参数 o，可以正常输出了。接下来分析 t，参数 t 还需要一个参数 n，回到控制台重新断点</p><p><img src="/posts/65473a17/023.png" alt></p><p>  执行到合适的位置，在控制台查看，发现 encCompanyId 的值有点眼熟，然后你抬头看了眼链接，😯哦！原来在链接里</p><p><img src="/posts/65473a17/024.png" alt></p><p>  然后发现代码少扣了一行，尴尬😅</p><p><img src="/posts/65473a17/025.png" alt></p><p>  将这行代码扣下来精简一下，然后打印，正常输出</p><p><img src="/posts/65473a17/026.png" alt></p><p>  通过 Postman 模拟一下请求，响应状态码 200 且有响应内容</p><p><img src="/posts/65473a17/027.png" alt></p><h3 id="响应加密">响应加密</h3><p>  还记得上面在搜索 kiv 时，疑似状态码的地方吗！在这打上断点，成功断上</p><p><img src="/posts/65473a17/028.png" alt></p><p>  经过调试，发现在执行到 JSON.parse(t) 的时候响应明文就出来了，并且这边也有一个 P</p><p><img src="/posts/65473a17/029.png" alt></p><p>  将关键代码扣下来精简一下，上图中的 e.data 就是加密的响应，kiv 和上面生成的一致</p><p><img src="/posts/65473a17/030.png" alt></p><h2 id="精简代码">精简代码</h2><p>  跟到上面这一步，修改一下就已经可以用 Python 调用了，但是这11万行实在是太多了，不优雅</p><p>  在代码开头声明两个全局变量，并在加载器新增如下图代码</p><p><img src="/posts/65473a17/031.png" alt></p><p>  在代码尾部加上一个 debugger，并将加解密代码放开，保证都会运行</p><p><img src="/posts/65473a17/032.png" alt></p><p>  执行调试，并将控制台输出的内容清空，输入 codes 然后回车，然后将输出的内容全部复制下来，替换到自执行函数中</p><p><img src="/posts/65473a17/033.png" alt></p><p>  最后一大堆模块就剩两个了，再次运行，结果正常。11万行就剩下5千来行了，nice！</p><p><img src="/posts/65473a17/034.png" alt></p><h2 id="写在最后">写在最后</h2><p>  至此整个加密分析与 webpack 加密代码扣取结束，将加解密逻辑封装一下就可以使用了，你要是想更优雅，可以考虑将整体逻辑使用 Python 或其他语言复现。如果觉得这篇文章有不足之处，欢迎您的留言。</p><p><b>参考：</b><br><a href="https://app.yinxiang.com/fx/970ae39c-9964-4aae-aa96-7e81fee4ef8f">JavaScript之webpack加密代码扣取</a><br><a href="https://blog.csdn.net/qq_37438485/article/details/119965300">webpack导出方法 - HKEN_</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;You konw more than you think you do. ——《了不起的麦瑟尔夫人》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;你远比自己想象的要出色。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
    <category term="webpack" scheme="https://mrxiuxing.com/tags/webpack/"/>
    
  </entry>
  
  <entry>
    <title>moumouhu x-zse-96 加密逆向 V3.0 算法还原</title>
    <link href="https://mrxiuxing.com/posts/d7c81aca.html"/>
    <id>https://mrxiuxing.com/posts/d7c81aca.html</id>
    <published>2022-09-25T11:15:55.000Z</published>
    <updated>2022-09-25T11:15:55.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>You can’t just walk away from things. ——《夜行动物》</center></strong></em><br><em><strong><center>逃避解决不了问题。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cuemhpaHUuY29tL3NlYXJjaD90eXBlPWNvbnRlbnQmcT1weXRob24=</code></pre><p><img src="/posts/d7c81aca/001.png" alt></p><h2 id="JS-分析">JS 分析</h2><p>  全局搜索大法，搜索关键词 x-zse-96，只有一个文件中包含该关键词，该文件中共搜索到2条匹配结果，全部在合适的位置打上断点，最终在12685处成功断上。</p><p><img src="/posts/d7c81aca/002.png" alt></p><p>  通过上图可以看出加密就是 T 方法中 signature 的值</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">signature</span>: (<span class="number">0</span>,<span class="title function_">F</span>(r).<span class="property">encrypt</span>)(<span class="title function_">f</span>()(s))</span><br></pre></td></tr></table></figure><p>  简化一下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">signature</span>: <span class="title function_">F</span>(r).<span class="title function_">encrypt</span>(<span class="title function_">f</span>()(s))</span><br></pre></td></tr></table></figure><p>  在合适的位置打上断点刷新页面，成功断上后在控制台观察</p><p><img src="/posts/d7c81aca/003.png" alt></p><p><img src="/posts/d7c81aca/004.png" alt></p><p><b>接下来让我们一步步分析</b></p><h3 id="s">s</h3><p>  回到源代码处，向上观察</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">i = n.<span class="property">zse93</span>  <span class="comment">// 固定值</span></span><br><span class="line">o = n.<span class="property">dc0</span>  <span class="comment">// cookie 中 dc0 的 值</span></span><br><span class="line">a = n.<span class="property">xZst81</span> <span class="comment">// x-zst-81 首页可为空</span></span><br><span class="line">c = <span class="title function_">H</span>(e) <span class="comment">// 请求链接</span></span><br><span class="line">u = <span class="title function_">q</span>(t)  <span class="comment">// &quot;&quot;</span></span><br><span class="line">s = [i, c, o, <span class="title function_">W</span>(u) &amp;&amp; u, a].<span class="title function_">filter</span>(<span class="title class_">Boolean</span>).<span class="title function_">join</span>(<span class="string">&quot;+&quot;</span>);  <span class="comment">// 拼接</span></span><br></pre></td></tr></table></figure><p>  因为不止一个请求通过该加密，所以我们在 c = H(e) 打上要分析链接的条件断点，方便后续调试</p><p><img src="/posts/d7c81aca/005.png" alt></p><p>  取消掉其他断点，仅保留该条件断点刷新页面，成功断上；单步执行到 s 处，然后在控制台分析</p><p><img src="/posts/d7c81aca/006.png" alt></p><p><img src="/posts/d7c81aca/007.png" alt></p><p>  经过多次调试得出 s = 固定值 + 请求链接 + cookie dc0 的值；如果不是首页第一次请求，或者你没有清除缓存，后面还会在拼接上 x-zst-81</p><h3 id="f-s">f()(s)</h3><p>  在条件断点成功断上后，再在 signature 处添加断点（如果需要刷新页面，需先移除 signature 处断点，清空缓存，然后再刷新页面，在 signature 处添加断点），执行到signature 处；在控台调试观察</p><p><img src="/posts/d7c81aca/008_1.png" alt></p><p><img src="/posts/d7c81aca/008.png" alt></p><p>  可以看出 f()(s) 的结果是固定，由数字字母构成的32位加密，那么我们有理由怀疑这是 md5 ，将拼接的字符串拿到 <a href="https://www.sojson.com/hash.html">https://www.sojson.com/hash.html</a> ，选择 md5 测试，结果与控制台一致</p><p><img src="/posts/d7c81aca/009.png" alt></p><h3 id="F-r-encrypt">F(r).encrypt</h3><p>  在控制台输入 F(r).encrypt，可以看到这个方法，单击输出进入该方法</p><p><img src="/posts/d7c81aca/010.png" alt></p><p>  可以看到，加密入口就是这里了</p><p><img src="/posts/d7c81aca/011.png" alt></p><p>  将整个js抠下来放到 VS Code 或者 WebStorm 中，找到上图处，从 D 方法外层折叠代码</p><p><img src="/posts/d7c81aca/012.png" alt></p><p>  将这一块整个扣下来，然后掐头去尾并按照下图注释</p><p><img src="/posts/d7c81aca/013.png" alt></p><p>至此，如果不想补环境或者搞算法，上面扣下来的代码使用 jsRPC 便够用了</p><h2 id="算法还原">算法还原</h2><p>  在扣下来的源码中可以看到大量控制流，在看了一些教程后了解到这就是jsvmp。本文应对的方法是插桩，也就是打日志断点。</p><p>  最开始使用的笨方法，也就是在每个控制流都打上日志断点，后来看了些大佬文章后，做了些精简，在以下位置做了插桩</p><p><img src="/posts/d7c81aca/014.png" alt></p><p><img src="/posts/d7c81aca/015.png" alt></p><p><img src="/posts/d7c81aca/016.png" alt></p><p><img src="/posts/d7c81aca/017.png" alt></p><p><img src="/posts/d7c81aca/018.png" alt></p><p><img src="/posts/d7c81aca/019.png" alt></p><h3 id="放到浏览器中">放到浏览器中</h3><p>  因为代码中有环境监测，所以需要将代码放到浏览器测试执行</p><p>  打开 Chrome 浏览器，进入目标站点页面，打开控制台；点击源代码；点击代码段；点击新代码段；重命名(可选)；将打完日志断点的代码粘贴到右侧</p><p><img src="/posts/d7c81aca/020.png" alt></p><p>  找一个 f()(s) 生成的 md5 进行后续测试。在测试的时候发现同一个 md5 生成的加密结果是不一样的，这说明要么加入了时间戳要么是随机数。经过测试是加入了随机数，为了方便调试那就把随机数的生成 hook 掉</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Math</span>.<span class="property">random</span> = <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0.12342184792608113</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="/posts/d7c81aca/021.png" alt></p><h3 id="加密规律">加密规律</h3><p>  通过日志可以看出加密结果是一个个拼接起来的，有一个字符串一直在拼接过程附近，那就是 6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE ，经过多次测试发现这个字符串是固定的，通过执行 charAt 得到需要拼接的结果，如下图最后 D 的生成</p><p><img src="/posts/d7c81aca/022.png" alt></p><p>  那么这个 37 又是哪里来的那？往上找。37 是通过 9950754 &gt;&gt;&gt; 18 &amp; 63 生成的</p><p><img src="/posts/d7c81aca/023.png" alt></p><p>   继续向上找 9950754，找到了 9 是如何生成的</p><p><img src="/posts/d7c81aca/024.png" alt></p><p>   继续向上找9950754，找到了 I 是如何生成的</p><p><img src="/posts/d7c81aca/025.png" alt></p><p>   还是没找到 9950754 如何生成的，那就继续向上找，图太长就不截图了</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">34</span> ^ <span class="number">0</span> = <span class="number">34</span></span><br><span class="line"><span class="number">214</span> &lt;&lt; <span class="number">8</span> = <span class="number">54784</span></span><br><span class="line"><span class="number">34</span> | <span class="number">54784</span> = <span class="number">54818</span></span><br><span class="line"><span class="number">151</span> &lt;&lt; <span class="number">16</span> = <span class="number">9895936</span></span><br><span class="line"><span class="number">54818</span> | <span class="number">9895936</span> = <span class="number">9950754</span></span><br><span class="line"><span class="number">9950754</span> &amp; <span class="number">63</span> = <span class="number">34</span></span><br><span class="line"><span class="string">&#x27;6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE&#x27;</span>.<span class="title function_">charAt</span>(<span class="number">34</span>) = <span class="number">0</span></span><br></pre></td></tr></table></figure><p>  至此，加密结果的最后 4 位结果就找到了。然后就发现，上面出现的数字有的是生成的，但是 151,34,214 是没找到啊！别急，我们待会再看。继续往上找规律</p><p>  从最开始生成加密的位置寻找，经过观察日志发现，加密结果可以分为 16 组，每组先算出第一个数（同上，每组都有3个未知的数），然后再计算另外3个数；而且这16组又分成4组，每个计算略有差异，也就是重复 1 - 4 组逻辑</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1 组第一个数</span></span><br><span class="line"><span class="number">92</span> ^ <span class="number">58</span> = <span class="number">102</span></span><br><span class="line"><span class="number">165</span> &lt;&lt; <span class="number">8</span> = <span class="number">42240</span></span><br><span class="line"><span class="number">102</span> | <span class="number">42240</span> = <span class="number">42342</span></span><br><span class="line"><span class="number">173</span> &lt;&lt; <span class="number">16</span> = <span class="number">11337728</span></span><br><span class="line"><span class="number">42342</span> | <span class="number">11337728</span> = <span class="number">11380070</span></span><br><span class="line"><span class="number">11380070</span> &amp; <span class="number">63</span> = <span class="number">38</span></span><br><span class="line"><span class="string">&#x27;6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE&#x27;</span>.<span class="title function_">charAt</span>(<span class="number">38</span>) = a</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2 组第一个数</span></span><br><span class="line"><span class="number">174</span> ^ <span class="number">0</span> = <span class="number">174</span></span><br><span class="line"><span class="number">56</span> ^ <span class="number">58</span> = <span class="number">2</span></span><br><span class="line"><span class="number">2</span> &lt;&lt; <span class="number">8</span> = <span class="number">512</span></span><br><span class="line"><span class="number">174</span> | <span class="number">512</span> = <span class="number">686</span></span><br><span class="line"><span class="number">121</span> &lt;&lt; <span class="number">16</span> = <span class="number">7929856</span></span><br><span class="line"><span class="number">686</span> | <span class="number">7929856</span> = <span class="number">7930542</span></span><br><span class="line"><span class="number">7930542</span> &amp; <span class="number">63</span> = <span class="number">46</span></span><br><span class="line"><span class="string">&#x27;6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE&#x27;</span>.<span class="title function_">charAt</span>(<span class="number">46</span>) = <span class="number">7</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3 组第一个数</span></span><br><span class="line"><span class="number">168</span> ^ <span class="number">0</span> = <span class="number">168</span></span><br><span class="line"><span class="number">66</span> &lt;&lt; <span class="number">8</span> = <span class="number">16896</span></span><br><span class="line"><span class="number">168</span> | <span class="number">16896</span> = <span class="number">17064</span></span><br><span class="line"><span class="number">40</span> ^ <span class="number">58</span> = <span class="number">18</span></span><br><span class="line"><span class="number">18</span> &lt;&lt; <span class="number">16</span> = <span class="number">1179648</span></span><br><span class="line"><span class="number">17064</span> | <span class="number">1179648</span> = <span class="number">1196712</span></span><br><span class="line"><span class="number">1196712</span> &amp; <span class="number">63</span> = <span class="number">40</span></span><br><span class="line"><span class="string">&#x27;6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE&#x27;</span>.<span class="title function_">charAt</span>(<span class="number">40</span>) = W</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4 组第一个数</span></span><br><span class="line"><span class="number">178</span> ^ <span class="number">0</span> = <span class="number">178</span></span><br><span class="line"><span class="number">202</span> &lt;&lt; <span class="number">8</span> = <span class="number">51712</span></span><br><span class="line"><span class="number">178</span> | <span class="number">51712</span> = <span class="number">51890</span></span><br><span class="line"><span class="number">108</span> &lt;&lt; <span class="number">16</span> = <span class="number">7077888</span></span><br><span class="line"><span class="number">51890</span> | <span class="number">7077888</span> = <span class="number">7129778</span></span><br><span class="line"><span class="number">7129778</span> &amp; <span class="number">63</span> = <span class="number">50</span></span><br><span class="line"><span class="string">&#x27;6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE&#x27;</span>.<span class="title function_">charAt</span>(<span class="number">50</span>) = b</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment">// 16 组第一个数</span></span><br><span class="line"><span class="number">34</span> ^ <span class="number">0</span> = <span class="number">34</span></span><br><span class="line"><span class="number">214</span> &lt;&lt; <span class="number">8</span> = <span class="number">54784</span></span><br><span class="line"><span class="number">34</span> | <span class="number">54784</span> = <span class="number">54818</span></span><br><span class="line"><span class="number">151</span> &lt;&lt; <span class="number">16</span> = <span class="number">9895936</span></span><br><span class="line"><span class="number">54818</span> | <span class="number">9895936</span> = <span class="number">9950754</span></span><br><span class="line"><span class="number">9950754</span> &amp; <span class="number">63</span> = <span class="number">34</span></span><br><span class="line"><span class="string">&#x27;6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE&#x27;</span>.<span class="title function_">charAt</span>(<span class="number">34</span>) = <span class="number">0</span></span><br></pre></td></tr></table></figure><p>  每组的另外3个的计算如下，先计算数字再由 6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE 去计算最终结果，在日志中已经很清楚了</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">每组第一个计算出的数 &gt;&gt;&gt; <span class="number">6</span> &amp; <span class="number">63</span></span><br><span class="line">每组第一个计算出的数 &gt;&gt;&gt; <span class="number">12</span> &amp; <span class="number">63</span></span><br><span class="line">每组第一个计算出的数 &gt;&gt;&gt; <span class="number">18</span> &amp; <span class="number">63</span></span><br></pre></td></tr></table></figure><p>  将上面的逻辑使用代码实现，即完成了算法还原的第一步。下面就需要搞清楚，那些不知来源的3个一组的数是哪里来的。</p><h3 id="加密数组">加密数组</h3><h4 id="计算-md5-得到最初的-32-位数组">计算 md5 得到最初的 32 位数组</h4><p>  到现在都没看到最初 md5 这不合理啊，我们去搜索一下，找到它最后出现的位置，可以看到这是将 md5 的 32 个字符挨个取出去计算 charCodeAt 的值，得到一个 32 位数组，继续向下看</p><p><img src="/posts/d7c81aca/026.png" alt></p><h4 id="加密值变化的原因及-34-位数组">加密值变化的原因及 34 位数组</h4><p>  加密值变化的原因就在这里，随机值乘以固定值 127 ，通过 floor 向下取整；向最初的32位数组使用 unshift 在最前面添加一个 0，然后再使用 unshift 添加刚才取整得到的数，至此生成了 34 位数组</p><p><img src="/posts/d7c81aca/026_1_1.png" alt></p><h4 id="拼接的-48-位数组">拼接的 48 位数组</h4><p>  上面我们提到了，加密结果是计算了16组，每组的第一个数都有3个未知来源的数参与计算，那么我们需要的应该是一个48位的数组，34位还不够，继续向下找。向34位的数组添加 14 ,直到数组的长度为 48 位。</p><p><img src="/posts/d7c81aca/026_1.png" alt></p><h4 id="最终的-48-位数组">最终的 48 位数组</h4><p>  继续向下，将上一步生成的 48 位数组通过 slice 截取 32 位，得到一个新的数组</p><p><img src="/posts/d7c81aca/027.png" alt></p><p>  继续向下看，进入到了一个方法中，有两个参数，一个是参数 e，上面生成的 32 位数组，另一个参数 t，未知的 16 位数组，往上找找看</p><p><img src="/posts/d7c81aca/028.png" alt></p><p>  可以看到 16 位数组是由这个方法计算得出的，那么它的 16 位数组入参还没看到怎么来的，继续向上找</p><p><img src="/posts/d7c81aca/029.png" alt></p><p>  这是将之前生成的 48 位数组截取了前 16 位，然后每一位都再进行运算，得到一个新的 16 位数组，这里的计算在日志中即可找到，就不一一列举了</p><p><img src="/posts/d7c81aca/030.png" alt></p><h2 id="写在最后">写在最后</h2><p>  至此整个算法流程结束，上面 <b>最终的 48 位数组</b> 提到的方法扣下来后缺什么补什么即可。需要注意的是，在计算的时候是从最终的 48 位数组倒序取的，每三个一组，正好16组。如果觉得这篇文章有不足之处，欢迎您的留言。</p><p><b>参考：</b><br><a href="https://blog.csdn.net/qq_43308242/article/details/126194990">js逆向JSVMP篇新版某乎_x-zes-96算法还原 - 时光依旧不在</a><br><a href="https://www.52pojie.cn/forum.php?mod=viewthread&amp;tid=1686683">【JS逆向系列】某乎x96参数3.0版本与jsvmp进阶 - 漁滒</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;You can’t just walk away from things. ——《夜行动物》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;逃避解决不了问题。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
  </entry>
  
  <entry>
    <title>mouchacha请求头加密逆向分析</title>
    <link href="https://mrxiuxing.com/posts/803a4511.html"/>
    <id>https://mrxiuxing.com/posts/803a4511.html</id>
    <published>2022-05-03T12:51:03.000Z</published>
    <updated>2022-05-03T12:51:03.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>People shouldn’t brood around their past. ——《最后的巫师猎人》</center></strong></em><br><em><strong><center>不要沉溺于过往之中。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cucWNjLmNvbS9maXJtLzU3NmMyMWUzNDY4YTZiMTc4YmJmMjkxZTQ4MjBlODk2Lmh0bWw=</code></pre><p><img src="/posts/803a4511/qcc001.png" alt></p><p>  形如上图这种，该网站很多链接的请求头中都有类似的加密，key：value 都不固定。</p><h2 id="JS-分析">JS 分析</h2><p>  既然是在请求头中的加密，那么较大概率是在 js 中生成的。全局搜索大法，搜索关键词 headers，经测试，main.be3416fa.js 文件就是我们要寻找的。这个文件超过20万行，不能莽，打上断点慢慢来。<br><img src="/posts/803a4511/qcc002.png" alt></p><p>  成功断上，主要代码如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> n = (<span class="number">0</span>, i.<span class="property">default</span>)(t) , r = (<span class="number">0</span>, s.<span class="property">default</span>)(t, e.<span class="property">data</span>);</span><br><span class="line">e.<span class="property">headers</span>[n] = r</span><br></pre></td></tr></table></figure><p>  将这两句转成容易看懂的，可以看到 headers 的 key 是 n，value 是 r。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> n = i.<span class="title function_">default</span>(t), r = s.<span class="title function_">default</span>(t, e.<span class="property">data</span>);</span><br><span class="line">e.<span class="property">headers</span>[n] = r</span><br></pre></td></tr></table></figure><h3 id="分析-n">分析 n</h3><p>  在合适的位置打上断点<br><img src="/posts/803a4511/qcc003.png" alt></p><p>  进入下一步，嗯。。。怎么又有 i.default 和 s.default，先不管扣下来继续往下走。<br><img src="/posts/803a4511/qcc004.png" alt></p><p>  进入到 s.default，这里的 i.default.n 与 i.default.codes 都是固定的写死即可。<br><img src="/posts/803a4511/qcc005.png" alt></p><p>  继续下一步<br><img src="/posts/803a4511/qcc006.png" alt></p><p>  继续下一步跳转到了另一个文件，在这里看到了 HMAC<br><img src="/posts/803a4511/qcc007.png" alt></p><p>  继续往下走了不知多少步，到了这里，看到了 SHA512，至此我们已经晓得这个网址用了什么算法了。接下来我们不扣算法代码，因为全扣下来太多了，在 node 里很容易实现，这里就不贴了，直接百度一下即可。<br><img src="/posts/803a4511/qcc008.png" alt></p><p>  到这里 n 就分析完了。</p><h3 id="分析-r">分析 r</h3><p>  合适的位置打上断点，跟进去<br><img src="/posts/803a4511/qcc009.png" alt></p><p>  嗯。。。又看到了 i.default 和 s.default，盲猜这里的 i.default、 s.default 方法和 n 里面的是一样的。<br><img src="/posts/803a4511/qcc010.png" alt></p><p>  继续下一步，进入到 s.default 里面，果然和 n 里面 s.default 是一样的<br><img src="/posts/803a4511/qcc011.png" alt></p><p>  跳出 s.default，进入到 i.default。果然 i.default 也是和 n 里面的 i.default 一样。<br><img src="/posts/803a4511/qcc012.png" alt><br><img src="/posts/803a4511/qcc013.png" alt></p><p>  既然这样，就不用往下扣了呗！整理整理，一共五十来行代码，运行看看和浏览器的是不是一致的。没毛病，一毛一样<br><img src="/posts/803a4511/qcc014.png" alt></p><h2 id="写在最后">写在最后</h2><p>  本次分析没什么特别注意的，主要是 i.default 和 s.default 很多地方都有它们，一不留神就被绕懵了，分不清哪个是哪里的了，我们只需捋清楚逻辑，一步步来就能成功。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;People shouldn’t brood around their past. ——《最后的巫师猎人》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;不要沉溺于过往之中。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
  </entry>
  
  <entry>
    <title>error: RPC 失败。HTTP 413 curl 22 The requested URL returned error: 413</title>
    <link href="https://mrxiuxing.com/posts/ae01f2a2.html"/>
    <id>https://mrxiuxing.com/posts/ae01f2a2.html</id>
    <published>2022-05-02T03:15:08.000Z</published>
    <updated>2022-05-02T03:15:08.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>You have a chance to write your own story. ——《坏蛋联盟》</center></strong></em><br><em><strong><center>你的故事由你书写。</center></strong></em></p><span id="more"></span><h2 id="错误复现">错误复现</h2><p>  记录下今天遇到的一个问题，在使用 git push 时，出现了 error: RPC 失败。HTTP 413 curl 22 The requested URL returned 错误。<br><img src="ae01f2a2/git_http_413_001.png" alt></p><h2 id="错误分析">错误分析</h2><p>  找一下 HTTP的 413 是什么状态码。这是因为请求实体过大出的错。<br><img src="ae01f2a2/git_http_413_002.png" alt></p><h2 id="解决问题">解决问题</h2><p>  我的 Git 私服是由 Nginx 反向代理过的，那么应该是 Nginx 的问题，无法处理实体过大的请求。可以在三个地方修改，以避免这个问题。</p><h3 id="http">http</h3><p>  全局配置，会作用于所有请求</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http &#123;</span><br><span class="line">    client_max_body_size 100M;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="server">server</h3><p>  当前配置，仅会作用于当前请求</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">    client_max_body_size 100M;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="location">location</h3><p>  当前匹配，仅会作用于当前匹配的请求</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">location xxx &#123;</span><br><span class="line">    client_max_body_size 100M;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="写在最后">写在最后</h2><p>  上面三种，选择适合自己的就好。修改完 Nginx 的配置，别忘了重启一下 Nginx 哦！</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo service nginx restart 或 sudo systemctl restart nginx</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;You have a chance to write your own story. ——《坏蛋联盟》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;你的故事由你书写。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Git" scheme="https://mrxiuxing.com/categories/Git/"/>
    
    
    <category term="Nginx" scheme="https://mrxiuxing.com/tags/Nginx/"/>
    
    <category term="Git" scheme="https://mrxiuxing.com/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>moumoutoutiao _signature 逆向分析</title>
    <link href="https://mrxiuxing.com/posts/58dd853.html"/>
    <id>https://mrxiuxing.com/posts/58dd853.html</id>
    <published>2022-05-01T07:15:33.000Z</published>
    <updated>2022-05-01T07:15:33.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>My courage always rises with every attempt to intimidate me. ——《傲慢与偏见》</center></strong></em><br><em><strong><center>凡试图打败我的，皆让我勇气倍增。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cudG91dGlhby5jb20vP3dpZD0xNjUxMjA5OTE1Mjk5</code></pre><p><img src="/posts/58dd853/tt001.png" alt></p><p>  经过分析，该请求共有两个参数是一直变化的 max_behot_time 和 _signature，max_behot_time 没有加密，初始值为 0，本次请求响应中就存在下次请求需要的 max_behot_time，所以接下来的内容主要是分析 _signature 参数的的生成。</p><h2 id="JS-分析">JS 分析</h2><p>  没有什么是全局搜索大法搞不定的，如果有，那就放弃吧！！！只有在一个文件中包含 _signature，我觉得我又行了，打上断点，成功断上。<br><img src="/posts/58dd853/tt002.png" alt></p><h3 id="window-byted-acrawler-sign">window.byted_acrawler.sign</h3><p>  可以看到我们需要重点关注的就是 S 函数，跟进去看看是个什么东东。这个 i 应该就是我们需要的，i 上面的三个判断可直接忽略。<br><img src="/posts/58dd853/tt003.png" alt></p><p>  把 i 的生成转换成容易看得懂的，在控制台看看效果</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i = <span class="variable language_">window</span>.<span class="property">byted_acrawler</span>.<span class="property">sign</span>.<span class="title function_">call</span>(n,o)</span><br></pre></td></tr></table></figure><p><img src="/posts/58dd853/tt004.png" alt></p><p>  进入到 window.byted_acrawler.sign 内部，跳转到了 acrawler.js 中，整个文件就六百来行，全扣下来，在本地试一下能不能运行。<br><img src="/posts/58dd853/tt005.png" alt><br><img src="/posts/58dd853/tt006.png" alt></p><h3 id="补环境">补环境</h3><p>  看样子是不行，那就缺啥补啥呗！</p><h4 id="referrer">referrer</h4><p>  我们需要先看下 referrer 的值什么，在控制台找到报错的位置，在合适的位置打上断点，然后右键 修改断点… 这样就打上了一个条件断点。</p><p><img src="/posts/58dd853/tt007.png" alt><br><img src="/posts/58dd853/tt008.png" alt></p><p>  刷新浏览器，等成功断上之后，在控制台看看结果<br><img src="/posts/58dd853/tt009.png" alt></p><h4 id="window">window</h4><p>  ReferenceError: window is not defined，那就补个 window<br><img src="/posts/58dd853/tt010.png" alt><br><img src="/posts/58dd853/tt011.png" alt></p><h4 id="sign">sign</h4><p>  TypeError: Cannot read property ‘sign’ of undefined，出现了这个错误，在本地代码打个断点，观察一下。<br><img src="/posts/58dd853/tt012.png" alt></p><p>  在本地变量发现了可疑参数 exports 与 module，搜索一下 exports，还好就只有两个，而且 module 就在旁边。<br><img src="/posts/58dd853/tt013.png" alt></p><p>  在浏览器环境看一下这两个东东是什么，合适的位置打上断点，然后在控制台执行一下这两句。<br><img src="/posts/58dd853/tt014.png" alt></p><p>  可以看到在浏览器环境下 exports 与 module 都是 undefined，既然这样，在本地代码直接将这两句改为 undefined 即可。<br><img src="/posts/58dd853/tt015.png" alt></p><h4 id="href">href</h4><p>  TypeError: Cannot read property ‘href’ of undefined<br><img src="/posts/58dd853/tt016.png" alt></p><p>  浏览器下在合适的位置打上一个条件断点，然后在控制台观察 href 的值。<br><img src="/posts/58dd853/tt017.png" alt><br><img src="/posts/58dd853/tt018.png" alt></p><p>  在本地补上 href<br><img src="/posts/58dd853/tt019.png" alt></p><h4 id="length">length</h4><p>  TypeError: Cannot read property ‘length’ of undefined，报错的地方还是与 href 报错的地方相同，那就再打上一个条件断点，然后在控制台观察看看<br><img src="/posts/58dd853/tt020.png" alt><br><img src="/posts/58dd853/tt021.png" alt><br><img src="/posts/58dd853/tt022.png" alt></p><p>  这个显然不是我们需要的，既然报错的地方还是与 href 报错的地方一致，那就还让条件等于 href，看看是不是在 location 仅仅补一个 href 还不够。重复在 href 打条件断点的步骤，观察 location 中的值。<br><img src="/posts/58dd853/tt023.png" alt><br><img src="/posts/58dd853/tt024.png" alt></p><p>  可以看到 location 中不仅仅包含 href，接下来让我们挨个试试。经测试只需在 location 中再补充一个 protocol 即可，当然也可以全补进去。<br><img src="/posts/58dd853/tt025.png" alt></p><h4 id="userAgent">userAgent</h4><p>  TypeError: Cannot read property ‘userAgent’ of undefined<br><img src="/posts/58dd853/tt026.png" alt></p><p>  在报错的位置打上一个条件断点，刷新浏览器，在控制台观察变量<br><img src="/posts/58dd853/tt027.png" alt><br><img src="/posts/58dd853/tt028.png" alt></p><p>  在 navigator 补充 userAgent<br><img src="/posts/58dd853/tt029.png" alt></p><p>  这应该是成了吧？！！！试一下，能用，就是短了点。<br><img src="/posts/58dd853/tt030.png" alt><br><img src="/posts/58dd853/tt031.png" alt></p><h4 id="cookie">cookie</h4><p>  网上搜了下相关的资料，说是可以补个 cookie 就可以变长了，而且还得补的靠下面些，那咱就来试试。<br><img src="/posts/58dd853/tt032.png" alt><br><img src="/posts/58dd853/tt033.png" alt></p><h2 id="写在最后">写在最后</h2><p>  至此，本文结束，用代码将上面的逻辑复现即可。这个站点在去年就已经搞出来了，因为种种原因搁置到现在，不过再捋一遍将逻辑整理出来，也是一种收获。本文的难点主要是在补环境上，很多人可能补着补着就放弃了，我刚开始也是这样，需要沉下心来，一步步的，实在想不通可以去网上查查资料，看看有没有大佬或者前辈做过类似的。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;My courage always rises with every attempt to intimidate me. ——《傲慢与偏见》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;凡试图打败我的，皆让我勇气倍增。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
  </entry>
  
  <entry>
    <title>mouyan4代滑块验证码逆向分析</title>
    <link href="https://mrxiuxing.com/posts/e5287f45.html"/>
    <id>https://mrxiuxing.com/posts/e5287f45.html</id>
    <published>2022-04-27T00:43:48.000Z</published>
    <updated>2022-04-27T00:43:48.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>What matters is what you do, not what you think. ——《疯狂前女友》</center></strong></em><br><em><strong><center>行动比空想更重要。</center></strong></em></p><span id="more"></span><p><b>PS: 本文仅供学习参考、仅供学习参考、仅供学习参考，不得用于商业用途。</b></p><h2 id="请求分析">请求分析</h2><p>本次目标站点</p><pre><code>aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vYWRhcHRpdmUtY2FwdGNoYS1kZW1v</code></pre><!--![](极验第四代滑块验证码/gt001.png)--><h3 id="第一个请求">第一个请求</h3><p>  需要从源码中提取adaptive-captcha-demo.js链接地址以进行下一步请求<br><img src="/posts/e5287f45/gt002.png" alt></p><h3 id="第二个请求">第二个请求</h3><p>  提取 captchaId 参数<br><img src="/posts/e5287f45/gt003.png" alt></p><p>  上面的两个请求一般情况下是可以忽略的，因为构造这两个请求是为了获取 captchaId 的值，但一般情况下这个值是固定的。</p><h3 id="第三个请求">第三个请求</h3><p>  这里面包含了验证码图片、js等文件链接地址，这意味着逆向从这里开始。<br><img src="/posts/e5287f45/gt004.png" alt></p><p>分析一下这个请求所需要的参数</p><pre><code>captcha_id: 24f56dc13c40dc4a02fd0318567caef5  # 固定值，不出意外以为一个站定就一个challenge: bcf91a78-71a0-4820-9578-dffff5cebd82  # 动态值，需要寻找生成方式client_type: web  # 固定值，目标站点类型risk_type: slide  # 固定值，验证码类型lang: zh  # 固定值callback: geetest_1651037128229  # 动态值，geetest_+13位时间戳</code></pre><p>  分析 challenge 参数，祭出全局搜索大法，在3个文件中匹配到了7个，经过测试，定位到了 gt4.js 文件第310行，在这打上断点。<br><img src="/posts/e5287f45/gt005.png" alt></p><p><img src="/posts/e5287f45/gt006.png" alt></p><p>  点击进入下一步函数调用，进入到了 uuid 函数<br><img src="/posts/e5287f45/gt007.png" alt></p><p>  我们点击堆栈回到上一步，发现 config.challenge 是没有值的，也就是说 challenge 是由uuid 这个函数生成<br><img src="/posts/e5287f45/gt008.png" alt></p><p>  这里可以把js代码抠出来，可以改写成 Python 代码，也可以使用 Python 中的 uuid 包生成</p><p><img src="/posts/e5287f45/gt009.png" alt></p><p><b>使用 Python 中的 uuid 包生成</b></p><p><img src="/posts/e5287f45/gt010.png" alt></p><p>  综上，看下效果<br><img src="/posts/e5287f45/gt011.png" alt></p><h3 id="第四个请求">第四个请求</h3><p>  点击按钮开始验证，验证码浮出，滑动验证码通过验证，控制台 verify 验证链接出现，接下来的重中之重就是构造这个请求。<br><img src="/posts/e5287f45/gt012.png" alt></p><p>分析一下需要哪些参数</p><pre><code>captcha_id: 24f56dc13c40dc4a02fd0318567caef5  # 和前面一致client_type: web  # 和前面一致lot_number: 4e89854276c2413aa650294441107bd6  # 第三步请求获取risk_type: slide  #payload:  # 第三步请求获取process_token:  # 第三步请求获取payload_protocol: 1  # 固定值pt: 1  # 固定值w: # 动态值需要分析callback: geetest_1651040592478  # 动态值，geetest_+13位时间戳</code></pre><p>  嗯。。。。缺了个 w，点击 verify 链接的启动器(如果你没有修改语言，那么显示的应该是Initiator)，发现请求调用堆栈全部来自于 gcaptcha4.js 文件。<br><img src="/posts/e5287f45/gt013.png" alt></p><p>  点击进入 gcaptcha4.js 文件，格式化一下。嗯。。。共13485行，混淆的不要不要的。<br><img src="/posts/e5287f45/gt014.png" alt></p><h2 id="JS-还原与替换">JS 还原与替换</h2><p>  为了保住我的头发，我还是不选择硬刚了，万一刚着刚着头发就没了，咋搞！！！还是整点靠谱的方式吧 —— AST还原混淆JS。</p><h3 id="babel库安装">babel库安装</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install @babel/core --save-dev</span><br></pre></td></tr></table></figure><h4 id="导入模块">导入模块</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> parser = <span class="built_in">require</span>(<span class="string">&quot;@babel/parser&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> traverse = <span class="built_in">require</span>(<span class="string">&quot;@babel/traverse&quot;</span>).<span class="property">default</span>;</span><br><span class="line"><span class="keyword">const</span> t = <span class="built_in">require</span>(<span class="string">&quot;@babel/types&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> generator = <span class="built_in">require</span>(<span class="string">&quot;@babel/generator&quot;</span>).<span class="property">default</span>;</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&quot;fs&quot;</span>);</span><br></pre></td></tr></table></figure><h4 id="复制源码">复制源码</h4><p>  将 gcaptcha4.js 中代码全部复制下来，保存至本地。折叠起来效果如下<br><img src="/posts/e5287f45/gt015.png" alt></p><h4 id="AST-还原">AST 还原</h4><p>主要步骤</p><ul><li>删除节点中的extra属性</li><li>控制流平坦化</li><li>删除无关函数</li></ul><h3 id="替换站点-gcaptcha4-js-文件">替换站点 gcaptcha4.js 文件</h3><p>  可以使用 Chrome 的插件 ReRes (如何安装可自行百度)，可以使用 Charles 替换 js 文件，也可以直接在 Chrome 上替换 js 文件。详情请查看 《<a href="https://mrxiuxing.com/posts/1037912.html">本地js文件替换源网页js的几种方式</a>》</p><h3 id="替换效果">替换效果</h3><h4 id="替换前">替换前</h4><p><img src="/posts/e5287f45/gt028.png" alt></p><h4 id="替换后">替换后</h4><p><img src="/posts/e5287f45/gt029.png" alt></p><h2 id="JS-分析">JS 分析</h2><p>  全局搜素大法，搜索 w 参数，然后在合适的位置打上断点可以看到 w 是由 var r = (0, d[“default”])(l[“default”][“stringify”](e), a) 生成。<br><img src="/posts/e5287f45/gt030.png" alt></p><p>  先来看看 e 是个啥？如下图：<br><img src="/posts/e5287f45/gt031.png" alt></p><h3 id="e-的组成">e 的组成</h3><pre><code>device_id  # 固定值em  # 固定值ep # 固定值geetest # 固定值lang # 固定值lot_number # 第三步请求获取passtime # 通过时间pow_msg # 需分析pow_sign # 需分析 pyxo &quot;1940377373&quot; # 每天更新（key，value都会变），可写死setLeft # 滑动距离track  # 计算轨迹得出userresponse # 需分析</code></pre><h4 id="pow-msg-与-pow-sign">pow_msg 与 pow_sign</h4><h5 id="pow-msg">pow_msg</h5><p>  全局搜索大法，打上断点</p><p><img src="/posts/e5287f45/gt032.png" alt><br>  可以看到 pow_msg 是 u 和 p组成的</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;pow_msg&quot;</span>: u + p</span><br></pre></td></tr></table></figure><p>u 的组成</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">u = n + <span class="string">&quot;|&quot;</span> + a + <span class="string">&quot;|&quot;</span> + s + <span class="string">&quot;|&quot;</span> + o + <span class="string">&quot;|&quot;</span> + t + <span class="string">&quot;|&quot;</span> + e + <span class="string">&quot;|&quot;</span> + r + <span class="string">&quot;|&quot;</span>;</span><br></pre></td></tr></table></figure><pre><code>n:&quot;1&quot; # 第三个请求获取的 version 固定就行a:0 # 第三个请求获取的 bits 固定就行s:&quot;md5&quot; # 第三个请求获取的 hashfunc 固定就行o:&quot;2022-04-28T09:38:40.768352+08:00&quot; # 第三个请求获取的 datetimet:&quot;24f56dc13c40dc4a02fd0318567caef5&quot; # captchaIde:&quot;37cda35e6a0b4c8d86a2fd8cfaa75af5&quot; # 第三个请求获取的 lot_numberr:&quot;&quot; 固定值</code></pre><p>p 的组成</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> p = (<span class="number">0</span>, b[<span class="string">&quot;guid&quot;</span>])()</span><br></pre></td></tr></table></figure><p>  分析下 b[“guid”]<br><img src="/posts/e5287f45/gt033.png" alt></p><h5 id="pow-sign">pow_sign</h5><p>可以明显看出 pow_sign 是 g 进行 md5 生成，而 g = u + p 即 g = pow_msg，那我们直接将 pow_msg md5 即可。<br><img src="/posts/e5287f45/gt034.png" alt></p><h4 id="setLeft、track、passtime、userresponse">setLeft、track、passtime、userresponse</h4><h5 id="setLeft">setLeft</h5><p>  setLeft 就是 滑动距离取整<br><img src="/posts/e5287f45/gt035.png" alt></p><h5 id="track-与-passtime">track 与 passtime</h5><p>  track 是由 _ = p[“default”][“prototype”][“$_BABR”](i); 生成，i 就是滑块轨迹，这句代码上面的一行是做了个判断，轨迹的长度范围在 0-160 之间。<br><img src="/posts/e5287f45/gt036.png" alt></p><p>  我们进入 p[“default”][“prototype”][“$_BABR”]<br><img src="/posts/e5287f45/gt037.png" alt></p><p>  passtime 是由当前时间戳减去点击滑块开始的时间。<br><img src="/posts/e5287f45/gt038.png" alt></p><p>  不过这玩意好像没那么简单，我们在使用的时候也不好控制。哎！怎么感觉这个 a 的值有点熟悉啊？这个不就是 i 最后一个列表的最后一个值吗？<br><img src="/posts/e5287f45/gt039.png" alt></p><p>  多试几次，发现还真的是啊！而且是除去第一个列表后是一个不断增大值。<br><img src="/posts/e5287f45/gt040.png" alt></p><p><img src="/posts/e5287f45/gt041.png" alt></p><p>  继续分析 i ，发现 i 的第一个列表中第一个与第二个值的范围大致是 -50-0，第三个值总是0；第二个列表中的三个值都是 0 。如下面的一些图：<br><img src="/posts/e5287f45/gt042.png" alt><br><img src="/posts/e5287f45/gt043.png" alt><br><img src="/posts/e5287f45/gt044.png" alt></p><p>  经过分析得出大致结论：</p><ul><li>i 的第一个列表中第一个与第二个值的范围大致是 -50-0，第三个值总是0；</li><li>第二个列表中的三个值都是 0；</li><li>从第三个列表开始，列表中的第一个值是递增的变化不大，列表中的第二个值也是递增的，但是变化不大，所以可以直接把第二个值定死，第三个值也是不断累加，但是变化较大。细心的朋友或许还发现了另一个规律，那就是：最后一个列表的第一个值如果可以被10整除那它就等于 setLeft ，否则加上1等于setLeft；</li><li>i 的长度在 0-160 之间，当然在生成的时候肯定不能太小；</li><li>当上述条件成立轨迹生成完毕</li></ul><h5 id="userresponse">userresponse</h5><p>  userresponse 直接由 setLeft 除去一个定值生成<br><img src="/posts/e5287f45/gt045.png" alt></p><h3 id="l-“default”-“stringify”">l[“default”][“stringify”]</h3><p>  可以看到 l[“default”][“stringify”] 就是将 e 转换成字符串<br><img src="/posts/e5287f45/gt046.png" alt></p><h3 id="d-“default”">d[“default”]</h3><p>  接下来着重看 d[“default”] 做了些什么，通过断点跳转至如下图方法中。<br><img src="/posts/e5287f45/gt047.png" alt><br>  可以看到该方法中两个 if 仅做判断，没有参与计算，所以可以直接忽略掉，将其余代码美化一下方便阅读。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> n = (<span class="number">0</span>,c[<span class="string">&quot;guid&quot;</span>])(), a = <span class="keyword">new</span> _[<span class="string">&quot;default&quot;</span>]()[<span class="string">&quot;encrypt&quot;</span>](n);</span><br><span class="line"><span class="keyword">while</span> (!a || <span class="number">256</span> !== a[<span class="string">&quot;length&quot;</span>])</span><br><span class="line">    n = (<span class="number">0</span>, c[<span class="string">&quot;guid&quot;</span>])(), a = <span class="keyword">new</span> _[<span class="string">&quot;default&quot;</span>]()[<span class="string">&quot;encrypt&quot;</span>](n);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o = i[<span class="string">&quot;default&quot;</span>][<span class="string">&quot;encrypt&quot;</span>](e, n);</span><br><span class="line"><span class="keyword">return</span> (<span class="number">0</span>, c[<span class="string">&quot;arrayToHex&quot;</span>])(o) + a;</span><br></pre></td></tr></table></figure><p>  接下来一点点分析</p><h4 id="c-“guid”">c[“guid”]</h4><p><img src="/posts/e5287f45/gt048.png" alt></p><h4 id="“default”">_[“default”]</h4><p>  这个方法就是 C 方法，但不止下图的这么点，全部扣下来大概要500来行代码，因为太长这里就不做展示了。<br><img src="/posts/e5287f45/gt049.png" alt></p><h4 id="i-“default”">i[“default”]</h4><p>  下图展示的是 i[“default”][“encrypt”]，而我们要想正常运行需要完整的将 i[“default”] 扣下来，整体大概200来行代码。<br><img src="/posts/e5287f45/gt050.png" alt></p><h4 id="c-“arrayToHex”">c[“arrayToHex”]</h4><p>c[“arrayToHex”] 方法就下图这么多，全部扣下来即可。<br><img src="/posts/e5287f45/gt051.png" alt></p><p>  至此 w 参数的分析就完活了。。。验证码分析也结束了。。。剩下的就是用代码实现以上逻辑即可。</p><h2 id="写在最后">写在最后</h2><p>  本文的难点主要集中在还原上，代码不还原就很难进行下一步；还原后，捋清楚整体流程，慢慢摸索就能够搞定了。本文并没有提到具体滑块距离的实现，算是个小坑，感兴趣的朋友可以自行探索，实现的方法可不止一种哦！以上，搞下来整体通过率在 97% 左右，最后附上效果图。<br>  第一次<br><img src="/posts/e5287f45/gt052.png" alt><br>  第二次<br><img src="/posts/e5287f45/gt053.png" alt><br>  第三次<br><img src="/posts/e5287f45/gt054.png" alt></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;What matters is what you do, not what you think. ——《疯狂前女友》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;行动比空想更重要。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
    <category term="验证码" scheme="https://mrxiuxing.com/tags/%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
    
  </entry>
  
  <entry>
    <title>本地js文件替换源网页js的几种方式</title>
    <link href="https://mrxiuxing.com/posts/1037912.html"/>
    <id>https://mrxiuxing.com/posts/1037912.html</id>
    <published>2022-04-27T00:43:43.000Z</published>
    <updated>2022-04-27T00:43:43.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Everything wile be okay in the end. If it’s not okay, it’s not the end. ——《涉外大饭店》</center></strong></em><br><em><strong><center>一切都终会都会好起来的。如果没有好起来，说明没有到最后。</center></strong></em></p><span id="more"></span><h4 id="ReRes-插件替换-JS-文件">ReRes 插件替换 JS 文件</h4><p>  使用 ReRes 插件替换 JS 文件，需要将 <b>允许访问文件网址</b> 权限打开。<br><img src="/posts/1037912/gt016.png" alt></p><p>  点击进入拓展程序选项，按照下图所示配置，最后别忘了在 Enable 打上勾（默认应该是打上勾的）。<br><img src="/posts/1037912/gt017.png" alt></p><h4 id="Charles-替换-JS-文件">Charles 替换 JS 文件</h4><p>  清空浏览器缓存，防止抓不到包。点击 Tools 然后点击 Map Local…</p><p><img src="/posts/1037912/gt018.png" alt></p><p>  点击 Choose 按钮，选择文件，点击 OK 。<br><img src="/posts/1037912/gt019.png" alt></p><p>  我们还需要解决跨域问题，否则的话是无法成功替换的。点击 Tools 然后点击 Rewrite…<br><img src="/posts/1037912/gt020.png" alt></p><p>  新建一个规则，添加站点，填写具匹配规则，如下图<br><img src="/posts/1037912/gt021.png" alt></p><p>  新增替换规则，类型选择 Add Header，在 Replace 填写替换规则，如下图：<br><img src="/posts/1037912/gt022.png" alt></p><h4 id="Chrome-替换-JS-文件">Chrome 替换 JS 文件</h4><p>  本地新建一个文件夹，用来存放要替换的 js 文件。Chrome 控制台，点击验证码，点击替换，点击选择放置替换项的文件夹。<br><img src="/posts/1037912/gt023.png" alt></p><p>  点击浏览器顶部的允许，让其获取该文件夹的完整访问权限<br><img src="/posts/1037912/gt024.png" alt></p><p>  点击源代码，点击网页，找到要替换的 js（如果找不到，就清空下浏览器缓存），右键点击保存并覆盖，如下图：<br><img src="/posts/1037912/gt025.png" alt></p><p>  这时右侧区域就变成可以编辑的了，我们就可以把还原好的 JS 粘贴至此处然后保存，如下图：<br><img src="/posts/1037912/gt026.png" alt></p><p>  回到替换，发现多了个目录，目录的名称是替换掉网站该 js 的链接，里面的 js 文件就是我们刚刚替换的本地文件，这个文件可以直接在此处编辑（编辑后别忘了 Ctrl + s 保存哦！）。<br><img src="/posts/1037912/gt027.png" alt></p><p>  以上三种方式均可实现 JS 文件的替换，任选其一即可。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Everything wile be okay in the end. If it’s not okay, it’s not the end. ——《涉外大饭店》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;一切都终会都会好起来的。如果没有好起来，说明没有到最后。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
  </entry>
  
  <entry>
    <title>Linux常用命令(六) tmux：终端多路复用器</title>
    <link href="https://mrxiuxing.com/posts/7323d879.html"/>
    <id>https://mrxiuxing.com/posts/7323d879.html</id>
    <published>2022-04-17T05:05:16.000Z</published>
    <updated>2022-04-17T05:05:16.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>The only person standing in your way is you. ——《黑天鹅》</center></strong></em><br><em><strong><center>唯一能够阻碍你的人是你自己。</center></strong></em></p><span id="more"></span><h2 id="简介">简介</h2><p>tmux 是一个终端多路复用器：它允许从单个屏幕创建、访问和控制多个终端。tmux可以从屏幕上分离出来，继续在后台运行，然后重新连接。在 tmux 中运行的程序可以是像vim 或top、htop这样的全屏交互程序，像bash或ksh这样的 shell，或者可以在 Unix 终端中运行的任何其他程序。</p><p>tmux可以在 OpenBSD、FreeBSD、NetBSD、Linux、macOS 和 Solaris 上运行。</p><p>官方地址：<a href="https://github.com/tmux/tmux">https://github.com/tmux/tmux</a></p><p>tmux 的主要用途是：</p><ul><li>通过在 tmux 中运行它们来保护远程服务器上正在运行的程序免受连接中断的影响。</li><li>允许从多个不同的本地计算机访问远程服务器上运行的程序。</li><li>在一个终端中同时处理多个程序和 shell，和窗口管理器类似。</li></ul><h2 id="安装">安装</h2><table><thead><tr><th style="text-align:left">平台</th><th>安装命令</th></tr></thead></table><!-- |Arch Linux|pacman -S tmux| --><p>|Debian or Ubuntu|apt install tmux|</p><!-- |Fedora|dnf install tmux| --><p>|RHEL or CentOS|yum install tmux|<br>|macOS（使用 Homebrew）|brew install tmux|<br>|macOS（使用 MacPorts）|port install tmux|</p><!-- |openSUSE|zypper install tmux| --><h2 id="常用命令">常用命令</h2><h3 id="不指定名称创建会话">不指定名称创建会话</h3><p>默认会生成编号，从0开始依次递增。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux new</span><br></pre></td></tr></table></figure><h3 id="指定名称创建会话">指定名称创建会话</h3><p>这样创建的会话，就算会话多了也容易查找。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux new -s &lt;name&gt;</span><br></pre></td></tr></table></figure><h3 id="列出当前所有会话">列出当前所有会话</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">tmux ls</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">或</span></span><br><span class="line">tmux list-session</span><br></pre></td></tr></table></figure><h3 id="连接会话">连接会话</h3><p>使用会话编号连接</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux attach -t 0</span><br></pre></td></tr></table></figure><p>使用会话名称连接</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux attach -t &lt;name&gt;</span><br></pre></td></tr></table></figure><h3 id="切换会话">切换会话</h3><p>从当前会话切换到另一个会话</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux switch -t &lt;name&gt;</span><br></pre></td></tr></table></figure><h3 id="关闭会话">关闭会话</h3><p>使用会话编号关闭</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux kill-session -t 0</span><br></pre></td></tr></table></figure><p>使用会话名称关闭</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux kill-session -t &lt;name&gt;</span><br></pre></td></tr></table></figure><h3 id="重命名会话">重命名会话</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux rename-session -t &lt;old-name&gt; &lt;new-name&gt; </span><br></pre></td></tr></table></figure><h2 id="常用快捷键">常用快捷键</h2><table><thead><tr><th style="text-align:left">快捷键</th><th>作用</th></tr></thead><tbody><tr><td style="text-align:left">按下 Ctrl + b 松开，按下 d</td><td>后台挂起当前会话</td></tr><tr><td style="text-align:left">按下 Ctrl + b 松开，按下 s</td><td>查看会话列表</td></tr><tr><td style="text-align:left">按下 Ctrl + b 松开，按下 方向键</td><td>选择其他会话</td></tr></tbody></table>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;The only person standing in your way is you. ——《黑天鹅》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;唯一能够阻碍你的人是你自己。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>常见的 JavaScript 混淆及应对</title>
    <link href="https://mrxiuxing.com/posts/3af2352a.html"/>
    <id>https://mrxiuxing.com/posts/3af2352a.html</id>
    <published>2021-06-17T05:09:44.000Z</published>
    <updated>2021-06-17T05:09:44.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Each day brings a new beginning.——《双峰》</center></strong></em><br><em><strong><center>每天都是一个新的开始。</center></strong></em></p><span id="more"></span><h2 id="AAEncode">AAEncode</h2><h3 id="示例">示例</h3><p>  AAEncode 是一种将 JavaScript 转化为常用颜文字网络表情组合的混淆方式，如下图：</p><p><img src="/posts/3af2352a/001.png" alt></p><h3 id="方案一">方案一</h3><p>  打开控制台，将代码粘贴其中，执行让其报错（如果没报错请放弃这个方案😂），根据报错信息进入 VM 中，就能看到源码（不过，我遇到的用这个方法从来没有成功过😭）</p><p><img src="/posts/3af2352a/003.png" alt></p><h3 id="方案二">方案二</h3><p>  打开控制台，点击源代码，找到代码块，点击新增代码块，将 JS 代码粘贴进去，在代码最顶部添加一个 debugger，执行代码进行调试，一步步调试，直到达到想要的效果</p><p><img src="/posts/3af2352a/004.png" alt></p><p><img src="/posts/3af2352a/005.png" alt></p><p><img src="/posts/3af2352a/006.png" alt></p><h3 id="方案三">方案三</h3><p>  打开控制台，将代码粘贴其中，去掉最后一个 (‘_’) 表情，直接执行得到结果或者在后面添加 toString 让其转化为字符串</p><p><img src="/posts/3af2352a/007.png" alt></p><h2 id="JJEncode">JJEncode</h2><h3 id="示例-2">示例</h3><p>  JJEncode 是一种将 JavaScript 转化为仅由符号组合的混淆方式，如下图：</p><p><img src="/posts/3af2352a/008.png" alt></p><h3 id="方案一-2">方案一</h3><p>  打开控制台，将代码粘贴其中，执行让其报错，根据报错信息进入 VM 中，就能看到源码（这个方法同样从来没有成功过😭）</p><p><img src="/posts/3af2352a/009.png" alt></p><h3 id="方案二-2">方案二</h3><p>  打开控制台，点击源代码，找到代码块，点击新增代码块，将 JS 代码粘贴进去，在代码最顶部添加一个 debugger，执行代码进行调试，一步步调试，直到达到想要的效果</p><p><img src="/posts/3af2352a/010.png" alt></p><p><img src="/posts/3af2352a/011.png" alt></p><p><img src="/posts/3af2352a/012.png" alt></p><h3 id="方案三-2">方案三</h3><p>  打开控制台，将代码粘贴其中，去掉最后一个 () ，直接执行得到结果或者在后面添加 toString 让其转化为字符串</p><p><img src="/posts/3af2352a/013.png" alt></p><h2 id="JSFuck">JSFuck</h2><h3 id="示例-3">示例</h3><p>  JSFuck 是一种由 [、 ]、(、)、!、+ 六种字符组合而成的混淆方式，如下图：（仅截取部分）</p><p><img src="/posts/3af2352a/014.png" alt></p><h3 id="方案一-3">方案一</h3><p>  打开控制台，将代码粘贴其中执行，如果可以执行可能会得到解混淆后的代码，如猿人学内部练习题JSFuck那道；否则出现错误则该方案没戏了</p><p><img src="/posts/3af2352a/015.png" alt></p><h3 id="方案二-3">方案二</h3><p>  打开控制台，点击源代码，找到代码块，点击新增代码块，将 JS 代码粘贴进去，在代码最顶部添加一个 debugger，执行代码进行调试，一步步调试，直到达到想要的效果</p><p><img src="/posts/3af2352a/016.png" alt></p><p><img src="/posts/3af2352a/017.png" alt></p><h3 id="方案三-3">方案三</h3><p>  打开控制台，将代码粘贴其中，去掉最后一个 () 及其中的代码，直接执行得到结果或者在后面添加 toString 让其转化为字符串</p><p><img src="/posts/3af2352a/018.png" alt><br><img src="/posts/3af2352a/019.png" alt><br><img src="/posts/3af2352a/020.png" alt></p><h2 id="eval">eval</h2><p>  eval 混淆的话，简单的一眼就能看出来，复杂的话多看几眼也看不出来</p><h3 id="简单的">简单的</h3><p>  直接用 eval 包裹函数字符串或者是经过 base64 编码的函数字符串，可以直接抠出来用或者解码之后使用。在某些混淆方案中，还会生成 debugger 字符串，再由 eval 执行。</p><p><img src="/posts/3af2352a/021.png" alt></p><h3 id="复杂的">复杂的</h3><p>  以 function(p, a, c, k, e, d) 或者是 Function(p, a, c, k, e, d)开头，如下图：</p><p><img src="/posts/3af2352a/022.png" alt></p><p>  解决方案很简单，只需要将 eval 替换为 console.log 或者 alert 执行即可</p><p><img src="/posts/3af2352a/023.png" alt></p><p><img src="/posts/3af2352a/024.png" alt></p><h2 id="Obfuscator">Obfuscator</h2><p>  Obfuscator 一般简称 ob，是通过一系列转换，例如变量、函数、参数重命名，字符串删除，标识符混淆，死代码注入，防调试注入等，将被转换为不可读的内容达到保护代码的目的，同时运行方式与以前完全相同，可以说混淆程度相当严重了。</p><h3 id="混淆特征">混淆特征</h3><ul><li>大数组</li><li>数组移位（自执行函数，进行移位操作，有明显的 push、shift 关键字，建议不格式化）</li><li>解密函数（可能有定时器，建议不格式化）</li><li>逻辑代码 (控制流平坦化、 无限debugger自执行函数、死代码注入)</li></ul><h3 id="官网案例">官网案例</h3><p>  混淆前</p><p><img src="/posts/3af2352a/025.png" alt></p><p>  混淆后</p><p><img src="/posts/3af2352a/026.png" alt></p><h3 id="解决流程">解决流程</h3><ul><li>熟悉通用混淆代码</li><li>找到代码入口、扣取代码主体</li><li>完善缺失逻辑、补环境等</li></ul><p><b>相关网站：</b><br><a href="http://www.metools.info/code/">在线开发加密解密验证工具-ME2在线工具</a><br><a href="https://www.sojson.com/js/">SOJSON在线-JS解密 | JS加密 | JS 美化 | JS格式化 | JS混淆</a><br><a href="http://www.atoolbox.net/Category.php?Id=27">加密解密 - 加密解密工具</a><br><a href="http://www.jsfuck.com/">JSFuck-官网</a><br><a href="https://obfuscator.io/">JavaScript Obfuscator Tool</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Each day brings a new beginning.——《双峰》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;每天都是一个新的开始。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/categories/JS%E9%80%86%E5%90%91/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="逆向" scheme="https://mrxiuxing.com/tags/%E9%80%86%E5%90%91/"/>
    
    <category term="JS逆向" scheme="https://mrxiuxing.com/tags/JS%E9%80%86%E5%90%91/"/>
    
    <category term="JS混淆" scheme="https://mrxiuxing.com/tags/JS%E6%B7%B7%E6%B7%86/"/>
    
  </entry>
  
  <entry>
    <title>树莓派折腾记（二）-- 停电后 Ubuntu Server 全部文件变成只读模式</title>
    <link href="https://mrxiuxing.com/posts/ce2fd1e4.html"/>
    <id>https://mrxiuxing.com/posts/ce2fd1e4.html</id>
    <published>2021-01-21T12:34:14.000Z</published>
    <updated>2021-01-21T12:34:14.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Success is working up in the morning, so excited about what you hava to do.——《名扬四海》</center></strong></em><br><em><strong><center>成功是清晨醒来，对自己的人生充满期待。</center></strong></em></p><span id="more"></span><h2 id="前言">前言</h2><p>  在写这个系列之前我的树莓派就已经被我折腾完了。我安装了ARM版本的anaconda，配置了虚拟环境，安装了docker，docker倒是有ARM版本的，然后又在docker安装了MySQL、MongoDB等，最后舍弃TF，将系统迁移到了固态硬盘（这些在后面的折腾记中会一一提到）。在这台树莓派上我还跑着几个爬虫，然而，灾难发生了，在一次停电过后，Ubuntu 全部文件变成只读模式（readonly），且很多命令不可用。</p><h2 id="问题复现">问题复现</h2><p>  这是使用MobaXterm登录后的样子，报错如下图：并且是顿了好久才进入了系统</p><p><img src="/posts/ce2fd1e4/pi_001.png" alt></p><p>  执行 docker ps -a 命令后，报错如下图：</p><p><img src="/posts/ce2fd1e4/pi_002.png" alt></p><p>  执行 sudo service docker restart 后，报错如下图：</p><p><img src="/posts/ce2fd1e4/pi_003.png" alt></p><p>  执行 sudo su 后，报错如下图并卡顿了一会，虽说切换到了root用户，但是打开文件还是只读模式</p><p><img src="/posts/ce2fd1e4/pi_004.png" alt></p><h2 id="解决问题">解决问题</h2><p>  此刻的我好像抓住了重点，突然断电导致了整个文件系统变成了只读。既然是文件系统的问题，那就修复它吧。经过一番搜索发现 fsck 命令可以解决这一问题。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">fsck -t ext4 -v /dev/sda2</span><br><span class="line">reboot</span><br></pre></td></tr></table></figure><p>  根据提示信息一路y下去，然后 reboot 重启即可，如下图：</p><p><img src="/posts/ce2fd1e4/pi_005.png" alt></p><p>  在 fsck -t ext4 -v /dev/sda2 命令中 ext4 代表的是文件系统类型，在 <a href="https://mrxiuxing.com/posts/2f81a42d.html">树莓派折腾记（一）-- 安装系统（Ubuntu Server 20.04.1）</a> 曾提到过，/dev/sda2 代表的是根目录设备文件名称，如下图：</p><p><img src="/posts/ce2fd1e4/pi_006.png" alt></p><p><b>PS：关于更多fsck的参数可以查看 <a href="https://www.runoob.com/linux/linux-comm-fsck.html">Linux fsck 命令 | 菜鸟教程</a></b></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Success is working up in the morning, so excited about what you hava to do.——《名扬四海》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;成功是清晨醒来，对自己的人生充满期待。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/tags/Linux/"/>
    
    <category term="Ubuntu" scheme="https://mrxiuxing.com/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>树莓派折腾记（一）-- 安装系统（Ubuntu Server 20.04.1）</title>
    <link href="https://mrxiuxing.com/posts/2f81a42d.html"/>
    <id>https://mrxiuxing.com/posts/2f81a42d.html</id>
    <published>2021-01-18T14:09:31.000Z</published>
    <updated>2021-01-18T14:09:31.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>We can’t change the past. But we can make the future.——《都市侠盗》</center></strong></em><br><em><strong><center>不念过往，未来可期。</center></strong></em></p><span id="more"></span><h2 id="前言">前言</h2><p>  最近刚整个了个树莓派4B 8GB版本，详细配置如下：</p><p><img src="/posts/2f81a42d/pi_001.png" alt><br><img src="/posts/2f81a42d/pi_002.png" alt></p><p>  准备给它装上 Ubuntu Server 20.04 当服务器来用。关于什么是树莓派这里不做过多赘述，有兴趣的话可以浏览下面几篇文章：<a href="https://baike.baidu.com/item/%E6%A0%91%E8%8E%93%E6%B4%BE/80427?fr=aladdin">树莓派 （微型电脑）_百度百科</a> 、<a href="https://baike.baidu.com/item/%E6%A0%91%E8%8E%93%E6%B4%BE%E7%94%B5%E8%84%91/3928138?fr=aladdin">树莓派电脑_百度百科</a> 、<a href="https://www.zhihu.com/question/32163206/answer/1025151986">能用树莓派干些什么有趣的事情？</a></p><h2 id="镜像准备">镜像准备</h2><p>  既然要安装ubuntu server，那肯定要去ubuntu官网了！</p><p><img src="/posts/2f81a42d/pi_01.png" alt></p><p>  ubuntu 有可供树莓派安装使用的基于arm64架构的系统。我的是树莓派4B，就按照下图所示进行下载。</p><p><img src="/posts/2f81a42d/pi_02.png" alt></p><p>  如果你的浏览器转了好大会圈圈还不下载，就点击下图中的的红框，立即下载。推荐使用迅雷下载，不然你可能要等到花都谢了！！！</p><p><img src="/posts/2f81a42d/pi_03.png" alt></p><p>  下载完成之后它是一个以xz结尾的压缩文件，有600M+大小，我们需要将它解压，解压后的文件后缀为img，有3G+大小，也就是我们所需要的镜像。如下图所示：</p><p><img src="/posts/2f81a42d/pi_04.png" alt></p><h2 id="烧录系统">烧录系统</h2><p>  树莓派安装系统与我们平常用的台式机笔记本安装系统的方式不太一样。树莓派需要进行烧录安装系统，然后我们就需要用到一个专业软件了 balenaEtcher 这是github地址： <a href="https://github.com/balena-io/etcher/releases">https://github.com/balena-io/etcher/releases</a></p><p>  安装好balenaEtcher之后，按照下图所示操作即可</p><p><img src="/posts/2f81a42d/pi_05.png" alt></p><p>  稍等片刻，出现如下图所示，系统就烧录进TF卡中了。关闭balenaEtcher即可</p><p><img src="/posts/2f81a42d/pi_06.png" alt></p><p>  此时，第一个坑点来了。烧录完之后，你的电脑可能找不到TF了，这时你可以重新插拔一下TF卡，如果还是不显示你就重启一下电脑。<br>  随之而来的是第二个坑，从windows自带的磁盘工具查看到还有很大一部分空间未分配，你的可用空间（主分区）大概只有2G左右的样子。</p><p><img src="/posts/2f81a42d/pi_07.png" alt></p><p>  这时就需要另一个工具了 DiskGenius 一个强大的磁盘管理工具，官网地址：<a href="https://www.diskgenius.cn/">https://www.diskgenius.cn/</a> 。打开DiskGenius，找到你的TF卡，不出意外你的TF下有两个分区</p><p><img src="/posts/2f81a42d/pi_08.png" alt></p><p>  选中主分区（Ext4分区），然后右键扩容分区</p><p><img src="/posts/2f81a42d/pi_09.png" alt></p><p>  点击开始，会有一个警告弹窗，点击确定，稍等片刻即可扩容完毕</p><p><img src="/posts/2f81a42d/pi_10.png" alt></p><p>  如下图，这是扩容之后的样子，所有的空闲空间已经扩容到了主分区（Ext4分区）了</p><p><img src="/posts/2f81a42d/pi_11.png" alt></p><p>  使用 WIN + E 打开文件管理，不出意外在你的设备和驱动器下有一个 system-boot 盘符，打开这个盘符，找到 network-config 这个文件（wifi网络配置文件）</p><p><img src="/posts/2f81a42d/pi_11_1.png" alt></p><p>  打开network-config，发现内容如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">version: 2</span><br><span class="line">ethernets:</span><br><span class="line">  eth0:</span><br><span class="line">    dhcp4: true</span><br><span class="line">    optional: true</span><br><span class="line">#wifis:</span><br><span class="line">#  wlan0:</span><br><span class="line">#    dhcp4: true</span><br><span class="line">#    optional: true</span><br><span class="line">#    access-points:</span><br><span class="line">#      myhomewifi:</span><br><span class="line">#        password: &quot;S3kr1t&quot;</span><br><span class="line">#      myworkwifi:</span><br><span class="line">#        password: &quot;correct battery horse staple&quot;</span><br><span class="line">#      workssid:</span><br><span class="line">#        auth:</span><br><span class="line">#          key-management: eap</span><br><span class="line">#          method: peap</span><br><span class="line">#          identity: &quot;me@example.com&quot;</span><br><span class="line">#          password: &quot;passw0rd&quot;</span><br><span class="line">#          ca-certificate: /etc/my_ca.pem</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>  修改为如下内容，你可以添加多个wifi配置，如下内容中TP-LINK_1以及TP-LINK_2都是wifi名称，不需要引号引起来，除非wifi名称中含有空格，密码填入对应wifi名称下password后的引号中即可。此外缩进一定要使用空格。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">version: 2</span><br><span class="line">ethernets:</span><br><span class="line">  eth0:</span><br><span class="line">    dhcp4: true</span><br><span class="line">    optional: true</span><br><span class="line">wifis:</span><br><span class="line">  wlan0:</span><br><span class="line">    dhcp4: true</span><br><span class="line">    optional: true</span><br><span class="line">    access-points:</span><br><span class="line">      TP-LINK_1:</span><br><span class="line">        password: &quot;TP-LINK_1的密码&quot;</span><br><span class="line">      TP-LINK_2:</span><br><span class="line">        password: &quot;TP-LINK_2的密码&quot;</span><br><span class="line">#      myhomewifi:</span><br><span class="line">#        password: &quot;S3kr1t&quot;</span><br><span class="line">#      myworkwifi:</span><br><span class="line">#        password: &quot;correct battery horse staple&quot;</span><br><span class="line">#      workssid:</span><br><span class="line">#        auth:</span><br><span class="line">#          key-management: eap</span><br><span class="line">#          method: peap</span><br><span class="line">#          identity: &quot;me@example.com&quot;</span><br><span class="line">#          password: &quot;passw0rd&quot;</span><br><span class="line">#          ca-certificate: /etc/my_ca.pem</span><br></pre></td></tr></table></figure><h2 id="启动系统">启动系统</h2><p>  <b>首先说明</b>：默认用户名，密码都是ubuntu。首次登录后会让你再输一遍密码也就是ubuntu，然后创建一个复杂密码、 确认密码。如下图所示：如果你是使用ssh工具登录，验证新密码后会退出，你只需使用你刚设置的新密码重新登录即可</p><p><img src="/posts/2f81a42d/pi_11_2.png" alt></p><h3 id="配置网络">配置网络</h3><p>  <b>对于我们安装的ubuntu server亲测有两种方法可以查看ip地址。</b></p><p>  <b>方法①、网线 + 笔记本电脑共享网络 + 显示器（需miniHDMI转HDMI接头）+ 键盘 + 树莓派</b></p><p>  首先在笔记本电脑中依次打开 设置 --&gt; 网络和Internet --&gt; 更改适配器选项，如下图：</p><p><img src="/posts/2f81a42d/pi_12.png" alt></p><p><img src="/posts/2f81a42d/pi_13.png" alt></p><p>  进入到如下界面，可以看到我的笔记本使用的是WLAN连接到网络，网线插口未被使用</p><p><img src="/posts/2f81a42d/pi_14.png" alt></p><p>  我们选中WLAN，右键点击属性，进入到如下界面，然后点击共享</p><p><img src="/posts/2f81a42d/pi_15.png" alt></p><p>  按照下图所示，勾选允许其他网络用户通过此计算机的Internet连接来连接，家庭网络连接选择以太网，点击确定即可</p><p><img src="/posts/2f81a42d/pi_16.png" alt></p><p>  然后将烧录好的TF卡插入树莓派TF卡槽中，网线的一端插入笔记本网口，另一端插入树莓派网口，连接好显示器键盘，然后接通电源，稍等片刻便可进入系统。进入系统修改完密码，使用 ip addr 即可查看ip。</p><p>  <b>方法②、网线 + 路由器 + 树莓派</b></p><p>  这种方法最为便捷，不需要显示器以及键盘等配件。将烧录好的TF卡插入树莓派TF卡槽中，网线的一端插入路由器网口，另一端插入树莓派网口，然后接通电源。</p><p>  当然，使用这种方式连接网络什么时候开机我们是无法直接看到的。需要我们登录路由器，（一般来说地址是192.168.0.1，当然有些品牌型号可能例外，具体请查找说明书）然后通过路由器的设备管理查看新增的设备的ip即可，不出意外的话，新增了一个名称为ubuntu的设备。（有的路由器的设备管理可以区分是否使用网线连接的，我的路由器是TP-LINK WDR5620）如下图：</p><p><img src="/posts/2f81a42d/pi_16_1.png" alt></p><p>  然后就可以使用ssh工具连接到树莓派了，修改初始密码重新连接即可。</p><p>  如果你不知道路由器的登录密码，也可以使用360流量防火墙下的防蹭网来检测设备，腾讯安全管家应该也有类似功能。</p><p>  到了这里，有的朋友可能要问了：那你之前配置的wifi有什么用？树莓派不是支持wifi吗？</p><p>  答：ubuntu server 20.04.x安装完之后，首次启动会初始化一次配置，之前配置的wifi会初始化写入到 /etc/netplan 目录下的yaml配置文件，之后再修改的话只能修改这个yaml配置文件了。而且<b>树莓派安装最大的坑点来了–没有wlan0无线网卡</b>，如下图：只有eth0有线网卡。</p><p><img src="/posts/2f81a42d/pi_17.png" alt></p><p>  这不是我们想要的，在某些情况下我们是需要无线网卡的。经过不断的测试终于解决了没有wlan0的问题。</p><p>  第一步，在首次开机前就配置好wifi，也就是上文中扩容TF卡之后的操作</p><p>  第二步，依次执行下面的命令更新软件（切记：不要更换软件源）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line">sudo apt upgrade</span><br></pre></td></tr></table></figure><p>  如果你在执行sudo apt upgrade时，出现了类似下图问题</p><p><img src="/posts/2f81a42d/pi_11_3.png" alt></p><p>  使用Ctrl + c 停止，然后执行 sudo reboot 重启树莓派。重新依次执行sudo apt update 和 sudo apt upgrade即可。这时，你使用 ip addr 命令发现还是没有wlan0网卡，不要慌不要急，我们执行一下 sudo reboot 重启树莓派。然后执行 ip addr 命令，发现wlan0出现了，如下图：</p><p><img src="/posts/2f81a42d/pi_18.png" alt></p><p>  <b>上面的方法仅适用于ubuntu server 20.04.1版本</b>，ubuntu server 20.04.2版本是行不通的。通过对比发现：ubuntu server 20.04.1版本在更新软件的时候更新了内核</p><p><img src="/posts/2f81a42d/pi_19.png" alt></p><p>  并且随着内核的更新安装了驱动，如下图（太多了，只截取了部分）。</p><p><img src="/posts/2f81a42d/pi_20.png" alt></p><p>  而ubuntu server 20.04.2版本因内核是最新的，没有更新内核，也就没有安装驱动，所以ubuntu server 20.04.2版本无论怎么更新都是找不到wlan0无线网卡的。这里也是最大的坑点（系统在第一次启动初始化时并没有安装驱动）。</p><p>  至于我为什么发现了这点，是因为写这篇文章我拖了好久，以至于ubuntu server 20.04.2都发布了。因为官网只提供最新版本的下载，所以在官网上是找不到ubuntu server 20.04.1版本的。但是我们可以在历史版本找，这是链接： <a href="http://old-releases.ubuntu.com/releases/20.04.1/">http://old-releases.ubuntu.com/releases/20.04.1/</a> ，打开这个链接，滑到最后，选择ubuntu-20.04.1-preinstalled-server-arm64+raspi.img.xz 进行下载即可。</p><p><img src="/posts/2f81a42d/pi_21.png" alt></p><h3 id="更换软件源">更换软件源</h3><p>  官方的软件源还是太慢了，为了以后更新或安装软件时更加的快捷，推荐更换国内的软件源。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将官方源做一下备份</span></span><br><span class="line">sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将ubuntu的镜像站点替换为阿里云的镜像站点</span></span><br><span class="line">sudo sed -i &#x27;s/http:\/\/.*.ubuntu.com/https:\/\/mirrors.aliyun.com/g&#x27; /etc/apt/sources.list</span><br><span class="line">sudo apt update</span><br><span class="line">sudo apt upgrade</span><br></pre></td></tr></table></figure><h3 id="修改时区">修改时区</h3><p>  ubuntu server 安装完后，默认时区并不是Asia/Shanghai，也就是说对于我们来说时间是不准确的，所以我们需要修改下时区。我们可以使用 date -R 命令查看当前时区， 发现为 +0000。</p><p><img src="/posts/2f81a42d/pi_22.png" alt></p><p>  修改时区可以使用 sudo tzselect 命令。例如我要修改为北京时间，可以在输入 sudo tzselect 命令后依次输入 4 --&gt; 9 --&gt; 1 --&gt; 1 ，如下图：</p><p><img src="/posts/2f81a42d/pi_24.png" alt></p><p>  我们还需执行下面的命令让其生效</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime</span><br></pre></td></tr></table></figure><p>  这时使用 date -R 命令查看当前时区，就会发现时区变成了 +0800 ,此刻的时间对于我们来说也就是正确的时间了。</p><p><img src="/posts/2f81a42d/pi_25.png" alt></p><h3 id="配置静态IP">配置静态IP</h3><p>  之前我们在系统第一次启动前就配置了wifi，但是DHCP是开启的，也就是ip可能会因为我们重启机器而改变，所以我们需要将IP固定下来，毕竟不能每次使用ssh工具连接的时候都找一次ip吧。</p><p>  在上文中也提到过，首次启动会初始化一次配置，之前配置的wifi会初始化写入到 /etc/netplan 目录下的yaml配置文件。我们要想配置静态IP也是在这个yaml文件中。我们可以使用下面的命令查看配置文件全名。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ll /etc/netplan/ </span><br></pre></td></tr></table></figure><p>  不出意外的话你的配置文件全名应该也是50-cloud-init.yaml</p><p><img src="/posts/2f81a42d/pi_26.png" alt></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入配置目录</span></span><br><span class="line">cd /etc/netplan/</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">先做一下备份</span></span><br><span class="line">sudo cp 50-cloud-init.yaml 50-cloud-init.yaml.back</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">打开配置文件</span></span><br><span class="line">sudo vim 50-cloud-init.yaml</span><br></pre></td></tr></table></figure><p>  你会发现这不就是之前配置的wifi吗？只不过缩进由2个空格变成了4个空格，密码没有被双引号包裹而已。</p><p><img src="/posts/2f81a42d/pi_27.png" alt></p><p>  配置静态ip，我们需要将DHCP关闭，指定静态ip地址/子网掩码，指定网关地址，指定DNS地址。具体如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">network:</span></span><br><span class="line">    <span class="attr">ethernets:</span></span><br><span class="line">        <span class="attr">eth0:</span>  <span class="comment"># 有线网卡</span></span><br><span class="line">            <span class="attr">addresses:</span></span><br><span class="line">                <span class="bullet">-</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.109</span><span class="string">/24</span>  <span class="comment"># ip地址/子网掩码</span></span><br><span class="line">            <span class="attr">gateway4:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.1</span>  <span class="comment"># 网关地址</span></span><br><span class="line">            <span class="attr">nameservers:</span></span><br><span class="line">                    <span class="attr">addresses:</span> <span class="comment"># DNS地址，多个的话按照下面添加即可</span></span><br><span class="line">                        <span class="bullet">-</span> <span class="number">114.114</span><span class="number">.114</span><span class="number">.114</span></span><br><span class="line">                        <span class="bullet">-</span> <span class="number">8.8</span><span class="number">.8</span><span class="number">.8</span></span><br><span class="line">            <span class="attr">dhcp4:</span> <span class="literal">no</span>  <span class="comment"># DHCP</span></span><br><span class="line">            <span class="attr">optional:</span> <span class="literal">no</span></span><br><span class="line">    <span class="attr">version:</span> <span class="number">2</span></span><br><span class="line">    <span class="attr">wifis:</span></span><br><span class="line">        <span class="attr">wlan0:</span>  <span class="comment"># 无线网卡</span></span><br><span class="line">            <span class="attr">addresses:</span></span><br><span class="line">                <span class="bullet">-</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.110</span><span class="string">/24</span>  <span class="comment"># ip地址/子网掩码</span></span><br><span class="line">            <span class="attr">gateway4:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.1</span>  <span class="comment"># 网关地址</span></span><br><span class="line">            <span class="attr">nameservers:</span></span><br><span class="line">                    <span class="attr">addresses:</span> <span class="comment"># DNS地址，多个的话按照下面添加即可</span></span><br><span class="line">                        <span class="bullet">-</span> <span class="number">114.114</span><span class="number">.114</span><span class="number">.114</span></span><br><span class="line">                        <span class="bullet">-</span> <span class="number">8.8</span><span class="number">.8</span><span class="number">.8</span></span><br><span class="line">            <span class="attr">access-points:</span></span><br><span class="line">                <span class="attr">TP-LINK_1:</span>  <span class="comment"># wifi名称</span></span><br><span class="line">                    <span class="attr">password:</span> <span class="string">TP-LINK_1的密码</span>  <span class="comment"># wifi密码</span></span><br><span class="line">                <span class="attr">TP-LINK_2:</span>  <span class="comment"># wifi名称</span></span><br><span class="line">                    <span class="attr">password:</span> <span class="string">TP-LINK_2的密码</span>  <span class="comment"># wifi密码</span></span><br><span class="line">            <span class="attr">dhcp4:</span> <span class="literal">no</span>  <span class="comment"># DHCP</span></span><br><span class="line">            <span class="attr">optional:</span> <span class="literal">no</span></span><br></pre></td></tr></table></figure><p>  然后使用下面的命令让配置生效，当然你也可以重启树莓派。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo netplan --debug apply</span><br></pre></td></tr></table></figure><p>  如果你当前连接树莓派的ip地址与你在配置文件中指定的地址不一样，ssh工具会断开与树莓派的连接。当然这也说明你的配置大概率是成功了的，然后你就可以使用你刚刚在配置文件中指定的ip地址连接到树莓派了。</p><p>  使用 ip addr 命令查看效果。如下图：可以看到有限网卡 eth0 静态ip为 192.168.0.109 ，无线网卡 wlan0 静态ip为 192.168.0.110, ， 和我们配置的一样，说明配置生效了。</p><p><img src="/posts/2f81a42d/pi_28.png" alt></p><h2 id="写在最后">写在最后</h2><p>  至此，ubuntu server 20.04的安装以及配置就完毕了。整个安装过程可以说是一波三折，小问题不断。因为树莓派的受众很小，再加上很少有人使用树莓派当做服务器，所以很多问题网上没有同类型的只能自己摸索，如果你还遇到了其他问题，欢迎在下方留言。</p><p><b>参考：</b><br><a href="https://www.cnblogs.com/mrfangd/p/Ubuntu20_04-Change_repo.html">Ubuntu Server 20.04换用阿里源 - mrfangd</a><br><a href="https://www.cnblogs.com/jiu0821/p/5999566.html">linux修改系统时间和linux查看时区、修改时区的方法 - jiu__</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;We can’t change the past. But we can make the future.——《都市侠盗》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;不念过往，未来可期。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/tags/Linux/"/>
    
    <category term="Ubuntu" scheme="https://mrxiuxing.com/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu Server 20.04 配置静态IP</title>
    <link href="https://mrxiuxing.com/posts/32e09451.html"/>
    <id>https://mrxiuxing.com/posts/32e09451.html</id>
    <published>2020-12-29T14:45:51.000Z</published>
    <updated>2020-12-29T14:45:51.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>All that matters is knowing what you really want and goting after it . ——《速度与激情3：东京漂移》</center></strong></em><br><em><strong><center>重要的是知道自己想要什么，然后去争取。</center></strong></em></p><span id="more"></span><h2 id="前言">前言</h2><p>  在上篇文章<a href="https://mrxiuxing.com/posts/a4f1f8b3.html">Ubuntu Server 20.04 LTS 安装</a>中，留下了一点小坑，那就是没有配置静态IP，没有静态IP意味着你的虚拟机、电脑或路由设备重启，IP有可能会更换。如果你使用ssh工具连接虚拟机或者你把虚拟机当做服务器来用并安装一些服务的话，这意味着每次重启你都大概率需要修改IP。所以为了方便使用，我们需要将IP固定，也就是将动态IP设置为静态IP。</p><h2 id="配置">配置</h2><p>  为了方便使用一些网络命令，我们需要安装 net-tools。（我这样是已经安装过了的）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install net-tools</span><br></pre></td></tr></table></figure><p><img src="/posts/32e09451/ip_01.png" alt></p><p>  现在我们在终端输入 ifconfig ，可以看到我的网卡是 ens32，IP是192.168.0.200</p><p><img src="/posts/32e09451/ip_02.png" alt></p><p>  终端输入 route -n 看一下网关，是192.168.0.1</p><p><img src="/posts/32e09451/ip_03.png" alt></p><p>  Ubuntu Server 20.04 是使用 netplan 工具进行修改IP的，所以我们进入 /etc/netplan 目录, 然后使用 ll 命令查看目录有哪些文件,。不出意外的话你的Ubuntu Server 20.04在这个目录下也是只有一个以 yaml 结尾的文件。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /etc/netplan</span><br><span class="line">ll</span><br></pre></td></tr></table></figure><p><img src="/posts/32e09451/ip_04.png" alt></p><p>  我们需要修改的就是这个以 yaml 结尾的文件，为了防止意外，做个备份</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo cp 00-installer-config.yaml 00-installer-config.yaml.back</span><br></pre></td></tr></table></figure><p>  现在就可以放心的修改了，我们打开这个yaml文件，看看里面是啥？</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vi 00-installer-config.yaml</span><br></pre></td></tr></table></figure><p><img src="/posts/32e09451/ip_05.png" alt></p><p>  这比想象中的简单呀！上图中 dhcp4 的值为 true ，就是因为它，每次重启才会大概率会更换IP。我们需要把它关掉，然后加入我们需要的配置，如下图：</p><p><img src="/posts/32e09451/ip_06.png" alt></p><p>  如果你的网卡和网关都和我的一样，那么赶紧把下面的这一段复制粘贴保存吧！</p><p>  <b>注意：网卡设为你自己的网卡，网关设置为你自己的网关，IP要根据你的网关去设置，例如你的网关是192.168.0.1 那么你的IP 可以设置为 192.168.0.x , x的范围是大于1且小于255（因为你的网关是192.168.0.1所以的IP不能设置为192.168.0.1；广播地址为192.168.0.255，所以不能设置为192.168.0.255），掩码必须有，且一般为24。yaml文件中冒号是英文冒号，且冒号后面要有空格。</b></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="attr">network:</span></span><br><span class="line">  <span class="attr">ethernets:</span></span><br><span class="line">    <span class="attr">ens32:</span></span><br><span class="line">      <span class="attr">dhcp4:</span> <span class="literal">false</span></span><br><span class="line">      <span class="attr">addresses:</span> [<span class="number">192.168</span><span class="number">.0</span><span class="number">.200</span><span class="string">/24</span>]</span><br><span class="line">      <span class="attr">gateway4:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.1</span></span><br><span class="line">      <span class="attr">nameservers:</span></span><br><span class="line">              <span class="attr">addresses:</span> [<span class="number">192.168</span><span class="number">.0</span><span class="number">.1</span>,<span class="number">8.8</span><span class="number">.8</span><span class="number">.8</span>]</span><br><span class="line">  <span class="attr">version:</span> <span class="number">2</span></span><br></pre></td></tr></table></figure><p>  修改改完之后还需要重启 netplan 才能生效</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo netplan --debug apply</span><br></pre></td></tr></table></figure><p>  使用 ifconfig 或者 ip address 看看有没有生效（我是修改完才写的这篇文章，所以就不再修改了）</p><p><img src="/posts/32e09451/ip_07.png" alt></p><p>  使用 ping 检测下网络连通性</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ping www.baidu.com -c 4</span><br></pre></td></tr></table></figure><p><img src="/posts/32e09451/ip_08.png" alt></p><p>  至此，大功告成！！！</p><h2 id="写在最后">写在最后</h2><p>  在文章中我并没有说遇到了哪些坑点，而是尽量帮你避开坑点，尤其是文中的<b>注意</b>,坑点基本上都在那了。我遇到的我都做了详细说明，如果你还遇到了其他坑点，欢迎在评论留言。<br>  如果某天你的虚拟机突然上不去网络，排除外部因素后（大概就是IP冲突了），可以试试 dhclient 这个命令，动态获取或释放IP地址，这个只是一次性的，在你机器重启后，IP还会还原之前的配置。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo dhclient ens32  # ens32改成你的网卡</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;All that matters is knowing what you really want and goting after it . ——《速度与激情3：东京漂移》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;重要的是知道自己想要什么，然后去争取。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/tags/Linux/"/>
    
    <category term="Ubuntu" scheme="https://mrxiuxing.com/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu Server 20.04 LTS 安装</title>
    <link href="https://mrxiuxing.com/posts/a4f1f8b3.html"/>
    <id>https://mrxiuxing.com/posts/a4f1f8b3.html</id>
    <published>2020-12-27T05:42:51.000Z</published>
    <updated>2020-12-27T05:42:51.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>Keep  smiling and never give up, even when things get you down. ——《黑鹰坠落》</center></strong></em><br><em><strong><center>即使现实令你沮丧, 也要保持微笑，永不永不言败。</center></strong></em></p><span id="more"></span><h2 id="本地环境">本地环境</h2><p>  Windows 10 专业版 1909<br>  VMware Workstation 16 Pro</p><h2 id="镜像准备">镜像准备</h2><p>  进入<a href="https://ubuntu.com/download/server">Ubuntu Server 下载页面</a>，点击Option 3</p><p><img src="/posts/a4f1f8b3/server_01.png" alt></p><p>  进入如下图的页面，点击 Download Ubuntu Server 20.04.1 LTS，就可以下载了。</p><p><img src="/posts/a4f1f8b3/server_02.png" alt></p><p>  这样直接下载的速度大概是2~3m的样子，如果你觉得下载慢，可以使用迅雷下载。这里我使用的是Chrome 可以在搜索栏输入 chrome://downloads/ 既可进入Chrome的下载内容，然后右键复制链接地址在迅雷下载，我使用的是百兆带宽的WiFi，因为连接的设备较多，但依然可以达到5m左右。</p><p><img src="/posts/a4f1f8b3/server_03.png" alt></p><h2 id="VMware-准备">VMware 准备</h2><p>  选择自定义（高级），点击下一步</p><p><img src="/posts/a4f1f8b3/server_04.png" alt></p><p>  点击下一步</p><p><img src="/posts/a4f1f8b3/server_05.png" alt></p><p>  选择稍后安装操作系统，点击下一步</p><p><img src="/posts/a4f1f8b3/server_06.png" alt></p><p>  客户端操作系统选择Linux，版本选择Ubuntu 64 位即可，点击下一步</p><p><img src="/posts/a4f1f8b3/server_07.png" alt></p><p>  虚拟机名称输入你想给虚拟机起的名字，这个名字是展示在VMware上的名字以及后面设置磁盘文件名称的默认文件名；位置，即你想要储存的位置，如果你C盘剩余空间不富裕，就不要选择C盘了；点击下一步</p><p><img src="/posts/a4f1f8b3/server_08.png" alt></p><p>  处理器数量以及每个处理器内核数量可以根据需求设置，但这里后期是可以修改的，所有我选择的是默认；然后点击下一步</p><p><img src="/posts/a4f1f8b3/server_09.png" alt></p><p>  这里是分配给虚拟机内存。可以在1号位置直接输入，也可以在2号位置上下滑动。（但是最好不要出超过最大推荐内存，我的机器是16G内存，这里显示的最大推荐内存是13.4GB，也就是我给这台虚拟机分配的最大内存不能超过13.4GB）点击下一步</p><p><img src="/posts/a4f1f8b3/server_10.png" alt></p><p>  网络连接选择使用桥接网络（这样虚拟机就有了一个除192.168.0.1的类似于192.168.X.X的内网IP）点击下一步</p><p><img src="/posts/a4f1f8b3/server_11.png" alt></p><p>  选择I/O控制器类型，这里选择推荐即可；点击下一步</p><p><img src="/posts/a4f1f8b3/server_12.png" alt></p><p>  选择磁盘类型，这里选择推荐即可；点击下一步</p><p><img src="/posts/a4f1f8b3/server_13.png" alt></p><p>  选择创建新虚拟磁盘；点击下一步</p><p><img src="/posts/a4f1f8b3/server_14.png" alt></p><p>  根据需求设置磁盘大小，因大多数云服务器都是40GB，所以我这里设置的40GB；选择将虚拟机存储为单个文件；点击下一步</p><p><img src="/posts/a4f1f8b3/server_15.png" alt></p><p>  指定磁盘文件，这里默认就好；点击下一步</p><p><img src="/posts/a4f1f8b3/server_16.png" alt></p><p>  这里我们还可以再自定义一下硬件</p><p><img src="/posts/a4f1f8b3/server_17.png" alt></p><p>  如果你对刚才设置的内存或者处理器不太满意可以更改一下，也可以移除一些用不到的设备，如声卡和打印机，如果你不需要更改上面所说的设置，那就点击新CD/DVD（STSA）</p><p><img src="/posts/a4f1f8b3/server_18.png" alt></p><p>  选择使用ISO映像文件，点击浏览，找到你刚刚下载的Ubuntu Server 20.04 镜像文件；点击关闭</p><p><img src="/posts/a4f1f8b3/server_19.png" alt></p><p>  点击完成</p><p><img src="/posts/a4f1f8b3/server_20.png" alt></p><p>  至此，VMware Workonstation 准备完毕</p><p><img src="/posts/a4f1f8b3/server_21.png" alt></p><h2 id="安装-Ubuntu-Server">安装 Ubuntu Server</h2><p>  点击上图中的开启此虚拟机，等待片刻，直到出现下图所示。使用键盘上的上下键选择语言，这里我选择的是English（主要是没有中文），然后按下回车键</p><p><img src="/posts/a4f1f8b3/server_22.png" alt></p><p>  这里选择Continue without updating 回车</p><p><img src="/posts/a4f1f8b3/server_23.png" alt></p><p>  选择键盘布局，或选择“识别键盘”自动检测您的键盘布局 ，我使用的默认设置，回车</p><p><img src="/posts/a4f1f8b3/server_24.png" alt></p><p>  配置至少一个此服务器可用于与其他计算机对话的接口也就是IP，这里使用的是自动分配的IP，你在安装过程中生成的和我的可能不一样，不用担心，直接回车</p><p><img src="/posts/a4f1f8b3/server_25.png" alt></p><p>  以“http://[[user][：pass]@]host[：port]/”的格式配置代理，我没有代理，直接回车</p><p><img src="/posts/a4f1f8b3/server_26.png" alt></p><p>  配置你在使用apt时的镜像源，这是我设置的是 <a href="http://mirrors.aliyun.com/ubuntu/">http://mirrors.aliyun.com/ubuntu/</a> ，也就是阿里云的镜像源。键盘按下上键。进入 mirror address 输入框，输入阿里云镜像源，然后下键回到Done，如下图所示，回车（当然你也可以直接回车然后在安装完系统之后进行配置）</p><p><img src="/posts/a4f1f8b3/server_27.png" alt></p><p>  如果你是新手，不要选中2号位置，（如果选中2号选项，则可以选择设置LVM并使用LUKS加密LVM配置）然后回车即可</p><p><img src="/posts/a4f1f8b3/server_28.png" alt></p><p>   如果你觉得这样设置可以，直接回车</p><p><img src="/posts/a4f1f8b3/server_29.png" alt></p><p>   选择Continue将会清空整个磁盘进行安装，并且你将无法回退到上一步，（这里的清空磁盘指的是你分配给此虚拟机的虚拟磁盘，并不是你的物理磁盘，不用担心）。我们选中Continue，回车</p><p><img src="/posts/a4f1f8b3/server_30.png" alt></p><p>   这里依次输入的是：你的名字、你服务器的名字、用户名、密码、验证密码，设置完回车即可</p><p><img src="/posts/a4f1f8b3/server_31.png" alt></p><p>   这里我们选中 Install OpenSSH server，当然这个也是可以在安装完系统之后安装，这里为了省事就选中了，然后选中 Done 回车</p><p><img src="/posts/a4f1f8b3/server_32.png" alt></p><p>   安装适用于Ubuntu的软件，这里根据需求选择即可，然后选中 Done 回车</p><p><img src="/posts/a4f1f8b3/server_33.png" alt></p><p>   到了这里，便开始 Ubuntu Server 的安装了。安装速度取决于你的网速、磁盘速度、软件选择的多少。</p><p><img src="/posts/a4f1f8b3/server_34.png" alt></p><p>   如果出现了下图所示的情况，你可以选择继续等待，知道安装完毕，也可以选中 Cancel update and reboot（取消更新并重启）这里我选择了取消更新并重启</p><p><img src="/posts/a4f1f8b3/server_35.png" alt></p><p>   取消更新也是需要等待片刻的</p><p><img src="/posts/a4f1f8b3/server_36.png" alt></p><p>   出现如下图所示，直接回车，然后虚拟机就会重启了，等待片刻</p><p><img src="/posts/a4f1f8b3/server_37.png" alt></p><p>   如果出现如下图所示，直接回车，就可以登录系统了</p><p><img src="/posts/a4f1f8b3/server_38.png" alt></p><p>   输入你的用户名，回车；然后输入你的密码（密码是不会显示的，就直接输完回车即可）然后就会来到如下图所示页面</p><p><img src="/posts/a4f1f8b3/server_39.png" alt></p><p>   因为我们在安装系统时取消了更新，所以有些包不是最新的，使用 sudo apt update 然后输入密码，看一下有多少需要更新。如下图所示，有80个包可以更新，可以使用 apt list --upgrade 查看具体是哪些需要更新。（因为在安装系统的时候我们设置了 mirror address 为阿里云，所以这里使用apt时命中的都是阿里云的源）接下来使用 sudo apt upgrade -y 更新包<br><img src="/posts/a4f1f8b3/server_40.png" alt></p><p>  我们可以再次执行 sudo apt update 以及 sudo apt upgrade 发现没有需要更新的包了。</p><p><img src="/posts/a4f1f8b3/server_41.png" alt></p><p>  此时我们还需要执行下面这条命令，来将我们的时区修改为东八区，不然在使用程序提取的时间，会比当前时间晚8个小时。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">timedatectl set-timezone &#x27;Asia/Shanghai&#x27;</span><br></pre></td></tr></table></figure><p>  至此 Ubuntu Server 20.04 安装完毕。因为是在虚拟机安装的，所以千万别忘了快照这个超级实用的功能，为了避免手残造成的失误，方便回退到你设置快照的某一时间节点。</p><p>  我们可以使用 ip addr 查看当前机器的ip地址。有了这个地址我们就可以使用ssh工具连接到这台机器了。如下图，这台机器的地址是 192.168.0.106</p><p><img src="/posts/a4f1f8b3/server_42.png" alt></p><p>   VMware 还有一个强大的功能就是克隆。如果你想搭建集群，又不想一台一台安装，那就可以使用克隆功能（前提：被克隆的机器状态必须是关闭）。还有另一种方法，如本次安装机器的名字是 UbuntuServer，你在安装目录下找到UbuntuServer文件夹，复制一下，换个名字即可。</p><h2 id="写在最后">写在最后</h2><p><b>   文中有个小坑，有时间再填 。。。</b></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;Keep  smiling and never give up, even when things get you down. ——《黑鹰坠落》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;即使现实令你沮丧, 也要保持微笑，永不永不言败。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/categories/Linux/"/>
    
    
    <category term="Linux" scheme="https://mrxiuxing.com/tags/Linux/"/>
    
    <category term="Ubuntu" scheme="https://mrxiuxing.com/tags/Ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>Scrapy</title>
    <link href="https://mrxiuxing.com/posts/6b8faf09.html"/>
    <id>https://mrxiuxing.com/posts/6b8faf09.html</id>
    <published>2020-11-16T11:32:59.000Z</published>
    <updated>2020-11-16T11:32:59.000Z</updated>
    
    <content type="html"><![CDATA[<p><em><strong><center>If you only do what you can do， you will never be more than you are now. ——《功夫熊猫3》</center></strong></em><br><em><strong><center>如果你只做力所能及的事情, 你就没办法进步。</center></strong></em></p><span id="more"></span><h2 id="什么是-Scrapy">什么是 Scrapy</h2><p>  Scrapy是适用于Python的一个快速、高层次的屏幕抓取和web抓取框架，用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛，可以用于数据挖掘、监测和自动化测试。</p><p>  Scrapy是一个适用爬取网站数据、提取结构性数据的应用程序框架，它可以应用在广泛领域：Scrapy 常应用在包括数据挖掘，信息处理或存储历史数据等一系列的程序中。通常我们可以很简单的通过 Scrapy 框架实现一个爬虫，抓取指定网站的内容或图片。</p><p>  截止2020年11月16日，Scrapy最新版本为2.4，Python版本最低要求3.6+，适用于Linux，Windows，macOS，BSD</p><h2 id="Scrapy-官方站点">Scrapy 官方站点</h2><p>   github地址：<a href="https://github.com/scrapy/scrapy">https://github.com/scrapy/scrapy</a><br>   官方社区：<a href="https://scrapy.org/">https://scrapy.org/</a><br>   官方文档：<a href="https://docs.scrapy.org/en/latest/">https://docs.scrapy.org/en/latest/</a></p><h2 id="安装-Scrapy">安装 Scrapy</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install scrapy</span><br></pre></td></tr></table></figure><p>你也可以指定源，一般这样会快很多。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install scrapy -i https://pypi.douban.com/simple</span><br></pre></td></tr></table></figure><p>  如果你的scrapy是很早之前安装的，你也可以使用下面的命令进行升级安装。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install scrapy -i --upgrade https://pypi.douban.com/simple</span><br></pre></td></tr></table></figure><p>  我在使用上面的命令进行更新时出现了以下错误：<br><img src="/posts/6b8faf09/scrapy_1.png" alt></p><p>  意思是：gerapy 0.9.2 版本要求 pymysql 的版本为 0.7.10，但是您的 pymysql 版本为 0.10.1，这是不兼容的。不过在上图的最后一行中，我们可以了解到 scrapy 是更新成功的。</p><p>  使用下面的命令查看scrapy 现在的版本。确定scrapy是更新成功了。既然安装成功就暂时先不管这些小问题了。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scrapy --version</span><br></pre></td></tr></table></figure><p><img src="/posts/6b8faf09/scrapy_2.png" alt></p><p>  使用 pip install scrapy 安装，极可能会遇到 Twisted 安装报错以及其他包版本的错误。<br>  关于 Twisted 可以在这个网站下载：<a href="https://pypi.org/project/Twisted/#files">https://pypi.org/project/Twisted/#files</a><br>  在下载之前一定要先确定你机器上的Python版本，在cmd中输入python即可，如下图：</p><p><img src="/posts/6b8faf09/scrapy_3.png" alt></p><p>  可以看出我的python版本是3.7，机器是64位机器，那么我就可以下载下图所示版本：</p><p><img src="/posts/6b8faf09/scrapy_4.png" alt></p><p>  下载完成之后 使用 win + R 输入cmd 打开命令窗口，并输入 pip install 然后将你下载好的Twisted文件拖进命令窗口，然后回车即可安装。</p><p>  安装完 Twisted 再次执行 pip install scrapy 命令进行安装，如果遇到其他包版本问题，按照错误提示安装指定版本的包即可。</p><p>  当然我最推荐的安装方式是：先安装anaconda，然后安装scrapy，这样可以避免很多错误以节约时间。关于anaconda的下载可以到<a href="https://mirror.tuna.tsinghua.edu.cn/help/anaconda/">清华大学开源软件镜像站</a>，关于镜像源的更换可以看<a href="https://jingyan.baidu.com/article/1876c8527be1c3890a137645.html">百度经验 - anaconda清华源更换</a>这篇文章。</p><p>  在清华源镜像站中 Anaconda3-2020.02-Windows-x86_64.exe 是最新的支持python3.7的版本，Anaconda3-2020.07-Windows-x86_64.exe是最新的支持python3.8的版本，关于其他版本可自行安装测试。</p><h2 id="Scrapy架构">Scrapy架构</h2><p><img src="/posts/6b8faf09/scrapy_5.jpg" alt></p><h3 id="Scrapy-组件">Scrapy 组件</h3><p><b>1、Scrapy Engine (引擎):</b><br>  负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯，信号、数据传递等。</p><p><b>2、Scheduler (调度器):</b><br>  负责接受引擎发送过来的Request请求，并按照一定的方式进行整理排列，入队，当引擎需要时，交还给引擎。</p><p><b>3、Downloader (下载器):</b><br>  负责下载Scrapy Engine(引擎)发送的所有Requests请求，并将其获取到的Responses交还给Scrapy Engine(引擎)，由引擎交给Spider来处理。</p><p><b>4、Spider (爬虫):</b><br>  负责处理所有Responses，从中分析提取数据，获取Item字段需要的数据，并将需要跟进的URL提交给引擎，再次进入Scheduler(调度器)。</p><p><b>5、Item Pipeline (管道):</b><br>  负责处理Spider中获取到的Item，并进行进行后期处理（详细分析、过滤、存储等）的地方。</p><p><b>6、Downloader Middlewares (下载中间件):</b><br>  介于Scrapy引擎和下载器之间的中间件，可以自定义扩展下载功能的组件。</p><p><b>7、Spider Middlewares (爬虫中间件):</b><br>  介于Scrapy引擎和爬虫之间的中间件，可以自定义扩展操作引擎和爬虫中间通信的功能组件。</p><p><b>8、Scheduler Middlewares(调度中间件):</b><br>  介于Scrapy引擎和调度之间的中间件，可以自定义扩展操作引擎和调度器中间通信的功能组件。</p><h3 id="Scrapy-运行流程">Scrapy 运行流程</h3><p>1、Spider：Engine，Engine 起来干活了！Spider 将 第一个 requests 发送给 Engine (引擎)<br>2、Engine：知道了！ Engine 对 requests 不做任何的处理就发送给 Scheduler (调度器)<br>3、Scheduler：看了一眼这个 requests，Scheduler 将 requests 发送给 Engine (引擎) ，并告诉引擎：你把它交给 Downloader (下载器)<br>4、Engine：知道了！ Engine 默默地将 requests (Downloader Middlewares中process_request进行处理) 发送给 Downloader (下载器)<br>5、Downloader：Engine，你看，我下载好了，快夸夸我。Downloader 将 response (Downloader Middlewares中process_response进行处理) 发送给 Engine (引擎)<br>6、Engine：知道了！Engine 将 response 交给 spider (爬虫)<br>7、Spider：Engine，Engine 我处理好了。Spider 将 item 以及下一个 requests 发送给Engine (引擎)<br>8、Engine：知道了！Engine 将 item 交给 Item Pipeline (管道) 过滤储存，将下一个 requests 发送给 Scheduler (调度器)</p><p>  如果你只有一个链接需要抓取，那么在第8步的时候 Engine 将 item 交给 Item Pipeline 后，Engine就会停止，直至 Item Pipeline处理完所有的 item ，整个程序才算停止。在后面我会举例子证明这一点。如果你有很多的链接需要抓取，那么这个流程就是：1 ——&gt; 2 ——&gt; 3 ——&gt; 4 ——&gt; 5 ——&gt; 6 ——&gt; 7 ——&gt; 8 ——&gt; 3 ——&gt; 4 ——&gt; 5 ——&gt; 6 ——&gt; 7 ——&gt; 8 ——&gt; 3 ——&gt; … …  ——&gt; 8 。直至所有 item 处理完成，程序停止。</p><h2 id="文中提到的网站">文中提到的网站</h2><p><a href="https://github.com/scrapy/scrapy">Scrapy | github</a>, <a href="https://scrapy.org/">Scrapy | 官方社区</a>, <a href="https://docs.scrapy.org/en/latest/">Scrapy | 官方文档</a>, <a href="https://pypi.org/project/Twisted/#files">pypi | Twisted</a>, <a href="https://mirror.tuna.tsinghua.edu.cn/help/anaconda/">清华大学开源软件镜像站</a>, <a href="https://jingyan.baidu.com/article/1876c8527be1c3890a137645.html">百度经验 | anaconda清华源更换</a></p><h2 id="未完待续-…">未完待续 …</h2>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;center&gt;If you only do what you can do， you will never be more than you are now. ——《功夫熊猫3》&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;&lt;center&gt;如果你只做力所能及的事情, 你就没办法进步。&lt;/center&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/categories/%E7%88%AC%E8%99%AB/"/>
    
    
    <category term="爬虫" scheme="https://mrxiuxing.com/tags/%E7%88%AC%E8%99%AB/"/>
    
    <category term="Python" scheme="https://mrxiuxing.com/tags/Python/"/>
    
    <category term="Scrapy" scheme="https://mrxiuxing.com/tags/Scrapy/"/>
    
  </entry>
  
</feed>
