Cycript(一):基础简介

Cycript 简介

  • 什么是 Cycript ?

    Cycript 允许开发者通过具有语法高亮显示和代码补全功能的交互式控制台,使用 Objective-C++ 和 JavaScript 语法的混合体,来探索和修改 iOS 或 macOS 上正在运行的应用程序(Cycript 还可以在 Android 和 Linux 上独立运行,并提供对 Java 的访问,但是不能注入进程)

    可以将 Cycript 看成是一个理解 Objective-C 语法的 JavaScript 解释器,Cycript 二进制文件也是该语言的交互式解释器(Read-Eval-Print Loop)

  • Cycript 的特点

    ① 注入进程(Inject Into Processes)

    bash# cycript -p SpringBoard
    

    ② Objective-C 消息(Objective-C Messages)

    cy# [UIApp description]
    @"<SpringBoard: 0x10ed05e40>"
    

    ③ JavaScript 扩展(JavaScript Extensions)

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

    ④ 毫不费力地进行探索(Effortless Exploration)

    cy# choose(CALayer)[0]
    #"<CALayer: 0x115807910>"
    

    ⑤ 桥接对象模型(Bridged Object Model)

    cy# @[0,1] instanceof Array
    true
    

    ⑥ 外部函数调用(Foreign Function Calls)

    cy# var a = malloc(128)
    0x1147c9d00
    

    ⑦ 神奇的 Tab 键代码补全(Magical Tab-Complete)

    cy# ({m: 4, b: 5}).<Tab><Tab>
    b m
    

    ⑧ C++11 Lambda 语法(C++11 Lambda Syntax)

    cy# [&](int a)->int{return a}
    0x111001000
    
  • 使用示例:hook fopen 函数

    假设我们有一个程序可以打开 /etc/passwd 来检查我们的密码。我们希望它使用 /var/passwd-fake。 首先,我们需要 fopen 函数的地址

    cy# fopen = dlsym(RTLD_DEFAULT, "fopen")
    (typedef void*)(0x7fff900c34ec)
    

    但是,我们不能在没有类型签名的情况下调用此函数,所以让我们将它转换为 Functor。使用 Cycript,我们可以使用高级 C typedef 语法

    cy# fopen = (typedef void* (char *, char *))(fopen)
    (extern "C" void* fopen(char *, char *))
    

    接下来,让我们导入 Substrate,这样我们就可以使用 MS.hookFunction 来修改 fopen 函数

    // 交换假的 passwd 文件,并记录所有调用参数和调用结果
    cy# @import com.saurik.substrate.MS
    cy# var oldf = {}
    cy# var log = []
    cy# MS.hookFunction(fopen, function(path, mode) {
            if (path == "/etc/passwd") path = "/var/passwd-fake";
            var file = (*oldf)(path, mode);
            log.push([path.toString(), mode.toString(), file]);
            return file;
        }, oldf)
    

    除了我们的恶意修改之外,我们还可以查看所有对 fopen 函数的调用,并跟踪返回的 FILE * 的值是什么。 让我们试试看

    cy# fopen("/etc/passwd", "r");
    (typedef void*)(0x7fff72c14280)
    cy# log
    [["/var/passwd-fake","r",(typedef void*)(0x7fff72c14280)]]
    
  • 使用示例:JS/ObjC 对象桥接(JS/ObjC Object Bridging)

    为了方便,一些原生 JavaScript 类型被桥接到了相应的 Objective-C 类型,所以你可以使用

    [[41, "foo", true, [8,6], {a:12,b:46}, 36] indexOfObject:"foo"]
    

    代替

    [[NSArray arrayWithObjects:
      [NSNumber numberWithInt:41],
      "foo",
      [NSNumber numberWithBool:YES],
      [NSArray arrayWithObjects:[NSNumber numberWithInt:8], 
      						    [NSNumber numberWithInt:6], 
      						    nil],
      [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:12], "a",
        										 [NSNumber numberWithInt:46], "b",
      											 nil],
      [NSNumber numberWithInt:36],
      nil] indexOfObject:"foo"]
    

    JS 和 ObjC 数据类型对照表

    // JS 类型 -- ObjC 类型
    number	-- NSNumber (CFNumber)
    boolean	-- NSNumber (CFBoolean)
    string	-- NSString
    Array	-- NSArray
    object (Associative array)	-- NSDictionary
    

    Cycript 中的 null 等价于 Objective-C 中的 nil。此外,Cycript 中还定义了 nilYESNO

  • 了解更多信息

    1. 阅读 Cycript 手册 — 我通过阅读 Visual Basic 3.0 手册学会了 Win32 编程
    2. #cycript @ irc.saurik.com — 与其他志趣相投的人交谈始终是一项有趣的活动
    3. Cycript 源代码 — 当你遇到严重的问题时,请阅读源代码
    4. Cycript 的 iPhone Dev Wiki 页面 — 虽然其中的一些信息已经过时,但是其中还是有很多有用的东西可以查看
    5. Adam Bell 的 WWJC 演讲 — Adam 就 iOS 内部进行了精彩的演讲,使用 Cycript 进行演示
    6. ECMAScript 规范 — Cycript 基于 JavaScript:你应该很了解这门语言
  • 关于 Cycript 的演讲

    1. saurik 解释如何在 macOS 上针对 iOS 模拟器使用 Cycript
    2. Adam Bell 解释如何使用 Cycript 对 Tweak 进行原型设计示例代码

Cycript 附加的特性

  • 附加语法(Additional syntax)

    新语法:[obj msg:var]
    意义:使用参数 var 将消息 msg: 发送给对象 obj。这是 Objective-C 的消息发送语法
    脱糖表示:objc_msgSend(obj, @selector(msg:).value, var ...)


    新语法:@import(module)
    意义: 导入一个 .cy 文件。类似于 JavaScript 的 require() 函数


    新语法:@selector(selname)
    意义:使用 Objective-C 的语法,返回名为 selname 的方法选择器
    脱糖表示:new Selector("selname");


    新语法:obj->ivar
    意义:获取 Objective-C 对象 obj 的成员变量 ivar
    脱糖表示:obj.$cyi.ivar


    新语法:*ptr
    意义:取消引用指针,或列出对象的所有 ivar(以便你可以使用 (*obj).ivar 访问它们)
    脱糖表示:ptr.$cyi


    新语法:obj->[key]
    意义:等效于 (*obj)[key]


    新语法:&var
    意义:获取变量的地址。只有 ObjC 类的实例才可以获取地址
    脱糖表示:var.$cya()


    新语法:

    @class classname : superclass {}
    +methodname { function body }
    -methodname { function body }
    ...
    @end
    

    意义:声明一个 Objective-C 类。 省略类名以声明一个匿名类


    新语法:

    @class existingclass
    +methodname { function body }
    -methodname { function body }
    ...
    @end
    

    意义:向现有类插入额外的方法。现有类本身可以是一个表达式,例如 @class ([obj class])


    新语法:new classname
    意义:虽然不完全是一种新语法,但是这种构造对于 Objective-C 类具有新的含义。虽然这类似于 [classname alloc],但是所申请的内存资源将由 JavaScriptCore 的垃圾回收器进行管理。要完全初始化该类,你需要调用 [new classname initWithFoo:...]


    新语法:@"str"
    意义:相当于 "str"


    新语法:[super ...]
    意义:用于表示超类的局部变量
    脱糖表示:objc_msgSend(???, ... )


    新语法:0bxxxxxx
    意义:二进制字面量

  • 仅限 REPL 的附加语法(REPL-only additions)

    这些附加语法用于进行调试

    ?debug:用于切换调试的输出
    ?bypass:用于绕过语法错误,以友好的方式打印
    ?expand:用于切换是否将换行符等显示为真正的换行符,或只是 \n
    ?gc:强制进行 JavaScript 垃圾回收
    ?syntax:用于切换语法的高亮显示

  • 附加的类型(Additional types)

    Selector(selname):声明一个方法选择器

    Functor(function body, type encoding):将 Objective-C 类型编码与函数相关联
    例如,new Functor(function(x,y){return (x+y).toString(16);}, "*dd"); 声明了一个 (double, double) → char* 的函数

    Pointer(address, type encoding):将输入的数字视为指针
    与 C 指针一样,结果可以使用 * 取消引用,并使用 [i] 下标,但不直接支持指针的算术运算

    Type(type encoding):创建一个类型。结果值可以是 new-ed 以获得一个 Pointer
    例如,var p = new new Type("d");
    要释放指针,请使用 free() 函数

    Instance(address):将地址视为 Objective-C 对象的一个​​实例

    Super(self, selector):返回一个对象,当发送消息时,该对象将被转发到自己(self)的超类

    此外:
    intidchardouble 等标识符被预定义为相应的类型(new Type("i") 等)
    因此,如果要 alloc 一个指针,则可以简单地使用 new int 甚至是 new int[42]

  • 附加的变量和方法(Additional variables and methods)

    _:用于保存最后执行的命令的结果的值的变量(仅限 REPL 环境)

    ObjectiveC.images:镜像的关联数组(可以将 JS 的关联数组理解为 OC 中的字典)。键是被加载的库的路径,值是该库中所有的类

    ObjectiveC.classes:类的关联数组(可以将 JS 的关联数组理解为 OC 中的字典)。 键是类名,值是类本身

    ObjectiveC.protocols:协议的关联数组(可以将 JS 的关联数组理解为 OC 中的字典)。键是协议名称,值是协议本身

    obj.toJSON():将指定的对象 obj 转换为 JSON

    obj.toCYON():将指定的对象 obj 转换为 CYON(Cycript Object Notation,Cycript 对象表示法)

    obj.value:对于某些类型的对象,会返回地址

    class.messages:消息的关联数组(包含指定类 class 中的所有消息)(可以将 JS 的关联数组理解为 OC 中的字典)
    键是方法选择器的名称,值是方法的实现(函数指针)

    system.print(string):将指定的字符串 string 打印到系统日志

    system.args:当前可执行文件的参数

    selector.type(class):返回指定的类 class 中指定的方法选择器 selector 的类型编码
    例如,@selector(copyWithZone:).type(NSString) 返回 @12@0:4^{_NSZone=}8

其他

  • JavaScript 1.6+ 的特性(JavaScript 1.6+ Features)

    Cycript 支持以下由 Mozilla 扩展的 JavaScript 1.6+ 的特性:

    1. for each/in(JS1.6)
    2. Array comprehensions (JS1.7)
    3. E4X(不适用于 iOS)
  • 其他用途(Other uses)

    使用 Cycript 可执行二进制文件的 -c 标志,可以将 Cycript 源文件编译成标准的 JavaScript 1.5 源文件,例如:

    Your-iPhone:~ mobile$ echo "[x*x for each(x in [1,2,3])]" | cycript -c > x.js
    Your-iPhone:~ mobile$ cat x.js
    (function($cyv,x){$cyv=[];(function($cys){$cys=[1,2,3];for(x in $cys){x=$cys[x];$cyv.push(x*x)}})();return $cyv})()
    
    // 上面的代码经过整理后,如下所示:
    Your-iPhone:~ mobile$ echo "[x*x for each(x in [1,2,3])]" | cycript -c > x.js
    Your-iPhone:~ mobile$ cat x.js
    (function($cyv, x) { 
    	$cyv=[];
    	(function($cys) { 
    		$cys = [1, 2, 3];
    		for (x in $cys) {
    			x = $cys[x];
    			$cyv.push(x * x)
    		}
    	})();
    	return $cyv
    })()
    
  • 注意事项(Considerations)

    因为输入到控制台的每个命令都在自动释放池中运行
    所以在一个命令中声明的变量,可能会在下一个使用它的命令运行时,被释放了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值