<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>曹培の技术栈</title>
  
  <subtitle>拥抱变化</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://itenthusiast.github.io/"/>
  <updated>2018-06-14T09:07:46.000Z</updated>
  <id>https://itenthusiast.github.io/</id>
  
  <author>
    <name>曹培</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>RN准备篇-web知识点扫盲</title>
    <link href="https://itenthusiast.github.io/2018/06/14/RN%E5%87%86%E5%A4%87%E7%AF%87-web%E7%9F%A5%E8%AF%86%E7%82%B9%E6%89%AB%E7%9B%B2/"/>
    <id>https://itenthusiast.github.io/2018/06/14/RN准备篇-web知识点扫盲/</id>
    <published>2018-06-14T08:34:51.000Z</published>
    <updated>2018-06-14T09:07:46.000Z</updated>
    
    <content type="html"><![CDATA[<p><a href="http://www.w3school.com.cn" target="_blank" rel="noopener">W3school</a>：你想知道的这里都有</p><p><a href="http://www.bootcss.com" target="_blank" rel="noopener">Bootstrap</a>、<a href="http://www.jquery123.com" target="_blank" rel="noopener">jQuery</a>：站在巨人的肩膀上敲代码</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;http://www.w3school.com.cn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;W3school&lt;/a&gt;：你想知道的这里都有&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.bootcss.com&quot; target
      
    
    </summary>
    
      <category term="ReactNative" scheme="https://itenthusiast.github.io/categories/ReactNative/"/>
    
    
      <category term="HTML、CSS、JavaScript DOM" scheme="https://itenthusiast.github.io/tags/HTML%E3%80%81CSS%E3%80%81JavaScript-DOM/"/>
    
      <category term="Bootstrap" scheme="https://itenthusiast.github.io/tags/Bootstrap/"/>
    
      <category term="jQuery" scheme="https://itenthusiast.github.io/tags/jQuery/"/>
    
  </entry>
  
  <entry>
    <title>浅析KVO、KVC</title>
    <link href="https://itenthusiast.github.io/2018/05/31/%E6%B5%85%E6%9E%90KVO%E3%80%81KVC/"/>
    <id>https://itenthusiast.github.io/2018/05/31/浅析KVO、KVC/</id>
    <published>2018-05-31T12:31:15.000Z</published>
    <updated>2018-10-11T07:58:09.842Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Thu Oct 11 2018 16:04:47 GMT+0800 (CST) --><h2 id="kvo"><a class="markdownIt-Anchor" href="#kvo"></a> KVO</h2><p><strong>Key-Value Observing</strong>，“键值监听”：用于监听某一对象属性值的改变。</p><p>某一对象被监听后，系统利用 Runtime API 对该对象的原始类动态生成一个 “<code>NSKVONotifying_CPPerson</code>” 子类，并且让 instance 对象的 isa 指针指向这个子类。</p><p>在新生成的这个子类中会重写父类的 <code>setAge:</code>、<code>class</code>、<code>dealloc</code>、<code>_isKVOA</code> 四个方法。</p><p>当被监听对象的属性值发生改变后，会在 <code>setAge:</code> 方法中调用 NSFoundation 的 “<code>_NSSetIntValueAndNotify</code>” 函数。且在该函数内部重写了父类的 <code>willChangeValueForKey：</code>、<code>setAge：</code>、<code>didChangeValueForKey：</code>方法。</p><p>并且在 <code>didChangeValueForKey：</code>方法中出发监听器 Observe 的监听方法 “<code>observeValueForKeyPath: ofObject: change: context:</code>”。</p><p>本质：</p><ul><li><p>由于 KVO 的本质是重写了原始类的 <code>Setter 方法</code>，所以像直接修改成员变量是不会触发监听方法的。</p></li><li><p>若想手动触发监听器的监听方法：手动执行 <code>willChangeValueForKey：</code>、<code>didChangeValueForKey：</code>方法。</p></li><li><img src="/2018/05/31/浅析KVO、KVC/1.png" class="[机型]" title="[100][100]"></li></ul><h2 id="kvc"><a class="markdownIt-Anchor" href="#kvc"></a> KVC</h2><p><strong>Key-Value Coding</strong>，“键值编码”：可以通过某一个 key 来访问对应的属性。</p><p>KVC 不管有没有调用 <code>setter 方法</code>，都会触发 KVO 的监听方法。因为它会手动触发监听。</p><p>赋值操作：<strong><code>setValue: forKey:</code></strong></p><ul><li><p>首先会寻找 <code>setAge:</code>、<code>_setAge:</code> 两个方法的实现。</p></li><li><p>若没有，会通过 <code>accessInstanceVariablesDirectly</code> 方法判断是否允许访问成员变量，若为 YES，则访问的顺序为 <code>_age</code>、<code>_isAge</code>、<code>age</code>、<code>isAge</code>。</p></li></ul><img src="/2018/05/31/浅析KVO、KVC/2.png" class="[机型]" title="[100][100]"><p>取值操作：<strong><code>valueForKey:</code></strong></p><ul><li><p>首先会寻找 <code>getAge:</code>、<code>age:</code>、<code>isAge:</code>、<code>_age:</code> 四个方法的实现。</p></li><li><p>若没有，同上。</p></li><li><img src="/2018/05/31/浅析KVO、KVC/3.png" class="[机型]" title="[100][100]"></li></ul><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      
      
        &lt;!-- build time:Thu Oct 11 2018 16:04:47 GMT+0800 (CST) --&gt;&lt;h2 id=&quot;kvo&quot;&gt;&lt;a class=&quot;markdownIt-Anchor&quot; href=&quot;#kvo&quot;&gt;&lt;/a&gt; KVO&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Key
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="OC进阶语法" scheme="https://itenthusiast.github.io/categories/iOS/OC%E8%BF%9B%E9%98%B6%E8%AF%AD%E6%B3%95/"/>
    
    
      <category term="KVO、KVC" scheme="https://itenthusiast.github.io/tags/KVO%E3%80%81KVC/"/>
    
  </entry>
  
  <entry>
    <title>iOS签名机制</title>
    <link href="https://itenthusiast.github.io/2018/05/28/iOS%E7%AD%BE%E5%90%8D%E6%9C%BA%E5%88%B6/"/>
    <id>https://itenthusiast.github.io/2018/05/28/iOS签名机制/</id>
    <published>2018-05-28T10:07:50.000Z</published>
    <updated>2018-05-28T13:03:58.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><p>曾几何时，人类变得越来越聪明，慢慢地，个体之间的信任问题也成了科技发展历程中的那条鲶鱼。</p><p>前段时间在了解 iOS 逆向开发时都是在越狱设备上进行修改、调试。若想在非越狱设备上“非法”安装 APP ，这事儿就变得没那么简单了。但是如果我们知道 Apple 对我们的 APP 都做了哪些防护处理，理论上是可以越过AppStore 直接在非越狱设备上安装应用的。在此之前有必要先了解一些身份认证相关的知识点。</p><h2>身份认证</h2><p>认证就意味着，需要你提供一些身份证明，来证明你是谁。HTTP 提供了一个原生的 <em>质询/响应框架（challenge/response）</em> ：Web 应用程序收到一条 HTTP 请求报文时，服务器没有按照请求执行动作，而是以一个“认证询问”进行响应，要求用户提供一些保密信息来证明他是谁。</p><p>HTTP 通过一组可定制的控制首部，为不同的认证协议提供了一个可拓展框架。 两个官方的认证协议：基本认证、摘要认证。</p><p>**基本认证：**浏览器收到来自服务器的质询时，请求用户输入这个安全域的用户名和密码，然后将用户名和密码稍加干扰码发送给服务器去认证身份，服务器对用户名和密码进行解码，验证其正确性。使用 <strong>Base-64</strong> 编码方式可以对信息进行扰乱，这样可以防止敏感信息被不小心注意到。虽然简单便捷，但不安全，很容易被中间人通过反编码过程进行解码。只能用它来防止非恶意用户无意间进行访问，或将其与 SSL 这样的加密技术配合使用。所以经过 Base-64 编码的密码实际上就是“明文”传送。假冒服务器也可以很容易骗过基本认证。</p><p>**摘要认证：**又称为单向散列函数、哈希函数或加密的校验和。是另一种 HTTP 认证协议，与基本认证兼容，但却更为安全。主要表现为：不会以明文方式在网络上发送信息；可以防止数据内容被篡改。摘要是一种单向函数，主要用于将无限的输入值转换为有限的浓缩输出值，而且这个过程是不可逆的。常见的摘要函数有 <strong>MD5</strong>，会将任意长度的字节序列转换为一个 128 位的摘要。<strong>安全散列算法</strong>（ <strong>SHA-3</strong>） 是另一种常见的摘要函数。</p><h2>对称密钥</h2><p>前面提到的认证技术，对很多网络事务来说都能很好的工作，但在充满各种利益驱动和而已对手的环境中，对于一些大规模的购物、银行事务来说，并不足够强大。这些更重要的事务需要将 HTTP 和数字加密技术结合起来使用。</p><p><strong>密码</strong>是一种编码方案，加密之前的原始报文通常称为<strong>明文</strong>，使用了密码之后的编码报文通常称为<strong>密文</strong>。<strong>密钥</strong>就是改变密码行为的数字化参数，要在密码机上输入正确的密钥，解密过程才能正确进行。每个密码机都有不同的密钥值，每个密钥值对应着不同的加密算法。密钥越长，编码的组合就越多。</p><p>在对称密钥加密技术中，发送端使用的加密密钥和接收端解密用的密钥是相同的密钥。在很多情况下，编/解码算法都是众所周知的，这时密钥的机密状态就是很重要的了。以此在发送者和接收者相互对话之前，一定会有一个共享保密密钥的过程，这个传输过程就未必安全了。而且面对 N 个节点需要使用 N^2 个密钥。DES、3DES已经不安全了，目前 <strong>AES</strong> 取代 DES 成为新标准的一种对称加密算法。</p><img src="/2018/05/28/iOS签名机制/1.png" class="[机型]" title="[100][100]"><h2>公开密钥</h2><p>公开密钥加密技术使用的是非对称密钥。加密密钥是可以公开的<strong>公钥</strong>（public key），但用来解密的<strong>私钥</strong>（private key）由主机自己保管。很好的解决了对称密钥加密中对密钥数目的 N^2 扩展问题。虽然解决了对称密钥加密的不安全性，但是在单个任务执行速度上会比对称加密慢很多。</p><img src="/2018/05/28/iOS签名机制/2.png" class="[机型]" title="[100][100]"><p><strong>RSA</strong> 算法就是一个公开密钥加密系统。</p><p><strong>混合密码系统</strong>：在两个节点间的通信，通过生成临时会话密钥，用以快速地对称加密明文消息。然后再通过共享的公钥对临时密钥进行安全的非对称加密。解密过程与之相反。</p><img src="/2018/05/28/iOS签名机制/3.png" class="[机型]" title="[100][100]"><h2>数字签名</h2><p>数字签名就是附加在报文上的特殊计算出来的加密校验码。它可以验证报文内容是否被篡改，但不是用来保证报文的机密性。通常是用非对称公开密钥技术产生。首先使用摘要函数将报文数据转换为固定长度的散列值；使用“指纹”一样的<strong>私钥</strong>对散列值进行加密；将加密的结果与原始报文数据一并发送给接收者。接收者则使用公开的<strong>公钥</strong>解密出相应的散列值，然后与接收到的原始报文数据产生的散列值进行比对。</p><img src="/2018/05/28/iOS签名机制/4.png" class="[机型]" title="[100][100]"><h2>数字证书</h2><p>在验证签名之前，需要验证公钥的合法性。在这个环节中， <strong>证书</strong>（certs）却可以很好的胜任验证公钥合法性的任务。一些官方的“证书颁发机构”（<strong>Certificate Authority，CA</strong>）使用其自己的私钥对要传输信息进行二次数字签名生成证书，证书里面就包括对象的公钥、对象信息、证书有效期、证书颁发者及其它扩展信息。现在使用的大部分证书都是 <strong>X.509 v3</strong> 证书格式。</p><img src="/2018/05/28/iOS签名机制/5.png" class="[机型]" title="[100][100]"><h2>iOS 签名机制</h2><p>使用此机制可以确保安装到 iPhone 设备上的 APP 都是经过 Apple 官方允许的。</p><img src="/2018/05/28/iOS签名机制/6.png" class="[机型]" title="[100][100]"><img src="/2018/05/28/iOS签名机制/7.png" class="[机型]" title="[100][100]"><h2>参考文献：</h2><blockquote><p>第三部分 识别、认证与安全</p><footer><strong>HTTP权威指南</strong><cite>David Gourley等（著） 陈涓 赵振平（译）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;曾几何时，人类变得越来越聪明，慢慢地，个体之间的信任问题也成了科技发展历程中的那条鲶鱼。&lt;/p&gt;
&lt;p&gt;前段时间在了解 iOS 逆向开发时都是在越狱设备上进行修改、调试。若想在非越狱设备上“非法”安装 APP ，这事儿就变得没那么简单了。但是如果我们
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="密钥、数字签名、证书" scheme="https://itenthusiast.github.io/tags/%E5%AF%86%E9%92%A5%E3%80%81%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E3%80%81%E8%AF%81%E4%B9%A6/"/>
    
      <category term="iOS重签名" scheme="https://itenthusiast.github.io/tags/iOS%E9%87%8D%E7%AD%BE%E5%90%8D/"/>
    
  </entry>
  
  <entry>
    <title>使用theos肆无忌惮的Hook</title>
    <link href="https://itenthusiast.github.io/2018/05/17/%E4%BD%BF%E7%94%A8theos%E8%82%86%E6%97%A0%E5%BF%8C%E6%83%AE%E7%9A%84Hook/"/>
    <id>https://itenthusiast.github.io/2018/05/17/使用theos肆无忌惮的Hook/</id>
    <published>2018-05-17T07:08:55.000Z</published>
    <updated>2018-05-17T11:26:50.000Z</updated>
    
    <content type="html"><![CDATA[<h2>theos</h2><ul><li><p>下载地址：<code>git clone --recursive &lt;https://github.com/theos/theos.git&gt; ～/theos</code></p></li><li><p>配置环境变量：</p><ul><li><img src="/2018/05/17/使用theos肆无忌惮的Hook/1.png" class="[机型]" title="[100][100]"></li><li>执行 <code>source .bash_profile</code> 命令，立即生效</li></ul></li><li><p>创建tweek项目</p><ul><li>执行 <code>nic.pl</code> 命令，选择 <code>11</code>创建 tweek 项目</li><li>填写项目信息<ul><li><img src="/2018/05/17/使用theos肆无忌惮的Hook/2.png" class="[机型]" title="[100][100]"></li><li><img src="/2018/05/17/使用theos肆无忌惮的Hook/3.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li><li><p>打开 Makefile文件，添加通过 USB 连接 iPhone 所需的端口号等信息</p><ul><li><code>export THEOS_DEVICE_IP = localhost(IP地址)</code></li><li><code>export THEOS_DEVICE_PORT = 10010</code>【可配置到<code>～/.bash_profile</code>】</li></ul></li><li><p>在 <code>Tweak.xm</code> 文件内编写 hook 程序。</p><ul><li>Logos语法：<code>@hook @end</code>、<code>@new</code>、<code>@orig</code>、<code>@ctor{程序启动时} @dtor{程序结束时}</code>;</li><li>程序秒退：<code>abort()</code>;</li><li>图片资源放置路径：项目文件 <code>/layout/Library/Caches/</code> 。</li><li><ul><li>使用时：&quot;<code>/Library/Caches/imageName</code>&quot;。<ul><li>宏定义图片资源路径：使用起来更方便。<ul><li><code>\#define CPFile(path) @&quot;/Library/Caches/&quot; #path</code><ul><li>使用时：<code>imageName</code>。</li></ul></li></ul></li></ul></li></ul></li><li>多文件开发<ul><li>修改<code>wechatweak_FILES = src/Tweak.xm src/model/*.m</code><ul><li>Import @“model/Person.h”</li></ul></li></ul></li><li>安装 Release 版本：<code>make package debug=0</code></li><li>使用<code>@new</code>定义一个新方法时，需要提前在<code>@interface</code>中声明一下</li></ul></li><li><p>编译</p><ul><li>来到项目文件执行 <code>make</code> 指令<ul><li><img src="/2018/05/17/使用theos肆无忌惮的Hook/4.png" class="[机型]" title="[100][100]"><ul><li>解决方案：<code>sudo xcode-select --switch /Applications/Xcode.app</code></li></ul></li><li><img src="/2018/05/17/使用theos肆无忌惮的Hook/5.png" class="[机型]" title="[100][100]"><ul><li>解决方案：<code>brew install ldid</code></li></ul></li></ul></li></ul></li><li><p>打包： <code>make package</code></p></li><li><p>安装插件： <code>make install</code></p></li><li><p>卸载插件</p><ul><li><code>Device/Libraey/MobilSubstrate/DynamicLibraries</code>  删除插件文件</li><li>通过 Cydia 工具卸载插件</li></ul></li></ul><h2>参考文献：</h2><blockquote><p>第3章 MacOS工具集</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;theos&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;下载地址：&lt;code&gt;git clone --recursive &amp;lt;https://github.com/theos/theos.git&amp;gt; ～/theos&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置环
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="theos" scheme="https://itenthusiast.github.io/tags/theos/"/>
    
      <category term="tweak" scheme="https://itenthusiast.github.io/tags/tweak/"/>
    
      <category term="Hook" scheme="https://itenthusiast.github.io/tags/Hook/"/>
    
  </entry>
  
  <entry>
    <title>Mach-O文件细述</title>
    <link href="https://itenthusiast.github.io/2018/05/17/Mach-O%E6%96%87%E4%BB%B6%E7%BB%86%E8%BF%B0/"/>
    <id>https://itenthusiast.github.io/2018/05/17/Mach-O文件细述/</id>
    <published>2018-05-17T06:46:35.000Z</published>
    <updated>2018-05-17T10:51:08.000Z</updated>
    
    <content type="html"><![CDATA[<h2>Mach-O文件结构</h2><ul><li><p>官方描述： <a href="https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html" target="_blank" rel="noopener">https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html</a></p></li><li><p><strong>Header</strong>：文件类型、目标架构类型信息</p></li><li><p><strong>Load commands</strong>： 描述文件在虚拟内存中的逻辑结构、布局</p></li><li><p><strong>Raw segment data</strong>：  定义在Load commands中的segment的原始数据</p></li></ul><h2>常见的 Mach-O 文件种类</h2><ul><li><strong>MH_OBJECT</strong>：目标文件（.o）、静态库文件（.a）</li><li><strong>MH_EXECUTE</strong>：可执行文件（.app）</li><li><strong>MH_DYLIB</strong>：动态库文件（.dylib、.framework）</li><li><strong>MH_DYLINKER</strong>：动态链接编辑器（/usr/lib/dyld）</li><li><strong>MH_DSYM</strong>:   符号表（打包生成可执行文件时附带生成的文件）<ul><li><img src="/2018/05/17/Mach-O文件细述/1.png" class="[机型]" title="[100][100]"></li></ul></li></ul><p>dyld 程序的作用：加载 <strong>MH_DYLIB</strong>、<strong>MH_BUNDLE</strong>、<strong>MH_EXECUTE</strong>（这一点类似 window 中的 commend 程序）</p><ul><li><img src="/2018/05/17/Mach-O文件细述/2.png" class="[机型]" title="[100][100]"></li></ul><h2>窥探 Mach-O 文件内信息的工具</h2><ul><li><p>otool命令：查看Mach-O文件特定部分和段的内容</p><ul><li><img src="/2018/05/17/Mach-O文件细述/3.png" class="[机型]" title="[100][100]"></li></ul></li><li><p>file命令：查看Mach-O文件的具体类型</p><ul><li><img src="/2018/05/17/Mach-O文件细述/4.png" class="[机型]" title="[100][100]"></li></ul></li><li><p>lipo命令</p><ul><li><img src="/2018/05/17/Mach-O文件细述/5.png" class="[机型]" title="[100][100]"></li><li><img src="/2018/05/17/Mach-O文件细述/6.png" class="[机型]" title="[100][100]"></li><li><img src="/2018/05/17/Mach-O文件细述/7.png" class="[机型]" title="[100][100]"></li></ul></li><li><p>MachOView：<a href="https://github.com/gdbinit/MachOView" target="_blank" rel="noopener">https://github.com/gdbinit/MachOView</a></p><ul><li><img src="/2018/05/17/Mach-O文件细述/8.png" class="[机型]" title="[100][100]"></li></ul></li></ul><h2>参考文献：</h2><blockquote><p>第2章 越狱iOS平台简介</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;Mach-O文件结构&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;官方描述： &lt;a href=&quot;https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/MachOTop
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="Mach-O" scheme="https://itenthusiast.github.io/tags/Mach-O/"/>
    
  </entry>
  
  <entry>
    <title>亲，iOS逆向开发了解一下</title>
    <link href="https://itenthusiast.github.io/2018/05/17/%E4%BA%B2%EF%BC%8CiOS%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91%E4%BA%86%E8%A7%A3%E4%B8%80%E4%B8%8B/"/>
    <id>https://itenthusiast.github.io/2018/05/17/亲，iOS逆向开发了解一下/</id>
    <published>2018-05-17T01:26:41.000Z</published>
    <updated>2018-05-17T07:08:28.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><blockquote><p>打个比喻，iOS逆向工程就像一杆长矛，专门刺破 App 看似安全的防护盾。有趣的是，很多制作 App 的公司还没有意识到这样一杆长矛的存在，固步自封地以为自己的盾坚不可摧。</p><p>对于微信和 WhatApp 之类的 IM 应用，交流的信息是它们的核心对于银行、支付、电商类的软件。交易数据和客户信息是它们的核心。所有的核心数据都是需要重点保护的。于是开发人员通过反测试、数据加密、代码混淆等各种手段保护自己的 App，为的就是增加逆向工程的难度，避免类似的安全问题影响用户体验。</p><p>可是目前 App 防护所用到的技术跟 iOS 逆向工程所使用的技术根本就不是同一个维度的。一般的 App 防护，感觉就像是一个城堡，将 App 的 MVC 布置在城堡内部，外围圈上厚厚的城墙，看上去易守难攻。但是当我们站到高处鸟瞰这个 App 所在的城堡，它的内部结构就不再是秘密。</p><p>这时，所有的 Object-C 函数定义、所有的 property、所有的全局变量以及所有的逻辑完全暴露在我们面前，而城堡的防护意义荡然无存。处在这个维度，城墙已经不再是阻碍。我们更应该关注的是如何从诺大的城堡里找到那个要找的人。</p><p>iOS 应用逆向工程可以“透视”市面上绝大多数 App，它们的设计理念与实现细节在逆向工程中暴露无遗。比如了解一些老牌经典软件、当下火热的 App 的技术点，引用了哪些 framework 库等。 对于iOS开发者来说，如果清楚的了解系统的 API 实现细节的话，无疑是对我们工作有帮助的。在自己的 App 里使用一些文档中没有提及的私有功能，了解官方系统级源码的开发规范等。善于学习、借鉴别人成熟的方案岂不是一件很愉快的事，相信这些会使得学习者的个人能力得到质的提升。</p><p>以上比喻只是 iOS 逆向工程的一隅之见，但也形象地说明了 iOS 逆向工程的威力。概括的说，iOS 逆向工程主要有以下两个作用：</p><p>1、分析目标程序，拿到关键信息，可以归类于安全相关的逆向工程；</p><p>2、借鉴他人的程序功能来开发自己的软件，可以归类于正向开发相关的逆向工程；</p></blockquote><h2>准备</h2><p>最近有在了解一些 iOS 逆向开发相关概念，《iOS应用逆向工程》这本书则可以很好的解决我大部分疑惑。我觉得学习一项新技术或者新知识点，比较高效率的方法大概就是实践了。所以，你可能需要提前准备一台 iPhone 设备，一台已经完美越狱的 iPhone。硬件版本推荐最新的 RAM64 架构机型，也就是 iPhone5s 及以后版本。软件版本推荐是越狱较为稳定的 iOS8、9 系统。【本人在某宝入手一台：iPhone6s、iOS 9.1、16G内存、完美越狱、￥1680】PP助手越狱平台：<a href="http://jailbreak.25pp.com/" target="_blank" rel="noopener">http://jailbreak.25pp.com/</a></p> <img src="/2018/05/17/亲，iOS逆向开发了解一下/1.png" class="[机型]" title="[100][100]"><h2>按下空格键预览逆向</h2><p>对于一款他人团队开发的软件，没有源码且不借助三方工具的情况下，我们能了解到的就是一个普通的用户视角所能呈现出的部分。不同的是，有类似开发经验者可以去猜测原作者实现逻辑的大致招数，实际上很多开发者初期的时候也都是这么做的，尝试了解一些经典软件的设计架构。但这么做有个问题在于，它受限于我们自身的知识储备、开发经验，可能一不小心就触发了我的知识盲区，这就有点尴尬，如果有妹子在场那就更得不偿失了。尝试去揣测他人作品的念头和行为本身就是逆向的思维，即使手段没那么高明。</p><p>外挂、插件、破解等均属于逆向工程的应用，我们当然知道技术同样具有两面性，这里不谈“副作用”，只聊技术本身。</p><p>本文就是记录一下如何更加科学的去逆向别人的 APP，以及沿途所需具备的知识点。具体技术细节另辟文章单独细述。</p><h2>APP 逆向思路</h2><p>为了更方便的了解一些 iOS 逆向工程的理论，就要使用各种工具帮助学习者去实践。相对于正向开发的 Xcode 这样大而全的神器，逆向工程使用的工具就显的不那么“智能”了，很多工作需要我们提前手工完成。iOS 逆向工程的工具可以分为四大类：监测工具、反汇编工具、调试工具，以及开发工具。</p><p>一款 APP，它的UI设计、操作逻辑、页面的层级结构，这些属于界面分析。除了正常上手使用了解，你可能还需要一些专业工具【Cycript、Reveal 这两款工具麻烦了解一下】来辅助你更优雅的完成这部分工作。</p><p>我们想要更加深层次的了解 APP 的实现，如果能看到它的源码岂不是快哉？【这里有个概念可能会帮助你打消这个念头，反汇编原理麻烦了解一下】。实际上是有一些工具【class-dump、Hopper Disassembler、IDA等】来帮助我们一定程度上拿到部分代码逻辑，比如说头文件。值得注意的是，介于 Apple 的安全理念，我们从 AppStore 商店下载的 APP 都是经过加密过的，系统在可执行文件上进行了加壳操作。不过一些三方下载平台上的 APP 会提前完成去壳操作，比如PP助手。所以在使用上述工具进行抽取操作前需借助一些工具【otool、MachOView】判断当前这款软件是否仍被加壳，如果有，需要使用一些工具【clutch、dumpdecrypted】执行脱壳操作。如果我们了解关于【Mach-O】文件内部结构的话，这会有助于我们理解这一系列操作背后的原理。</p><p>这里需要提的一点是，Mac 上我们可以在系统的 Terminal 终端程序中输入指令来完成一系列操作，在逆向开发过程中我们同样需要对 iPhone 设备做一些操作，比如Cycript、clutch、dumpdecrypted等命令工具均需要这些权限。但是 iPhone 上有没有类似终端一样的程序来完成这项工作，毕竟屏幕尺寸在这摆着呢，所以我们需要通过【SSH】的方式把拥有终端程序的 Mac 和 iPhone 设备连接起来。</p><p>前面几个步骤都是在不运行目标程序的情况下进行分析，称其为静态分析。在我们日常正向开发 iOS 程序时除了构思、编码，更少不了在 Xcode 中断点调试和测试代码。那在 iOS 逆向工程中，用到的调试工具主要是【debugserver、LLDB等】，这个过程称为动态分析。</p><p>从 UI 层面切入到代码层面，用反汇编工具和调试工具分析过二进制文件后，就可以整理分析结果，使用开发工具编写程序了。至于开发工具在越狱 iOS 中种类就不那么单一了，有基于 Xcode 的 iOSOpenDev，还有偏命令行的 【Theos】。</p><p>以 UI 分析切入，锁定目标所属的控制器。根据拿到的类名，去 dump 出来的文件集合里找出对应的头文件。创建 tweak 项目，根据具体需求通过编写 tweak 程序修改原程序地执行逻辑，具体做法就是重写对应方法的实现，注入自己的逻辑。至于如何快速地决定采取何种策略修改功能，离不开正向开发积累的经验。比如去除 APP 内的广告，可以选择直接 remove 掉对应的控件，也可以选择过滤掉从服务器请求来的广告数据模型。最终完成后，以安装.deb插件的方式注入你想加入或去除的功能，然后就是【签名打包.ipa】。</p><h2>未完待续。。。。。。</h2><p>可能吧。</p><h2>参考文献：</h2><blockquote><p>第1章 概念篇；第3章 MacOS工具集；第4章 iOS工具集</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;打个比喻，iOS逆向工程就像一杆长矛，专门刺破 App 看似安全的防护盾。有趣的是，很多制作 App 的公司还没有意识到这样一杆长矛的存在，固步自封地以为自己的盾坚不可摧。&lt;/p&gt;
&lt;p&gt;对于微信和 WhatApp 之类的 
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="iOS应用逆向工程" scheme="https://itenthusiast.github.io/tags/iOS%E5%BA%94%E7%94%A8%E9%80%86%E5%90%91%E5%B7%A5%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>使用class-dump、HopperDisassmbler将反编译进行的更彻底</title>
    <link href="https://itenthusiast.github.io/2018/05/15/%E4%BD%BF%E7%94%A8class-dump%E3%80%81HopperDisassmbler%E5%B0%86%E5%8F%8D%E7%BC%96%E8%AF%91%E8%BF%9B%E8%A1%8C%E7%9A%84%E6%9B%B4%E5%BD%BB%E5%BA%95/"/>
    <id>https://itenthusiast.github.io/2018/05/15/使用class-dump、HopperDisassmbler将反编译进行的更彻底/</id>
    <published>2018-05-15T06:30:46.000Z</published>
    <updated>2018-05-17T11:32:38.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><p>同一种架构平台下，每一条汇编指令都有与之对应的机器指令，但是反汇编成某一种高级语言，这个过程是不可逆的。这时候会有一些工具在这个过程中辅助我们把可执行文件“反编译”成对应的汇编语言、高级语言伪代码。</p><h2>反编译工具一：class-dump</h2><ul><li>作用是把Mach-O文件的class信息给dump出来，生成对应的.h文件</li><li>下载地址： <a href="http://stevenygard.com/projects/class-dump" target="_blank" rel="noopener">http://stevenygard.com/projects/class-dump</a></li><li>将 <code>class-dump</code>，复制到 Mac 的 <code>/usr/local/bin</code> 路经下。</li><li>提取命令：<code>class-dump -H Mach-O文件路径 -o 头文件存放路径</code></li></ul><h2>反编译工具二：Hopper Disassmbler</h2><ul><li><p>作用是能够将Mach-O文件的机器语言代码“反编译成汇编代码”</p></li><li><p>应用：抽取动态库共享缓存中的UIKit框架</p><ul><li><p>路径：<code>/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm*</code></p><ul><li>在Mac／iOS中，系统是使用 <code>/usr/lib/dyld</code> 程序加载动态库</li><li>dyld：动态加载器。 <a href="https://opensource.apple.com/tarballs/dyld/" target="_blank" rel="noopener">https://opensource.apple.com/tarballs/dyld/</a></li></ul></li><li><p>抽取工具制作</p><ul><li>下载dyld源码后，利用源码中提供的 <code>launch-cache/dsc_extractor.cpp</code>， 编译该文件生成 <code>dsc_extractor</code> 工具。</li></ul></li><li><p>使用 dsc_extractor 工具抽取目标动态库</p><ul><li><code>dsc_extractor 目标动态库路径 抽取结果存放路径</code></li><li><img src="/2018/05/15/使用class-dump、HopperDisassmbler将反编译进行的更彻底/1.png" class="[机型]" title="[100][100]"></li><li><img src="/2018/05/15/使用class-dump、HopperDisassmbler将反编译进行的更彻底/2.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li></ul><h2>参考文献：</h2><blockquote><p>第3章 MacOS工具集</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;同一种架构平台下，每一条汇编指令都有与之对应的机器指令，但是反汇编成某一种高级语言，这个过程是不可逆的。这时候会有一些工具在这个过程中辅助我们把可执行文件“反编译”成对应的汇编语言、高级语言伪代码。&lt;/p&gt;
&lt;h2&gt;反编译工具一：class-dump
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="class-dump" scheme="https://itenthusiast.github.io/tags/class-dump/"/>
    
      <category term="HopperDisassmbler" scheme="https://itenthusiast.github.io/tags/HopperDisassmbler/"/>
    
  </entry>
  
  <entry>
    <title>想知道的更多，先给你的APP脱壳吧</title>
    <link href="https://itenthusiast.github.io/2018/05/13/%E6%83%B3%E7%9F%A5%E9%81%93%E7%9A%84%E6%9B%B4%E5%A4%9A%EF%BC%8C%E5%85%88%E7%BB%99%E4%BD%A0%E7%9A%84APP%E8%84%B1%E5%A3%B3%E5%90%A7/"/>
    <id>https://itenthusiast.github.io/2018/05/13/想知道的更多，先给你的APP脱壳吧/</id>
    <published>2018-05-13T08:34:10.000Z</published>
    <updated>2018-05-17T11:31:36.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><p>从 AppStore 下载的 App 都是经过加密过的，可执行文件被加上了一层“壳”。在这种情况下，想要获取头文件，需要先解密 App 的可执行文件，俗称“脱壳”。</p><h2>加壳</h2><p>利用特殊的算法，对可执行文件的编码进行压缩、加密，以达到保护程序代码的目的。加密后的可执行文件在被载入内存前，在文件外围会有一个壳程序。载入内存后，执行壳程序会对之前的加密操作进行解密，原可执行文件才得以运行。</p><ul><li>验证可执行文件是否被加密<ul><li><code>otool -l 文件路径 | grep cryptid</code><ul><li><img src="/2018/05/13/想知道的更多，先给你的APP脱壳吧/1.png" class="[机型]" title="[100][100]"></li></ul></li><li>MachOView工具查看</li><li><img src="/2018/05/13/想知道的更多，先给你的APP脱壳吧/2.png" class="[机型]" title="[100][100]"></li></ul></li></ul><h2>脱壳</h2><ul><li>脱壳只是为了拿到里面的头文件内容，并不影响Tweak过程。</li><li>硬脱壳<ul><li>在知道加密算法的前提下，执行解密算法还原出加密前的可执行文件。</li><li>iOS逆向普遍采取硬脱壳的方式。</li></ul></li><li>动态脱壳<ul><li>仍然将加密后的可执行文件同壳程序一起载入内存运行，利用壳程序执行完解密操作后，将解密后的可执行文件从内存中导出。</li></ul></li></ul><h2>脱壳工具一：Clutch</h2><ul><li>下载地址： <a href="https://github.com/KJCracks/Clutch" target="_blank" rel="noopener">https://github.com/KJCracks/Clutch</a></li><li>将Clutch文件拷贝到 iPhone 的  <code>usr/bin</code> 目录下<ul><li>若提示权限不够，执行：<code>chmod +x /usr/bin/Clutch</code></li></ul></li><li>查看iPhone设备上未脱壳的应用：<code>Clutch -i</code></li><li>脱壳操作：<code>Clutch -d App序号或&lt;BundleId&gt;</code></li><li>脱壳的结果是<code>.ipa包</code>。</li></ul><h2>脱壳工具二：dumpdecryted</h2><ul><li>下载地址： <a href="https://github.com/stefanesser/dumpdecrypted" target="_blank" rel="noopener">https://github.com/stefanesser/dumpdecrypted</a></li><li>来到文件路径，执行 <code>make</code> 指令进行编译，生成 <code>.dylib</code>动态库文件<ul><li><img src="/2018/05/13/想知道的更多，先给你的APP脱壳吧/3.png" class="[机型]" title="[100][100]"></li><li>解决方案：尝试先安装 theos 工具</li></ul></li><li>将.dylib动态库文件拷贝到 iPhone 的 <code>/var/root</code> 目录下</li><li>使用环境变量 <code>DYLD_INSERT_LIBRARIES</code> 将 <code>.dylib</code> 动态库文件注入到待脱壳程序中。<ul><li><code>DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib 待脱壳程序路径</code></li></ul></li><li>脱壳的结果是一个 Mach-O 文件。<ul><li><img src="/2018/05/13/想知道的更多，先给你的APP脱壳吧/4.png" class="[机型]" title="[100][100]"></li></ul></li></ul><h2>参考文献：</h2><blockquote><p>第4章 iOS工具集-dumpdecryted</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;从 AppStore 下载的 App 都是经过加密过的，可执行文件被加上了一层“壳”。在这种情况下，想要获取头文件，需要先解密 App 的可执行文件，俗称“脱壳”。&lt;/p&gt;
&lt;h2&gt;加壳&lt;/h2&gt;
&lt;p&gt;利用特殊的算法，对可执行文件的编码进行压缩、加
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="脱壳" scheme="https://itenthusiast.github.io/tags/%E8%84%B1%E5%A3%B3/"/>
    
  </entry>
  
  <entry>
    <title>Cycript配合Reveal分析界面层级结构</title>
    <link href="https://itenthusiast.github.io/2018/05/13/Cycript%E9%85%8D%E5%90%88Reveal%E5%88%86%E6%9E%90%E7%95%8C%E9%9D%A2%E5%B1%82%E7%BA%A7%E7%BB%93%E6%9E%84/"/>
    <id>https://itenthusiast.github.io/2018/05/13/Cycript配合Reveal分析界面层级结构/</id>
    <published>2018-05-13T07:38:33.000Z</published>
    <updated>2018-05-17T11:30:50.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><p>在 Mac 上安装 Reveal。</p><p>需要说明的是，在此之前需要先在越狱过的这台 iPhone 上的 Cydia 里安装几个插件：</p><p>1、<strong>adv-cmds</strong></p><ul><li>软件源：<a href="http://apt.saurik.com" target="_blank" rel="noopener">http://apt.saurik.com</a></li><li>作用：ps -A指令打印出当前设备上正在运行的app信息（进程ID）</li></ul><p>2、<strong>Cycript</strong></p><ul><li>软件源：<a href="http://apt.saurik.com" target="_blank" rel="noopener">http://apt.saurik.com</a></li><li>作用：用来调试、修改正在运行的APP</li></ul><p>3、<strong>Reveal Loader</strong></p><ul><li>官网： <a href="https://revealapp.com" target="_blank" rel="noopener">https://revealapp.com</a></li><li>软件源：<a href="http://apt.so/codermjlee" target="_blank" rel="noopener">http://apt.so/codermjlee</a></li><li>作用：辅助 Mac 端的新版 Reveal 进行 UI 分析 view 的层级结构</li><li>安装完成后，打开【设置】，选择要调试的APP</li><li>找到Mac上的<code>RevealServer</code>文件，替换掉 iPhone 上的 <code>/Library/RHRevealLoder/RevealServer</code> 文件。</li><li>重启手机</li></ul><h2>Cycript 操作</h2><ul><li>官网： <a href="http://www.cycript.org" target="_blank" rel="noopener">http://www.cycript.org</a></li><li>作用：可以分析 controller 的层级结构</li><li>进入网易云音乐的界面监听状态：<code>cycript -p neteasemusic</code></li><li>取消输入： <code>ctrl + c</code></li><li>退出监听状态：<code>ctrl + d</code></li><li>使用其他 cycript 框架 <code>.cy文件</code><ul><li>将 <code>.cy文件</code> 存放到 <code>/usr/lib/cycript0.9</code> 路经下</li><li>导入文件名：<code>@import testTool</code></li></ul></li></ul><h2>参考文献：</h2><blockquote><p>第4章 iOS工具集-Cycript</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;在 Mac 上安装 Reveal。&lt;/p&gt;
&lt;p&gt;需要说明的是，在此之前需要先在越狱过的这台 iPhone 上的 Cydia 里安装几个插件：&lt;/p&gt;
&lt;p&gt;1、&lt;strong&gt;adv-cmds&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;软件源：&lt;
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="Cycript操作" scheme="https://itenthusiast.github.io/tags/Cycript%E6%93%8D%E4%BD%9C/"/>
    
      <category term="Reveal" scheme="https://itenthusiast.github.io/tags/Reveal/"/>
    
  </entry>
  
  <entry>
    <title>Mac通过SSH连接iPhone</title>
    <link href="https://itenthusiast.github.io/2018/05/11/Mac%E9%80%9A%E8%BF%87SSH%E8%BF%9E%E6%8E%A5iPhone/"/>
    <id>https://itenthusiast.github.io/2018/05/11/Mac通过SSH连接iPhone/</id>
    <published>2018-05-11T02:31:53.000Z</published>
    <updated>2018-05-17T11:30:40.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><p>在 iOS 逆向工程中经常会要通过 Mac 访问 iPhone 端数据，我们查资料得知 iPhone 默认使用22端口进行 SSH 通信，采用的是 TCP 协议之后，这事貌似就变得有据可依了。</p> <img src="/2018/05/11/Mac通过SSH连接iPhone/1.png" class="[机型]" title="[100][100]"><p>在 Mac 上安装 iFunBox 、PP助手电脑板软件，用于访问 iPhone 目录中的文件。</p><p>需要说明的是，在此之前需要先在越狱过的这台 iPhone 上的 Cydia 里安装几个插件：</p><p>1、<strong>Apple File Conduit“2&quot;</strong></p><ul><li>软件源：<a href="http://apt.saurik.com" target="_blank" rel="noopener">http://apt.saurik.com</a>；<a href="http://apt.25pp.com" target="_blank" rel="noopener">http://apt.25pp.com</a> （须在 Cydia 里手动添加）</li><li>作用：可以在Mac端通过iFunBox查看iPhone端的目录结构</li></ul><p>2、<strong>AppSync Unified</strong></p><ul><li><p>软件源：<a href="http://apt.25pp.com" target="_blank" rel="noopener">http://apt.25pp.com</a></p></li><li><p>作用：可以绕过系统的验证，随意的安装、运行破解的ipa包</p></li></ul><p>3、<strong>iFile</strong></p><ul><li>软件源：<a href="http://apt.thebigboss.org/repofiles/cydia" target="_blank" rel="noopener">http://apt.thebigboss.org/repofiles/cydia</a></li><li>作用：在iPhone端可以随意查看手机的目录结构</li></ul><p>4、<strong>PP 助手</strong></p><ul><li>软件源：<a href="http://apt.25pp.com" target="_blank" rel="noopener">http://apt.25pp.com</a></li></ul><p>5、<strong>OpenSSH</strong></p><ul><li>软件源：<a href="http://apt.saurik.com" target="_blank" rel="noopener">http://apt.saurik.com</a></li><li>作用：实现在Mac终端登录访问iPhone设备。</li><li>使用步骤：见“OpenSSH Access How-To”<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/2.png" class="[机型]" title="[100][100]"></li></ul></li></ul><p>6、<strong>Vi IMproved</strong></p><ul><li><p>软件源：<a href="http://apt.saurik.com" target="_blank" rel="noopener">http://apt.saurik.com</a></p></li><li><p>作用：使用终端在iPhone中编辑文件内容。</p></li></ul><p><strong>【注意】</strong></p><ul><li><p>如果通过Cydia安装deb源失败，可以通过网上直接下载相应的deb包，将其放到/var/root/Media/Cydia/AutoInstall（可手动创建目录结构），重启手机。</p><ul><li>例如通过导入一下文件安装 iFile。</li></ul></li><li><p>默认情况下，iOS终端不支持中文输入和显示</p><ul><li>新建 <code>～/.inputrc</code>       文件，文件内容为<ul><li><code>set convert-meta off</code>：不把中文字符转化为转义序列</li><li><code>set output-meta on</code>：允许向终端输出中文字符</li><li><code>set meta-flag on</code>  ：允许向终端输入中文字符</li><li><code>set input-meta on</code>  ：</li></ul></li></ul></li></ul><h2>访问方式一：WiFi</h2><ul><li>Mac 通过 Wi-Fi 访问 iPhone 的 22端口。</li><li>登录指令：<code>ssh root@10.0.132.44</code><ul><li>初始密码：alpine</li><li>修改初始密码指令：<code>passwd 123456</code>、<code>passwd mobile 123456</code></li></ul></li><li>退出指令：exit</li></ul><h2>访问方式二：USB</h2><ul><li>Mac 通过 USB 访问 iPhone 的 22端口。</li><li>Mac 上自带一个 usbmuxd  服务程序，作用是将数据通过Mac的端口由USB传输到iPhone对应的端口号。<ul><li>usbmuxd 路经：/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/usbmuxd</li></ul></li><li>下载 usbmuxd 工具包： <a href="https://cgit.sukimashita.com/usbmuxd.git/snapshot/usbmuxd-1.0.8.tar.gz" target="_blank" rel="noopener">https://cgit.sukimashita.com/usbmuxd.git/snapshot/usbmuxd-1.0.8.tar.gz</a></li><li>运行 <a href="http://tcprelay.py" target="_blank" rel="noopener">tcprelay.py</a> 程序将iPhone的22端口映射到Mac本地的10010端口： <code>python tcprelay.py -t 22: 10010</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/3.png" class="[机型]" title="[100][100]"></li><li>保持当前映射状态，不能关闭该命令窗口</li></ul></li><li>登录指令：<code>ssh root@localhost -p 10010</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/4.png" class="[机型]" title="[100][100]"></li></ul></li></ul><h2>SSH、Open SSH、SSL、Open SSL</h2><p><strong>SSH</strong></p><ul><li>“安全外壳协议”（Secure Shell）</li><li>SSH 是目前较可靠，专为<a href="https://baike.baidu.com/item/%E8%BF%9C%E7%A8%8B%E7%99%BB%E5%BD%95" target="_blank" rel="noopener">远程登录</a>会话和其他网络服务提供安全性的协议。</li><li>&lt;<a href="https://baike.baidu.com/item/ssh/10407?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/ssh/10407?fr=aladdin</a></li></ul><p><strong>Open SSH</strong>**</p><ul><li>是SSH协议的免费开源实现。这里就可以通过Open      SSH的方式实现Mac远程登录iPhone。</li><li><a href="https://baike.baidu.com/item/OpenSSH/1137789?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/OpenSSH/1137789?fr=aladdin</a></li><li>Open SSH的数据加密就是通过Open SSL完成。</li></ul><p><strong>SSL</strong></p><ul><li>“安全套接层”（Secure      Sockets Layer）</li><li>是一种为网络通信提供安全及数据完整性的协议，作用在传输层。</li><li><a href="https://baike.baidu.com/item/ssl/320778?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/ssl/320778?fr=aladdin</a></li></ul><p><strong>Open SSL</strong></p><ul><li>是SSL协议的免费开源实现。</li><li>绝大部分HTTPS请求等价于：HTTP+OpenSSL</li><li><a href="https://baike.baidu.com/item/openssl/5454803?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/openssl/5454803?fr=aladdin</a></li></ul><h2>慢镜头：SSH 通信过程</h2><p><strong>建立安全连接</strong></p><ul><li><p>客户端发送登录请求到服务器</p></li><li><p>服务器端发送公钥等信息给客户端</p></li><li><p>客户端拿到服务器发送过来的公钥后，比对自己  <code>~/.ssh</code> 路经下的 <code>known_hosts</code> 文件。</p><ul><li>首次登录会要求客户端保存公钥等信息。<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/5.png" class="[机型]" title="[100][100]"></li></ul></li><li>连接前：<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/6.png" class="[机型]" title="[100][100]"></li></ul></li><li>连接后：<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/7.png" class="[机型]" title="[100][100]"></li></ul></li><li>我们来到服务器端（iPhone）<code>/etc/ssh</code> 路经下的 <code>ssh_host_rsa_key.pub</code> 文件验证公钥内容<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/8.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li><li><p>【注意】</p><ul><li>如果服务器身份信息发生了变更，而客户端仍需要连接时，可以选择删除之前记录下来的公钥等信息。<ul><li><code>vim</code>的“<code>dd</code>”指令：删除 <code>known_hosts</code> 文件中的指定记录值。</li><li><code>ssh-keygen -R 10.0.132.44</code> 指令：删除 known_hosts 文件中指定ip对应的记录值。</li></ul></li><li>目录结构<ul><li>客户端 <code>～/.ssh</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/9.png" class="[机型]" title="[100][100]"></li></ul></li><li>服务器端 <code>/etc/ssh</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/10.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li></ul></li></ul><p><strong>客户端认证</strong></p><ul><li>方式一：基于密码的客户端认证</li><li>方式二：基于密钥的客户端认证【SSH-2默认】<ul><li>客户端生成公钥、私钥文件：<code>～/.ssh</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/11.png" class="[机型]" title="[100][100]"></li><li>公钥：<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/12.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li><li>将客户端公钥内容追加到服务器端 <code>～/.ssh</code>  路经下的 <code>authorized_keys</code> 授权文件内【自动】<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/13.png" class="[机型]" title="[100][100]"></li><li><strong>authorized_keys</strong><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/14.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li><li>或者【手动】将客户端公钥内容追加到服务器端 <code>～/.ssh</code> 路经下的 <code>authorized_keys</code> 授权文件内<ul><li>将客户端 <code>～/.ssh</code> 路经下的公钥文件 <code>id_rsa.pub</code> 跨平台拷贝到服务器端的 <code>～/.ssh</code>路经下<ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/15.png" class="[机型]" title="[100][100]"></li><li><img src="/2018/05/11/Mac通过SSH连接iPhone/16.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li><li>服务器端将验证信息发送给客户端</li><li>客户端使用自己 ～/.ssh      路经下的 id_rsa 私钥文件进行验证服务器端发送过来的信息。</li><li>……</li></ul></li><li>目录结构<ul><li>客户端 <code>~/.ssh</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/17.png" class="[机型]" title="[100][100]"></li></ul></li><li>服务器端 <code>~/.ssh</code><ul><li><img src="/2018/05/11/Mac通过SSH连接iPhone/18.png" class="[机型]" title="[100][100]"></li></ul></li></ul></li><li>【提示】<ul><li>如果发现配置了密钥登录后仍服务器需要验证密码，需要在服务器设置文件<ul><li><code>chmod 755 ~</code></li><li><code>chmod 755 ~/.ssh</code></li><li><code>chmod 644 ~/.ssh/authorized_keys</code></li></ul></li></ul></li></ul><p><strong>数据传输</strong></p><p>一系列写入、查询操作。</p><h2>参考文献：</h2><blockquote><p>第4章 iOS工具集-OpenSSH</p><footer><strong>iOS应用逆向工程 第二版</strong><cite>沙梓社 吴航（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;在 iOS 逆向工程中经常会要通过 Mac 访问 iPhone 端数据，我们查资料得知 iPhone 默认使用22端口进行 SSH 通信，采用的是 TCP 协议之后，这事貌似就变得有据可依了。&lt;/p&gt;
 &lt;img src=&quot;/2018/05/11/M
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="逆向开发" scheme="https://itenthusiast.github.io/categories/iOS/%E9%80%86%E5%90%91%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="Mac访问iPhone" scheme="https://itenthusiast.github.io/tags/Mac%E8%AE%BF%E9%97%AEiPhone/"/>
    
  </entry>
  
  <entry>
    <title>还原OC对象的方法调用轨迹（下）</title>
    <link href="https://itenthusiast.github.io/2018/05/03/%E8%BF%98%E5%8E%9FOC%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8%E8%BD%A8%E8%BF%B9%EF%BC%88%E4%B8%8B%EF%BC%89/"/>
    <id>https://itenthusiast.github.io/2018/05/03/还原OC对象的方法调用轨迹（下）/</id>
    <published>2018-05-03T08:25:24.000Z</published>
    <updated>2018-05-04T01:39:10.000Z</updated>
    
    <content type="html"><![CDATA[<p></p><h2>引言</h2><h7>我们了解类的本质构成及“类继承体系”， superclass 指针确立了继承关系，而 isa 指针描述了当前实例对象所属的类，并却能够看出对象位于“类继承体系”的哪一部分。如果某对象传递消息：<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">id</span> returnValue = [object fun: parameter];</span><br></pre></td></tr></table></figure>编译器会把这条消息转换为如下函数：<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">id</span> returnValue = objc_msgSend(object, <span class="keyword">@selector</span>(fun:), parameter);</span><br></pre></td></tr></table></figure> objc_msgSend 函数会依据接受者与方法的类型来调用适当的方法。为了完成此操作，该函数需要在接受者所属的类中搜寻其“方法列表”（methods）（该函数会将匹配结果换存在“缓存表”（cache）里面，每个类都有这样一块缓存，便于下次还向该类发送此方法），如果能找到与方法名称相符的方法，就跳转至相对应的实现代码。若是找不到就沿着“类继承体系”的“方法调用轨迹”继续向上查找，找到了合适的方法之后再跳转至实现代码，在此之前都称为消息传递阶段。如果最终还是没找到相符的方法，那就执行“消息转发”（message forwarding）操作。<h2>消息转发（message forwarding）</h2>在编译期间向某类发送了其无法解读的消息并不会报错，因为在运行期可以继续向类中添加方法，所以编译器在编译时还无法确认类中会不会有该方法的实现。当最终都没能解读此消息后，就会启动 <b>消息转发（message forwarding）</b>机制。如果经过消息转发阶段仍未找到对象执行解读此消息，结果则会调用“doesNotRecognizeSelector”方法抛出异常以程序崩溃而告终。不过开发者在编写自己的类时，可在消息转发阶段设置“挂钩”，用以执行预定的逻辑，而不至于应用程序崩溃。<img src="/2018/05/03/还原OC对象的方法调用轨迹（下）/1.png" class="[还原OC对象的方法调用轨迹（下）]"><b>消息转发阶段分为两大阶段：</b>第一阶段会先征询接受者所属的类，看其是否为该方法动态添加了方法的实现部分，称为<b>“动态方法解析”（dynamic method resolution）</b>；如果仍没解决问题会来到第二阶段，如果来到了该阶段，代表着接受者自己就无法再以动态新增方法的手段来响应该消息了。此时，运行期系统会请求接受者以其他手段来处理与消息相关的方法调用，又细分为两步：首先请接受者看有没有其他对象<b>“备援的接受者”（replacement receiver）</b>来处理这条消息，若存在，则把消息转发给那个对象，消息转发结束。若没有，则启动<b>“完整的消息转发机制”（full forwarding mechanism）</b>，运行期系统会把与该消息有关所有的细节封装到 NSInvocation 对象中，再给接受者最后一次机会，令其设法解决当前还未处理的消息。<br><h3><b>1、动态方法解析（dynamic method resolution）</b></h3><br>对象在收到无法解读的消息后，首先会调用其所属类的下列方法：<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+(<span class="built_in">BOOL</span>)resolveInstanceMethod: (SEL)selector <span class="comment">//方法类型为对象方法</span></span><br></pre></td></tr></table></figure>或者<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+(<span class="built_in">BOOL</span>)resolveClassMethod: (SEL)selector <span class="comment">//方法类型为类方法</span></span><br></pre></td></tr></table></figure>表示这个类是否能够新增一个实例方法来处理这个消息。使用这个方法的前提是，该类已经把相关方法的实现代码准备好了，就等着运行的时候动态插在类里面就可以了。关于这的方案的应用<blockquote><footer><strong>Effective Objective-C 2.0</strong><cite>Matt Galloway（著） 爱飞翔（译）</cite></footer></blockquote>这本书举了“@dynamic”属性的实现为例子，实际开发中也可能会被用来“注入”调试信息，或者<a href="https://github.com/ITEnthusiast/TrendsAnalytics" target="_blank" rel="noopener">收集用户信息</a>等。<img src="/2018/05/03/还原OC对象的方法调用轨迹（下）/2.png" class="[还原OC对象的方法调用轨迹（下）]"><br><h3><b>2、备援接受者（replacement receiver）</b></h3><br>该阶段当前接受者还有第二次机会处理这个未知的消息，会调用 <figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+(<span class="keyword">id</span>)forwardingTargetForSelector: (SEL)selector </span><br></pre></td></tr></table></figure>来获取备援接受者对象。一个对象的内部，可能还有一系列其他对象，该对象可经由此方法将能够处理该消息的相关内部对象当作结果返回。<p></p><img src="/2018/05/03/还原OC对象的方法调用轨迹（下）/3.png" class="[还原OC对象的方法调用轨迹（下）]"><img src="/2018/05/03/还原OC对象的方法调用轨迹（下）/4.png" class="[还原OC对象的方法调用轨迹（下）]"><p></p><h3><b>3、完整的消息转发机制（full forwarding mechanism）</b></h3>首先创建 NSInvocation 对象，把与尚未处理的那条消息有关的全部细节都封装在里面，这个对象包含方法、目标接受者及参数。在触发 NSInvocation 对象时，“消息派发系统”（message-dispatch system）会调用<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-(<span class="keyword">void</span>)forwardingInvocation: (<span class="built_in">NSInvocation</span> *)invocation </span><br></pre></td></tr></table></figure>把消息指派给目标对象。该方法的实现内容大致分两种：第一种，简单的修改目标，使该消息在新目标上得以调用即可。由于此方法功能跟“备援接受者”阶段的效果一样，所以很少有人采用；第二种就是在消息被触发前，以某种方式改变消息本体的参数或更换消息等。这个阶段也可以调用父类的同名上述方法，这样继承体系中的每个类都有机会处理此调用请求，直至 NSObject 基类，继而还是会调用“doesNotRecognizeSelector”方法抛出异常，俗称程序崩溃。<p></p><img src="/2018/05/03/还原OC对象的方法调用轨迹（下）/5.png" class="[还原OC对象的方法调用轨迹（下）]"><p></p><h2>总结</h2><b>消息传递阶段</b>：作为面向对象高级编程语言几乎都有与之相对应的函数调用轨迹。<b>消息转发阶段</b>：接受者在每一步都有机会处理这个消息，只不过越往后，处理消息的代价就越大。“动态绑定”的机制为Object-C语言赋予了动态语言所拥有的特性，也因为此灵活性在代码执行速度上就不及“静态绑定”的函数调用操作那样迅速了，即使在类的内部缓存着曾经调用过的方法。消息转发全流程如下图：<p></p><img src="/2018/05/03/还原OC对象的方法调用轨迹（下）/6.jpg" class="[还原OC对象的方法调用轨迹（下）]"></h7><h2>参考文献：</h2><blockquote><p>第二章：对象、消息、运行期</p><footer><strong>Effective Objective-C 2.0</strong><cite>Matt Galloway（著） 爱飞翔（译）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;/p&gt;&lt;h2&gt;引言&lt;/h2&gt;
&lt;h7&gt;
我们了解类的本质构成及“类继承体系”， superclass 指针确立了继承关系，而 isa 指针描述了当前实例对象所属的类，并却能够看出对象位于“类继承体系”的哪一部分。如果某对象传递消息：&lt;figure class=&quot;highl
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="OC进阶语法" scheme="https://itenthusiast.github.io/categories/iOS/OC%E8%BF%9B%E9%98%B6%E8%AF%AD%E6%B3%95/"/>
    
    
      <category term="objc消息转发机制" scheme="https://itenthusiast.github.io/tags/objc%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E6%9C%BA%E5%88%B6/"/>
    
  </entry>
  
  <entry>
    <title>还原OC对象的方法调用轨迹（上）</title>
    <link href="https://itenthusiast.github.io/2018/04/26/%E8%BF%98%E5%8E%9FOC%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8%E8%BD%A8%E8%BF%B9%EF%BC%88%E4%B8%8A%EF%BC%89/"/>
    <id>https://itenthusiast.github.io/2018/04/26/还原OC对象的方法调用轨迹（上）/</id>
    <published>2018-04-26T08:39:36.000Z</published>
    <updated>2018-05-14T10:39:40.000Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><p>我们清楚编程语言函数调用的底层逻辑之后，其实再往上了解一些高级语言针对函数调用的包装思想，种类基本上就不统一了。很多语言，比如 C 语言，调用一个方法其实就是跳到内存中的某一点并开始执行一段代码，没有任何动态的特性，因为这一切在编译时就决定好了。 Objective-C 作为一门面向对象语言，它扩展 C 语言加入了面向对象特性和动态消息传递机制，其动态特性的基石便是它的 Runtime 库。在 Objective-C 中，</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[object fun]</span><br></pre></td></tr></table></figure><p>并不会立即执行 fun 这个方法的代码，它是要在运行时给 object 发送一条叫 fun 的消息，这个消息也许会由 object 来处理，也许会被转发给另一个对象。</p><h2>OC对象的本质</h2><p>我们平时编写的 OC 代码，其底层实现其实都是 C／C++ 代码。实际上苹果只开放了一小部分源码，所以借助其底层语言可以帮助我们了解验证 OC 的一些本质性问题。OC世界中的“万物之源” NSObject 对象的底层实现是使用 C 语言的结构体。其中只有一个 Class（objc_class*）类型的 isa 指针（64位机器下实际只会占用8个字节，但是由于<a href="https://baike.baidu.com/item/内存对齐/9537460?fr=aladdin" target="_blank" rel="noopener">内存对齐</a> 的原因会被分配16字节空间）。至于继承自 NSObject 的自定义类，除了 isa 指针外还有其成员变量信息。</p><h2>OC对象的分类</h2><p>单就某一个类对象而言， OC 中的对象分为三种： <strong>instance 对象、 class 对象、 meta-class 对象</strong></p><p><strong>instance对象</strong> 是通过 alloc 方法实例化出来的对象，每次 alloc 出来的对象都会被分配一个新地址来存储各自的信息，存储信息包括： isa 指针、成员变量的值。<br>**class对象 *<em>则是针对类本身而言的，每个类在内存中有且只有一份，相应的也是用来存放关于类的信息： isa 指针、 superclass 指针、类的属性信息、对象方法信息、协议信息及成员变量信息等等（注意：这里存放的是成员变量信息并非成员变量的值，比如成员变量的类型、名字等描述信息）。<br><strong>meta-class对象</strong> 跟 class 对象类似，也是针对类本身存在的，内存中有且只有一个该对象，结构也和 class 对象一样都是 Class（objc_class</em>）类型，只是用途不同，它主要用来放置： isa 指针、 superclass 指针及类的类方法信息。 class 和 meta-class 对象的本质结构都是 struct objc_class 类型的结构体，这个类型的结构体内部设置了存放该类有关所有信息的属性。</p><h2>isa &amp; superclass</h2><p>实际上这三种对象也是通过 isa 指针联系起来的：instance 对象的 isa 指针的值对应了 class 对象的地址，class 对象的 isa  指针的值对应了 meta-class 对象的地址，而 meta-class 对象的 isa 指针的值指向了基类的 meta-class 对象的地址（从64bit开始， isa 需要与特定的值 ISA_MASK 做一次位运算才能算出所指向的真实地址，这个 ISA_MASK ，源码中给出了不同设备架构对应的不同的值：arm64：ISA_MASK为0x0000000ffffffff8ULL；x86_64：为0x00007ffffffffff8ULL）<br>类有继承，在多重继承关系下则通过 superclass 指针将自身与父类及基类联系起来。由于 instance 对象中不含有 superclass 指针，所以实例对象与父类之间的联系均要通过 class 对象和 meta-class 对象中的 superclass 指针建立，这里可以结合这三种类分别存放了何种信息来理解。 class 对象的 superclass 指针指向的是其父类的 class 对象的地址，若其没父类（如：基类）则为 nil ； meta-class 对象的 superclass 指针指向的是父类的 meta-class 对象的地址，若其没父类（如：基类）则指向其 class 对象的地址。可以配合着网上流传的比较经典的图理解上述流程。<br></p><img src="/2018/04/26/还原OC对象的方法调用轨迹（上）/1.png" class="[还原OC对象的方法调用轨迹（上）]" title="[100][100]"><p>所以 instance 实例对象调用自身对象方法的轨迹为：通过 isa 找到自身的 class，方法若不存在，则通过 class 的 superclass 找到父类的 class 对象的该方法，以此类推找下去（若找到基类都没找到方法的实现，这时候就是发挥 OC 语言动态特性的时候了）；相似的， class 对象调用类方法的轨迹为：通过 class 对象的 isa 指针找到自身的 meta-class 对象，若其中没有该方法的实现部分，就通过 meta-class 对象的 superclass 指针找到父类的 meta-class 对象的该方法，以此类推找下去。</p><h2>objc_class结构体源码小撇</h2><p>这里根据 objc 源码（C++编写）配合分析上述中提到的 objc_class 结构体的内部一些比较熟悉的变量和函数。该结构体内部第一个变量就是 Class（objc_class*）类型的 isa 指针；同样类型的 superclass 指针；接着是 cache_t 类型（结构体）的 cache 变量，用来缓存该对象曾经调用过的方法的一些信息，比如有16字节的 capacity、 bucket_t 类型（结构体）的 _buckets： _key 和 _imp 等；还有一个 class_data_bits_t 类型（结构体）的 bits 变量，调用该结构体中的 data() 函数得到 class_rw_t 结构体类型的数据（拿bits与FAST_DATA_MASK进行位运算，源码中给出的值是0xfffffffcUL）。 class_rw_t 结构体中含有该类的方法列表 methods（数组）、属性列表 properties（数组）、协议列表 protocols（数组）、其中有个 class_ro_t 结构体类型的 ro 变量。 class_ro_t 结构体包含 instance 对象占用的内存空间大小 instanceSize 变量、类的名称 name 变量、成员变量列表 ivars 。<br>这里附上objc4的源码地址：<a href="https://opensource.apple.com/tarballs/objc4/" target="_blank" rel="noopener">https://opensource.apple.com/tarballs/objc4/</a></p><h2>参考文献：</h2><blockquote><p>第二章：对象、消息、运行期</p><footer><strong>Effective Objective-C 2.0</strong><cite>Matt Galloway（著） 爱飞翔（译）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;我们清楚编程语言函数调用的底层逻辑之后，其实再往上了解一些高级语言针对函数调用的包装思想，种类基本上就不统一了。很多语言，比如 C 语言，调用一个方法其实就是跳到内存中的某一点并开始执行一段代码，没有任何动态的特性，因为这一切在编译时就决定好了。 O
      
    
    </summary>
    
      <category term="iOS" scheme="https://itenthusiast.github.io/categories/iOS/"/>
    
      <category term="OC进阶语法" scheme="https://itenthusiast.github.io/categories/iOS/OC%E8%BF%9B%E9%98%B6%E8%AF%AD%E6%B3%95/"/>
    
    
      <category term="objc消息传递机制" scheme="https://itenthusiast.github.io/tags/objc%E6%B6%88%E6%81%AF%E4%BC%A0%E9%80%92%E6%9C%BA%E5%88%B6/"/>
    
  </entry>
  
  <entry>
    <title>探索函数调用得本质之旅</title>
    <link href="https://itenthusiast.github.io/2018/04/19/%E6%8E%A2%E7%B4%A2%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E5%BE%97%E6%9C%AC%E8%B4%A8%E4%B9%8B%E6%97%85/"/>
    <id>https://itenthusiast.github.io/2018/04/19/探索函数调用得本质之旅/</id>
    <published>2018-04-19T09:11:00.000Z</published>
    <updated>2018-10-10T04:53:22.108Z</updated>
    
    <content type="html"><![CDATA[<h2>引言</h2><h7>编程人员在日常编码工作中难免会使用一个“工具”来辅助我们实现一些功能。这个“工具”的具体体现大概就是各种各样的函数，也有些语言称它为方法。在调用函数的流程中需要注意几个关键因素：函数名、参数、返回值；其中函数名我们通过阅读一些文档可以了解到，包括其具体实现的功能。这里就后两项作为切入点，利用汇编语言从内存层面简单剖析一下函数调用的实质。</h7><h2>CPU执行指令依据</h2><h7>一个程序被运行，这个被编译、链接后的“指令集”会被加载到运行设备的内存中，等待CPU一条条执行。内存为该程序分配的这段内存空间中，从逻辑角度根据数据功能类分为一个个段（实际上是一整段连续的内存空间），大致分为代码段、数据段、堆栈段、附加段（内存中数据都是二进制数据，对于不同段中数据的不同功能取决于CPU是依据哪类寄存器找到的。比如CS寄存器配合IP寄存器指定的是代码段，DS寄存器配合着通用寄存器指定的是数据段，SS寄存器配合着SP指定的则是栈段）。我们的代码指令被放入代码段，CPU也是首先依据CS:IP找到该段地址并执行其中的内容，函数相关操作则是利用栈段来实现。</h7><h2>函数调用流程</h2><h7>实际编程过程中一些高级语言不需要程序员手动分配栈段大小，这部分工作由编译器来完成。先假设内存中一段空间被分配为栈段，栈段大小确定后，栈底处于地址值最大的位置，当有数据PUSH进来的时候栈顶地址值（由寄存器SP来记录）会随之变小，反之亦反。现在有函数调用执行需要用到这部分空间来完成操作。首先将函数参数push入栈；在使用call命令调用函数名时，会将返回地址push入栈；寄存器BP的值记录着调用该函数的上一级函数的SP值（寄存器BP的功能是用来辅助SP完成栈段的一些操作），此时将BP的值PUSH入栈保存；由于SP的值是随着出入栈的操作一直变化着，这里使用BP来记录此刻SP的值；函数内可能会有类似局部变量等数据需要占用空间，这里需要提前预留指定的空间大小（例如：10个字节）：需要SP的值减10（由于这部分空间可能对于一些函数是不需要的，所以不同平台的编译器的做法也有不同。如图所示可以看出VC++6.0和Xcode处理相同代码的优化区别）；<img src="/2018/04/19/探索函数调用得本质之旅/1.jpg" class="[VC++6.0和xCode编译器得优化区别]" title="[100] [100]"><br><br>如果函数内可能会使用到其他寄存器，特别是一些寄存器数量不是很多的平台，这里需要做保护处理，将要用的寄存器之前的值PUSH入栈；在真正实现本函数功能之前还有一个有关安全的操作需要处理，就是讲预留出来的局部变量空间统一填充指定的数据（这里使用CC填充，原因是CC有在系统层面代表着暂定，至少没有意外的危险），因为不确定这段栈段空间之前存过什么数据，可能会有风险。填充的方法就是利用寄存器ES配合着寄存器DI指定这段空间的地址，使用stosw命令反复执行；到此为止，准备工作算是基本完成了，接着就是实现函数功能体部分了，而这部分准备工作实际工作中往往由对应的编译器来完成。执行完业务逻辑代码后，沿着准备阶段相反的流程恢复栈段空间；POP出栈之前受保护的寄存器值；将BP的值赋值给SP来恢复“栈顶指针”SP预留空间之前的值；此时栈顶元素是BP之前存放进来的数值，需要POP出栈给BP寄存器；执行ret命令将函数的返回地址出栈，此时通过修改SP的值至栈底，方法就是增加SP的值（此数值就是之前PUSH进栈参数的字节数）达到恢复栈平衡。假若此函数中又调用了其他函数，从内存角度看的话是继续向内存地址减小的方向叠加数据，操作流程同以上过程，栈段剩余内存空间也会随之相应减少。所以程序如果出现死循环情况，栈段空间迟早会被耗完，所以在使用递归调用函数的时候一定要注意结束条件。</h7><img src="/2018/04/19/探索函数调用得本质之旅/2.png" class="[函数调用流程]" title="[100] [100]"><h2>汇编语言描述函数调用流程</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">push 参数</span><br><span class="line">call 函数名</span><br><span class="line">push bp</span><br><span class="line">mov bp, sp</span><br><span class="line">sub sp, 10</span><br><span class="line">push ai</span><br><span class="line">使用CC（INT 3）填充局部变量空间</span><br><span class="line">执行业务逻辑</span><br><span class="line">pop ai</span><br><span class="line">mov sp, bp</span><br><span class="line">pop bp</span><br><span class="line">ret</span><br><span class="line">恢复栈平衡</span><br></pre></td></tr></table></figure><h2>参考文献：</h2><blockquote><p>第二章 寄存器；第四章 第一个程序；第十章 CALL和RET指令</p><footer><strong>汇编语言（第三版）</strong><cite>王爽（著）</cite></footer></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2&gt;
引言
&lt;/h2&gt;
&lt;h7&gt;
编程人员在日常编码工作中难免会使用一个“工具”来辅助我们实现一些功能。这个“工具”的具体体现大概就是各种各样的函数，也有些语言称它为方法。在调用函数的流程中需要注意几个关键因素：函数名、参数、返回值；其中函数名我们通过阅读一些文档可以了解到，
      
    
    </summary>
    
      <category term="Assembly" scheme="https://itenthusiast.github.io/categories/Assembly/"/>
    
    
      <category term="函数调用流程" scheme="https://itenthusiast.github.io/tags/%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B/"/>
    
      <category term="测试Tag" scheme="https://itenthusiast.github.io/tags/%E6%B5%8B%E8%AF%95Tag/"/>
    
  </entry>
  
  <entry>
    <title>测试</title>
    <link href="https://itenthusiast.github.io/2016/10/01/%E6%B5%8B%E8%AF%95/"/>
    <id>https://itenthusiast.github.io/2016/10/01/测试/</id>
    <published>2016-10-01T02:51:00.000Z</published>
    <updated>2018-10-10T10:46:14.740Z</updated>
    
    <summary type="html">
    
    </summary>
    
    
  </entry>
  
</feed>
