使用 choose 函数获取 Objective-C 对象(Objective-C objects using choose)
在 Cycript 0.9.502
版本中引入并在此处记录的 choose()
函数,允许我们获取某个类所有的现有对象的数组
(hcg 注:简单地说,就是在内存的堆空间中查找指定的类及其子类的所有实例对象)
使用地址获取 Objective-C 对象(Objective-C objects from addresses)
cy# var p = #0x8614390
cy# p
["" ]
获取 Javascript 变量(Javascript variables)
// 需要测试
cy# typedef int a;
cy# for (x in this) if (x == 'a') system.print('yay');
获取成员变量(Getting ivars)
通常,只需要输入 *varName
即可:
cy# *controller
{isa:"PrefsRootController",_contentView:">" ,_navBar:...
有时,*varName
会不起作用:
cy# *UIApp
{message:"hasProperty callback returned true for a property that doesn't exist.",name:"ReferenceError"}
此时,你可以这样做:
cy# [i for (i in *UIApp)]
["isa","_delegate","_touchMap","_exclusiveTouchWindows","_event",...
你也可以使用下面的 tryPrintIvars
函数获取尽可能多的成员变量以及它们的值:
function tryPrintIvars(a){ var x={}; for(i in *a){ try{ x[i] = (*a)[i]; } catch(e){} } return x; }
// 上面的代码经过整理后,如下所示:
function tryPrintIvars(a) {
var x={};
for(i in *a) {
try { x[i] = (*a)[i]; }
catch(e) {}
}
return x;
}
tryPrintIvars
函数的使用示例:
cy# *a
{message:"hasProperty callback returned true for a property that doesn't exist.",name:"ReferenceError"}
cy# tryPrintIvars(a)
{isa:"SBWaveView",_layer:"" ,_tapInfo:null,_gestureInfo:null,_gestureRecognizers:...
获取 BundleID(Getting bundle identifier)
NSBundle.mainBundle.bundleIdentifier
获取方法(Getting methods)
下面的 printMethods
函数用于获取方法:
// @param.className 类名
// @param.isa 如果为 undefined,则打印对象方法。否则,打印类方法
function printMethods(className, isa) {
var count = new new Type("I");
var classObj = (isa != undefined) ? objc_getClass(className).constructor : objc_getClass(className);
var methods = class_copyMethodList(classObj, count);
var methodsArray = [];
for(var i = 0; i < *count; i++) {
var method = methods[i];
methodsArray.push({selector:method_getName(method), implementation:method_getImplementation(method)});
}
free(methods);
return methodsArray;
}
printMethods
函数的使用示例:
cy# printMethods("MailboxPrefsTableCell")
[{selector:@selector(layoutSubviews),implementation:0x302bf2e9},{selector:@selector(setCurrentMailbox:),implementation:0x302bee0d},...
你也可以只查看 isa
的 prototype
属性。例如,获取 rootViewController
的方法:
UIApp.keyWindow.rootViewController.isa.prototype
获取名称与特定正则表达式匹配的方法(Get methods matching particular RegExp)
function methodsMatching(cls, regexp) { return [[new Selector(m).type(cls), m] for (m in cls.prototype) if (!regexp || regexp.test(m))]; }
// 上面的代码经过整理后,如下所示:
function methodsMatching(cls, regexp) {
return [
[new Selector(m).type(cls), m]
for (m in cls.prototype)
if (!regexp || regexp.test(m))
];
}
使用示例:
cy# methodsMatching(NSRunLoop, /forKey:$/)
[["v20@0:4I8@12@16","didChange:valuesAtIndexes:forKey:"],
["v20@0:4I8@12@16","willChange:valuesAtIndexes:forKey:"],
["v16@0:4@8@12","setValue:forKey:"]]
获取类方法(Getting class methods)
class.prototype
只包含对象方法。要 hook 类方法,你需要访问元类。一个简单的方法是:
cy# NSRunLoop.constructor.prototype['currentRunLoop'] = ...
或者,将 printMethods()
中可选的第二个参数设置为 true
,例如 printMethods("NSRunLoop", true)
cy# printMethods("NSRunLoop", true)
[{selector:@selector(currentRunLoop),implementation:&(extern "C" id 674681217(id, SEL, ...))}...
替换现有的 Objective-C 方法(Replacing existing Objective-C methods)
你可以通过替换 prototype
数组中的内容来模拟 MSHookMessage
,例如:
cy# original_NSRunLoop_description = NSRunLoop.prototype['description'];
(extern "C" id ":description"(id, SEL))
cy# NSRunLoop.prototype['description'] = function() { return original_NSRunLoop_description.call(this).toString().substr(0, 80)+", etc."; }
function (){var e;e=this;return original_NSRunLoop_description.call(e).toString().substr(0,80)+", etc."}
cy# [NSRunLoop currentRunLoop]
#"{wakeup port = 0x1003, stopped = false, ign, etc."
// 上面的代码经过整理后,如下所示:
cy# original_NSRunLoop_description = NSRunLoop.prototype['description'];
(extern "C" id ":description"(id, SEL))
cy# NSRunLoop.prototype['description'] = function () {
return original_NSRunLoop_description.call(this).toString().substr(0, 80) + ", etc.";
}
function (){var e;e=this;return original_NSRunLoop_description.call(e).toString().substr(0,80)+", etc."}
cy# [NSRunLoop currentRunLoop]
#"{wakeup port = 0x1003, stopped = false, ign, etc."
注意 func.call(this)
的构造。这会将原始函数中的 this
绑定到用户指定的函数
如果需要多个变量,则请使用 function(arg1, arg2, arg3, ...) {...func.call(self, arg1, arg2, arg3, ...);}
,例如:
cy# original_SpringBoard_menuButtonDown = SpringBoard.prototype['menuButtonDown:']
0x17dbab1
cy# SpringBoard.prototype['menuButtonDown:'] = function(arg1) {original_SpringBoard_menuButtonDown.call(this, arg1);}
function (e) {var e;var $cy0=this;original_SpringBoard_menuButtonDown.call($cy0,e);}
// 上面的代码经过整理后,如下所示:
cy# original_SpringBoard_menuButtonDown = SpringBoard.prototype['menuButtonDown:']
0x17dbab1
cy# SpringBoard.prototype['menuButtonDown:'] = function(arg1) {
original_SpringBoard_menuButtonDown.call(this, arg1);
}
function (e) {var e;var $cy0=this;original_SpringBoard_menuButtonDown.call($cy0,e);}
请注意,因为后续参数不会自动映射到相应的 Objective-C 类型,所以你需要使用 [NSString stringWithString:"foo"]
而不是 "foo"
[c for each (c in ObjectiveC.classes) if (class_getSuperclass(c) && [c isSubclassOfClass:UIView])]
// 上面的代码经过整理后,如下所示:
[c for each (c in ObjectiveC.classes)
if (class_getSuperclass(c) && [c isSubclassOfClass:UIView])]
(需要使用 class_getSuperclass
函数来防止由于对象所属的类没有继承自 NSObject
,而导致的崩溃)
function loadFramework(fw) {
var h = "/System/Library/", t = "Frameworks/" + fw + ".framework";
[[NSBundle bundleWithPath:h+t]||[NSBundle bundleWithPath:h+"Private"+t] load];
}
包含其他 Cycript 文件(Include other Cycript files)
在 Cycript 0.9.274-1
以及后续的几个版本中,Cycript 没有导入原生文件的功能。如果 Cycript 将被挂载到另一个进程,则因为数据将保留在目标进程里,所以你可以首先使用以下命令加载另一个 .cy
文件:
localhost:~ mobile$ cycript -p SpringBoard main.cy
0x12345678
localhost:~ mobile$ cycript -p SpringBoard
cy# ...
如果 Cycript 是独立启动的,则仍然可以通过 Cycript 编译器和 Javascript 的 eval
函数的组合来伪造 include
功能:
// include other .cy files
function include(fn) {
var t = [new NSTask init]; [t setLaunchPath:@"/usr/bin/cycript"]; [t setArguments:["-c", fn]];
var p = [NSPipe pipe]; [t setStandardOutput:p]; [t launch]; [t waitUntilExit];
var s = [new NSString initWithData:[[p fileHandleForReading] readDataToEndOfFile] encoding:4];
return this.eval(s.toString());
}
// 上面的代码经过整理后,如下所示:
// include other .cy files
function include(fn) {
var t = [new NSTask init];
[t setLaunchPath:@"/usr/bin/cycript"];
[t setArguments:["-c", fn]];
var p = [NSPipe pipe];
[t setStandardOutput:p];
[t launch];
[t waitUntilExit];
var s = [new NSString initWithData:[[p fileHandleForReading] readDataToEndOfFile]
encoding:4];
return this.eval(s.toString());
}
从 Cycript 0.9.502
版本开始有导入原生文件的功能。请参阅 @import’s documentation
在最新版本的 Cycript 中,NSLog
应该可以正常工作。如果 NSLog
没有正常工作,则请在控制台中输入:
NSLog_ = dlsym(RTLD_DEFAULT, "NSLog")
NSLog = function() { var types = 'v', args = [], count = arguments.length; for (var i = 0; i != count; ++i) { types += '@'; args.push(arguments[i]); } new Functor(NSLog_, types).apply(null, args); }
// 上面的代码经过整理后,如下所示:
NSLog_ = dlsym(RTLD_DEFAULT, "NSLog")
NSLog = function() {
var types = 'v', args = [], count = arguments.length;
for (var i = 0; i != count; ++i) {
types += '@';
args.push(arguments[i]);
}
new Functor(NSLog_, types).apply(null, args);
}
然后你就可以像往常一样使用 NSLog
了:
cy# NSLog_ = dlsym(RTLD_DEFAULT, "NSLog")
0x31451329
cy# NSLog = function() { var types = 'v', args = [], count = arguments.length; for (var i = 0; i != count; ++i) { types += '@'; args.push(arguments[i]); } new Functor(NSLog_, types).apply(null, args); }
{}
cy# NSLog("w ivars: %@", tryPrintIvars(w))
// 上面的代码经过整理后,如下所示:
cy# NSLog_ = dlsym(RTLD_DEFAULT, "NSLog")
0x31451329
cy# NSLog = function() {
var types = 'v', args = [], count = arguments.length;
for (var i = 0; i != count; ++i) {
types += '@';
args.push(arguments[i]);
}
new Functor(NSLog_, types).apply(null, args);
}
{}
cy# NSLog("w ivars: %@", tryPrintIvars(w))
如果你附加到了某个进程,则输出将保存在 syslog
中:
Nov 17 20:26:01 iPhone3GS Foobar[551]: w ivars: {\n contentView = <UIView: 0x233ea0; ....}
使用 CGGeometry 函数(Using CGGeometry functions)
CGPoint
、CGSize
、CGRect
是数字类型的结构体(float
或 double
),可以在 Cycript 中用简单的数组来表示:
cy# view.frame = [[10, 10], [100, 100]];
[[10,10],[100,100]]
如果你更喜欢使用 CGXxxxMake
函数,则你可以自己构建它们:
function CGPointMake(x, y) { return [x, y]; }
function CGSizeMake(w, h) { return [w, h]; }
function CGRectMake(x, y, w, h) { return [[x, y], [w, h]]; }
使用 NSError(Using NSError)
cy# var error = new @encode(NSError *)
&null
cy# var thing; [[NSFileManager defaultManager] copyItemAtPath:@"aaadsdsds" toPath:@"bbbdsdsdsds" error:error]; thing = *error
cy# thing
#'Error Domain=NSCocoaErrorDomain Code=260 "The file \xe2\x80\x9caaadsdsds\xe2\x80\x9d couldn\xe2\x80\x99t be opened because there is no such file." UserInfo=0x100310af0 {NSFilePath=aaadsdsds, NSUnderlyingError=0x1003108e0 "The operation couldn\xe2\x80\x99t be completed. No such file or directory"}'
// 上面的代码经过整理后,如下所示:
cy# var error = new @encode(NSError *)
&null
cy# var thing;
[[NSFileManager defaultManager] copyItemAtPath:@"aaadsdsds"
toPath:@"bbbdsdsdsds"
error:error];
thing = *error
cy# thing
#'Error Domain=NSCocoaErrorDomain
Code=260
"The file \xe2\x80\x9caaadsdsds\xe2\x80\x9d couldn\xe2\x80\x99t be opened because there is no such file."
UserInfo=0x100310af0 {
NSFilePath = aaadsdsds,
NSUnderlyingError = 0x1003108e0
"The operation couldn\xe2\x80\x99t be completed. No such file or directory"
}'
将 Cycript 的输出写入到文件中(Writing Cycript output to file)
因为 Cycript 的输出是一个 NSString
,所以可以调用 -writeToFile:
方法将 Cycript 的输保存到某处。例子:
[[someObject someFunction] writeToFile:"/var/mobile/cycriptoutput.txt" atomically:NO encoding:4 error:NULL]
// 上面的代码经过整理后,如下所示:
[[someObject someFunction] writeToFile:"/var/mobile/cycriptoutput.txt"
atomically:NO
encoding:4
error:NULL]
例如,你可以使用它来获取 SpringBoard
视图树的转储:
iPhone:~$ cycript -p SpringBoard
cy# [[UIApp->_uiController.window recursiveDescription] writeToFile:"/var/mobile/viewdump.txt" atomically:NO encoding:4 error:NULL]
// 上面的代码经过整理后,如下所示:
iPhone:~$ cycript -p SpringBoard
cy# [[UIApp->_uiController.window recursiveDescription] writeToFile:"/var/mobile/viewdump.txt"
atomically:NO
encoding:4
error:NULL]
打印视图层级结构(Printing view hierarchy)
cy# UIApp.keyWindow.recursiveDescription().toString()
"<UIWindow: 0x13a900; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x13a9d0>>
<UITextField: 0x13abf0; frame = (20 40; 280 31); text = ''; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x13ad10>>
<UITextFieldRoundedRectBackgroundView: 0x143d10; frame = (0 0; 280 31); userInteractionEnabled = NO; layer = <CALayer: 0x143dc0>>
<UIImageView: 0x144030; frame = (0 0; 8 15); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1440b0>>
<UIImageView: 0x144400; frame = (8 0; 264 15); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x144430>>
<UIImageView: 0x144460; frame = (272 0; 8 15); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x144490>>
<UIImageView: 0x1444c0; frame = (8 0; 0 15); userInteractionEnabled = NO; layer = <CALayer: 0x1444f0>>
<UIImageView: 0x144520; frame = (0 15; 8 1); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x144550>>
<UIImageView: 0x144580; frame = (8 15; 264 1); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1445b0>>
<UIImageView: 0x1445e0; frame = (272 15; 8 1); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x144610>>
<UIImageView: 0x144640; frame = (8 15; 0 1); userInteractionEnabled = NO; layer = <CALayer: 0x144670>>
<UIImageView: 0x1446a0; frame = (0 16; 8 15); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1446d0>>
<UIImageView: 0x144700; frame = (8 16; 264 15); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x144730>>
<UIImageView: 0x144760; frame = (272 16; 8 15); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x144790>>
<UIImageView: 0x1447c0; frame = (8 16; 0 15); userInteractionEnabled = NO; layer = <CALayer: 0x1447f0>>
<UILabel: 0x13aaf0; frame = (9 8; 266 15); text = 'Test'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1399f0>>"
Cycript 脚本(Cycript scripts)
用于加载 Cycript 文件 /var/root/common.cy
的自定义 shell 函数:
cyc () { cycript -p /var/root/common.cy > /dev/null; cycript -p ; }
将此 shell 函数添加到 /etc/profile.d/cycript.sh
中,以使其在所有会话中都可用
用法:
cyc ProcessName
警告:
如果你对一个进程多次运行此命令,则脚本将多次被加载到 Cycript 中
根据你正在加载的脚本的不同,这可能会产生意想不到的后果
这不是正确的做法,saurik 建议你不要这样做
链接:https://github.com/limneos/weak_classdump
用法:
root# cycript -p Skype weak_classdump.cy; cycript -p Skype
'Added weak_classdump to "Skype" (1685)'
cy# UIApp
""
cy# weak_classdump(HellcatApplication);
"Wrote file to /tmp/HellcatApplication.h"
cy# UIApp.delegate
""
cy# weak_classdump(SkypeAppDelegate,"/someDirWithWriteAccess/");
"Wrote file to /someDirWithWriteAccess/SkypeAppDelegate.h"
root# cycript -p iapd weak_classdump.cy; cycript -p iapd
'Added weak_classdump to "iapd" (1127)'
cy# weak_classdump(IAPPortManager)
"Wrote file to /tmp/IAPPortManager.h"
root# cycript -p MobilePhone weak_classdump.cy; cycript -p MobilePhone
'Added weak_classdump to "MobilePhone" (385)'
#cy weak_classdump_bundle([NSBundle mainBundle],"/tmp/MobilePhone")
"Dumping bundle... Check syslog. Will play lock sound when done."
实用工具(Utils)
链接:https://github.com/Tyilo/cycript-utils
安装 utils.cy
到已越狱 iOS 设备的 /usr/lib/cycript0.9/com/tyilo
目录下:
mkdir -p /usr/lib/cycript0.9/com
git clone https://github.com/Tyilo/cycript-utils.git /usr/lib/cycript0.9/com/tyilo
然后在 Cycript 中:
cy# @import com.tyilo.utils; 0
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)