iOS 逆向编程(十四)Cycript 语法入门

一、Cycript 语法简介
  • 根据 上一篇文章 进入 Cycript 调试模式之后,就需要通过脚本语言来进行操作调试了,比如:获取页面控制器获取对象属性手动调用对象方法页面上添加自己的UI…等等。

  • Cycript 也是有语法例子的,可以进去参考,命令格式:

    命令结果
    UIApp获取当前 App 的 Appdelegate 对象
    UIApp.keyWindow获取当前的keyWindow对象
    UIWindow.keyWindow获取当前的keyWindow对象
    var定义一个对象
    #内存地址获得当前内存地址的对象
    #内存地址获得当前内存地址的对象
    *对象获得对象的所有成员变量
    choose(UILabel)查找当前界面的所有UILabel
  • 在进入 cycript 调试环境后,就可以直接写 OCJS … 等支持的语法了,例如:

    • OC(其实也就是跟 OC 语法非常非常像的语法格式):

      cy# [UIApp description]
      @"<NMApplication: 0x113d4d710>"
      

      UIApp 等于 UIApplication 对象,这两种写法都一样,脚本给与的缩写。

      cy# UIApp
      #"<NMApplication: 0x113d4d710>"
      
    • JS

      cy# [for (x of [1,2,3]) x+1]
      [2,3,4]
      

二、定义变量
  • 不支持 OC 里面那种 NSString *name = @"dzm" 这种写法

    $ var 变量名 = 变量值
    
    // 进入调试
    iPhone:~ root# cycript -p neteasemusic
    
    // 创建变量 window
    cy# var window = UIApp.keyWindow
    #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x28043da10>; layer = <UIWindowLayer: 0x280af58a0>>"
    
    // 输出变量 window 跟上面的 UIApp.keyWindow 值一致
    cy# window
    #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x28043da10>; layer = <UIWindowLayer: 0x280af58a0>>"
    
三、获取常用对象(举例)
  • UIApp 等于 UIApplication 对象。

    cy# [UIApp description]
    @"<NMApplication: 0x113d4d710>"
    
    cy# UIApp
    #"<NMApplication: 0x113d4d710>"
    
  • 获取 keyWindow 对象(OTTouchObservingWindow 其实就是 UIWindow,只是编译之后 iOS 内部做了转换)。

    cy# UIApp.keyWindow
    #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x281ac01e0>; layer = <UIWindowLayer: 0x28146e0a0>>"
    
  • 获取 rootViewController

    cy# UIApp.keyWindow.rootViewController
    #"<NMRootNavigationController: 0x10a8cd400>"
    
四、内存地址访问对象
  • 获取到 keyWindow 的内存地址 0x10a65b610

    cy# UIApp.keyWindow
    #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x281ac01e0>; layer = <UIWindowLayer: 0x28146e0a0>>"
    

    可以通过 #0x10a65b610 这种方式通过内存地址访问这个对象,你会发现跟上面输出 的对象是一样的:

    cy# #0x10a65b610
    #"<OTTouchObservingWindow: 0x10a65b610; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x28043da10>; layer = <UIWindowLayer: 0x280af58a0>>"
    

    也可以通过内存地址的方式获取该对象下面的其他属性值:

    cy# #0x10a65b610.rootViewController
    #"<NMRootNavigationController: 0x10a8cd400>"
    
五、ObjectiveC.classes 获取已加载的所有OC类
  • ObjectiveC.classes:你可以理解成获取列出来所有的 .h 头文件或者 class 对象,就是列出当前已经加载所有类。

    cy# ObjectiveC.classes
    {
     // 系统类 
     __NSGenericDeallocHandler:__NSGenericDeallocHandler,_NSZombie_:_NSZombie_,__NSMessageBuilder:__NSMessageBuilder,.....
    
     // 自定义类
     RMLog:RMLog,NMSearchShowCell,NMSearchPlaylistCell:NMSearchPlaylistCell,NMDjRadioPurchaseProgramCell:NMDjRadioPurchaseProgramCell,......
    }
    

    这样一看做 iOS 都知道啥意思了,都是类名,自定义类,Cell 之类的。

六、查看对象的所有成员变量
  • 在后面经常会需要知道 某个对象 里面有哪些成员 变量 或者 属性

  • 只需要在对象前面加个 * 号即可,例如:*UIApp :

    cy# *UIApp
    {isa:NSKVONotifying_NMApplication,_hasOverrideClient:0,_hasOverrideHost:0,_hasInputAssistantItem:0,_delegate:#"<NMAppDelegate: 0x2838b2010>",_event:#"<UIEvent: 0x2804cdce0>",_motionEvent:#"<UIMotionEvent: 0x2831b4d00> timestamp: 0 subtype: 0",_remoteControlEvent:#"<UIRemoteControlEvent: 0x28149b1c0>",_remoteControlEventObservers:1,_topLevelNibObjects:null,_networkResourcesCurrentlyLoadingCount:0,_hideNetworkActivityIndicatorTimer:null,_editAlertController:null,_statusBar:#"<UIStatusBar: 0x10a863800; frame = (0 0; 320 20); opaque = NO; autoresize = W+BM; layer = <CALayer: 0x280a8ab20>>",_statusBarRequestedStyle:0,_statusBarWindow:#"<UIStatusBarWindow: 0x10a6561d0; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x280438930>; ......}
    

    上面的所有属性都是以 , 逗号分开,每一个逗号就是一个成员变量。

    挑个出来看看,比如 代理属性,前面是 属性名称,后面是

    _delegate:#"<NMAppDelegate: 0x2838b2010>"
    
七、递归打印 View 的所有子控件
  • iOS 开发中,可以通过 控制台 LLDB 打断点后 po 的方式递归输出指定视图的所有子控件。

    • 语法 po [self.view.window recursiveDescription]
    (lldb) po [self.view.window recursiveDescription]
     <UIWindow: 0x7f9f8b618460; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x60000077a1c0>; layer = <UIWindowLayer: 0x600000926de0>>
        | <UITransitionView: 0x7f9f8b719630; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b300>>
        |    | <UIDropShadowView: 0x7f9f8b71a060; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x60000093b280>>
        |    |    | <UIView: 0x7f9f8b719ef0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b2c0>>
    
     <UIWindow: 0x7f9f8b618460; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x60000077a1c0>; layer = <UIWindowLayer: 0x600000926de0>>
        | <UITransitionView: 0x7f9f8b719630; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b300>>
        |    | <UIDropShadowView: 0x7f9f8b71a060; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x60000093b280>>
        |    |    | <UIView: 0x7f9f8b719ef0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x60000093b2c0>>
    

  • Cycript 里面递归输出子控件,其实也是使用 recursiveDescription

    cy# [UIApp.keyWindow recursiveDescription]
    ......
    
    cy# [UIApp.keyWindow recursiveDescription].toString()
    ......
    

    由于输出太多我就不贴了,跟上面 iOS 中一样,一层一层的列出来。一种是递归列出所有子控件,一种是将列出的子控件转成字符串输出,两者没什么区别,就是排版的区别,都可以。

七、choose 删选出指定类型的对象
  • 意思是:指定一个类,只要是继承或者等于这个类的都会被列出来,只会列出已加载到缓存中。

  • 例如 UIViewController,列出当前已加载到缓存是继承或者直接使用 UIViewController 的,网易云首页举例

    cy# choose(UIViewController)
    

    输出:

    [#"<NMVoiceHomeViewController: 0x11a400200>",#"<NMSettingTabViewController: 0x11a404020>",#"<NMSingTabViewController: 0x11a407540>",#"<NMContainerViewController: 0x11a409ce0>",#"<NMContainerViewController: 0x11a414d60>",#"<NMMainViewController: 0x10a6d8780>",#"<NMContainerViewController: 0x10a5f5220>",#"<NMMineTabViewController: 0x10a5f6910>",#"<NMContainerViewController: 0x11a519390>",#"<NMContainerViewController: 0x11a52f3f0>",#"<NMTabBarController: 0x10b946000>",#"<NMNavigationController: 0x10b9a7000>",#"<NMNavigationController: 0x10b9cc400>",#"<NMNavigationController: 0x10b9d6c00>",#"<NMNavigationController: 0x10b9e0200>",#"<NMNavigationController: 0x10b9ea600>",#"<NMNavigationController: 0x10a83f400>",#"<NMRootNavigationController: 0x10a8cd400>",#"<NMNavigationController: 0x10a8e7200>",#"<NMNavigationController: 0x10a912a00>",#"<NMNavigationController: 0x10a919c00>",#"<NMNavigationController: 0x10a975800>"]

    上面这些就是当前软件已经加载到缓存的控制器,且都是继承或者等于 UIViewController

    如果一个都没有则为空数组,如果输出崩溃,则重新进入调试模式即可:

    cy# choose(UITableViewCell)
    []
    
  • 现在只是语法大致了解一下,后面会通过 Cycript 写一些脚本用来快捷调试,比如:拿到当前正在显示的控制器,这个就可以充利用上面的属性获取方式以及 iOS 开发中的获取控制器方式配合来获取了。

  • 后面会写到封装这样一个脚本,毕竟这个会很方便的帮我们找到当前控制器,也就知道在哪个文件可以做调整了。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡尔特斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值