2逆向 - Cycript
Wed, May 2, 2018
想知道当前运行的App的布局、实例方法、都是啥
想动态给当前App增加一些羞羞的东东?
...
嗯呐,就是这一节,你来对了!
概述
文档:Cycript
是Objective-C++、ES6、Java等语法的混合物
用途:探索、修改、调试正在运行的mac/iOS App
安装:Cydia安装
退出:ctl + D
一般会通过Cycript测试函数,确定函数的作用之后,再编写Tweak修改程序 |
基本用法
调试进程
cycript -p 进程ID(或名称)
获取进程ID和进程名称方法 ps指令(process status) 需Cydia安装adv-cmds 用法 ps -A 列出所有进程 ps -A|grep 关键词 |
示例:cycript -p momo_ios
此时已经在监听这个app了
常用语法
[UIApplication shareApplication] // 获取application对象内存地址 等价 UIApp var app = UIApp.keyWindow // 获取keyWindow并赋值给变量app UIApp.keyWindow.rootViewController // 获取rootViewController var redView = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)]; // 暂时报错,之后解决 // 获取对象 - 通过内存地址来访问 #内存地址.rootViewController // ObjectiveC.classes - 当前App加载的要用到的所有类 // 查看对象的所有成员变量(示例:查看UIApp的) *UIApp // 递归打印view的所有子控件(同LLDB) // po self.view // po [self.view recursiveDescription] // 递归打印所有子View // [UIApp.keyWindow recursiveDescription].toString // 转为String格式 终极格式:UIApp.keyWindow.recursiveDescription().toString() // choose(UIViewController) - 挑选内存中的所有控制器对象 |
封装cy文件
用法举例 - MJTool
放置脚本到/usr/lib/cycript0.9/MJTool.cy
导入库:@import MJTool
问题:希望找到当前登录界面
一般方法需要一层一层拿
封装方法:MJTopVc() - 直接拿到最前面的VC
想知道点击登录的时候调用了那个方法
打印所有的对象方法名 MJInstanceMethodNames(控制器)
拿到方法名之后,可以直接调用这个控制器的方法
想在登录之前做些事情,可以Hook出这个函数,注入一些方法
希望找出文本框是谁,做些处理
先在文本框中输入一些东东
MJSubviews(目标View) - 拿到所有子View,通过输入的东东定位到是哪一个
- 找到「登录」按钮并删掉
- 方法:通过「登录」两个字,找到这个控件(需要转码再找)
- 调用这个东东的 removeFromSuperview
- 这里只是把控件从内存中干掉了
用法演示
Yoe:~ root# cycript -p QuickMud-mobile cy# @import mjcript {} cy# MJAppId @"com.mkjump.mud" cy# MJDocPath @"/var/mobile/Containers/Data/Application/E5CA2CEA-CF2F-4D39-8684-6762EA60D1F6/Documents" cy# MJCachesPath @"/var/mobile/Containers/Data/Application/E5CA2CEA-CF2F-4D39-8684-6762EA60D1F6/Library/Caches" cy# MJAppPath @"/var/containers/Bundle/Application/BE34C307-5FBE-4707-A502-7AEDEA4F74BD/QuickMud-mobile.app" cy# MJKeyWin function (){return UIApp.keyWindow} cy# MJKeyWin() #"<UIWindow: 0x146db14d0; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x146ec7810>; layer = <UIWindowLayer: 0x146db1410>>" cy# MJRootVc() #"<RootViewController: 0x146ed47a0>" cy# MJFrontVc() #"<RootViewController: 0x146ed47a0>" cy# var mapView = [[MKMapView alloc] init] #"<MKMapView: 0x148ea1c80; frame = (0 0; 0 0); clipsToBounds = YES; layer = <CALayer: 0x14b223910>>" cy# mapView.frame = MJRectMake(50,50,100,100) {0:{0:50,1:50},1:{0:100,1:100}} cy# [#0x146ed47a0.view addSubview:mapView] cy# MJLoadFramework('MapKit') # 如果当前App木有导入某动态库,可以通过这个东东加载进来 #"NSBundle </System/Library/Frameworks/MapKit.framework> (loaded)" // 找所有的子VC们 #"<RootViewController: 0x146ed47a0>" cy# MJChildVcs(#0x146ed47a0) "<RootViewController 0x146ed47a0>, state: appeared, view: <CCEAGLView 0x146ed1300>" cy# MJChildVcs(MJRootVc()) "<RootViewController 0x146ed47a0>, state: appeared, view: <CCEAGLView 0x146ed1300>" |
封装举例
基本写法
// test.cy (function(exports) { // 函数内部定义的函数是私有的,需要导出 // function sum(a, b) { // return a + b; // } // function minus(a, b) { // return a - b; // } // 利用exports导出方法 // 之后,可以使用 文件名.方法名() 使用这个脚本的方法 exports.sum = function sum(a, b) { return a + b; }; exports.minus = function minus(a, b) { return a - b; }; // 这样写,是定义了一个全局的函数。 // 调用的时候不用exports.docPath(), 直接docPath()即可 docPath = functino() { return NSSearchPathForDirectoriesInDomains(NSMocumentDirectory, NSUserDomainMask, YES)[0]; } })(exports); |
基本用法
把写好的脚本文件拷贝到iOS中路径:
cy环境下,@import test.cy
封装总结
- .cy是Cycript的脚本文件
- exports用于向外提供一个接口
脚本放置封装路径
作者的脚本放置路径:
原因:用域名来起文件夹,保证文件是唯一的
引用方式: @import com.azen.test 每一个.之间都是一个文件夹 如果放在了cycript0.9中,直接import就好
使用技巧
场景 | API | 示例 | 备注 | |
---|---|---|---|---|
1 | 知道对象在内存中,但不知道地址 | choose指令 | choose(SBScreenShotter) | 并非每次都一定能找到 |