Lldb实用调试命令(逆向开发效果类似cycript)

在iOS的逆向开发中cycript与Reveal是不可缺少的分析第三方app的利器,利用它们可以很快的定位到UI,找到对应的控制器,进而缩小了代码的区域。最近实用lldb进行调试,发现lldb也可以达到类似的效果。

首先需要在非越狱设备调试任意第三方app参考这篇文章

这边还是以微信作为例子:

UNADJUSTEDNONRAW_thumb_1.jpg

这边要达到的效果是找到登录按钮所在内存地址,并且找到所属控制器,修改登录按钮的文字。开始吧!

开始之前先简单介绍一下lldb常用命令,p & po — 打印变量或者对象

因为LLDB支持前缀匹配,因此你可以将print简写为p或者pri,而print则代表expression --;po则代表expression -O --,意为print object,打印对象。输入p指令可打印其对象类型、内存地址以及该对象的值等具体信息,而po指令则是打印其调用description方法得到的值。

(注:打印集合类型对象时,p指令会省略具体的值,只提示集合的数量等信息,因此若需查看集合中的值应使用 po指令,如下图所示:)

  • 打印NSString:

  • 打印NSDictionary:

expression

expression命令的作用是执行一个表达式,并将表达式返回的结果输出。expression的完整语法是这样的:

expression <cmd-options> -- <expr>

:命令选项,一般情况下使用默认的即可,不需要特别标明。

–: 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,–可以省略

: 要执行的表达式

说expression是LLDB里面最重要的命令都不为过。因为他能实现2个功能。

  • 我们在代码运行过程中,可以通过执行某个表达式来动态改变程序运行的轨迹。

  • 假如我们在运行过程中,突然想把self.view颜色改成红色,看看效果。我们不必写下代码,重新run,只需暂停程序,用expression改变颜色,再刷新一下界面,就能看到效果

/ /改变颜色
  (lldb) expression -- self.view.backgroundColor = [UIColor redColor]
  // 刷新界面
  (lldb) expression -- (void)[CATransaction flush]

也就是说我们可以通过expression来打印东西。

假如我们想打印self.view:

 (lldb) expression -- self.view
 (UIView *) $1 = 0x00007fe322c18a10

命令就介绍这个几个有用到的,其他的可以自行百度,下面就来调试吧!

  • 先用xcode调试界面:

WechatIMG9126.jpeg

WechatIMG9127.jpeg

以上两张截图中其实已经包含了很多的我们想要的信息,包括UI的布局结构,层次,每个UIView对应的内容地址,以及父控件或容器.选中一下登录按钮,你回发现xcode右边的调试界面中如下内容:

WechatIMG9128.jpeg

包含了很多有用的信息,包括按钮的类名,内存地址以及Action和Target等

这样的话可以通过调用UIButton的nextResponder方法一直找到其所在的控制器,怎么去调用这个方法?,这时候lldb的expression命令就派上用场了,(e 0x105651b50)登录按钮的内存地址,执行以下命令:

e 0x105651b50

会打印出:

(long) $188 = 4385479504

其中$188就代表登录按钮对象,当然你可以直接使用0x105651b50(即登录按钮对象),改变登录按钮的文字试试:

e [$188 setTitle:@"飘金" forState:UIControlStateNormal]

结果报错如下:

WechatIMG9130.jpeg

这是因为缺少UIKit库

e @import UIKit

引入UIKit库在试试,What??还是错!

WechatIMG9131.jpeg

这是因为$188只是代表登录按钮所在内存地址,lldb并不知道他是什么类型对象,试着把$188强转换类型:

WechatIMG9132.jpeg

由图可知是登录按钮FixTitleColorButton类型

//0x105651b50和$188是一样的,e [(FixTitleColorButton *)0x105651b50 setTitle:@"飘金" forState:UIControlStateNormal]是一样的效果,你可以试试!
e [(FixTitleColorButton *)$188 setTitle:@"飘金" forState:UIControlStateNormal]

这回没有报错,也没有提示任何信息,但是UI 界面中也没看到任何变化,试着刷新界面的命令:

expression  (void)[CATransaction flush]

命令执行后也没有任何任何输出,单步调试下一步:

c

执行后看看UI界面:

UNADJUSTEDNONRAW_thumb_16.jpg

成功了,expression很强大,可以执行OC代码(和cycript一样,你可以通过方法recursiveDescription打印UIView结构,并且查看到每个UIView的相关信息),我们可以这样来定义一个变量

:

//获取到UIApplication对象,其中必须要在变量前加"$",后面引用定义好的对象时要带上"$",如下引用是$app
 e UIApplication *$app = [UIApplication sharedApplication]
//获取到keyWindow,引用时是$keyWindow
 e UIWindow *$keyWindow = $app.keyWindow
//获取到登录按钮
e FixTitleColorButton *$loginButton = (FixTitleColorButton *)0x15dd11770
//$loginButton只是0x000000015dd11770的别名
e $loginButton
(FixTitleColorButton *) $loginButton = 0x000000015dd11770

###找出登录按钮所在控制器,利用nextResponder方法

(lldb) po [$loginButton nextResponder]
<UIView: 0x15dd0e080; frame = (0 582; 375 65); autoresize = W+TM; layer = <CALayer: 0x174039640>>

(lldb) po [(UIView *)0x15dd0e080 nextResponder]
<UIView: 0x15de27410; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x17022f760>>

(lldb) po [(UIView *)0x15de27410 nextResponder]
<WCAccountLoginFirstViewController: 0x15e80dc00>

这边看到控制器是WCAccountLoginFirstViewController,有个小技巧,当你不知道界面元素是哪一种类型的UI子类时你可以一并强转成UIView,因为控件都是UIView的子类.

利用lldb强大的命令行还可以在方法打断点的时候看到入参等信息,强大的地方很多,在逆向中起到很大的作用,很多有趣有用的功能等待大家去发现,可以多去试试!

个人博客地址

简书地址

2 个赞

@snakeninny 感觉可以给个微博推荐?

1 个赞

:grin: