之前介绍了怎么获取APP的所有类的结构信息,这个有什么用呢?用处大了,比如以这一步为基础,下一步通过注入来做更多研究工作。

注入的最小单位是函数,实际上,编译执行的程序在编译后,类就不复存在了,留下来的只是二进制代码(指令或数据都是一样的二进制代码)。所幸的是,跟我们打交道的,并不是二进制代码(那会困难很多),而是函数,而且是某个类的函数。

那么,在用classdump拿到成千上万个类与函数后,哪个函数才是我们关心的呢?怎么锁定它们呢?

本文介绍锁定目标类与函数的可行的办法。

基本上小程研究的目标APP都有丰富的界面,而小程关心的场景基本都是由特定的界面触发,所以,从界面入手是个不错的选择。

有没有办法找出某个界面对应哪一个类呢?这样就可以在classdump拿到的众多的类中仔细研究这个界面类拥有的函数与成员变量。

小程觉得目前最好的办法就是使用Reveal工具。

(一)使用Reveal

先下载一个Reveal(有破解版本),比如1.6版本或1.5版本,或2.0版本。

(1)拷贝libReveal.dylib到手机

Reveal最大的一个作用是把手机上的某个APP的界面同步显示到电脑上,要做到这个效果,Reveal既要在电脑上运行,同时也要把一个“内鬼”打入到手机。这个打入手机的“内鬼”就是libReveal.dylib。

在电脑上运行Reveal后,点击菜单Help,选择Show Reveal Library in Finder,再点击iOS Library,就可以找到libReveal.dylib。

拷贝到DynamicLibrary目录下:

scp libReveal.dylib root@192.168.2.57:/Library/MobileSubstrate/DynamicLibraries

基本上,手机上的APP在启动运行后,都可以加载DynamicLibraries里面的动态库(以dylib为后缀),至于加载哪个动态库,则由plist文件决定。这个知识点很重要,这意味着你可以写一个plist文件,让某个APP在启动时加载你写的动态库,这是注入的前提。

/Library/MobileSubstrate/DynamicLibraries,这个目录,在手机成功越狱后就会存在。

对于Reveal2.0版本,也以上面的办法定位动态库文件,但这个文件名叫“RevealServer”,可以拷贝过去后命名为“libReveal.dylib”即可。

(2)拷贝libReveal.plist到手机

找一个plist文件来修改,或者直接写一个plist文件,命名为libReveal.plist。

libReveal.plist文件要指定让哪个APP启动时加载Reveal.dylib,比如:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Filter</key>
    <dict>
        <key>Bundles</key>
        <array>
            <string>com.tencent.QQKSong</string>
        </array>
    </dict>
</dict>
</plist>

上面的plist内容,让“全民k歌”启动时,加载Reveal.dylib。

com.tencent.QQKSong,是“全民k歌”的BoudleID。至于目标APP的BoundleID是多少,有很多办法可以查到,比如找到它的plist文件来查看,比如ps看进程信息,比如动态调试等等。

小白:如果不指定这个Filter呢,是不是所有的APP启动时都加载?

小程:iOS8之前的版本是这样的,但之后的版本都需要指定APP。所以,不管3724,加上这个Filter总是不会错的。

然后,拷贝libReveal.plist到手机:

scp libReveal.plist root@192.168.2.57:/Library/MobileSubstrate/DynamicLibraries

之后,在电脑上再次启动Reveal就可以连接目标APP,来分析界面类了。

比如,在手机上重启全民k歌,在电脑上重启Reveal并选择菜单项,连接全民k歌。可以看到,全民k歌的一个页面是这样(右下角的类名是重点):

(二)让全民k歌自动切换至歌手页面

为了“感性”一点,小程做一个演示,通过Reveal定位到全民k歌的目标类,并让全民k歌启动后自动切换至歌手页面。

首先通过Reveal,定位到底部导航条的所在的viewcontroller类是KSRootTabBarController

然后,通过查看classdump翻译到的类结构中,找到这个类。可以看到,点击“我要唱”按钮,实际就是触发KSRootTabBarController::onClickTabBarItem函数。

接着,就可以hook这个类了,让目标APP自动跳转。这一步的具体操作,小程会在后续详细介绍,读者只需要“感性”地知道这回事就好。

最终,自动跳转的效果是这样的:

以上讲解了Reveal的使用。Reveal是定位目标类与函数的有效的办法,除了这个办法,还有一个办法就是,观察所有类的类名,猜测可能有关系的类(比如应该具备某个关键字),再注入这些类的函数并用NSLog输出信息,或者动态调试观察执行的流程,最终确定目标类与函数。

小程在这里介绍动态调试目标APP的办法,这个办法在“漫天定位”的时候可能有用,实际大多数情况下可能不必用到。读者可以在需要时再阅读这部分内容。


目标:在电脑上远程调试手机上的进程。

(一)问题

(1)为什么不用本地调试的方式?

可以在手机上安装gdb,再使用gdb调试目标进程。但因为本地用gdb来调试可能会遇到很多gdb本身的问题(有一些可以解决,而有一些并不好解决),而lldb被苹果支持,并用于替代gdb。所以,可考虑使用更好的工具,即lldb+debugserver来远程调试目标进程。

(2)需要什么工具?

电脑上需要lldb,如果mac电脑上安装了xcode那就会有lldb。

手机上需要安装debugserver。

(3)手机如何安装debugserver?

debugserver在哪里?

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/xx.xx(目标手机是什么系统就选择什么版本的目录),双击下面的文件:DeveloperDiskImage.dmg,在打开的finder中,usr/bin目录中存放了debugserver。

怎么定制手机上相应的debugserver?

(aa)

双击打开DeveloperDiskImage.dmg后,

cd /Volumes/DeveloperDiskImage/Library/PrivateFrameworks,

scp -r ARMDisassembler.framework root@192.168.2.22:/System/Library/PrivateFrameworks/,

即把ARMDisassembler.framework拷贝到手机,之所以这样做,是据别人的经验,可以让lldb看汇编代码的效果更好,不妨照做。

(bb)

把usr/bin中的debugserver拷贝到另一个目录,为后续的签名作准备。

(cc)

创建一个entitlement.xml文件,内容如下:

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.springboard.debugapplications</key>
    <true/>
    <key>get-task-allow</key>
    <true/>
    <key>task_for_pid-allow</key>
    <true/>
    <key>run-unsigned-code</key>
    <true/>
</dict>
</plist>
    
(dd)

使用ldid来签名:

ldid -Sentitlement.xml debugserver

拷贝到手机:

scp debugserver root@192.168.2.22:/usr/bin/

在手机上测试是否可用(比如ssh到手机后输入命令):

debugserver
(4)重签名工具ldid怎么安装?

如果有安装iOSOpenDev,那在目录/opt/iOSOpenDev/bin/下面就有ldid。

如果没有,那可以拉代码下来安装:

git clone git://git.saurik.com/ldid.git
cd ldid
git submodule update --init
./make.sh
which ldid #查看是否安装成功
(5)怎么使用debugserver与lldb?

在手机上启动服务器debugserver:

debugserver *:54321 -a "Spotify"

*表示侦听任意ip的连接,54321为端口,-a连接目标进程的名称(可用ps取得)。

然后,在电脑上启动lldb并连接服务器:

process connect connect://192.168.2.22:54321

如果手机端的debugserver提示:Waiting for debugger instructions for process 0,那说明已经连接上。

之后可以在lldb端发送命令,比如查看当前调试进程的模块信息:

image list -o -f

注意,第一次image list -o -f,可能要等上几分钟。当有返回后,再输入c,就可以让程序继续运行。

(6)如果觉得debugserver运行很慢,怎么解决?

使用usbmuxd来启用数据线来调试。

* 如何安装与使用usbmuxd?
    * 下载:http://7xibfi.com1.z0.glb.clouddn.com/uploads/default/original/2X/a/aa9cecf05b47d08a59324edeaaeea3f17e0608ee.zip
    * 具体使用可以参考:http://www.jianshu.com/p/9333a706641a。

小程直接用lldb跟debugserver,第一次image list等了几分钟,整体还可以接受

(7)怎么调试?
* debugserver *:54321 -a "Spotify"  --手机上启动debugserver并附加到目标进程
* lldb  --电脑上启动lldb
* process connect connect://192.168.2.22:54321  --lldb连接debugserver
* image list -o -f  --查看目标进程的所有模块的信息,比如输出:
    [  0] 0x000d8000 /var/containers/Bundle/Application/A80AF35B-DAD3-4D7E-B467-6C4230E32556/Spotify.app/Spotify(0x00000000000dc000)
    第二个值为程序在内存中的基地址,记录这个值。
* br s -a (0x000d8000+目标代码的偏移地址)
* c  --让程序继续执行
* (操作程序)让程序触发断点。
* 使用lldb命令进行调试。

(二)示例

这里简单演示下怎么调试Spotify这个APP。

从class-dump得到的信息,尝试调试类SPTNowPlayingBarModel的pause函数,看看在播放bar条上暂停播放时会不会断在这个函数上。

class-dump得到的pause的地址是0x00477ff1,在hopper上跳转到这个地址(G),也可以看到-[SPTNowPlayingBarModel pause]的信息。

* 运行目标应用
* debugserver *:54321 -a "Spotify"   手机端启动服务器,之后都在客户端lldb中操作。
* lldb
* process connect connect://192.168.2.22:54321
* image list -o -f 或 image list -o -f | grep Spotify
这一步可能要等一会,在ipod5的6.0系统会很快,而在iphone5s的10.1.1系统则很慢。
* 从上面的命令找到基地址为:0x000e6000 (每次运行不一样)
* 查看一下即将下断点的代码是怎么样的:dis -a '0x000e6000+0x00477ff1'  结果跟用hopper看到的一样(0x0047ff1处)。
* br s -a '0x000e6000+0x00477ff1'
* c
* 让程序触发到断点,使用lldb的命令查看数据或设置值,或单步等。

(三)lldb的常用命令

打印信息:
expr或e、po、p或print
display 表达式
undisplay 序号

断点:
br l或breakpoint list    #所有断点
breakpoint set -a 函数地址,可简写:br s -a xxx  #下断点
br s --函数关键字(可模糊)
    breakpoint set --shlib foo.dylib --name foo
br del 1    #删除断点
br del 2 3 4    #删除几个断点
内存断点
    watchpoint set expression 地址
    watchpoint set variable 变量名称
条件断点
    watchpoint modify -c 表达式

单步:
s/si    #单步进入函数
n/ni    #单步
f       #跳出函数

线程:
thread backtrace,或bt,或bt all  #列出所有线程

模块信息:
image list [-o -f]
image lookup -a expression
    image lookup -a $pc
image lookup -r -n xxx
    image lookup -r -n playPause #用来查看一个函数(关键字)的地址
image lookup -r -s

显示地址处的代码:
disassemble/dis -a 地址
    dis -a $pc
    dis -s 0x0002c000 -c 9  #后面的参数-c用来限制显示的代码数。

内存操作:
memory read [起始地址 结束地址]/寄存器 -outfile 输出路径

寄存器操作:
register read/格式
register write 寄存器名称 数值

总结一下,本文重点在于使用Reveal,从界面入手,定位到关键类与函数,进而找到目标类与函数,为分析借鉴或注入提供基础。对于动态调试,读者未必需要掌握,如果它实际帮不到你的话。


iOS逆向开发(3):锁定APP的目标类与函数 | reveal | lldb | debugserver | 远程调试的更多相关文章

  1. iOS逆向开发(4):注入目标函数 | fishhook | MobileSubstrate | MSHookFunction | iOSOpenDev

    从获得APP的所有类声明,到锁定目标类与函数,现在是时候注入函数了. 所谓"注入函数",小程的意思是让APP执行到小程写的代码中,跟"钩子"的概念一致.小程把个 ...

  2. qt-qml移动开发之在ios上开发和部署app流程简单介绍

    qt5.3已经全面支持移动开发,除了mac,windows,linux.还支持ios,android,wp,meego等移动平台,本教程是作者依据自己的经验,从头讲怎么样在ios上公布自己的app.因 ...

  3. iOS逆向开发(2):获取APP的类声明 | class-dump | dumpdecrypted

    之前介绍了怎么操作越狱的iOS设备(以下简称为手机),但简单操作手机并不是目标,小程的目标是手机上特定的APP,比如微信.淘宝.QQ音乐等等,因为小程可以从这些APP上拿到一些有用的信息或资源--比如 ...

  4. iOS逆向开发(1):基础工具 | ssh | scp | socat

    小白:小程,我一直想问,什么是逆向来着?是逆向行驶吗? 小程:理解为逆向行驶也没错.一般的项目是从无到有,而逆向是从已有的状态入手,分析出已有的流程与结构的手段. iOS上的逆向开发,是一件有趣的事情 ...

  5. iOS逆向开发(8):微信自动添加好友

    这一次,小程演示怎么让一个APP自动地运行,从而代替手工的操作.同样以"微信"以例,实现在一个微信群里面,对所有的成员,自动地一个一个地发出添加好友的请求. 知识点还是之前介绍的东 ...

  6. iOS逆向开发(6):微信伪造位置

    仍然以微信为例,实战地练习一下使用Reveal.iOSOpenDev等工具注入APP的流程,积累经验.这一系列的文章都是学习过程的总结,不带任何商业目的. 本文解决一个问题:如何伪造一个经纬度,在微信 ...

  7. iOS逆向开发(0):修改二进制代码与重签名 | hopper | codesigh

    小白:小程,你知道有些iOS程序是没人性的吗?老是不按我的意愿来运行! 小程:我怎么知道你的意愿就是有人性的? 本文解决一个问题:修改别人的二进制程序并运行起来. 让别人的程序按你的意愿来运行,文明一 ...

  8. iOS逆向开发(5):微信强制升级的突破 | 多开 | 微信5.0

    接下来的几篇文章,小程以微信为例,实战地演示一下:如何注入iOS的APP.其中使用到的知识,基本在前面的文章中都有介绍到. 小白:小程,我想用回旧版本的微信! 小程:为什么要用旧版本微信呢? 小白:你 ...

  9. iOS逆向开发(7):微信伪装他人

    上一节小程介绍了微信在进入"附近的人"时修改位置信息的办法,这一次,小程来修改"自己"的信息,伪装成别人. 但是,这里的伪装只是"本地的伪装" ...

随机推荐

  1. kali自定义分辨率(1920*1080)

    运行一下两行代码: xrandr --newmode -hsync +vsync xrandr --addmode Virtual1 "1920x1080_60.00"

  2. Self Attention需要掌握的基本原理

    字面意思理解,self attention就是计算句子中每个单词的重要程度. 1. Structure 通过流程图,我们可以看出,首先要对输入数据做Embedding 1. 在编码层,输入的word- ...

  3. Linux 解压/压缩xxx.zip格式(unZip Zip的安装和使用)

    Linux系统没有自带的压缩解压工具:需要我们自己安装:当压缩包为.zip格式时,需要安装zip包 1.apt-get安装: apt-get install zip 2.yum安装: yum inst ...

  4. Python开发——11.异常及异常处理

    一.异常 1.定义 异常及时程序运行时发生错误的信号 2.种类 异常分为语法错误和逻辑错误,语法错误在程序执行之前就应该改正. 常用异常 AttributeError 试图访问一个对象没有的树形,比如 ...

  5. 产生 unmerge path git

    1. Pull is not possible because you have unmerged files. 症状:pull的时候 $ git pull Pull is not possible ...

  6. 大叔学ML第五:逻辑回归

    目录 基本形式 代价函数 用梯度下降法求\(\vec\theta\) 扩展 基本形式 逻辑回归是最常用的分类模型,在线性回归基础之上扩展而来,是一种广义线性回归.下面举例说明什么是逻辑回归:假设我们有 ...

  7. C++ 基础知识回顾总结

    一.前言 为啥要写这篇博客?答:之前学习的C和C++相关的知识,早就被自己忘到一边去了.但是,随着音视频的学习的不断深入,和C/C++打交道的次数越来越多,看代码是没问题的,但是真到自己操刀去写一些代 ...

  8. IDEA中使用lombok插件

    Lombok是什么? lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具,简单来说,比如我们新建了一个类,然后在其中写了几个字段,然后通常情况下 ...

  9. [Postman]创建第一个集合(2)

    邮递员收藏是一组可以组织到文件夹中的已保存请求. 您在Postman中发送的每个请求都会显示在侧栏的“ 历史记录”选项卡下.在小规模上,通过历史部分重用请求很方便.但是,随着邮递员使用量的增加,在历史 ...

  10. 来啊踩fastjson打印入参导致业务跑偏的坑

    线上代码对日志的记录,重要性自不必说.但是怎样记录日志也是有讲究的! 日志可以直接在每个方法中进行日志记录,优点是想怎么记就怎么记,缺点是记日志的代码可能会超过你的业务代码,可读性急剧下降,这也是日志 ...