自定义LLDB命令实战

作者: 桃红宿雨 | 来源:发表于2017-10-15 02:44 被阅读159次

    前言

    本文翻译自Custom LLDB Commands in Practice
    翻译的不对的地方还请多多包涵指正,谢谢~

    自定义LLDB命令实战

    LLDB: 拥有此技能, 还有什么不能!

    欢迎来到一篇来源于我们新书《Apple调试进阶&逆向工程》灵感的特别版文章~本文将展示书中的一些非常棒的工具做成的最终产品结果。

    本文你将使用一些自定义的LLDB调试命令和脚本来探索SpringBoard应用,在理解这些脚本上会给一些帮助。

    什么,你说SpringBoard是啥?嗯,它是iOS的主屏幕应用,负责启动iOS应用,Siri解释你说的语言,查看通知和小工具栏等等。

    通过本篇教程,你将探索在SpringBoard背后的一些逻辑,利用调试脚本来为你做繁重的工作。

    开始吧

    在开始探索之旅前你需要有一点需要注意的配置工作。

    这里下载启动包。这包含了一个你将要安装到电脑的LLDB命令和脚本的文件夹。

    LLDB通过加载在电脑上搜索几个预定义的位置的内容。其中一个地址就在~/.lldbinit。(若没发现该隐藏文件,就在~/目录下主动创建)

    使用你最喜欢的文本编辑器并打开这个文件。为了这个特定的例子,我仅仅使用了一个简单的终端文本编辑器nano,但你可以自由地使用觉得顺手的。

    1. 使用Finder,打开你刚下载的包含lldb_commands的文件夹。保持Finder开着直到你将一个文件拖到终端窗口。在终端内,使用nano或者你的文本编辑器打开~/.lldbinit

      nano ~/.lldbinit
      

      然后按下回车键。


    2. 在nano或者你的文本编辑器,如下编辑

      command script import 
      

      保证在文本后面有一个空格,因为你将从Finder窗口把内容的地址加进来。

    3. 通过Finder窗口,打开lldb_commands文件夹,然后找到名叫dslldb.py的文件。把它拖到终端窗口中。(译者注:这步的操作其实很简单就是将文件的地址放在~/.lldbinit文件内容command script import的后面)

    4. 保存~/.lldbinit文件并关闭编辑器。对于nano来说,你需要按下Ctrl + O来保存,按下Ctrl + X退出。

    整体总结下,下面就是你需要该做的样子:

    dslldb.py文件将搜索所有在同一个文件夹的所有Python脚本并在LLDB启动的时候将它们加载到LLDB。另外,它会找到所有的.txt文件并加载文件内的命令。我们在书内对这个过程深入介绍了,但现在就让我们享受用这些命令能做的事情吧。

    测试命令可用性

    在新的终端窗口内,输入如下命令:

    lldb
    

    这样会在终端内启动一个空的LLDB会话,现在输入如下命令:

    help search
    

    这将参考帮助文档显示你新添加的叫search的命令。假设一切都进行得很好,你会获得关于该命令的帮助文本。

    如果你得到以下信息:

    (lldb) help search
    error: 'search' is not a known command.
    Try 'help' to see a current list of commands.
    Try 'apropos search' for a list of related commands.
    Try 'type lookup search' for information on types, methods, functions, modules, etc.
    

    这意味着LLDB命令没有被成功加载,保证你LLDB命令的文件夹路径中没有空格且不存在引号。

    若一切正常,你可以访问以下一些命令:

    • search:根据某一个特定类遍历所有在对上的指针。而且能够通过特定的模块(比如 UIKit)或者一些条件来过滤对象。

    • lookup:执行正则搜索查找类,函数,或者方法。

    • msl:为一个特定的指针的最后一次创建或者开辟获取堆栈追踪信息。

    • methods:Dump所有NSOjbect子类的方法(仅iOS)。

    • ivars:Dump所有NSOjbect子类实例对象所有的实例变量。

    这些只是LLDB能做的一小部分,你可以使用SpringBoard探索更多神奇的命令。

    注:如想了解我最新最棒的命令,可检出这个地址。无论何时我需要一个命令,都会创建它并将它放到那个仓库里。你可能会发现一些令人惊奇的东西~

    你不需要终端了,可以自由的关掉它并打开我们的XCode~

    玩转SprintBoard

    我经常想看看开发人员在产出的过程中如何做到的。通过探索别人已经做好的,我可以学到它们的实现方式并且自己写出更好的代码。

    遗憾的是,Apple不会开放任何它们自己程序的开源代码,因此需要通过其他方式学习Apple是如何设计程序的。iOS模拟器提供了iOS程序的几个功能示例,我使用LLDB通过它们来侦查出这些程序的实现方式。

    很多人似乎认为普通的调试和逆向工程应用使用了不同的调试技巧。我非常赞同这个观点。逆向工程某人的应用能极大挑战你的调试技巧,这就是我为什么一直鼓励大家通过逆向工程来调试。如果你能够快速的找到你感兴趣的东西而不用去读源码的话,想象一下当你分配一个任务找到你程序的一个bug的时候会多么的快~

    嵌入到SprintBoard

    使用LLDB使连接任何一个电脑上的应用成为可能(只要你禁用了Rootless机制)。幸运的是,你没必要禁用Rootless机制就可以把LLDB命令嵌入到iOS模拟器应用上。

    这意味着你可以使用Xcode附加到SpringBoard并使用所有你顺手的命令。

    打开任何一个Xcode工程---是的,任何一个。你并不是要编译这个应用,而是使用已经存在的窗口探索SpringBoard程序。

    打开模拟器,跳到Xcode。在Debug按钮里,点击 Attach to Process,选择 SprintBoard。

    给LLDB和Xcode一些时间让其附加到SpringBoard。成功后,你会看到一个暂停按钮出现在Xcode LLDB 工作面板上。通过点击暂停按钮来暂停这个进程。

    为了能够在代码中调用SBIconImageView类,你需要加在合适的实现了该类的动态链接库。在书中,这些内容在第十五章写明了-- “Hooking & Executing Code with dlopen & dlsym”

    让我们回到SBIconImageView类,搜索到所有正在内存中的该类对象是不是很棒?OK,使用 search 命令,你可以动态地搜到在堆栈中所有该类的实例。在LLDB中,输入:

    (lldb) search SBIconImageView
    

    你会看到类似以下片段的输出:

    (lldb) search SBIconImageView
    <__NSArrayM 0x618000858270>(
    <SBIconImageView: 0x7ff6ad7492f0; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x6100002226a0>>,
    <SBIconImageView: 0x7ff6b0a78e30; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x608000225520>>,
    <SBIconImageView: 0x7ff6ad743d90; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x610000221700>>,
    

    棒极了,但这些实例在哪儿?你可以很遍历所有这些实例且能使用search命令对他们执行自定义的动作。输入:

    (lldb) search SBIconImageView -p '[obj setHidden:YES]'
    

    回到模拟器,你会看到你刚刚做的事情:

    你能准确猜出SBIconImageView类做什么的吗?!通过显示所有的SBIconImageView实例来撤销你刚刚做的事情。

    (lldb) search SBIconImageView -p '[obj setHidden:NO]'
    

    搜索命令很不错,但它返回的是所有屏幕应用的结果---太多了。如果你只想要找到代表Messages(信息)应用的SBIconImageView实例呢?

    使用methods命令,你可以搜索你感兴趣的能帮你唯一标识特定SBIconImageView实例的代码。

    例如SBIconImageView类有一个属性叫icon,它持有一个叫SBApplicationIcon的类(不同的SDK可能类不一样)。Dump 所有这个类的方法:

    (lldb) methods SBApplicationIcon
    

    这个方法内部有一个叫displayName的属性。你可以使用这个知识点并通过displayName来快速找到特定的SBApplicationIcon实例!

    在LLDB输入:

    (lldb) search SBIconImageView -c '[[[obj icon] displayName] containsString:@"Messages"]'
    

    这将返回一个displayName包含"Messages"的SBApplicationIcon实例。你会得到如下类似结果:

    <__NSArrayM 0x618000e5dac0>(
    <SBIconImageView: 0x7fb7b567e020; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x61000023a660>>
    )
    

    拷贝SBApplicationIcon实例指针。我的例子中,它是0x7fb7b567e020,但你的地址应该是不一样的。通过tv命令来隐藏这个界面:

    (lldb) tv 0x7fb7b567e020
    

    Message应用的图片现在应该消失了:

    现在你发现它啦~ 找到这个实例所有的属性值:

    (lldb) ivars 0x7fb7b567e020
    

    ivars命令和methods一样也是从已经编译成iOS可执行文件的代码构建出来的。你只是在调试的过程中以应用的方法使用此代码。你可以在书中第七章“Image”学到找到这些代码的方法。

    我们来最后一个命令 --- lookup --- 你会再书中第22章“SB Examples, Improved Lookup”创建的。该命令会根据正则表达式搜索所有可执行文件的代码。

    在LLDB中输入:

    (lldb) lookup Test
    

    这样你会看到很多代码,实际上可以使用--summary选项来简化:

    (lldb) lookup Test -s
    

    你会看到如下类似片段:

    1 hits in: AssistantServices
    39 hits in: ChatKit
    9 hits in: FrontBoard
    5 hits in: VideoToolbox
    28 hits in: CoreData
    7 hits in: MPUFoundation
    5 hits in: CoreDuet
    2 hits in: BaseBoardUI
    7 hits in: MediaServices
    5 hits in: PassKitCore
    11 hits in: MusicLibrary
    16 hits in: Foundation
    6 hits in: Sharing
    2 hits in: libsqlite3.dylib
    8 hits in: PhotoLibrary
    

    如果希望只搜索BaseBoardUI模块的信息呢?可以基于一个模块来使用lookup命令过滤搜索结果。

    (lldb) lookup Test -m BaseBoardUI
    ****************************************************
    2 hits in: BaseBoardUI
    ****************************************************
    -[UIView(BaseBoardUI) bs_isHitTestingDisabled]
    
    -[UIView(BaseBoardUI) bs_setHitTestingDisabled:]
    

    这意味着我可以对在SpringBoard应用内的所有 UIView 使用这些代码!例如,我可以输入 po [[UIApp keyWindow] bs_isHitTestingDisabled] 命令打印出属性值。

    不在输入内的是基于SpringBoard应用的所有代码。这也可以理解,因为可执行代码被剥离了(译者注:应用自定义代码和引用的模块,框架剥离开),你看不到调试的符号信息。对于Framework则不同,因为它们需要记录这些信息,当加载时,它能知道准确的地址。

    不能使用lookup命令来搜索可执行文件实在是让我有些难过...

    等下你猜?你可以的

    输入一下命令:

    (lldb) lookup Test -X
    

    这个命令将利用Objective-C的运行时来执行正则搜索,而不是使用DWARF调试信息。

    如你所见,有很多在最终的SpringBoard应用的测试代码。试试以下命令:

    (lldb) po [[SBTestDataProvider sharedInstance] publish]
    

    一旦你通过点击继续按钮或者在LLDB中输入continue命令恢复应用后,你会看到一个弹窗~

    是的!通知哦~

    那么这是不是一篇很有意思的调试课呢~

    何去何从

    你也看到了,自定义调试命令有强大的能力。《Advanced Apple Debugging & Reverse Engineering》书能让你的调试能力有很大的飞跃。

    如果你喜欢本篇文章,可以购买书籍《Advanced Apple Debugging & Reverse Engineering》

    以下是书的一部分内容介绍:

    • 开始:关于LLDB及其大量的命令和选项
    • Python能力:使用LLDB的Python模块创建强大自定义的调试命令从而窥探和提高现有程序
    • 理解汇编:真正理解汇编层面代码是如何工作的,以及怎样在内存中探索这些代码
    • Ptrace和Friends:学习如何利用ptrace,dlopen和dlsym来hook C和Swift函数探索没有源码的代码
    • 脚本桥接:扩展调试器让它几乎做任何你想做的事,学习如何在脚本中传递可选参数或参数
    • DTrace:使用DTrace深入探索函数获取大量的进程信息
    • 等等。。。

    相关文章

      网友评论

      本文标题:自定义LLDB命令实战

      本文链接:https://www.haomeiwen.com/subject/fejquxtx.html