美文网首页
第二十章:有选项和参数的桥接脚本(四)

第二十章:有选项和参数的桥接脚本(四)

作者: 填坑侠 | 来源:发表于2018-10-11 14:43 被阅读26次

    真实的例子:用bar指令查看swift返回的字符串

    在Objective-C环境中判断一个swift对象的返回值是非常困难的, 但仍然是可行的.
    你将要做另一个例子.你还记得Person类的fullName方法吗?我想让你仅在fullName方法返回Ray Wenderlich时代码才停止执行. 那么让我们来看一下如何用bar命令来实现它吧.
    但是在你去改变我们的dlopen符号断点之前.你需要先运行一下.你看, swift里的指针与你之前所了解的不一样, 在使用swift字符串的时候更像是在使用char * char * or unichar或者unichar *, 这取决于环境.
    通过在断点标志上上单击来暂时禁用dlopen符号断点.
    打印Person.swift文件的前16行代码.在LLDB中输入一下内容:

    (lldb) source list -f Person.swift -c 16
    

    你会得到下面的输出:

    屏幕快照 2018-10-11 下午1.55.57.png
    看一下fullName, 你会发现它是一个只有getter方法的计算型函数.
    这就意味着你可以在swift的属性后面调用getter语法. 在LLDB中输入下面内容:
    (lldb) rb fullName.getter
    

    你将会触发两个断点: 一个Objective-C bridge的断点, 一个实际的Swift方法断点.在工作台上点击触发断点.在我这里, 我将会在Advanced Debugging & Reverse Engineering工作台上点击, 因为它与我感兴趣的内容有关:

    屏幕快照 2018-10-11 下午2.01.25.png
    fullName的getter方法被调用的时候就会触发断点, 代码停止执行.
    在LLDB中跳出这个函数:
    (lldb) finish
    

    检查返回的值.如果是64位的机器, 那就检查RAX. 如果是AARCH64, 那就检查X0.当有疑问的时候, 那就尽管在Objective-C 环境中po寄存器里的值, 看会发生什么.

    (lldb) expression -lobjc -O -- $rax
    

    你将会得到一些随机数:

      105553116755808
    

    再说一次, swift字符串的基地值可以被看作是C语言里的char *或者 unichar *.但是会稍微更复杂一点, 你在下一章中就会学到.
    char *做开头. 把它转化为C语言的char *看一下是否有效:

    (lldb) expression -lobjc -O -- (char *)$rax
    

    你会得到下面的结果:

    "D"
    

    这里发生了一些让人疑惑的事. 检查RAX寄存器指向的内存地址:

    (lldb) memory read $rax
    

    你将会得到一些类似下面的输出:

    0x600000077760: 44 00 65 00 72 00 65 00 6b 00 20 00 53 00 65 00
    0x600000077770: 6c 00 61 00 6e 00 64 00 65 00 72 00 01 00 00 00
    D.e.r.e.k. .S.e.l.a.n.d.e.r.....
    

    啊.....!这不是char *, 而是unichar *!那些0x00s意味着他们是Unichar, 但是如果强转成Unichar, 它们就会认为字符串已经结束了.在LLDB中将它们转化成合适的类型:

      (lldb) expression -lobjc -O --  (unichar *)$rax
    

    你将会得到类似下面的输出:

     u"Derek Selander\x01"
    

    这就意味着你可以用一个NSString的API将Unichar转化到一个字符串里, 然后与适当的对象做一个比较.
    也就是说你将会用到下面这个可爱的API...

    - (instancetype)initWithBytes:(const void *)bytes
                           length:(NSUInteger)len
                         encoding:(NSStringEncoding)encoding;
    

    你有了起始地址(在寄存器里).你需要弄清楚它的长度和编码类型.长度可以用LLDB来检测, 输入下面的内容:

    po strlen("Derek Selander") * 2
    

    你正在获取字符串的长度并且将长度扩大了两倍, 因为UTF16字符串占用了双倍的字节.你在输出中得到的结果应该是28.
    既然已经到这里了, 那就看一下Ray Wenderlich的名字的长度吧:

    po strlen("Ray Wenderlich") * 2
    

    太酷了, 这两次测试的结果都是28.
    现在来处理一下编码问题...
    打开一个新的终端窗口并输入下列内容:

    open -h NSString.h
    

    这将会弹出NSString的头文件:

    屏幕快照 2018-10-11 下午2.23.42.png
    找到代表NSUTF16LittleEndianStringEncoding的数字, 这个枚举的数值将会是你所需的编码类型的正确数值.
    看起来你需要的正确数值是0x94000100.
    现在你所需要的东西都已经具备了. 回到LLDB窗口中输入下面的内容:
    (lldb) e -lobjc -O -- [[NSString alloc] initWithBytes:$rax length:28
    encoding:0x94000100]
    

    哇!生效了!现在你可以运行你的NSString的查询语句了.
    你已经得到了创建action所需要的信息.回到dlopen符号断点里, 然后输入下面这个完整的表达式.

    bar fullName.getter -c '[[[NSString alloc] initWithBytes:obj length:28
    encoding:0x94000100] containsString:@"Ray Wenderlich"]'
    

    确保你的输入没有错字, 不然你就悲剧了.现在重新启用这个断点.
    构建, 并运行, 在随机事件上点击确保代码的执行没有停止.
    选择Server Side Swift with Perfect然后观察会发生什么.
    如果你的断点没有问题, 控制流应该会停在包含Ray WenderlichfullName的getter方法处.

    接下来学什么?

    接下来要学的东西会非常的刺激, 但是现在你已经学会了如何在你自己的Python中嵌入选项.
    读完这章可能你已经没有多少精力了, 但是你应该回顾一下bar指令.在调试时, 很多时候,我都想知道某个有趣的对象的栈桢情况.

    相关文章

      网友评论

          本文标题:第二十章:有选项和参数的桥接脚本(四)

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