iOS 逆向 - lldb高级篇 Chisel 与 Cycript

前言

上一篇文章 iOS 逆向 - LLDB 中讲述了 lldb 的一些基础用法 , 并无涉及太多其余内容 , 逆向过程当中经常使用的动态调试方法其实还有一些 , 本文针对上篇文章和实际逆向中的运用进行一个补充 .git

主要针对 Chisel 以及 Cycript 两个部分 .github

若是篇幅不长 , 咱们来说一讲自定义 cy 指令.shell

逆向调试注意

  • 有部分同窗反应重签名微信应用被封号的状况 . 这里说明一下 , 微信 / 抖音 等应用是有防御和监测操做的 , 网上流传部分破解微信防御的 logos 代码 , 笔者测试效果不是百分百 , 有须要的小伙伴能够评论留言 .vim

  • 调试别人应用自己是会形成相似问题的 , 所以尽可能不要登陆我的使用帐号 , 如非必须登陆 , 不要登陆帐号 .ruby

  • 确实须要登陆 , 登陆一个小号 , 另外提早准备好另外一个帐户去解封 .bash

  • 在越狱环境下 , 使用插件的方式调试 , 无须重签名 , 被封号的概率是很低的 .微信

  • 最后再强调一次 : 玩逆向只是为了更好地防御 .app

Chisel

概述

Chisel 也是 Facebook 发布的一个 lldb 的插件 , 可以作到帮助调试和提供用户自定义指令集的功能 . 接下来会具体阐述 .函数

安装

命令 : brew install chisel工具

( 尚未安装 HomeBrew 的自行安装 )

安装后使用 : brew list 查看安装结果

配置

如何配置 ?

1 -- 打开下载目录

  • 先找到下载的文件 : cd /usr/local/Cellar/chisel
  • open . , 找到 fblldb.py 脚本文件.
  • cmd + opt + c 拷贝文件路径.

2 -- lldb配置脚本路径加载

  • 若是你的用户家目录下没有 .lldbinit 文件 , 请查阅上一篇文章 iOS 逆向 - LLDB 中最后讲自动启用加载指令所述 , 使用 vim 自行建立便可 .
  • vim .lldbinit
  • s 进入编辑模式
  • 添加加载指令 : command script import /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py ( 路径换成你本身的 )
  • ESC , :wq 保存退出

使用

测试配置结果

先来简单试一下配置成功了没 .

注意 : 若是是正在运行的工程 , 那你须要使用 command source ~/.lldbinit , 来从新加载一下 lldb 配置文件 .

随便打开一个工程 , 进入断点模式 , 输入 pviews .

pviews 就是 Chisel 提供的一个查看视图层级的命令 . 以上看到 , 咱们已经配置成功了 .

经常使用指令

为了模拟逆向过程当中实际动态调试场景 . 我使用了我以前使用 MonkeyDev 重签名的微信应用来演示指令 , 对重签名不熟悉的同窗能够阅读一下 重签应用调试与代码修改 (Hook) , 与 shell 脚本自动重签名与代码注入 , 这两篇文章 .

MonkeyDev 的安装和使用咱们就很少赘述了 , 毕竟重签名原理理解了 , Monkey 其本质上也是利用脚本自动重签名 , 而后 代码注入 hook 部分集成了 Cydia Substrate 来作的.

咱们所须要作的只是把砸过壳的 ipa 或者 app 包 放到指定文件夹下便可完成重签名和代码注入 . 很是方便.

若是同窗们关于 MonkeyDev 使用有问题能够留言告知 .

运行工程 .

pviews 图层层级

打开注册页面 , 暂停 . 进入断点模式 ,

  • 指令 : pviews
  • 结果 :
  • 说明 : pviews 能够帮助咱们很清楚的看出图层逻辑层级关系以及内存地址 .
pvc 视图层级
  • 指令 : pvc

  • 结果 :

  • 说明 : pvc 能够帮助咱们很清楚的看出视图控制器层级关系以及内存地址 .

pactions 事件查找

使用 pviews , 随便找到一个按钮 , 复制其内存地址 .

  • 指令 : pactions 0x10b06e5e0
  • 结果 :
  • 说明 : pactions 能够拿到 buttontarget 以及 action , 在逆向时须要方法 hook 常常会使用.
presbonder 响应链

使用 pviews , 随便找到一个按钮 , 复制其内存地址 .

  • 指令 : presbonder 0x10b06e5e0
  • 结果 :
  • 说明 : presbonder 能够查看完整的响应链.
pclass 继承链

使用 pclass , 随便找到一个类 , 复制其内存地址 .

  • 指令 : pclass 0x10b910600
  • 结果 :
  • 说明 : pclass 能够查看完整的继承链.
pmethods 查看类方法 / 实例方法
  • 指令 : pclass 0x106b57bc0
  • 结果 :
  • 说明 : pmethods 能够查看类完整的类方法和实例方法.
pinternals 查当作员变量
  • 指令 : pinternals 0x10b660df0
  • 结果 :
fv / fvc
  • 指令 : fvc -v 0x10b8fe000
  • 结果 :
  • 说明 :
    • 经过内存地址查看类名 ( po 也能够 ) . , 视图控制器用 fvc , 视图用 fv .
    • fv + 类名 反之是同样的 , 回去工程搜索这个类 打印其内存地址 .
重点 : taplog
  • 指令 : taplog
  • 说明 : taplog 输入后会退出断点模式 , 再点击屏幕上任何可响应视图 , 会自动进入 lldb 模式并打印按钮.
  • 结果 :
重点 : flicker
  • 指令 : flicker 0x11827c040
  • 说明 : 经过内存地址 , 在断电模式下调用该指令会闪烁 view , 很是方便调试 , 以及肯定该内存地址是否为咱们想找到的那个视图 .
重点 : vs
  • 指令 : vs 0x11827c040
  • 说明 : 经过内存地址 , 进入调试模式 , 当前view会被添加红色以便查看 .
  • 结果 :
(lldb) vs 0x1120f3390

Use the following and (q) to quit.
(w) move to superview    // 来到当前视图的父视图
(s) move to first subview // 来到当前视图的第一个子视图
(a) move to previous sibling  // 来到当前视图同级关系下的前一个视图
(d) move to next sibling     // 来到当前视图同级关系下的后一个视图
(p) print the hierarchy   // 打印当前视图的层级结构

<FixTitleColorButton: 0x1120f3390; baseClass = UIButton; frame = (20 112; 374 47); clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x1118c0900>>
复制代码
  • 退出 vs 调试模式 , q 指令 .
提示

最后三条指令在逆向过程当中很是经常使用 , 你们多加练习与掌握 .

lldb_commands 插件

这个 LLDB 插件叫 lldb_commands . 地址为 github.com/DerekSeland…

安装

  • 直接 Clone 或者下载 , 把 lldb_commands 文件夹保存起来 , 我这边是放到 /usr/local/Cellar

  • 来到家目录 , 找到 .lldbinit , 添加一条指令 :
command script import /usr/local/Cellar/lldb_commands/dslldb.py
复制代码

路径换成本身的 lldb_commands 文件夹路径便可 .

工程来到 lldb 模式下 , 输入 search UIView , 便可测试有没有配置成功 .

经常使用指令

methods 快速定位方法

找到视图控制器地址 , 使用 methods 查看其全部的实例方法和属性 .

注意 : 因为逆向时 方法的符号是没有恢复的 , 所以根据类名和方法名下断点会失败 .

函数调用栈

因为符号并无恢复 , 所以 bt 指令查看函数调用栈时 , 会出现如下状况 .

( 后续会讲如何利用工具恢复 Mach-O 的符号 )

那么此时 , 利用 lldb_commands 提供的 sbt 指令 , 会帮助恢复一些符号 , 以便于查看方法名称 .

Mach-O Section 查看
  • Section 指令可让咱们快速查看 Mach-O 有哪些 Section 段 .
  • Section 可添加其余指令来查看 Mach-O 具体内容 .
# Dump the Mach-O segments to the main executable
  (lldb) section

  # Dump the Mach-O segments to UIKit
  (lldb) section UIKit

  # Dump the Mach-O sections of the __TEXT segment of UIKit
  (lldb) section UIKit __TEXT

  # Get the load address of all the hard-coded uint8_t * strings in the UIKit binary
  (lldb) section UIKit __TEXT.__cstring -l

  # Get the entitlements for the executable (simulator only, entitlements for actual app in __LINKEDIT)
  (lldb) section  __TEXT.__entitlements

  # Get all the load address to the lazy symbol stubs in the main executable
  (lldb) section  __DATA.__la_symbol_ptr -l
复制代码

效果以下 , 你们能够结合 MachOView 来查看结果.

Cycript

概述

Cycript 是由 Cydia ( 熟悉越狱的同窗应该都很清楚 ) 创始人 Saurik 推出的一款脚本语言,Cycript 混合了 OCJavaScript 语法的解释器,这意味着咱们可以在一个命令中使用 OC 或者 JavaScript,甚至二者并用。

它可以挂钩 正在运行的进程,可以在运行时修改不少东西。

到官网点击 Download SDK 便可下载 .

安装

  • 将下载后的文件夹放入 /opt/ 里便可 , 也能够自行选择位置 .
  • 配置环境变量 .
  • 注意 : 这里根据你使用的是 zsh 仍是 bash 去相应的资源文件配置 (家目录下的 .zshrc / .bash_profile) .
  • 笔者因为在 .zshrc 中也配置了 bash_profile 的引用 , 所以两处来配置这个环境变量都是能够的.

配置内容:

  • 添加 : export CY=/opt/cycript_0.9.594/ , 换成你本身的路径.
  • export PATH= 中添加 :$CY

重启 iTerm , 输入 cycript , 便可查看 .

若是有遇到 ruby 环境不对的同窗 , 去下载对应版本的便可 . /System/Library/Frameworks/Ruby.framework/Versions

使用

在越狱环境下 , 是能够在 Cydia 直接安装 Cycript 插件的.

越狱手机截图上传有点麻烦 , 直接拍的.. 瑕疵请忽略 , 看个意思

那么非越狱环境下 , 就要借助 Cycript 提供的 iOS Framework , 注入进去了 . 并且在 MonkeyDev 中 , 是默认已经作好了 Cycript 的注入的 .

并且添加了 默认 6666 端口号的监听 .

也就是说只要用 MonkeyDev 来重签跑起来的程序进程 , 6666 端口就能够附加使用了 ( 固然 , 在 Monkey 里代码能够本身定义端口号 ) .

好了 说了这么多 , 开始使用.

  • 1 . 保证电脑和手机在同一个局域网内 ( 由于要作端口映射 )
  • 2 . 运行 MonkeyDev 重签名程序 / 或者直接打开之前重签好的工程 , 无须 Xcode 运行也能够
  • 3 . 终端输入 cycript -r 192.168.0.116:6666
    • 换成你本身的手机 ip 地址
    • 另外 , 请不要将应用程序放到后台 , 会影响附加.

出现如下 , Congratulations , you're good to go !

提示

使用 tab 键 , 写代码能够补全 .

UIWindow.keyWindow()

查看当前 Window .

UIApplication sharedApplication

指令 : [UIApplication sharedApplication ] 可简写为 UIApp

自定义变量

指令 : 本身 var 一个对象 , 然后咱们就可使用 .

另外注意 : 只要 APP 进程没有挂 , 这个变量是一直存在的 .

# + 地址能够直接使用

# + 对象地址 能够直接调用对象的方法

查看视图层级

UIWindow.keyWindow().recursiveDescription().toString()

随便找一个 , 例如注册页面有一个 .text+86label , 拿到其内存地址 ,

命令 :

#0x10b95d800.text = "hhh"
复制代码

显示结果

基于这种直接修改进程内存的方式 , 你们能够本身去玩一玩 . 好比登陆了修改一下钱包余额 , 而后改一改 frame , 练习一下 .

如下指令结果我就不一一贴图了 , 文章太长 , 不便阅读 , 你们本身尝试 .

获取页面上全部控件

choose(UIButton)

choose(UILabel)

隐藏 / 显示状态栏

[UIApp setStatusBarHidden:YES]

APP角标

[UIApp setApplicationIconBadgeNumber: 99]

获取Bundle ID

APPID 结果 :

@"com.libin.LBMonkeyApp"

页面层级

pviews()

pvcs()

根据按钮地址获取按钮 target & Action

pactions (#0x10b29da40)

结果 :

"<WCAccountRegisterViewController: 0x10b9d9800> onAgreementCheckBoxClick:"
复制代码

根据按钮地址获取响应链

rp(#0x10b29da40)

退出cy 调试模式

control + d

注意:

pviews / pvc / pactions / rp 这些指令是 MonkeyMDConfig.plist 中额外封装了自定义的 cy 源的 . 也就是说使用越狱环境本来的 cycript 插件是没有这些指令可用的 .

那么咱们闲着也是闲着 , 咱们也来本身写一个 cy 源来玩一下 ?

自定义 cy 指令

  • 在咱们的 monkey 工程主工程 target 中新建一个空文件, 我这里取名 lb.cy .

  • Build Phases - Copy Files , 引入这个文件

  • 在空文件添加咱们想本身定义的指令 . 能够参照 Monkey 本来提供的那两个来写 raw.githubusercontent.com/AloneMonkey…

    这里我写了一些我经常使用的指令 , 好比获取 APPID / APPPATH , 当前跟视图 , 当前页面 这些 , 我贴在下面供你们参考 , 也能够拿去直接用 .

  • 从新运行工程 .

  • 输入咱们自定义的指令 例如 : LBCurrentVC()

  • 提示没找到指令 , 由于咱们尚未引入 , monkey 那两个在 config 中会自动引入.

  • @import lb

  • 再次输入 LBCurrentVC() , 获得结果.

//IIFE 匿名函数自执行表达式

(function(exports){

     APPID = [NSBundle mainBundle].bundleIdentifier,
     APPPATH = [NSBundle mainBundle].bundlePath,

     //若是有变化,就用function去定义!!
     LBRootvc = function(){
        return UIApp.keyWindow.rootViewController;
     };

     LBKeyWindow = function(){
        return UIApp.keyWindow;
     };

    LBGetCurrentVCFromRootVc = function(rootVC){
        var currentVC;
        if([rootVC presentedViewController]){
            rootVC = [rootVC presentedViewController];
        }

        if([rootVC isKindOfClass:[UITabBarController class]]){
            currentVC = LBGetCurrentVCFromRootVc(rootVC.selectedViewController);
        }else if([rootVC isKindOfClass:[UINavigationController class]]){
            currentVC = LBGetCurrentVCFromRootVc(rootVC.visibleViewController);
        }else{
            currentVC = rootVC;
        }

        return currentVC;
    };

    LBCurrentVC = function(){
        return LBGetCurrentVCFromRootVc(LBRootvc());
    };
 
})(exports);
复制代码

小提示

  • 使用 cycript 某一个控件 / 对象 内存地址时 , 要注意其生命周期 , 例如用一个 label 的内存地址调试 , 你页面退出 , 又重进 , 是从新建立了对象的 , 之前的内存地址也会没法再使用 . 不要忘记这点 .

  • cycript 使用须要同局域网每次要链接 , 或者其余咱们须要自定义的初始化动做 , 咱们能够将其写到一个脚本中 , 配置到 zsh / bash 环境变量中便可 , 你们能够玩一玩 , 有问题你们一块儿探讨一下 .

  • 逆向过程当中 , View Debugcycript 调试界面是很是经常使用的手段 , 所以 , 但愿你们能熟练掌握这些技巧 .

相关文章
相关标签/搜索