Jekyll2022-08-16T07:29:36+00:00https://la0s.github.io/atom.xmlLa0stwitter.com/La0s_LeeLa0szsh折腾记录2019-05-23T00:00:00+00:002019-05-23T00:00:00+00:00https://la0s.github.io/2019/05/23/Mac_fast<p>掌握一个工具或者快捷键有时候能让你的效率提升百倍,像我这么喜欢折腾的人当然也不例外(其实我是工具的暗黑模式控),Mac有很多提升工作效率的工具,这篇文章主要讲terminal方面的配置,毕竟程序员大部分时间是在和终端打交道的,这里写篇杂文记录一下我的配置过程。<!--more--><br />
Mac就不用说了iTerm2 + oh-my-zsh是王道,主要说一下重要的地方。我的主题也是用的是agnoster,<del>所以必须要安装powerline字体,但是这个字体我觉得都很丑,由于我一直习惯了iTerm2的monaco字体,所以单独下载这个字体(Google一搜真的有)<a href="https://github.com/supermarin/powerline-fonts/tree/master/Monaco">Monaco for Powerline</a>并安装即可。</del><br />
现在iTerm2已经内置了powerline字体,勾选Use built-in Powerline glyphs即可,不用再单独下载powerline字体了
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190523.4.png" alt="" />
我是工具的颜值党,所以调色也很重要,这个主要通过iTerm2配置
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190523.1.png" alt="" />
然后有一些关于agnoster theme的配置,这个去修改~/.oh-my-zsh/themes/agnoster.zsh-theme,里面的配置改改就明白是什么意思了。
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190523.2.png" alt="" />
我的zsh一些比较重要的插件,autojump这个插件很有用,会记录你访问过的所有路径,对于深目录可以直接j 跳转,zsh-syntax-highlighting用于命令语法高亮
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190523.3.png" alt="" />
当然vim主题也被我修改了。然后推荐一下常用的终端快捷键,这些快捷键掌握之后会省掉很多繁琐的操作</p>
<ul>
<li>清除当前行:Ctrl + U</li>
<li>到行首/行尾:Ctrl + A/E</li>
<li>搜索命令历史:Ctrl + R(这个命令很有用,别问我为啥)</li>
<li>清除光标之后的字符:Ctrl + K(当命令很长的时候在中间输入的时候很有用)</li>
<li>删掉光标之前的一个单词(不用一个一个删了):Ctrl + W</li>
<li>清除缓存:Command + K(这个我经常用来清理输出)</li>
<li>让光标按单词移动而不是字符:option + ←/→(iTerm2需要修改映射)</li>
</ul>La0s掌握一个工具或者快捷键有时候能让你的效率提升百倍,像我这么喜欢折腾的人当然也不例外(其实我是工具的暗黑模式控),Mac有很多提升工作效率的工具,这篇文章主要讲terminal方面的配置,毕竟程序员大部分时间是在和终端打交道的,这里写篇杂文记录一下我的配置过程。起落落落起的春招找实习之旅2019-05-13T00:00:00+00:002019-05-13T00:00:00+00:00https://la0s.github.io/2019/05/13/chunzhao<p>春招投的不多,只感受了腾讯和阿里的面试,这里记录一下其中比较有用的一些问题。
面的第一家是腾讯科恩实验室,因为过年的时候就发招聘了,一面3.5号,电面54分钟。<!--more--></p>
<ul>
<li>简单介绍一下项目,然后开始深挖简历,中间穿插着技术提问</li>
<li>项目里静态分析和基于xposed动态工具介绍一下,如果不使用xposed,怎么实现动态分析工具</li>
<li>做过脱壳没有,Android上的加固,脱壳技术介绍一下</li>
<li>如何从海量的APP找出一个二次打包的应用呢,有几种思路(流量特征,代码相似度检测,UI节点遍历等,开放题)</li>
<li>xposed框架原理(zygote劫持,修改method结构体,将Java方法转成native方法hook。需要对xposed的源码有一定的了解)</li>
<li>APP的漏洞了解吗,应用克隆漏洞呢(支付宝那个),activity暴露攻击怎么利用,webview漏洞,其他漏洞等</li>
<li>如何Hook动态加载的dex(博客写的loadclass方法)</li>
<li>ARM和x86汇编有什么区别,函数调用约定,函数传参和返回</li>
<li>栈溢出原理,格式化字符串漏洞,有哪几种保护手段,怎么绕过栈上canary保护,ROP攻击原理</li>
</ul>
<p>二面3.15号,隔了10天,组长面的,49分钟。</p>
<ul>
<li>现在Android平台上的几种加固方式(动态加载,类抽取,混淆,vmp),怎么脱壳,及各种技术细节</li>
<li>xposed框架原理(面试官很爱问这个问题),Frida原理,Frida和xposed有什么区别</li>
<li>Got表hook和inline hook区别</li>
<li>selinux安全相关</li>
<li>Android系统从按下开机键,启动流程是怎么样的(设备初始化,init进程,zygote进程,dalvik实例)</li>
<li>webview漏洞原理和防护,支付宝的克隆漏洞技术复现细节(tx面试官很爱问…)</li>
<li>做过APP自动测试工具没有,这些漏洞如果编写自动化测试框架有什么思路</li>
</ul>
<p>后来因为一些原因没有拿到这个offer(后来也庆幸自己没去,上天总是把最好的留到最后),中间穿插了阿里移动安全的面试,不得不说阿里的面试还是挺难的,一面两个面试官,46分钟。</p>
<ul>
<li>IDA有个F5的功能,那么如何阻止破坏F5分析呢(破坏堆栈,滥用noreturn函数等)</li>
<li>列举几个花指令的代码片段</li>
<li>Windows常见的加壳,脱壳技术</li>
<li>windows原创注入和hook的几种实现</li>
<li>Android的init_array和JNI_OnLoad的时机问题,如何绕过init_array段中的反调试</li>
<li>xposed hook原理,xposed究竟是如何实现一个dalvik模式的函数hook的,Frida是如何实现native函数hook</li>
<li>Android常见的加固和脱壳技术,vmp了解吗,如何让你设计一个虚拟机保护你怎么实现</li>
<li>Android反调试的几种手段</li>
<li>ollvm混淆的原理,手段,列举指令替换的例子,如何实现的控制流平坦化</li>
<li>iOS砸壳工具原理(DYLD_INSERT_LIBRARIES注入dylib动态库)</li>
<li>iOS如何在不越狱的情况下去hook一个c函数,了解fishhook吗,Method Swizzling呢</li>
<li>编程方面:static函数的区别,局部变量和全局变量放在哪个段,局部变量一定都是放在栈上吗,特殊情况</li>
<li>int a(int b){
return a?100:10;
}这个函数的作用是什么,用你最熟悉的一种汇编语言实现,这个函数的汇编指令大体上分成那几个模块</li>
</ul>
<p>总的来说阿里的面试难度还是挺大的(我只能复现这些问题,因为有些问题真的是第一次听说),再加上我准备不足,于是一面就GG了,听说阿里今年每一面只要评级A+的,没办法。到了四月的正规批,被腾讯csig安全捞了简历但是面试隔了一周,所以最后的面试都已经是四月底了,一面深圳的电话,32分钟。</p>
<ul>
<li>关于CTF比赛收获最大的一个题目是什么</li>
<li>现在这种爱加密和梆梆壳是如何函数级加密的,脱壳思路是什么</li>
<li>有没有编译过Android源码,怎么编译内核修改traceid字段实现的反调试bypass</li>
<li>dalvik和ART虚拟机上函数hook区别是什么,ART虚拟机为什么执行速度加快(dex2oat,安装的时候编译成native机器码跑)</li>
<li>混淆的so库有什么分析经验,IDApython相关问题,最擅长一门的编程语言是什么</li>
<li>给你一个商业保护题目分析一下,给你一周的时间,不会的话写一下大体的分析。</li>
</ul>
<p>这个题目是一个混淆的so库,问其中一个函数的作用(后来查了一下是头条系的,难度还是挺大的)。接下来二面是总监面,因为本来一周的so题目我两天分析完给他发过去了,可能他也比较满意,全程聊得都很轻松。三面就比较奇葩了,级别应该更高,问我什么怎么设计一个类似于斗地主一样的棋牌游戏,怎么设计一个操作系统,从日志里取出现次数最多的QQ号代码思路等。</p>
<p>最后成功拿到了深圳这边安全实验室的的offer,现在回头看来这个工作是最适合我的,感谢腾讯安全,给我的so库混淆题目还是很有难度的,比之前几场面试难多了,能把这个题目搞出来也算对得起无数个生涯奋斗的夜晚。感谢陪我面试走下来的x哥和小强,经过这两个多月的历练,感觉自己还是太菜了,需要学习的东西还有很多,在这个过程中得到的收获还是挺大的,对我以前的学习深度和技术思维都有了新的思考,以后要走的路依然很长,后续想起来什么再补充吧。</p>La0s春招投的不多,只感受了腾讯和阿里的面试,这里记录一下其中比较有用的一些问题。 面的第一家是腾讯科恩实验室,因为过年的时候就发招聘了,一面3.5号,电面54分钟。iOS重打包绕过签名校验防护2019-03-21T00:00:00+00:002019-03-21T00:00:00+00:00https://la0s.github.io/2019/03/21/iOS_Resign<p>还是上篇文章里那个有反调试的APP(v6.0.5),我们这里来试一下静态patch的方法,也就是重打包的方法。一般来说iOS重打包有两种方法:<br />
<strong>第一种方法:</strong> <del>直接使用Cydia Impactor使用开发者签名安装即可,这种情况下不会修改APP的BundleID(推荐)</del><br />
因为苹果调整了自签策略,现在Cydia Impactor已经不能用了,大胡子也不维护了:( 所以笔者这里推荐使用MonkeyDev来重新签名,新建一个MonkeyApp项目,并且将Build Settings的MONKEYDEV_DEFAULT_BUNDLEID修改为YES,这种方式不会修改APP的BundleID(推荐)<!--more-->
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.1.png" alt="" /></p>
<p><strong>第二种方法:</strong>先使用iOS App Signer手动签名
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.2.png" alt="" />
然后使用mobiledevice install_app ~/Downloads/*.ipa 命令安装
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.3.png" alt="" />
但是这种情况下会把自己的签名覆盖APP的BundleID,有可能会导致异常</p>
<p>先看越狱检测,直接在-[UIDevice isJailbroken]开头处使用Keypatch插件patch两条汇编MOV X0, #0 && RET 即可。
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.8.png" alt="" /></p>
<p>然后针对于这个APP的ptrace反调试,直接Nop掉sub_10004FF4C函数即可,然后Edit -> Patch program -> Apply patches to input file替换掉二进制文件
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.4.png" alt="" /></p>
<p>注意不要把IDA数据库文件打包进去…重新压缩成IPA文件,使用Cydia Impactor安装,但是一运行应用闪退了,这说明应用做了签名校验,一般来说iOS开发会用exit函数退出应用,先用restore-symbol把iOS的符号还原,替换文件重签名安装,然后以backboard后台的方式启动,设置断点b exit,bt打印堆栈,因为我们这里已经把符号还原了,所以签名验证的地方就是这个+[XYAppIdentifierManager appIdentifierWithOrganizationId:]函数
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.9.png" alt="" /></p>
<p>或者我们在IDA里直接找到_exit的调用者
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.5.png" alt="" />
一个很明显的OC函数[XYAppIdentifierManager appIdentifierWithOrganizationId:],查看此函数, 总体的流程就是读取embedded.mobileprovision文件里的application-identifier字段,然后对比硬编码的identifier字符串,参考<a href="https://www.jianshu.com/p/b1cf329e1ca8">iOS判断APP被第三方企业证书重新签名</a>
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.6.png" alt="" /></p>
<p>修改指向退出流程的跳转指令CBNZ -> CBZ,再重新打包应用就不会闪退了
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190321.7.png" alt="" /></p>
<p>参考:<br />
<a href="https://www.twblogs.net/a/5d5eb355bd9eee5327fdbdc8/">iOS重打包技术</a></p>La0s还是上篇文章里那个有反调试的APP(v6.0.5),我们这里来试一下静态patch的方法,也就是重打包的方法。一般来说iOS重打包有两种方法: 第一种方法: 直接使用Cydia Impactor使用开发者签名安装即可,这种情况下不会修改APP的BundleID(推荐) 因为苹果调整了自签策略,现在Cydia Impactor已经不能用了,大胡子也不维护了:( 所以笔者这里推荐使用MonkeyDev来重新签名,新建一个MonkeyApp项目,并且将Build Settings的MONKEYDEV_DEFAULT_BUNDLEID修改为YES,这种方式不会修改APP的BundleID(推荐)使用Frida绕过iOS反调试2019-03-07T00:00:00+00:002019-03-07T00:00:00+00:00https://la0s.github.io/2019/03/07/anti_ptrace<p>在iOS平台上只要jailbroken了之后基本就可以想干啥干啥了,利用lldb+debugserver可以随便动态调试,所以最常用的保护手段就是反调试了,这里针对某新闻APP(v6.0.4)为例
当我们利用debugserver进行attach的时候,会报错Segmentation fault: 11<!--more-->
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.1.png" alt="" /></p>
<p>这种情况基本就是APP做了反调试了,参考<a href="http://iosre.com/t/7-2-0-ios/770">干掉高德地图iOS反动态调试保护</a>,我们利用debugserver -x backboard *:1234 /var/containers/…/XinHuaShe.app/XinHuaShe 启动APP,然后在lldb中下断b ptrace函数,然后使用bt打印堆栈
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.2.png" alt="" /></p>
<p>将这里的地址转换一下就是0x0000000100000000+hex(333440)=0x100051680,在IDA中找到这个地址
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.3.png" alt="" /></p>
<p>代码很明显了,一般来说ptrace反调试会写成如下的代码
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.4.png" alt="" /></p>
<p>所以问题来了,有两种思路,因为sub_100051640只用来做反调试,所以可以replace掉这个sub_100051640函数,先看这种方法,代码如下</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">baseAddr</span> <span class="o">=</span> <span class="nx">Module</span><span class="p">.</span><span class="nx">findBaseAddress</span><span class="p">(</span><span class="dl">'</span><span class="s1">XinHuaShe</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">subAddr</span> <span class="o">=</span> <span class="nx">baseAddr</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mh">0x51640</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">subAddr is </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">subAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">));</span>
<span class="kd">var</span> <span class="nx">subAddr</span> <span class="o">=</span> <span class="nx">ptr</span><span class="p">(</span><span class="nx">subAddr</span><span class="p">);</span>
<span class="c1">//var open = new NativeFunction(ptracePtr, 'int', ['int', 'int','int', 'int']);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">subAddr</span><span class="p">,</span> <span class="k">new</span> <span class="nx">NativeCallback</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">argc</span><span class="p">,</span> <span class="nx">argv</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hook sub_ptrace Bypass!!!</span><span class="dl">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">},</span> <span class="dl">'</span><span class="s1">int</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="dl">'</span><span class="s1">int</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">pointer</span><span class="dl">'</span><span class="p">]));</span>
</code></pre></div></div>
<p>代码中最好确认subAddr地址是不是内存中的sub_100051640函数,使用frida spawn并注入
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.5.png" alt="" /></p>
<p>可以成功attach。<br />
但是有时候调用ptrace的函数不一定只用来反调试,也有可能做了一些初始化的东西,就不能随随便便replace,否则程序会运行错误的。所以这里介绍另一种更通用的办法——直接replace掉ptrace函数,因为ptrace地址一般是由ptrace_ptr = dlsym(handle, “ptrace”)返回的,所以首先要得到这个地址</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">f</span> <span class="o">=</span> <span class="nx">Module</span><span class="p">.</span><span class="nx">findExportByName</span><span class="p">(</span><span class="dl">'</span><span class="s1">dyld</span><span class="dl">'</span><span class="p">,</span><span class="dl">"</span><span class="s2">dlsym</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span> <span class="p">{</span>
<span class="na">onEnter</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">arg</span> <span class="o">=</span> <span class="nx">Memory</span><span class="p">.</span><span class="nx">readUtf8String</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">arg</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">"</span><span class="s2">ptrace</span><span class="dl">"</span><span class="p">)</span> <span class="o">></span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="c1">//return -1;</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">onLeave</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">retval</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span><span class="p">){</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">ptrace str: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">retval</span><span class="p">);</span> <span class="c1">//打印返回的ptrace地址:0x18bad1078</span>
<span class="c1">//retval.replace();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">ptracePtr</span> <span class="o">=</span> <span class="nx">ptr</span><span class="p">(</span><span class="dl">"</span><span class="s2">0x18bad1078</span><span class="dl">"</span><span class="p">);</span> <span class="c1">//ptrace函数地址</span>
<span class="kd">var</span> <span class="nx">OriginPtrace</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">NativeFunction</span><span class="p">(</span><span class="nx">ptracePtr</span><span class="p">,</span> <span class="dl">'</span><span class="s1">void</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="dl">"</span><span class="s2">pointer</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">pointer</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">pointer</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">pointer</span><span class="dl">"</span><span class="p">]);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">ptracePtr</span><span class="p">,</span> <span class="k">new</span> <span class="nx">NativeCallback</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">,</span> <span class="nx">arg4</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">arg1</span> <span class="o">==</span> <span class="mi">31</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hook ptrace Bypass!!!</span><span class="dl">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="nx">OriginPtrace</span><span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">,</span> <span class="nx">arg4</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">},</span><span class="dl">'</span><span class="s1">int</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="dl">'</span><span class="s1">int</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">pointer</span><span class="dl">'</span><span class="p">]));</span>
</code></pre></div></div>
<p>因为libsystem_kernel.dylib`__ptrace这个地址是固定的,所以硬编码即可,spawn并注入
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.6.png" alt="" />
之后正常调试
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.7.png" alt="" /></p>
<p>除了hook之外有没有办法在程序运行前把这些反调试的指令patch掉呢,对于sub_100051640函数,我们只需要在运行之前对函数开头patch两条汇编即可,相当于直接return 0了,机器码可利用Keypatch得到</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">MOV</span> <span class="nx">X0</span><span class="p">,</span> <span class="err">#</span><span class="mi">0</span> <span class="c1">//00 00 80 D2 memory write -s 4 0x0000000000084000+0x100051640 0xD2800000</span>
<span class="nx">RET</span> <span class="c1">//C0 03 5F D6 memory write -s 4 0x0000000000084000+0x100051644 0xD65F03C0</span>
</code></pre></div></div>
<p><img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190307.8.png" alt="" /></p>
<p>另外利用lldb提供的接口是不是可以写成script呢,这样就不用再考虑每次image的偏移,肯定也是可以的。</p>
<p>参考:<br />
<a href="https://kov4l3nko.github.io/blog/2018-05-27-sll-pinning-hook-sectrustevaluate/">Disabling SSL pinning by hooking SecTrustEvaluate(…)</a><br />
<a href="https://sig-switzerland.ch/wp-content/uploads/2017/05/SIGS-TechCon2017_Kudelski-Bypassing_iOS_App.pdf">Bypassing iOS anti-debugging protections</a></p>La0s在iOS平台上只要jailbroken了之后基本就可以想干啥干啥了,利用lldb+debugserver可以随便动态调试,所以最常用的保护手段就是反调试了,这里针对某新闻APP(v6.0.4)为例 当我们利用debugserver进行attach的时候,会报错Segmentation fault: 11使用Needle绕过iOS越狱检测2019-01-14T00:00:00+00:002019-01-14T00:00:00+00:00https://la0s.github.io/2019/01/14/Needle_jailbreak<p>首先介绍一下Needle这个工具,和Android上的Drozer一样也是iOS安全测试框架,旨在简化对iOS应用程序进行安全评估的整个过程,Needle所涵盖的测试领域的一些示例包括:数据存储,进程间通信,网络通信,静态代码分析,挂钩和二进制保护(复制而来)。里面有很多集成了很多模块能方便的帮我们完成测试工作。<!--more--><br />
好了介绍到这,本文主要是针对三款有越狱检测的iOS应用,难度由低到高,这里主要用到了dynamic/detection/script_jailbreak-detection-bypass这个模块(此模块需要添加Darwin CC Tools依赖),关于模块的命令用法见Needle的wiki <a href="https://github.com/mwrlabs/needle/wiki/Modules-Usage">Modules-Usage</a>,这里就不赘述了<br />
首先针对第一个APP(v5.1.2)
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.1.png" alt="" /></p>
<p>通过needle提供的模块script_jailbreak_detection_bypass
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.2.png" alt="" /></p>
<p>而且我发现send在这里无法输出,所以改用console.log打印,发现是在-[UIDevice isJailbroken]这个类是(后者测试不是),查看此类
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.3.png" alt="" /></p>
<p>使用了两种方法检测越狱,因为它是直接通过方法的返回值判断是否越狱的,所以可以直接修改此Bool返回值即可绕过
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.4.png" alt="" /></p>
<p>成功绕过
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.5.png" alt="" /></p>
<p>这个APP越狱检测函数恰好叫isJailbroken所以能被hook绕过,下面来看Needle无法绕过的两个APP(v6.3.1)
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.6.png" alt="" /></p>
<p><del>Mac上的IDA7.0搜不出来中文字符(其实在string段里是显示中文的),原因是无法显示_ustring段的字符串,而Windows上是可以的。</del><br />
中文字符串是存在于<strong>__ustring</strong>段的,建议在IDA的字符串设置里勾选上Unicode类型,就可以正常搜出来了。
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.29.png" alt="" />
使用Needle的dynamic/detection/script_jailbreak-detection-bypass模块无法绕过,由于在Strings窗口无法显示中文字符串,直接在__cfstring段搜索越狱关键词,找到调用函数
-[AppDelegate application:didFinishLaunchingWithOptions:]
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.7.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.8.png" alt="" /></p>
<p>关键就在这个stat函数了:通过文件名filename获取文件信息,并保存在buf所指的结构体stat中,执行成功则返回0,失败返回-1。<br />
虽然Needle有hook这个函数(不得不说Needle考虑的情形比objection和其他工具多多了),但是返回值这么写是有问题的
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.9.png" alt="" /></p>
<p>所以参考objection的写法,重新改了本地的代码
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.10.png" alt="" /></p>
<p>成功绕过
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.11.png" alt="" /></p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/X7BQDjLsCZQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>最后不再借助这个框架,把Needle的代码拿出来自己手动写一个js</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">paths</span><span class="o">=</span><span class="p">[</span>
<span class="dl">"</span><span class="s2">/Applications/blackra1n.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/Cydia.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/FakeCarrier.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/Icy.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/IntelliScreen.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/MxTube.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/RockApp.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/SBSetttings.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Applications/WinterBoard.app</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/bin/bash</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/bin/sh</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/bin/su</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/etc/apt</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/etc/ssh/sshd_config</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Library/MobileSubstrate/DynamicLibraries/Veency.plist</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/Library/MobileSubstrate/MobileSubstrate.dylib</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/pguntether</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/private/var/lib/cydia</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/private/var/mobile/Library/SBSettings/Themes</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/private/var/stash</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/private/var/tmp/cydia.log</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/System/Library/LaunchDaemons/com.ikey.bbot.plist</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/bin/cycript</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/bin/ssh</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/bin/sshd</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/libexec/sftp-server</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/libexec/ssh-keysign</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/sbin/frida-server</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/usr/sbin/sshd</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/var/cache/apt</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/var/lib/cydia</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/var/log/syslog</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/var/mobile/Media/.evasi0n7_installed</span><span class="dl">"</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">/var/tmp/cydia.log</span><span class="dl">"</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">f</span> <span class="o">=</span> <span class="nx">Module</span><span class="p">.</span><span class="nx">findExportByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">libSystem.B.dylib</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">stat64</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span> <span class="p">{</span>
<span class="na">onEnter</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span> <span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">arg</span> <span class="o">=</span> <span class="nx">Memory</span><span class="p">.</span><span class="nx">readUtf8String</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">path</span> <span class="k">in</span> <span class="nx">paths</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">arg</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">paths</span><span class="p">[</span><span class="nx">path</span><span class="p">])</span> <span class="o">></span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hooking native function stat64: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">arg</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="c1">//return -1;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">onLeave</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">retval</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span><span class="p">){</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">stat64 Bypass!!!</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">retval</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">f</span> <span class="o">=</span> <span class="nx">Module</span><span class="p">.</span><span class="nx">findExportByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">libSystem.B.dylib</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">stat</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span> <span class="p">{</span>
<span class="na">onEnter</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span> <span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">arg</span> <span class="o">=</span> <span class="nx">Memory</span><span class="p">.</span><span class="nx">readUtf8String</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">path</span> <span class="k">in</span> <span class="nx">paths</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">arg</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">paths</span><span class="p">[</span><span class="nx">path</span><span class="p">])</span> <span class="o">></span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hooking native function stat: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">arg</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="c1">//return -1;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">onLeave</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">retval</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">is_common_path</span><span class="p">){</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">stat Bypass!!!</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">retval</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<p>spawn并注入
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.12.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.13.png" alt="" /></p>
<p>最后再看一个APP(v2.1.5),同样是Needle的模块无法绕过,而且这个越狱检测可以说是业界典范了
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.14.png" alt="" /></p>
<p>在__cfstring段搜索不安全环境
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.15.png" alt="" /></p>
<p>找到这个弹出提示框的-[AppDelegate showJailBreakDialog]
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.16.png" alt="" /></p>
<p>接着找调用它的地方,String窗口搜索showJailBreakDialog
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.17.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.18.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.19.png" alt="" /></p>
<p>同样都是返回bool值,直接hook一下-[BMKSecurityManager jailBreak]就可以绕过了
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.20.png" alt="" /></p>
<p>在Functions并没有搜到这个方法,猜测是在framework实现了此方法,双击OBJC_CLASS___BMKSecurityManager
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.21.png" alt="" /></p>
<p>找到包里的BMKit.framework/BMKit,搜索jailBreak
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.22.png" alt="" /></p>
<p>可以看到了它采用了7个方法检测越狱…
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.23.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.24.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.25.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.26.png" alt="" /></p>
<p>感兴趣的同学可以自己研究一下。cydia有个屏蔽越狱检测的Liberty Lite插件,上述三个APP都可以用这个tweak绕过,可以用cydownload下载.deb对里面的zzzzzLiberty.dylib逆向一下看看究竟是怎么实现屏蔽的。<br />
补充一下关于Needle这个框架,bug太多了,而且上次更新还是半年前,作者基本已经不维护了,这里分享一下Needle的两个设置选项,为什么要分享,因为官方wiki没有提供修改的方式,只能本地重新编译仓库源码(之前的objection也是这种方式改的)。一是设置选择进程的时候隐藏iOS自己的APP,二是默认设置APP启动方式是spawn(否则每次都要重新设置),用到了万能的grep命令。
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.27.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20190114.28.png" alt="" /></p>La0s首先介绍一下Needle这个工具,和Android上的Drozer一样也是iOS安全测试框架,旨在简化对iOS应用程序进行安全评估的整个过程,Needle所涵盖的测试领域的一些示例包括:数据存储,进程间通信,网络通信,静态代码分析,挂钩和二进制保护(复制而来)。里面有很多集成了很多模块能方便的帮我们完成测试工作。使用lldb+debugserver动态调试iOS应用2018-12-23T00:00:00+00:002018-12-23T00:00:00+00:00https://la0s.github.io/2018/12/23/lldb+debugserver<p>本来想拿某个字母车作样本的,结果那个iOS端有反sysctl调试,所以换了个投资APP
首先注册发送验证码抓包<!--more-->
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.1.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.2.png" alt="" /></p>
<p>看到data是加密的,砸完壳直接将二进制文件拖进IDA,String搜索requestTime,找到调用的函数,
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.3.png" alt="" /></p>
<p>看到data关键字,分析一下流程大致判断是将json Base64之后AES加密,接下来直接断这两个关键地方
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.4.png" alt="" /></p>
<p>lldb和debugserver配置这里就不多说了,网上博客和两本iOS逆向书都有<br />
<del>首先开一个终端进行两个端口转发:iproxy 2222 22和iproxy 12345 1234</del><br />
<del>第二个终端输入ssh -p 2222 root@localhost,直接attach上进程:debugserver *:1234 -a “MoneyPlatListedVersion”(ps -A找到进程),为此需要先打开APP</del><br />
<del>第三个终端输入lldb命令连接:process connect connect://localhost:12345</del><br />
<strong>更新iOS12以上调试步骤:</strong><br />
首先开一个终端进行两个端口转发:iproxy 2222 22和iproxy 1234 1111(将iPhone的1111端口映射到Mac的1234端口)<br />
第二个终端输入ssh -p 2222 root@localhost,直接attach上进程:cd /usr/bin && ./debugserver 127.0.0.1:1111 -a “MoneyPlatListedVersion”(ps -A找到进程),因此需要先打开APP<br />
第三个终端输入lldb命令连接:process connect connect://localhost:1234<br />
image list -o -f | grep MoneyPlatListedVersion 找到ASLR的基地址偏移(这里要注意调试的APP必须和IDA分析的是一致的,这样基地址才能对上,我之前因为换了iPhone6 plus的越狱机,而砸壳分析的文件还是老的5s脱出来的被坑了)
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.5.png" alt="" /></p>
<p>然后断在br s -a 0x0000000000014000+0x00000001000EA360,c运行点击获取验证码触发断点
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.6.png" alt="" /></p>
<p>先看objc_msgSend函数,receive是x0寄存器,selector是x1寄存器,参数1是x2,参数2是x3,以此类推…调用完成后返回值存在x0寄存器里<br />
selector是base64EncodedString,接着看receiver:po $x0
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.7.png" alt="" /></p>
<p>接着ni单步执行此函数,查看返回值po $x0,返回了base64的数据
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.8.png" alt="" /></p>
<p>同理我们断在-[NSData aes256_encrypt:IV:]的关键函数CCCrypt
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.9.png" alt="" />
对比</p>
<pre><code class="language-objective-c">CCCryptorStatus CCCrypt(
CCOperation op, /* kCCEncrypt, etc. */
CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */
CCOptions options, /* kCCOptionPKCS7Padding, etc. */
const void *key,
size_t keyLength,
const void *iv, /* optional initialization vector */
const void *dataIn, /* optional per op and alg */
size_t dataInLength,
void *dataOut, /* data RETURNED here */
size_t dataOutAvailable,
size_t *dataOutMoved)
</code></pre>
<p>我们分别打印出它的key,len,iv和dataIn的长度
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.10.png" alt="" /></p>
<p>然后使用memory read $x6 -count 748 -force打印dataIn数据
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.11.png" alt="" /></p>
<p>综上lldb调试还是非常方便的,和hook相比不用在意那么多参数类型,但是与frida的-f参数相比缺点也很明显,如果程序有反调试检测就得绕过了。</p>
<hr />
<p><strong>2021.8更新:</strong> iOS动态调试强烈推荐使用<a href="https://github.com/snare/voltron">voltron</a>这个工具,可以打造媲美IDA的调试界面。我一般开command、registers、disasm、memory这四个窗口,调试过程中对于寄存器的变化和内存写入的监控操作都会以高亮显示(类似于Windows平台的Ollydbg),lldb+voltron应该是我在iOS平台下体验过最好的调试方式了。
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181223.12.png" alt="" /></p>
<p>参考<br />
<a href="https://bbs.pediy.com/thread-212731.htm">lldb使用方法(学习笔记)</a><br />
<a href="http://www.iosre.com/t/7-2-0-ios/770">实战:干掉高德地图7.2.0版iOS客户端的反动态调试保护</a><br />
<a href="https://www.cnblogs.com/ludashi/p/5730338.html">iOS逆向工程之Hopper+LLDB调试第三方App</a></p>La0s本来想拿某个字母车作样本的,结果那个iOS端有反sysctl调试,所以换了个投资APP 首先注册发送验证码抓包iOS逆向第一站——搞定某车联网APP2018-12-15T00:00:00+00:002018-12-15T00:00:00+00:00https://la0s.github.io/2018/12/15/First_iOS<p>感谢牛轧糖大佬我涛哥带我走向车联网生涯,为我的研究生生涯积累了大量的样本。某美系车APP在更新之后登录数据变成了加密传输,而且最关键的是上篇文章的CC_hook.js竟然hook不到,这就引起了我的兴趣了。<!--more--><br />
点击登录
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.0.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.1.png" alt="" /></p>
<p>砸壳参考AloneMonkey大佬的<a href="http://iosre.com/t/frida-ios-dump/11640">frida-ios-dump</a>,IDA加载完二进制文件后在String窗口搜索loginbypassword
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.2.png" alt="" /></p>
<p>找到[WebService loginWithInfo:withCompletionHandler:]
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.3.png" alt="" /></p>
<p>fuctions窗口搜索(或者直接双击)callWebAPI:data:method:ssl:completionHandler:<br />
找到[WebService callWebAPI:data:method:ssl:completionHandler:]<br />
看到data关键加密信息,接着搜索setValue:forKey
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.4.png" alt="" /></p>
<p>[_priv_NBSSafeMutableDictionary setValue:forKey:]
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.5.png" alt="" /></p>
<p>无结果,说明很有可能是一个赋值的操作,加密不在这。回到上一步
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.6.png" alt="" /></p>
<p>v87由v86 = -[WebService returnDictionaryWithDataPath:](v11, “returnDictionaryWithDataPath:”, v201)返回<br />
双击returnDictionaryWithDataPath:
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.7.png" alt="" /></p>
<p>因为APP使用的是RSA加密,这也就证明了CC_hook.js hook为什么不到了。<br />
v8 = +[RSA encryptString:privateKey:](&OBJC_CLASS___RSA, “encryptString:privateKey:”, v4, v6);<br />
v4由convertToJsonData:返回(明文),v6由iBuickAppPrivate返回(密钥)。看一下密钥返回函数iBuickAppPrivate
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.8.png" alt="" /></p>
<p>最后,进入encryptString:privateKey:函数
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.9.png" alt="" /></p>
<p>到这里我们可以直接动态调试了(还不会…)或者是hook了,老方法首先用r2frida找到类和方法
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.10.jpg" alt="" /></p>
<p>hook +[RSA encryptString:privateKey:] 的两个参数(因为数据类型是id,可以直接转成ObjC类型)</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">ObjC</span><span class="p">.</span><span class="nx">available</span><span class="p">){</span>
<span class="k">try</span><span class="p">{</span>
<span class="kd">var</span> <span class="nx">className</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">RSA</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">funcName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">+ encryptString:privateKey:</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">hook</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="dl">'</span><span class="s1">ObjC.classes.</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">className</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">["</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">funcName</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">"]</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">[*] Class Name: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">className</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">[*] Method Name: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">funcName</span><span class="p">);</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">hook</span><span class="p">.</span><span class="nx">implementation</span><span class="p">,</span> <span class="p">{</span>
<span class="na">onEnter</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">param1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ObjC</span><span class="p">.</span><span class="nb">Object</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">args[2] -> </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">param1</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">param2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ObjC</span><span class="p">.</span><span class="nb">Object</span><span class="p">(</span><span class="nx">args</span><span class="p">[</span><span class="mi">3</span><span class="p">]);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">args[3] -> </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">param2</span><span class="p">);</span>
<span class="p">},</span>
<span class="na">onLeave</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">retval</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">retur</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ObjC</span><span class="p">.</span><span class="nb">Object</span><span class="p">(</span><span class="nx">retval</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">retval -> </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">retur</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">catch</span><span class="p">(</span><span class="nx">err</span><span class="p">){</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">[!] Exception2: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">err</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Objective-C Runtime is not available!</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>frida -U -f com.industryillusion.ibuick -l frida_ios_hook.js –no-pause
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.11.png" alt="" /></p>
<p>验证正确!
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.12.png" alt="" /></p>
<p>另外hook除了使用frida还可以直接使用objection的命令<br />
ios hooking watch method “+[RSA encryptString:privateKey:]” –dump-args<br />
ios hooking watch method “+[RSA encryptString:privateKey:]” –dump-return
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.13.png" alt="" /></p>
<p>但是参数的返回值它没有处理好,而且只适用于参数都是id(Objc)类型的,所以这里直接修改本地的源码转换成Objc类型再重新打印返回值
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.17.png" alt="" /></p>
<p>2019.2.27更新:objection v1.5.2版本修改位置
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.18.png" alt="" /></p>
<p>下面来看如何来hook非id(Objc)类型的参数
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.15.png" alt="" />
这里的三个参数类型是char *json, signed __int64 len, id a5,只有带id的才是ObjC对象,所以对于不同参数要注意这里的打印方式
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181215.16.png" alt="" /></p>
<p>参考:<br />
<a href="https://mabin004.github.io/2018/08/24/%E5%9C%A8iOS%E4%B8%8A%E4%BD%BF%E7%94%A8Frida/">在iOS上使用Frida</a></p>La0s感谢牛轧糖大佬我涛哥带我走向车联网生涯,为我的研究生生涯积累了大量的样本。某美系车APP在更新之后登录数据变成了加密传输,而且最关键的是上篇文章的CC_hook.js竟然hook不到,这就引起了我的兴趣了。秒破iOS APP加密数据2018-12-07T00:00:00+00:002018-12-07T00:00:00+00:00https://la0s.github.io/2018/12/07/iOS_Crypto<p>毕业第一步,先把项目需求实现了。其实这篇文章和之前Android上frida破解加密数据思路是一致的,因为iOS作为一种闭源系统,没有Android那么多的packers和so库,iOS官方封装了自己统一的Crypto库:<br />
<a href="https://opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h">CommonCryptor.h</a>/<a href="https://opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonDigest.h.auto.html">CommonDigest.h</a>/<a href="https://opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonHMAC.h.auto.html">CommonHMAC.h</a>,所以我们hook起来也很方便。<!--more-->拿我们之前搞过的一个投资APP(v4.2.9)为例,点击登录
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181207.1.png" alt="" /></p>
<p>request包
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181207.2.png" alt="" /></p>
<p>response包
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181207.3.png" alt="" /></p>
<p>直接hook CCCrypt函数,将这个脚本保存为CC_hook.js</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Intercept the CCCrypt call.</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">Module</span><span class="p">.</span><span class="nx">findExportByName</span><span class="p">(</span><span class="dl">'</span><span class="s1">libcommonCrypto.dylib</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">CCCrypt</span><span class="dl">'</span><span class="p">),</span> <span class="p">{</span>
<span class="na">onEnter</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Save the arguments</span>
<span class="k">this</span><span class="p">.</span><span class="nx">operation</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">CCAlgorithm</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">CCOptions</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">keyBytes</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">keyLength</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">ivBuffer</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">inBuffer</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">inLength</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">outBuffer</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">outLength</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span>
<span class="k">this</span><span class="p">.</span><span class="nx">outCountPtr</span> <span class="o">=</span> <span class="nx">args</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">CCCrypt(</span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">operation: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">operation</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">CCAlgorithm: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">CCAlgorithm</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">CCOptions: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">CCOptions</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">keyBytes: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">keyBytes</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">keyLength: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">keyLength</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">ivBuffer: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">ivBuffer</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">inBuffer: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">inBuffer</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">inLength: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">inLength</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">outBuffer: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">outBuffer</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">outLength: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">outLength</span> <span class="o">+</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span> <span class="o">+</span>
<span class="dl">'</span><span class="s1">outCountPtr: </span><span class="dl">'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">outCountPtr</span> <span class="o">+</span><span class="dl">'</span><span class="s1">)</span><span class="dl">'</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">operation</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Show the buffers here if this an encryption operation</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">In buffer:</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hexdump</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">inBuffer</span><span class="p">),</span> <span class="p">{</span>
<span class="na">length</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">inLength</span><span class="p">.</span><span class="nx">toInt32</span><span class="p">(),</span>
<span class="na">header</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">ansi</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}))</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Key: </span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hexdump</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keyBytes</span><span class="p">),</span> <span class="p">{</span>
<span class="na">length</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">keyLength</span><span class="p">.</span><span class="nx">toInt32</span><span class="p">(),</span>
<span class="na">header</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">ansi</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}))</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">IV: </span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hexdump</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">ivBuffer</span><span class="p">),</span> <span class="p">{</span>
<span class="na">length</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">keyLength</span><span class="p">.</span><span class="nx">toInt32</span><span class="p">(),</span>
<span class="na">header</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">ansi</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}))</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">onLeave</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">retVal</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">operation</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Show the buffers here if this a decryption operation</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Out buffer:</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hexdump</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">outBuffer</span><span class="p">),</span> <span class="p">{</span>
<span class="na">length</span><span class="p">:</span> <span class="nx">Memory</span><span class="p">.</span><span class="nx">readUInt</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">outCountPtr</span><span class="p">),</span>
<span class="na">header</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">ansi</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}))</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Key: </span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hexdump</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keyBytes</span><span class="p">),</span> <span class="p">{</span>
<span class="na">length</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">keyLength</span><span class="p">.</span><span class="nx">toInt32</span><span class="p">(),</span>
<span class="na">header</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">ansi</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}))</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">IV: </span><span class="dl">"</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hexdump</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">ivBuffer</span><span class="p">),</span> <span class="p">{</span>
<span class="na">length</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">keyLength</span><span class="p">.</span><span class="nx">toInt32</span><span class="p">(),</span>
<span class="na">header</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">ansi</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>
<p>直接使用 frida -U -f com.Dongfangjinke.dongfanghui -l CC_hook.js –no-pause
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181207.4.png" alt="" /></p>
<p>operation: 0x0代表加密,0x1代表解密</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">enum</span> <span class="p">{</span>
<span class="n">kCCEncrypt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="n">kCCDecrypt</span> <span class="o">=</span> <span class="mi">1</span>
<span class="p">};</span>
</code></pre></div></div>
<p>CCAlgorithm: 0x0指加密方式是AES加密,0x1指DES加密,0x2指3DES加密</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">enum</span> <span class="p">{</span>
<span class="n">kCCAlgorithmAES128</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="n">kCCAlgorithmAES</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="n">kCCAlgorithmDES</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
<span class="n">kCCAlgorithm3DES</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
<span class="n">kCCAlgorithmCAST</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span>
<span class="n">kCCAlgorithmRC4</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span>
<span class="n">kCCAlgorithmRC2</span> <span class="o">=</span> <span class="mi">5</span>
<span class="p">};</span>
</code></pre></div></div>
<p>CCOptions: 0x1指模式是CBC,通常0x3指ECB</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">enum</span> <span class="p">{</span>
<span class="n">kCCOptionPKCS7Padding</span> <span class="o">=</span> <span class="mh">0x0001</span><span class="p">,</span>
<span class="n">kCCOptionECBMode</span> <span class="o">=</span> <span class="mh">0x0002</span>
<span class="p">};</span>
</code></pre></div></div>
<p>size_t keyLength,指密钥位数(真正决定密钥长度的地方)。AES数据块长度为128位,所以IV长度需要为16个字符(ECB模式不用IV),密钥根据指定密钥位数分别为16、24、32个字符,IV与密钥超过长度则截取,不足则在末尾填充’\0’补足</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">enum</span> <span class="p">{</span>
<span class="n">kCCKeySizeAES128</span> <span class="o">=</span> <span class="mi">16</span><span class="p">,</span>
<span class="n">kCCKeySizeAES192</span> <span class="o">=</span> <span class="mi">24</span><span class="p">,</span>
<span class="n">kCCKeySizeAES256</span> <span class="o">=</span> <span class="mi">32</span><span class="p">,</span>
<span class="n">kCCKeySizeDES</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span>
<span class="n">kCCKeySize3DES</span> <span class="o">=</span> <span class="mi">24</span><span class="p">,</span>
<span class="n">kCCKeySizeMinCAST</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span>
<span class="n">kCCKeySizeMaxCAST</span> <span class="o">=</span> <span class="mi">16</span><span class="p">,</span>
<span class="n">kCCKeySizeMinRC4</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
<span class="n">kCCKeySizeMaxRC4</span> <span class="o">=</span> <span class="mi">512</span><span class="p">,</span>
<span class="n">kCCKeySizeMinRC2</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
<span class="n">kCCKeySizeMaxRC2</span> <span class="o">=</span> <span class="mi">128</span><span class="p">,</span>
<span class="n">kCCKeySizeMinBlowfish</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span>
<span class="n">kCCKeySizeMaxBlowfish</span> <span class="o">=</span> <span class="mi">56</span><span class="p">,</span>
<span class="p">};</span>
</code></pre></div></div>
<p>具体参阅<a href="https://opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonCryptor.h">CommonCryptor.h</a>各参数意义,key=DATA_KEY20150116和iv=20150116和Android客户端是一致的,而且由于Android是在so库生成的AES加密,避免了Android端java层hook不到的情况。</p>
<hr />
<p><strong>2021.6更新:</strong> 对上述脚本进行优化,增加了Base64转化过程,方便查找和过滤。当KEY与IV不是明文时,以Hex形式打印,并对原始的CCCrypt函数参数进行翻译。脚本放在了仓库里 <a href="https://github.com/la0s/Frida-scripts/blob/main/CC_Hook_2Base64.js">CC_Hook_2Base64.js</a><br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181207.5.png" alt="" /></p>
<p>参考<br />
<a href="https://github.com/dpnishant/appmon/tree/master/scripts/iOS/Crypto">appmon project提供的scripts</a> <br />
<a href="https://github.com/theart42/hack.lu/blob/master/IOS/Notes/05-Crypt/00-crypto-hooks.md">hack.lu的scripts</a><br />
<a href="https://www.frida.re/docs/javascript-api/#apiresolver">Frida-javascript-api-apiresolver</a></p>La0s毕业第一步,先把项目需求实现了。其实这篇文章和之前Android上frida破解加密数据思路是一致的,因为iOS作为一种闭源系统,没有Android那么多的packers和so库,iOS官方封装了自己统一的Crypto库: CommonCryptor.h/CommonDigest.h/CommonHMAC.h,所以我们hook起来也很方便。使用Frida绕过iOS sslpinning2018-11-02T00:00:00+00:002018-11-02T00:00:00+00:00https://la0s.github.io/2018/11/02/iOS_SSL<p>这里分享一下使用frida和objection来绕过某单车APP(v8.4.0)iOS端证书固定,感谢小贺同学提供的样本@Ccccccandyhe,起因是要抓取这个APP里附近车辆信息的流量,但是上来抓取发送验证码的地方就遇到了问题,用Charles显示也是红叉,猜测是用了SSL pinning
<!--more-->
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.1.jpg" width="50%" height="50%" /></p>
<p>我的手机是iPhone5s,11.1.2,越狱教程参考<a href="https://medium.com/ehsahil/basic-ios-apps-security-testing-lab-1-2bf37c2a7d15">Basic iOS Apps Security Testing lab — 1</a>,因为是iOS11系统,所以Cydia中有些插件和工具已经不再适用,建议多Google(生活总是一个error接着另一个error…)<br />
这里不使用ssl-kill-switch2这个插件(插件可以试验成功),我们使用frida和objection(<a href="https://github.com/sensepost/objection">基于frida的exploration toolkit</a>),首先在Cydia中和配置Switch插件一样去添加frida的源(这里不再详述请参见frida的官方文档),然后使用内置的ios sslpinning disable命令就可以成功绕过证书绑定。源码实现在/objection/hooks/ios/pinning/disable.js,通过查看发现它其实是通过hook AFNetworking,NSURLSession等常用库和一些底层的methods实现的。这点和switch插件是类似的。<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.2.png" alt="" /></p>
<p>其实这里可以看到这个APP是使用了AFNetworking library这个库来使用sslpinning,所以知道使用了AFNetworking框架,我们就可以自己写一个简单的frida js手动测试一下,通过hook AFSecurityPolicy evaluateServerTrust函数的返回值实现绕过,参考<a href="https://kov4l3nko.github.io/blog/2018-06-17-afnetwork-disable-ssl-pinning/">Cracking SSL pinning in AFNetworking</a></p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">resolver</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ApiResolver</span><span class="p">(</span><span class="dl">'</span><span class="s1">objc</span><span class="dl">'</span><span class="p">);</span><span class="c1">//创建已加载Object-C类方法的API查找器</span>
<span class="kd">var</span> <span class="nx">matches</span> <span class="o">=</span> <span class="nx">resolver</span><span class="p">.</span><span class="nx">enumerateMatchesSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">-[AFSecurityPolicy evaluateServerTrust:forDomain:]</span><span class="dl">"</span><span class="p">);</span><span class="c1">//查找evaluateServerTrust:forDomain函数,返回数组类型</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">matches</span><span class="p">.</span><span class="nx">lenght</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="se">\n</span><span class="s2">[E] -[AFSecurityPolicy evaluateServerTrust:forDomain:] is not found!</span><span class="se">\n</span><span class="dl">"</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">Interceptor</span><span class="p">.</span><span class="nx">attach</span><span class="p">(</span><span class="nx">ptr</span><span class="p">(</span><span class="nx">matches</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="dl">"</span><span class="s2">address</span><span class="dl">"</span><span class="p">]),{</span>
<span class="na">onLeave</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">retval</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">[I] -[AFSecurityPolicy evaluateServerTrust:forDomain:] hits!</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">retval</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span><span class="c1">//将返回值修改为1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">[I] -[AFSecurityPolicy evaluateServerTrust:forDomain:] is hooked!</span><span class="se">\n</span><span class="dl">"</span><span class="p">)</span>
<span class="p">}</span>
<span class="nx">main</span><span class="p">();</span>
</code></pre></div></div>
<p>将这个js保存为disable_ssl_pinning_in_loops.js,然后执行命令:frida -U -f com.mobike.bike -l disable_ssl_pinning_in_loops.js --no-pause,因为我这frida不支持中文无法用-n attach进程名(替代方法是-p PID),所以使用-f spawn单车的bundleid com.mobike.bike<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.3.png" alt="" /></p>
<p>成功绕过<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.4.jpg" width="50%" height="50%" /></p>
<p>同理某车载APP(v8.4.2)登录使用了ssl pinning<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.7.jpeg" width="50%" height="50%" /></p>
<p>分别使用objection和frida脚本测试<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.5.jpg" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.6.jpg" alt="" /></p>
<p>成功绕过<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181102.8.jpeg" width="50%" height="50%" /></p>
<p>未完待续…</p>
<p>参考<br />
<a href="https://kov4l3nko.github.io/">kov4l3nko.github.io</a><br />
<a href="https://www.guardsquare.com/en/blog/iOS-SSL-certificate-pinning-bypassing">Prevent bypassing of SSL certificate pinning in iOS applications</a></p>La0s这里分享一下使用frida和objection来绕过某单车APP(v8.4.0)iOS端证书固定,感谢小贺同学提供的样本@Ccccccandyhe,起因是要抓取这个APP里附近车辆信息的流量,但是上来抓取发送验证码的地方就遇到了问题,用Charles显示也是红叉,猜测是用了SSL pinning关于抓包的那些坑爹事2018-10-14T00:00:00+00:002018-10-14T00:00:00+00:00https://la0s.github.io/2018/10/14/Capture<p>前几天高博交给我几个VPN样本让我抓一下包,其中有一个样本某个ip地址用Charles抓不到,关闭代理依然能正常使用,这个就像我很早之前测过一个某面新闻也是这样的情况————用代理软件抓包无法抓到某个ip或域名,开启或者关闭代理都不影响APP本身的刷新,因为APP没有加壳,直接拿来逆了逆源码看看到底是怎么回事
<!--more-->
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.1.png" alt="" /></p>
<p>跟进AsyncHttpClient,实际上是调用了httpClient.getParams().setParameter(“http.route.default-proxy”, httpHost)
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.2.png" alt="" /></p>
<p>Google搜索AsyncHttpClient+抓包<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.31.png" alt="" /></p>
<p>类似的还有某mm商场客户端(v7.1.0)也采用了类似的方法导致抓不到包
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.32.png" alt="" /></p>
<p>关键的地方就在http.route.default-proxy这行了,如果没有设置ip端口则默认路由直连,不走wifi代理,这就是为什么我们用Charles抓不到包而且不用代理依然可以访问网络的原因了,解决方法也很简单,装一个ProxyDroid设置全局代理,将流量全部转发为wifi代理,这样我们就能愉快的抓包了,之前搞的那个某面新闻也是同样的道理。</p>
<hr />
<p>iOS平台上也有类似的VPN工具,没错就是大名鼎鼎的Shadowrocket,区别网络库直连和SSL pinning的方法就是看挂代理之后APP是否能通信正常,如果APP正常通信却抓不到包,那肯定就是前者了
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.14.png" width="50%" height="50%" /></p>
<p>下面再来介绍另一个重头戏————SSL pinning<br />
原理请Google关键词,简单来说就是APP通过预埋服务端证书来校验服务器证书和域名(单向校验,双向校验这里暂不讨论),这里我们拿一个之前外派的时候的APP,他使用了SSL pinning导致我们无法抓包登陆<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.8.png" alt="Desktop Preview" /></p>
<p>查看logcat<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.9.png" alt="" /></p>
<p>APP是360加固的,脱壳后进行反编译<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.10.png" alt="" /></p>
<p>找到assets文件夹下的证书(一般都会放在这)<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.11.png" alt="" /></p>
<p>这里我们不使用JustTrustMe模块(这里不得不提JustTrustMe项目的released那个apk版本太老了,需要重新编译,因为原项目无法实时打印出hook到的函数,所以我修改了一下打印日志的位置放在了repo里 <a href="https://github.com/la0s/JustTrustMe-master">修改版</a>)而使用神器frida,参考一篇很棒的外文文章 <a href="https://bbs.pediy.com/thread-222427.htm/">利用Frida绕过Certificate Pinning</a>,绕过 Certificate Pinning都离不开对应用程序的SSLContext的相关操作,利用Frida脚本控制SSLContext能全面绕过Android上的certificate pinning<br />
这里使用burp的证书,注意使用什么代理证书一定要统一,不能从burp导出的证书然后用Charles进行抓包测试<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.12.png" alt="" /></p>
<p>成功绕过了SSL pinning检查!<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.13.png" alt="Desktop Preview" /></p>
<p>再来看另一个APP<br />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.4.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.5.png" alt="" /></p>
<p>利用frida进行绕过
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.6.png" alt="" />
<img src="https://raw.githubusercontent.com/la0s/la0s.github.io/master/screenshots/20181014.7.png" alt="" /></p>
<p>未完待续…</p>
<p>参考<br />
<a href="https://yq.aliyun.com/articles/61299?comefrom=http://blogread.cn/news/">Android安全开发之安全使用HTTPS</a><br />
<a href="https://sec.xiaomi.com/article/43">小米安全中心——Frida.Android.Practice (ssl unpinning)</a><br />
<a href="https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e">Android Security: SSL Pinning</a></p>La0s前几天高博交给我几个VPN样本让我抓一下包,其中有一个样本某个ip地址用Charles抓不到,关闭代理依然能正常使用,这个就像我很早之前测过一个某面新闻也是这样的情况————用代理软件抓包无法抓到某个ip或域名,开启或者关闭代理都不影响APP本身的刷新,因为APP没有加壳,直接拿来逆了逆源码看看到底是怎么回事