Cycript使用技巧
iproxy 2222 22 #转发端口
ssh -p 2222 [email protected] # 登录iphone, 输入密码
ps -A 或 ps aux 列出所有的进程
ps -A | grep `关键词` 搜索关键词
# 开启Cycript监听(进入iPhone服务端操作)
cycript
cycript -p 进程ID # [不推荐, 进程ID每次都会变]
cycript -p 进程名称 # 推荐使用, 进程名不会变
# cycript的语法
Ctrl + C : 取消输入
Ctrl + D : 退出Cycript监听
Common + R : 清屏
UIApp :[UIApplication sharedApplication]
var 变量名 = 变量值 :定义变量
#内存地址 :用内存地址获取对象
ObjectiveC.classes :当前app已加载的所有OC类
*对象 :查看对象的所有成员变量
view.recursiveDescription().toString() :递归打印view的所有子控件(跟LLDB一样的函数)
choose(UIViewController) :查看当前界面中,存在内中的UIViewController类型的对象
choose(UITableViewCell):筛选出UITableViewCell类型的对象
MJTool的安装及使用
- 先从Mac端拷贝个MJ的cy封装文件到iPhone端,
scp -P 10011 [文件] root@localhost:/usr/lib/cycript0.9/MJTool.cy
- 连接登录到iPhone端,cycript监听网易云音乐进程(注意保持网易云音乐app是开启状态)
- 导入封装文件,查看当前界面
@import MJTool
MJFrontVc() #查看当前界面的控制器
MJInstanceMethodNames(#0x129c57f60) # 打印控制器的所有方法
[#0x129c57f60 loginButtonClicked:nil] # 调用登录方法
找到账号框和密码框
-
app账号框输入: 6666
密码框输入: 8888
打印控制器的子view: 找到6666和8888, 即找到了账号框和密码框:
验证: 拿到账号框, 账号框输入7777, app的输入框数字也变为7777, 说明这个账号框是对的,
-
用MJTool.cy 看下该控制器的其他东西:
6.1 类方法: MJClassMethodNames(#0x129c57f60)
6.2 对象方法: MJInstanceMethodNames(#0x129c57f60)
删除登录按钮, 通过'
登录
转为unicode编码:\u767b\u5f55
, 找到控件后执行删除操作:
[#0x129c4c6c0 removeFromSuperview]
-
添加个红色的view, 用到MJTool
MJFrontVc() # 找到当前界面的vc var redV = [[UIView alloc] init] redV.backgroundColor = [UIColor redColor] redV.frame = MJRectMake(10, 100, 100, 100) [#0x129c57f60.view addSubview:redV]
MJTool工具中正则表达式使用-----筛选
cy# MJFrontVc()
#""
// 筛选带有login的对象方法,注意是匹配大小写的
cy# MJInstanceMethodNames(#0x129c57f60,/login/)
[&"loginButtonClicked:",&"loginView"]
// 筛选以View结尾的对象方法,注意大小写
cy# MJInstanceMethodNames(#0x129c57f60,/View$/)
[&"loginView",&"loadView"]
想知道当前view是如何划分结构的——使用MJTool.cy:
10、想知道控制器有哪些对象方法——使用MJTool.cy:
MJInstanceMethodNames(#0x12324432)
MJInstanceMethodNames(#0x12324432,/click/)
// 筛选出带有click的
11、控制器有哪些成员变量——使用MJTool.cy:
MJIvarNames(#0x12324432)
MJIvarNames(#0x12324432,/click/)
// 筛选出带有click的
12、获取某个类的所有子类——使用MJTool.cy:
//当前app有哪些UIViewController的子类
MJSubclasses(UIViewController)
MJSubclasses("UIViewController",/NMR/)
//查看app有哪些自定义导航控制器
MJSubclasses(UINavigationController)
MJSubclasses("UINavigationController")
MJSubclasses("UINavigationController",/NMR/)
13、改微信钱包零钱
大致步骤:
- 连接登录iPhone服务端,打开微信,进入微信钱包页面,搜索进程
ps -A
,监视微信进程cycript -p WeChat
,导入工具@import MJTool
; -
MJFrontVc()
:当前控制器 -
MJVcSubviews(#0x13e90c200)
:当前控制器的所有子view -
command + F
搜索零钱的数字,确定是哪个对象 -
(#0x14065f880).text = '\xa5848,340.00'
,修改零钱 -
(#0x14065f880).backgroundColor = [UIColor redColor]
,修改背景 -
(#0x14065f880).frame = MJRectMake(0,85,320,13)
,修改宽高
14、修改变量:
cy# QLVipManager.prototype['isVipOrVipVisitor'] = function(){return YES};
function (){return YES}
cy# QLVipManager.prototype['isValid'] = function(){return YES};
function (){return YES}
cy# QLVipManager.prototype['isAnnualVipWithCache'] = function(){return YES};
function (){return YES}
cy# QLVipManager.prototype['isVipOrVipVisitorWithCache'] = function(){return YES};
function (){return YES}
cy# QLVipManager.prototype['isVipVisitorWithCache'] = function(){return YES};
function (){return YES}
cy# QLVipManager.prototype['isVipWithCache'] = function(){return YES};
function (){return YES}
MJTool.cy的代码
(function(exports) {
var invalidParamStr = 'Invalid parameter';
var missingParamStr = 'Missing parameter';
// app id
MJAppId = [NSBundle mainBundle].bundleIdentifier;
// mainBundlePath
MJAppPath = [NSBundle mainBundle].bundlePath;
// document path
MJDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
// caches path
MJCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
// 加载系统动态库
MJLoadFramework = function(name) {
var head = "/System/Library/";
var foot = "Frameworks/" + name + ".framework";
var bundle = [NSBundle bundleWithPath:head + foot] || [NSBundle bundleWithPath:head + "Private" + foot];
[bundle load];
return bundle;
};
// keyWindow
MJKeyWin = function() {
return UIApp.keyWindow;
};
// 根控制器
MJRootVc = function() {
return UIApp.keyWindow.rootViewController;
};
// 找到显示在最前面的控制器
var _MJFrontVc = function(vc) {
if (vc.presentedViewController) {
return _MJFrontVc(vc.presentedViewController);
}else if ([vc isKindOfClass:[UITabBarController class]]) {
return _MJFrontVc(vc.selectedViewController);
} else if ([vc isKindOfClass:[UINavigationController class]]) {
return _MJFrontVc(vc.visibleViewController);
} else {
var count = vc.childViewControllers.count;
for (var i = count - 1; i >= 0; i--) {
var childVc = vc.childViewControllers[i];
if (childVc && childVc.view.window) {
vc = _MJFrontVc(childVc);
break;
}
}
return vc;
}
};
MJFrontVc = function() {
return _MJFrontVc(UIApp.keyWindow.rootViewController);
};
// 递归打印UIViewController view的层级结构
MJVcSubviews = function(vc) {
if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
return vc.view.recursiveDescription().toString();
};
// 递归打印最上层UIViewController view的层级结构
MJFrontVcSubViews = function() {
return MJVcSubviews(_MJFrontVc(UIApp.keyWindow.rootViewController));
};
// 获取按钮绑定的所有TouchUpInside事件的方法名
MJBtnTouchUpEvent = function(btn) {
var events = [];
var allTargets = btn.allTargets().allObjects()
var count = allTargets.count;
for (var i = count - 1; i >= 0; i--) {
if (btn != allTargets[i]) {
var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
events.push(e);
}
}
return events;
};
// CG函数
MJPointMake = function(x, y) {
return {0 : x, 1 : y};
};
MJSizeMake = function(w, h) {
return {0 : w, 1 : h};
};
MJRectMake = function(x, y, w, h) {
return {0 : MJPointMake(x, y), 1 : MJSizeMake(w, h)};
};
// 递归打印controller的层级结构
MJChildVcs = function(vc) {
if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
return [vc _printHierarchy].toString();
};
// 递归打印view的层级结构
MJSubviews = function(view) {
if (![view isKindOfClass:[UIView class]]) throw new Error(invalidParamStr);
return view.recursiveDescription().toString();
};
// 判断是否为字符串 "str" @"str"
MJIsString = function(str) {
return typeof str == 'string' || str instanceof String;
};
// 判断是否为数组 []、@[]
MJIsArray = function(arr) {
return arr instanceof Array;
};
// 判断是否为数字 666 @666
MJIsNumber = function(num) {
return typeof num == 'number' || num instanceof Number;
};
var _MJClass = function(className) {
if (!className) throw new Error(missingParamStr);
if (MJIsString(className)) {
return NSClassFromString(className);
}
if (!className) throw new Error(invalidParamStr);
// 对象或者类
return className.class();
};
// 打印所有的子类
MJSubclasses = function(className, reg) {
className = _MJClass(className);
return [c for each (c in ObjectiveC.classes)
if (c != className
&& class_getSuperclass(c)
&& [c isSubclassOfClass:className]
&& (!reg || reg.test(c)))
];
};
// 打印所有的方法
var _MJGetMethods = function(className, reg, clazz) {
className = _MJClass(className);
var count = new new Type('I');
var classObj = clazz ? className.constructor : className;
var methodList = class_copyMethodList(classObj, count);
var methodsArray = [];
var methodNamesArray = [];
for(var i = 0; i < *count; i++) {
var method = methodList[i];
var selector = method_getName(method);
var name = sel_getName(selector);
if (reg && !reg.test(name)) continue;
methodsArray.push({
selector : selector,
type : method_getTypeEncoding(method)
});
methodNamesArray.push(name);
}
free(methodList);
return [methodsArray, methodNamesArray];
};
var _MJMethods = function(className, reg, clazz) {
return _MJGetMethods(className, reg, clazz)[0];
};
// 打印所有的方法名字
var _MJMethodNames = function(className, reg, clazz) {
return _MJGetMethods(className, reg, clazz)[1];
};
// 打印所有的对象方法
MJInstanceMethods = function(className, reg) {
return _MJMethods(className, reg);
};
// 打印所有的对象方法名字
MJInstanceMethodNames = function(className, reg) {
return _MJMethodNames(className, reg);
};
// 打印所有的类方法
MJClassMethods = function(className, reg) {
return _MJMethods(className, reg, true);
};
// 打印所有的类方法名字
MJClassMethodNames = function(className, reg) {
return _MJMethodNames(className, reg, true);
};
// 打印所有的成员变量
MJIvars = function(obj, reg){
if (!obj) throw new Error(missingParamStr);
var x = {};
for(var i in *obj) {
try {
var value = (*obj)[i];
if (reg && !reg.test(i) && !reg.test(value)) continue;
x[i] = value;
} catch(e){}
}
return x;
};
// 打印所有的成员变量名字
MJIvarNames = function(obj, reg) {
if (!obj) throw new Error(missingParamStr);
var array = [];
for(var name in *obj) {
if (reg && !reg.test(name)) continue;
array.push(name);
}
return array;
};
})(exports);
到这里为止,都还只是借用cycript的封装,修改调试内存中的东西,还不能达到永久修改的目的。
参考文章: https://www.jianshu.com/p/c6c5571d871d