Cycript Manual
by Jay Freeman (saurik)
General Questions
Answers to issues people have before even using Cycript.
What is Cycript?
Cycript is a hybrid of ECMAScript some-6, Objective-C++, and Java. It is implemented as a Cycript-to-JavaScript compiler and uses (unmodified) JavaScriptCore for its virtual machine. It concentrates on providing "fluent FFI" with other languages by adopting aspects of their syntax and semantics as opposed to treating the other language as a second-class citizen.
The primary users of Cycript are currently people who do reverse engineering work on iOS. Cycript features a highly interactive console that features live syntax highlighting and grammar-assisted tab completion, and can even be injected into a running process (similar to a debugger) using Cydia Substrate. This makes it an ideal tool for "spelunking".
However, Cycript was specifically designed as a programming environment and maintains very little (if any) "baggage" for this use case. Many modules from node.js can be loaded into Cycript, while it also has direct access to libraries written for Objective-C and Java. It thereby works extremely well as a scripting language.
Why develop Cycript?
My background is in the study of programming languages, compilers, and runtime code generation. I teach a class at the University of California, Santa Barbara that looks at machine language from the perspective of comparative linguistics. Cycript is essentially "cross-over fan fiction" for programming language runtime environments ;P.
Cycript began in 2007 as a bridge between Objective-C and Java called JOCStrap (Java/Objective-C BootStrap). That project was somewhat interesting, but what made it amazing was when I realized you could use Rhino (a Java implementation of JavaScript) to get an interactive console: that's arguably how I learned to use Cocoa/UIKit.
It wasn't until 2009, while at 360|iDev, that I finally dismantled JOCStrap and rebuilt it as Cycript. Over the years, I've added a number of features and improvements, and generally do demonstrations of new versions at 360|iDev (which I consider the conference "home" of Cycript). The result is a powerful debugging tool that I use alongside gdb.
Pronunciation
I pronounce "cycript" using a "geminate S" or "long S". The result sounds a little like a stereotypical serpent from a cartoon: "sssscript". I doubt anyone else will pronounce it like this, but I have my hopes. I also often use "cycript" as a verb with relation to automation of and injection into libraries and applications, which emphasizes its power as a scripting language.
Stability and Status
Cycript works. I keep improving the core language and library, but as a debugging tool this is not a problem. When I do make changes, they tend to be to little-used aspects of the bridge, or result in clear improvements that are easy to become comfortable with. Embedded as a static library this is also a non-problem.
I do not, however, recommend attempting to use it as a general purpose programming language. Frankly, Cycript isn't even very good at this: I tried to write a normal iPhone app in it once, and the result was actually slightly more verbose than Objective-C++ (due to C++11's auto feature). Just use Objective-C++ for apps ;P.
Getting Help
Users who are looking for help with Cycript are encouraged to join the #cycript channel on our IRC server, irc.saurik.com. There tend to be a bunch of people there who use Cycript and who generally seem interested in answering peoples' questions about how it is used. If you don't have an IRC client, you can try using Mibbit.
(Please understand that IRC involves live chat with other users. If you ask a question on IRC and no one seems to be around, you should stay online for at least a few hours: everyone is on a different schedule and are often in different timezones. People also tend to leave IRC windows open while doing other things, and so don't see new content immediately, but check in occasionally.)
Cycript Console
How to run the Cycript console and what to expect.
$ ./cycript
The first thing to do when using Cycript is to get comfortable running it. If you downloaded it for Mac OS X, you probably have it extracted to a folder somewhere: open a Terminal (preferably iTerm2) to the extracted folder, and run "./cycript". If you are using iOS, cycript should be on your path: you can just run "cycript".
iPhone:~$ cycript
cy#
The cy# prompt is a JavaScript console. Everything you type will be run by JavaScriptCore, Apple's implementation of the JavaScript language as used by Safari. As you type, your commands will be syntax-highlighted using Cycript's lexer. If you make any errors, you will get a syntax error. You can use ctrl-C to cancel and ctrl-D to exit.
cy# var a = 3 cy# Math.pow(a, 7) 2187 cy# var a 3 ..........^ | syntax error, unexpected NumericLiteral, expecting ; cy# function f(a) { a = a + 5; return ^C cy# ^D
iPhone:~$ cycript
Cycript's console is implemented using readline (yes, even on Mac OS X, where Cycript comes with its own copy of readline). If you do not know much about readline, I highly recommend learning more about it: there are numerous keyboard shortcuts that allow you to very rapidly manipulate commands. It also offers history search (look for ctrl-R).
Special attention was given to the behavior of editing multi-line commands. I spent hours with Brian Fox (the developer of readline) working out how to obtain some of the semantics I wanted using unmodified copies of the library. (Note that tab at the beginning of a line inside of a multi-line block context is treated as indentation.)
Console Behavior
Every command you type has its result value stored in a variable called _, which you can use in the next command. (Right now, this is stored global to the JavaScript VM, but I intend to store it per-console in a future release, so please do not attempt to rely on sharing _ between multiple consoles attached to the same VM.)
cy# 5 + 9
14
cy# _
14
CYON and ? commands
Sometimes, there are things you want to tell the console, as opposed to being parsed and run as JavaScript. These commands begin with a question mark. Of primary general interest is "?exit", which is an explicit command that can be used to quit Cycript instead of using ctrl-D. You can also toggle syntax highlighting using "?syntax".
cy# ?syntax syntax == false cy# ?exit
iPhone:~$ cycript
As an example, whenever the console renders a value, it does so using "CYON", which some people (not me...) claim stands for "Cycript Object Notation". The general idea is that everything the console renders is itself code that could be parsed by the language, and hopefully would generate a similar result. You can get CYON at runtime using .toCYON().
cy# "hello".toCYON()
'"hello"'
cy# [1,2].toCYON()
"[1,2]"
cy# new Number(3+10)
new Number(13)
cy# (function() { throw new Error(); })()
throw new Error("")
This output can sometimes be "messy", and is generally extremely "minimal". As an experimental feature, you can tell Cycript to reparse the output and then pretty print the result. This is done by using "?reparse" to toggle this feature. (If the CYON was invalid, this feature will just print the original text, so barring bugs in the pretty printer it should be "harmless".)
cy# ({a: 10, b: 15})
{a:10,b:15}
cy# ?reparse
reparse == true
cy# ({a: 10, b: 15})
{
a: 10,
b: 15,
}
Tab Completion
One of Cycript's key benefits is its sophisticated tab completion. Rather than being implemented at the level of tokens or strings, Cycript implements tab completion as part of the language parser and runtime environment. This means that you can often use tab completion in contexts distant from their call site, or on objects created at runtime.
cy# ({field: 123}).fi<TAB>
In this situation, we have typed an object literal, and would like to be able to tab complete from its fields. In most environments, this would either fail, or potentially complete to the keyword "finally". Cycript, in contrast, executes the literal and inspects it at runtime, getting a list of its properties (including from Object's prototype), and completes "field".
JavaScript Parser
Cycript does not rely on JavaScriptCore as a parser.
JavaScript Compiler
One thing that must be understood while using Cycript is that it does not simply take your commands as typed and pass them directly to JavaScriptCore. Instead, Cycript includes a complete implementation of the JavaScript grammar. This not only improves Cycript's implementation of tab completion, but also allows it to add new syntax.
As an example, one frustrating aspect of JavaScript is that if you use a for-in loop over an array, you don't iterate the elements of the array. Instead, for-in always iterates the property names of an object, which for an array are a sequence of strings, one for each valid index of the array. You then have to look up the elements.
cy# var before = [2, 4, 5];
cy# var after = []; for (var i in before) after.push(i); after
["0","1","2"]
cy# var after = []; for (var i in before) after.push(before[i]); after
2,4,5]
As this is a common irritation, ECMAScript 6 includes a new for-of feature. Cycript implements this feature as part of its grammar, and translates this feature to the older version of JavaScript that can be executed on JavaScriptCore. You can see what Cycript generated using "?debug" (especially handy if you suspect there's a bug).
cy# var after = [];
[]
cy# ?debug
debug == true
cy# for (var i of before) after.push(i)
cy= var $cy3,$cy2,i;{$cy3=before;for($cy2 in $cy3){i=$cy3[$cy2];after.push(i)}}
cy# after
cy= after
[2,4,5]
The syntax extensions present in Cycript will be discussed in subsequent sections of this manual as they become relevent. Most of these extensions add syntax similar to Objective-C++, allowing code to be copy/pasted as much as possible directly into the Cycript console. Other features were added from JavaScript 1.6+ and ECMAScript5+.
JavaScript Minifier
It should also be noted that one of the original goals of Cycript was to be a JavaScript minifier. Cycript is (or at least was) actually quite competitive on this front, providing better output than Yahoo and competitive output to Microsoft. Cycript was even better than Google's Closure Compiler when Closure's riskier advanced features are off.
Partly because of this history, the output you get from Cycript tends to be quite unlike the input you provided, and uses numerous tricks to decrease the resulting code size. Please understand that Cycript's primary goal was "size after gzip", so some of the things it generates (var lists) look verbose, but compress very well.
Honestly, though, the release of Google Closure (which happened while I was working on this) made me stop working on improving these features, so sometimes the output needlessly defines some extra variables, adds an extra set of parentheses, or fails to notice that a variable name should be shortened. Maybe some day ;P.
cy# (function() { var v = "\"" + ((0 + (1)) * (2 * 3)) + "hello" + global; return v; })
cy= (function(){var e;e='"6hello'+global;return e})
(In fact, as I worked on adding ECMAScript 6 support, I laxed even further on this, and currently those nested parentheticals cause optimization barriers, making that example no longer collapse to '"6hello'. I consider this a bug, not a "new normal".)
If you just want to see what Cycript will output for a given file, you can use the -c argument to compile a script to standard output. I do not actually recommend using this for general-purpose websites at this time (although I have done so in the past). If you want a production-grade minifying compiler, I currently recommend Google Closure.
iPhone:~$ echo '5 + 6' >file.cy
iPhone:~$ cycript -c file.cy; echo
11
iPhone:~$
ECMAScript 6 Status
One of my major recent focusses has been on ECMAScript 6 support, to which I will give much thanks to Yehuda Katz (on the ECMAScript 6 standardization committee) for working with me on some corner cases in the specification. It is my current belief that Cycript's grammar fully groks ECMAScript 6.
However, that is not to say that all of the features are implemented! As a simple example, Cycript does not yet support generators. Cycript also does not handle destructuring bind (and "notices" if used in a let binding). If you try to use this syntax, Cycript will throw a syntax error. (These features will be supported, just not yet.)
cy# var a = function *() {}
............^^^^^^^^^^^^^^^
| unimplemented feature
That said, do not assume that nothing will work ;P. I'm just leading with the major parts that aren't there so as to provide realistic expectations; but there are tons of things that do work, and in particular I spent an immense amount of time on the things other parsers get wrong (such as Unicode compliance for identifiers and string literals).
cy# class A {
constructor(value) { this.hello = value; }
world() { return this.hello + 3; } }
function A(t) {var e;e=this;e.hello=t;}
cy# class B extends A {
constructor(value) { super(value); this.test = 6; }
world() { return super.world() + 10; } }
function B(t) {var e;e=this;i.bind(e)(t);e.test=6;}
cy# var b = new B(10)
new B{hello:10,test:6}
cy# b.world()
23
CommonJS Modules
Many libraries for node.js can be imported using require().
Node.js Core Libraries
Cycript ships with the full set of "core" node.js libraries, though while it is now linked against libuv it does not yet provide the internal bindings. This means that this compatibility feature is "hit or miss", and will often miss. One of the next major steps for Cycript will be to improve this story (likely by implementing these bindings in Cycript, not C).
cy# var path = require('path')
cy# path.relative("/a/b/c", "/a/d")
"../../d"
cy# var util = require('util')
cy# util.format('%s:%s', 'foo', 'bar', 'baz');
"foo:bar baz"
ECMAScript 6 Module Syntax
Cycript provides ECMAScript 6 module syntax for import. (The syntax for export is not yet translated.) This works with all existing CommonJS modules. (Note that I currently am not compatible with the horrible hack used by babel.js. The correct solution to their problem is to have require() set module.default = module, or rely on "* as", not fork the module ecosystem :/.)
cy# import {format} from 'util';
cy= var $cy1,format;{$cy1=require("util");format=$cy1.format}
cy# import * as util from 'util';
cy= var $cy1,util;{$cy1=require("util");util=$cy1}
C Types and Pointers
How to interface with code written in C using Cycript syntax.
Type Objects
Code written in C cares a lot about types: you can't access a variable or call a function unless you know its complete type. Cycript thereby has to have a strong model of these types, and it has been a goal to make it easy for C programmers to quickly sit down and use Cycript's type support by way of extensions to JavaScript's syntax.
Types in Cycript are themselves values at runtime, instances of the object class Type. The basic primitive types from C are provided to Cycript. To build more complex types, you can directly use C type encoding syntax using the typedef keyword (which, in JavaScript tradition, can be used as an expression, though only in parentheses).
cy# int
(typedef int)
cy# typedef int *intp;
cy# (typedef const intp)
(typedef int *const)
cy# typedef int (*callback)(double, int[3]); callback
(typedef int (*)(double, int [3]))
Pointers and Casting
To really work with C means working with pointers. Cycript models pointers in JavaScript as objects of type Pointer. To allocate memory, you can use the JavaScript new operator on a Type object, which (like C++) performs a copy-construction. In CYON, a pointer is represented as the address of its value. Memory is garbage collected (I'm sorry).
You can get the runtime type of any value using typeid, which returns a Type object. You can use typeid even on things that are not pointers, though you will only get a result if the value has a type considered to be unambiguous (the main place you find issues with this is with JavaScript's number: it does not have a typeid result).
cy# typeid(3)
cy# p = new int(7)
&7
cy# typeid(p)
(typedef int *)
cy# typeid("hello")
(typedef char const*)
To work with the value being pointed to by the pointer, you can use the * indirection syntax from C. You can also cast between different types of pointer using Type objects as functions. If you need the address represented by a Pointer, you can cast to uintptr_t (or usually use .valueOf()). Numbers can be cast back to pointers using appropriate Type objects.
cy# p = new int
&0
cy# typeid(*p)
(typedef int)
cy# *p = 0x40000000|5<<21
1084227584
cy# *(typedef float *)(p)
5
cy# uintptr_t(p)
(typedef unsigned long)(4298159072)
cy# *@encode(int *)(4298159072)
1084227584
Function Pointers
To call functions from C, you need the address of the function. While many (dare I say, almost all) common Unix functions are already provided for you by Cycript, if you need to access something not provided (such as in a process you are exploiring), you can use extern "C" syntax to declare a prototype for the function. (This can also be an expression.)
cy# extern "C" int getuid();
(extern "C" int getuid())
cy# getuid()
501
Sometimes you will obtain a pointer to a function via some other means, such as dlopen and dlsym (which is provided for you by Cycript, along with constants such as RTLD_DEFAULT). Before you call these functions you will need to cast them to the correct type (either a function pointer, which you may choose to indirect, or a direct C function type).
cy# getuid = dlsym(RTLD_DEFAULT, "getuid")
(typedef void*)(0x7fff885f95b0)
cy# getuid()
throw new Error("cannot call a pointer to non-function")
cy# getuid = (typedef int())(getuid)
(extern "C" int getuid())
cy# getuid()
501
Variadic Functions
Variadic functions are a feature in C where you can pass "extra" arguments to a function. The type of these arguments is determined at the point of the call, and thereby cannot be inferred from the function. As JavaScript does not have different numeric types, a tradeoff was made to prefer int, but with a runtime check for size.
cy# printf("%c %hi %i %s\n", "7".charCodeAt(0), 7, 7, "7")
7 7 7 7
8
cy# NSLog(@"%@ %i\n", @"7", 7)
2016-01-09 21:17:29.895 cycript-apl[94629:507] 7 7
cy# printf("%i\n", 3.4)
throw new Error("could not infer type of argument '3.4'")
Part of the reason this works is that most variadic arguments that are used in random situations are either pointers (which are straightforward to infer), int (which is a sane default), or an integer type smaller than int (such as unsigned short). The C language promotes all types smaller than int to int for variadic function calls.
For types larger than int (such as a long long) or generally incompatible with int (such as a double), you will need to type cast the argument. Note that type casting a value smaller than int in Cycript uses C's type promotion rules and gives you back a type that is simply int. This was actually a forced decision due to Objective-C's BOOL :/.
cy# printf("%lli %g\n", (typedef long long)(7), double(7))
7 7
4
cy# typeid(short(7))
(typedef int)
C++11 Lambda Syntax
Sometimes, you need to be able to create functions on the fly for various reasons. You can do this using the C++11 lambda syntax. (Some people will probably find this syntax strange at first.) Note that Cycript only supports capture clauses that specify "&" for various reasons. The resulting lambda closes over the scope in JavaScript.
cy# qsort
(extern "C" void qsort(void *, unsigned long, unsigned long, int (*)(void const*, void const*)))
cy# var array = (typedef int[3])([6, 1, 3])
[6,1,3]
cy# qsort(array, array.length, int.size, [&](const int *l, const int *r)-> int {
return *l < *r ? -1 : *l > *r ? 1 : 0;
})
cy# array
[1,3,6]
C Structs and Arrays
Sometimes I can't believe this worked as well as it did ;P.
Structures
You won't get far in C before you run into a struct. In C, structs are kept in a separate namespace from functions and other variables. In C++ there is a "fallback" that lets you access structures without saying "struct", but this is not supported by Cycript. (It could work at the global level, but function scope makes that complex.)
The syntax for defining a structure is just like in C: you use the struct keyword, braces, fields with names... the one thing that's worth noting is that the C idiom for using a typedef to an anonymous struct will not only work in a typedef statement but also as part of a typedef expression, and this is how structure types are output as CYON.
cy# struct Test { int x; int y; }
(typedef struct {
int x;
int y;
})
Just like with primitive types, you can allocate a new instance of a struct using the new operator. This returns a pointer. (I have agonized over this particular decision, lying awake for hours in the middle of the night staring at the ceiling, entirely demoralized. I currently believe it to be the correct and consistent decision.)
Field access requires one to indirect the pointer before using field names as properties. Cycript provides the arrow operator from C to make this both easier and more familiar. When you access and set field values on this structure, the values are stored in memory as an actual struct in memory compatible with the struct as used in C.
cy# (struct CGRect)
(typedef struct {
struct CGPoint origin;
struct CGSize size;
})
cy# rect = new (struct CGRect)
&{origin:{x:0,y:0},size:{width:0,height:0}}
cy# r->origin.x = 13
throw new ReferenceError("Can't find variable: r")
cy# rect->origin.x = 13
13
cy# rect->size.height = rect->origin.x
(typedef double)(13)
cy# rect
&{origin:{x:13,y:0},size:{width:0,height:13}}
Whenever you want a value of a structure (such as an initializer to construct with new, or an assignment to an existing structure field) you can use an object with similar structure to the structure instead of using a legitimate value of the structure type. You can also use an array to initialize a structure (and array indexes to access fields).
cy# rect = new (struct CGRect)
&{origin:{x:0,y:0},size:{width:0,height:0}}
cy# rect->origin = [6, 12]
[6,12]
cy# (*rect)[1] = {height: 78, width: 44}
{height:78,width:44}
cy# rect
&{origin:{x:6,y:12},size:{width:44,height:78}}
You can also get the address of a structure (or part of a structure) using the C address-of operator, &. You get back an object of type Pointer. This feature currently does not work for primitive values (I almost fixed it while writing this new manual update, but decided to punt it for a little while longer; if you need it, tell me).
Technically, you can do pointer arithmetic on these pointers, though I don't particularly recommend doing so: this works because .valueOf() on a pointer returns a number, which is required for the built-in math operators. The issue is that this is not a type-aware mechanism, so everything is one byte, but it is useful for demonstration purposes.
cy# rect = new (struct CGRect)
&{origin:{x:0,y:0},size:{width:0,height:0}}
cy# &rect->origin
&{x:0,y:0}
cy# &rect->origin - rect
0
cy# &rect->size - rect
16
cy# &rect->size.width
throw new TypeError("'undefined' is not a function (evaluating 'rect.$cyi.size.width.$cya()')")
Arrays
Arrays in C/C++ are internally inconsistent data types. I have been programming in C++ for 20 years, and yet I was shocked that "new[]" is implemented using type introspection rather than as a syntactic construct. If you try to compile the following code you get an error as "new int[]" returns int *, even though we declared an int(*)[] :/.
#include <iostream>
#include <typeinfo>
template <typename Type_>
void f() {
Type_ *a = new Type_;
}
int main() {
f<int[3]>();
return 0;
}
You are welcome to allocate a new array in Cycript using new on an array type, but it is going to do something you don't want. I also have not implemented the "syntactic" version of this feature yet (though I intend to do so; I only realized this was a serious issue while writing the manual :/). The sane way to allocate an array is using a constructor cast.
cy# (typedef int[3])([2, 4, 6])
[2,4,6]
cy# var x = [6, 7, 8]
[6,7,8]
cy# x = (typedef double[x.length])(x)
[6,7,8]
cy# typeid(x[0])
(typedef double)
C Strings
I had "way too much fun" implementing C strings for Cycript. The key thing to remember is that C++ has three distinct types: signed char, unsigned char, and char; this is different from other integral types, where there are only the signed and unsigned variants. Cycript models characters as one-byte (yes, one "byte") long strings.
cy# (typedef char[3])("dsa")
["d","s","a"]
cy# p = (typedef char *)("dsa")
&"dsa"
cy# p[0]
"d"
cy# typeid(p[0])
(typedef char)
cy# p[0] = "Q"
"Q"
cy# p
&"Qsa"
The memory of a C string bridged from a JavaScript string is garbage collected (I'm sorry). If you get the memory from somewhere else, you will have to free it yourself. To access the underlying memory as a bunch of numbers instead of a bunch of strings you can cast the memory to a pointer to an array (yes, a pointer to an array).
cy# strdup
(extern "C" char *strdup(char const*))
cy# p = (typedef const char *)("dsa")
&"dsa"
cy# p = strdup(p)
&"dsa"
cy# p = (typedef unsigned char (*)[p.length])(p)
&[100,115,97]
cy# p = (typedef struct { char a; char b; char c; } *)(p)
&{a:"d",b:"s",c:"a"}
cy# free(p)
Placement New
In C++, you can allocate an object by using the new operator but passing it the address of an existing block of memory in which to construct the value of the new type. This is the moral companion of being able to type cast a pointer and perform an assignment. I am pretty sure that this is a good thing for Cycript to support.
cy# p = malloc(12)
(typedef void*)(0x100936110)
cy# (typedef signed char (*)[12])(p)
&[0,0,0,0,0,0,0,-48,24,56,9,16]
cy# p = malloc(12)
(typedef void*)(0x1008c8470)
cy# memset(p, 0, 12)
(typedef void*)(0x1008c8470)
cy# (typedef signed char (*)[12])(p)
&[0,0,0,0,0,0,0,0,0,0,0,0]
cy# double.call(p, 31337)
31337
cy# (typedef signed char (*)[12])(p)
&[0,0,0,0,64,-102,-34,64,0,0,0,0]
cy# (typedef double (*)[1])(p)
&[31337]
cy# free(p)
Objective-C (Basics)
Most of the time you are actually dealing with Objective-C objects.
Data Structures
Objective-C syntax generally involves large number of @ symbols. Most commonly, you will see @ before string constants to indicate instances of NSString instead of a raw C string. In recent versions of Objective-C, Apple added special syntax for Objective-C arrays and dictionaries. Cycript supports all of this syntax.
cy# @"hello"
@"hello"
cy# @[2, 4, 5]
@[2,4,5]
cy# @{"thing": 9, "field": 10}
@{"thing":9,"field":10}
cy# @YES
@true
cy# @null
@null
cy# @(5 + 7)
@12
Each of these Objective-C data types is bridged into JavaScript using an appropriate native type in JavaScript. This is done by placing JavaScript classes in the prototype inheritence chain of the Objective-C objects. We can see this in action using the JavaScript instanceof operator. (@null does not have a JavaScript analog.)
cy# @"hello" instanceof String
true
cy# @[2, 4, 5] instanceof Array
true
cy# @{"thing": 9, "field": 10} instanceof Object
true
cy# @YES instanceof Boolean
true
cy# @5 instanceof Number
true
Because of this bridging, it is generally possible to use JavaScript methods on Objective-C types. In particular, developers familiar with JavaScript will find working with arrays and strings very easy, as all of the appropriate JavaScript-specific methods should work. Note that their return values will be JavaScript native.
cy# @"hello".substr(1, 3)
"ell"
cy# @[2, 4, 5, "hello"].slice(1, 3)
[@4,@5]
Cycript also bridges property get syntax (either via square brackets or dot-notation, as these are equivalent in JavaScript) for usage with NSDictionary and NSArray. The JavaScript value of "undefined" is also bridged in a way that makes NSArray values feel "at home" (or maybe simply extra-scary ;P) to the JavaScript developer.
cy# var a = [NSMutableArray arrayWithObjects:[2, 4, 6] count:3]
cy# a.length
3
cy# a[10] = 6; a
@[2,4,5,,,,,,,,6]
cy# a.length = 4; a
@[2,4,5,,]
cy# a[3]
undefined
cy# a[0]
@2
cy# @{"field": 5}["field"]
@5
At a low-level, Objective-C objects are managed by Cycript using objects of type Instance. The @-syntax is implemented by a call to Instance.box, which takes a JavaScript data structure and converts it into one native to Objective-C. Honestly, I encourage you to not think about this while using Cycript, but it helps understand later examples.
cy# @(2 + 7)
cy= Instance.box((9))
@9
cy# @"hello"
cy= Instance.box("hello")
@"hello"
cy# @[2, 4, 5]
cy= Instance.box([2,4,5])
@[2,4,5]
Selectors and Messages
Instead of "calling methods", Objective-C is based on the idea of "sending messages". The syntax for these messages involves a keyword-infix notation for arguments inside of a set of square brackets. The names of messages are called "selectors", and the Objective-C compiler translates these into calls to objc_msgSend. So does Cycript.
cy# ?debug
debug == true
cy# [@"hello" stringByReplacingOccurrencesOfString:@"ell" withString:@"ipp"]
cy= objc_msgSend(Instance.box("hello"),"stringByReplacingOccurrencesOfString:withString:",Instance.box("ell"),Instance.box("ipp"))
@"hippo"
The second argument to objc_msgSend is supposed to be a "selector", which is effectively a C string (but one that is "interned", so there is only one copy and values can thereby be compared by pointer). Cycript automaticallly builds a selector out of the string in this case, but we can also obtain an actual Selector object with @selector.
cy# ?debug
debug == true
cy# @selector(this:is:a:message:)
cy= sel_registerName("this:is:a:message:")
@selector(this:is:a:message:)
In a weird way, a selector is equivalent to a "pointer to member function" type in C++. Due to this analog, it made sense to make Cycript's Selector type derive from JavaScript's Function. This means that you can use .call on a selector to send an arbitrary message to an object (which often feels better than Cycript's objc_msgSend).
cy# var capitalize = @selector(capitalizedString)
cy# capitalize.call(@"hello")
@"Hello"
Clearly, Objective-C selectors can be quite verbose: JavaScript uses "replace", whereas Objective-C uses the complete phrase "string by replacing occurrences of string ... with string ...". Thankfully, Cycript's tab completion works very well for these situations: it evaluates the object at runtime and completes from possible selectors.
If you pass a JavaScript value to an Objective-C message, it will be bridged to some reasonable Objective-C type. Note that this bridge will preserve the identity of the original object when possible and also works for the target of messages including their arguments. This means we can use Objective-C to manipulate JavaScript data.
cy# var a = [2, 4, 6]
[2,4,6]
cy# [a objectAtIndex:0]
@2
cy# [a setObject:"hello" atIndex:2]; a
[2,4,@"hello"]
cy# var o = {field: 4}
{field:4}
cy# [o setObject:a forKey:"value"]; o
{field:4,value:[2,4,@"hello"]}
Classes and Allocation
Objective-C classes are available as variables in Cycript. Classes themselves are a form of object in Objective-C: the fact that you normally need to use the class message to get their value (such as [NSObject class]) is more a limitation of the syntax of C than it is part of the semantics of the runtime environment; Cycript fixes this.
cy# NSObject
NSObject
cy# [NSObject class]
NSObject
cy# objc_getClass("NSObject")
NSObject
In order to allocate an instance of an object, you can use the normal Objective-C paradigm of sending alloc. Objective-C memory management is based on reference counting, and Cycript will keep a reference for the JavaScript VM to the object. However, alloc also returns an object with an incremented reference count: you should autorelease it.
cy# [[[NSObject alloc] init] autorelease]
#"<NSObject: 0x10050e220>"
That said, Cycript provides a more JavaScript-oriented syntax that handles the alloc/autorelease pattern for you: you can use new on a class type, at which point you only have to send the initialization message. Due to the way JavaScript and Objective-C's syntax precedence worked out, you only then need a single set of brackets/parentheses.
cy# [new NSObject init]
#"<NSObject: 0x100409930>"
I must also point out that JavaScript is itself garbage collected. This can cause some confusion with respect to the lifetimes of objects once they have been touched by JavaScript: this ends up causing the lifetime of the objects to last remarkably long (as JavaScript has no real reason to garbage collect often). You can force a collection using ?gc or calling gc().
cy# ?gc
collecting... done.
cy# gc()
cy#
Note that explicit GC will not work for all underlying versions of JavaScriptCore: in April of 2012, WebKit decided the "JSGarbageCollect" API should not actually collect garbage (Bug 84476) and it wasn't until a year later that a new API was added to provide this key functionality (Bug 111088). I sincerely hope they do not change their minds and remove this again.
Description and Field Access
In the previous section, we saw that objects were output as strings prefixed with #. This is because Cycript shows you the object's "description", which all objects in Objective-C are supposed to provide. However, to differentiate these descriptions from actual strings, # is used instead of @. The # represents "an object" in Cycript.
If you have a pointer to an object already (maybe one you got from a debugger, such as gdb, or one you are seeing in a description of an object in the output of a previous command, you can reify the object back into JavaScript using #. Of course, if the object doesn't exist or has been deallocated, this is likely to crash Cycript.
cy# UIApp
#"<SpringBoard: 0x10e803490>"
cy# s = #0x10e803490
#"<SpringBoard: 0x10e803490>"
cy# #0
nil
cy# #4
Segmentation fault: 11
Often, the description of an object is sufficient to understand what it does. Sometimes, however, you need more: what you are really interested in is the contents of the object as represented by its fields. Cycript lets you access the fields of an object using ->. It does this by way of returning a struct representation of the object from the * operator.
cy# c = [[UIApp windows][0] contentView]
#"<UIView: 0x10e883d40; frame = (0 0; 320 568); layer = <CALayer: 0x10e883e00>>"
cy# c->_subviewCache
@[#"<SBFStaticWallpaperView: 0x11459fc40; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x11459ee70>>"]
cy# *c
{isa:"UIView",_layer:#"<CALayer: 0x10e883e00>",_gestureInfo:null,_gestureRecognizers:null,_subviewCache:@[#"<SBFStaticWallpaperView: 0x11459fc40; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x11459ee70>>"],_charge:0,_tag:0,_viewDelegate:null,_backgroundColorSystemColorName:null,_countOfMotionEffectsInSubtree:1,_viewFlags:@error,_retainCount:8,_tintAdjustmentDimmingCount:0,_shouldArchiveUIAppearanceTags:false,_interactionTintColor:null,_layoutEngine:null,_boundsWidthVariable:null,_boundsHeightVariable:null,_minXVariable:null,_minYVariable:null,_internalConstraints:null,_constraintsExceptingSubviewAutoresizingConstraints:null}
Exceptions
As one might expect at this point, Objective-C exceptions are bridged back/forth with JavaScript. You can catch an exception in JavaScript that was thrown by Objective-C, or allow it to throw all the way to the console, where it will be rendered in some hopefully-useful fashion. Cycript's own C++ exceptions are also bridged.
cy# var a; try { [[NSMutableArray array] setObject:nil atIndex:0]; } catch (e) { a = e; throw e; }
throw #"*** -[__NSArrayM setObject:atIndex:]: object cannot be nil"
cy# a
#"*** -[__NSArrayM setObject:atIndex:]: object cannot be nil"
In some cases, you will find yourself in situations where your code works fine, but the console itself caught an exception while printing an object. In these situations, you will see "@error" printed to the output. This happens most often with relation to data types Cycript doesn't support very well, such as bitfields (as in the case above with _viewFlags).
Objective-C (Advanced)
Some of this stuff is more likely to be limited in scope or likely to change.
@property Support
In addition to fields (which Objective-C actually calls "instance variables" or "ivars"), Objective-C has a notion of "properties". The Objective-C compiler essentially implements this as a syntax tranformation where using dot-notation to access a member of an object pointer is translated using a getter/setter naming convention into Objective-C messages.
However, developers can also define explicit properties on their classes using a syntax with @property, which is information available at runtime. This allows one to differentiate between nouns (where property syntax makes a lot of sense, and is now supported in ECMAScript 5 with getters/setters) and verbs (where property syntax is almost "dangerous").
Cycript used to implement the behavior of the Objective-C compiler. Part of the reason for this is that Apple's own frameworks often have @property definitions in their "public header files", but Apple is clearly not actually using property syntax internally, so their compiled binaries often are missing all of the property information for use at runtime.
However, Apple has recently been pushing developers on Swift, a new programming language which they also do not seem to be using internally ;P. For Swift, they really wanted to fix this noun/verb issue, and so had to go back and "fix" all their old libraries to have the requisite @property definitions internally. Swift now requires you to "call" verbs.
One of my todo list items for Cycript has been better integration with Swift, and I've never liked implicit properties anyway, so Cycript no longer supports them. That said, for consistency with the existing code that is shipping in products using WebCycript (such as the wide number of Cydgets in Cydia), I have left this feature on inside Cydget contexts.
I have also gone out of my way to make the muscle memory for accessing these properties reasonably compatible, which turns out to also work well with the Swift command line environment: if you try to tab complete a verb that takes no arguments, you are completed all the way through the (). (One day I will make "("-completion work for JavaScript functions.)
That said, I am torn, and am interested in arguments people have for and against supporting implicit properties everywhere. In the mean time, I've also turned on implicit properties on runtimes where there are so few @property definitions that even basic things like using .description on an NSObject doesn't work. (But keep in mind: auto-verb is confusing in JS.)
UIApplication.sharedApplication().windows[0].contentView().subviews()[0]
#"<SBFStaticWallpaperView: 0x1590ca730; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x1590cabd0>>"
Prototypes and Messages
In JavaScript, all objects have a constructor property that references the constructor function that was used to create the object. This constructor function then has a prototype property that can be used to extend the functionality of all instances of that class. Cycript models this behavior correctly for Objective-C objects.
cy# @6..constructor
__NSCFNumber
cy# __NSCFNumber.superclass()
NSNumber
cy# NSNumber.prototype.add5 = function() { return this + 5; }
cy# @6..add5()
11
As Objective-C's class system actually comes from the same heritage as JavaScript's (via Smalltalk from Self), it made sense to expose the Objective-C message table as integrated into the JavaScript prototype chain. If you replace an existing message or copy a message from another object, the JavaScript is exposed to Objective-C.
cy# var oldm = NSObject.prototype.description
(extern "C" id ":description"(id, SEL))
cy# NSObject.prototype.description = function() { return oldm.call(this) + ' (of doom)'; }
cy# [new NSObject init]
#"<NSObject: 0x100d11520> (of doom)"
This functionality was previously exposed using a separate namespace: instead of "prototype", you had to use "messages". This is no longer the case.
Categories and Classes
In Objective-C, you can have a "category", which is a set of replacement or added message implementations for a given class. This syntax is supported with Cycript. For replaced methods, you can leave off the type signatures, as Cycript can infer them from the existing class. For any messages you add, you will need to specify their exact types.
cy# @implementation NSObject (MyCategory)
- description { return "hello"; }
- (double) f:(int)v { return v * 0.5; }
@end
cy# o = [new NSObject init]
#"hello"
cy# [o f:3]
1.5
Fully-fledged Objective-C classes can be declared using @implementation, which also replaces Objective-C's @interface (this actually works in Objective-C ;P). You can even declare instance variables using C type syntax (as the types of those matter greatly). If you look at how this syntax desugars to JavaScript, it calls documented Objective-C runtime functions.
cy# ?debug
debug == true
cy# @implementation A : NSObject {
int field;
}
- (int) message { return this->field; }
@end
cy= (function(s,r,i,n,t,e){r=object_getClass(s);i=objc_allocateClassPair(s,"A",0);e=object_getClass(i);{t=int.toString();n=new Type(t);class_addIvar(i,"field",n.size,n.alignment,t)}{n=sel_registerName("message");t=int.toString()+"@:";class_addMethod(i,n,new Functor(function(n,t){var e;e=new objc_super(n,s);return function(){var e;e=this;return e.$cyi.field}.call(n)},t),t)}objc_registerClassPair(i);return i})(NSObject)
A
cy# ?debug
debug == false
cy# a = [new A init]
#"<A: 0x10080c540>"
cy# a->field = 10
10
cy# [a message]
10
Blocks
Rather than raw function pointers, Objective-C lambda expressions are passed around as a special form of object called a "block". Cycript supports both creating and calling blocks, and has special syntax similar to Objective-C's to make it familiar. Unlike with Objective-C, the return type of the block is not optional and must be specified.
cy# block = ^ int (int value) { return value + 5; }
^int(int){}
cy# block(10)
15
Java (Initial Preview)
Cycript now supports Java and runs on Android.
Basic Syntax
In Java, classes are syntactically incompatible with objects that represent classes; Cycript thereby maintains this distinction. Classes are organized into namespaces called "packages", which are accessible using a top-level Packages variable (similar to Rhino; Nashorn's Java.type is not yet supported, but will be soon). Common prefixes are also provided.
Creating an instance of a class uses the new operator. Method calls use dot-notation. The main consideration one must have while working with Java is that functions can be "overloaded" by the type of their parameters. Cycript builds overload resolution sets and will try to find a compatible function. Using type casts can help guide the implementation.
cy# Packages.java.util.HashMap
java.util.HashMap
cy# hash = new java.util.HashMap
#"{}"
cy# hash.put("hello", 7)
null
cy# hash.put("world", int(7))
null
cy# hash
#"{world=7, hello=7.0}"
Java has a separate namespace for field access from methods. To model this correctly in Cycript, the arrow operator has been borrowed from Objective-C. To get a quick glimpse of the field values of an object you can thereby use the pointer indirection operator from C. Otherwise, a Java object normally renders using the result of toString with #.
cy# hash = new java.util.HashMap
#"{}"
cy# *hash
{entrySet:#"[]",keySet:null,loadFactor:0.75,modCount:0,size:0,table:null,threshold:0,values:null}
cy# hash->loadFactor
0.75
Dynamic Proxies
Any remotely serious work in Java requires the ability to implement interfaces. Java 1.3 supports a feature called "dynamic proxies" that allows an "invocation handler" to proxy an interface for another object. This feature is exposed in Cycript as a constructor argument on an interface. Here is an example of setting up a webserver using Jetty.
cy# var server = new org.eclipse.jetty.server.Server;
2016-01-09 22:34:20.785:INFO::main: Logging initialized @546ms
#"org.eclipse.jetty.server.Server@b81eda8"
cy# var connector = new org.eclipse.jetty.server.ServerConnector(server);
#"ServerConnector@50134894{HTTP/1.1,[http/1.1]}{0.0.0.0:0}"
cy# connector.setPort(8080);
cy# server.addConnector(connector);
cy# server.setHandler(new org.eclipse.jetty.server.Handler({
isRunning() { return false; }, // XXX: o_O
handle(target, base, request, response) {
response.setStatus(200);
response.setContentType("text/plain; charset=utf-8");
var writer = response.getWriter();
writer.print([new NSObject init].toString());
base.setHandled(true);
},
}));
cy# connector.open();
cy# server.start();
2016-01-09 22:35:04.137:INFO:oejs.Server:main: jetty-9.3.6.v20151106
2016-01-09 22:35:04.179:INFO:oejs.ServerConnector:main: Started ServerConnector@50134894{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2016-01-09 22:35:04.179:INFO:oejs.Server:main: Started @43971ms
$ curl http://localhost:8080/; echo
<NSObject: 0x100e22620>
This example was actually made slightly more frustrating, as Jetty expects you to subclass an abstract implementation of the Handler interface rather than implement the interface from scratch. At this time, Cycript does not support dynamic subclassing (which requires runtime class file generation), but could do so in a future version using a different technique.
Android Support
With support for Java, Cycript is now useful on Android. It has always supported desktop Linux, but some more work was needed to make Cycript operate well on Android. Running Cycript from the console sets up a complete environment with access to all of the Android runtime libraries. However, process injection is not yet supported (need to update cynject).
Note that the build of libJavaScriptCore.so I shipped in this initial release is from Ejecta-X because I just don't have it in me to figure out how to compile JavaScriptCore for Android before releasing this new build of Cycript, as I've already spent a month longer than I thought I might :/. I will get around to a better build of JavaScriptCore soon.
shell@flounder:/data/local/tmp $ ./cycript
WARNING: linker: libJavaScriptCore.so has text relocations. This is wasting memory and prevents security hardening. Please fix.
cy# android.os.Looper.prepare()
cy# var activity = new android.app.Activity
#"android.app.Activity@13538a68"
Note that there are currently other limitations in the Java bridge. I am super-excited to get this working better, and want the result to be every bit as amazing as Cycript for Objective-C, but I also decided that it was already useful enough to start getting feedback, so I made the executive decision to release this now. Come on IRC and tell me your experiences!
Object Queries
How to find an instance of an object by filtering the set of all objects.
The Axiom of Choice
One of the more common questions people first picking up Cycript ask is "how do I find an instance of a class so I can send a message to it?". This question seems reasonable: you want to learn how a class of objects work, and to do so you need an instance to play with... you know there are tons of them there, and you just want one of them.
However, objects don't really work that way: there is no list anywhere that is kept of all instances of a particular class. To do this, you'd have to hook the class's allocation and initialization routines before any instances are created, and then keep track of them as they are created to some data structure you could later query.
Because of this, the response has always been "that isn't possible". This is true: there is no way to solve this problem in the general case, as that information simply does not exist in the runtime of most languages, including Objective-C. But, it turns out that you can come pretty close often enough to make this work for purposes of debugging.
choose(Class)
The way you can go about this is by using a function that has been added to Cycript called "choose". This function takes an Objective-C class as an argument, and attempts to scavenge the heap looking for regions of memory that are the right size and shape as the class (or any subclasses of that class) you have specified.
cy# choose(SBIconModel)
[#"<SBIconModel: 0x1590c8430>"]
Sometimes this will crash, but often it will work. The result is an array, because there might be multiple instances that are found. In some cases, this array might be very large. If you are trying to find the view for an icon in SpringBoard, for example, you might get back hundreds of objects. Luckily, JavaScript is a programming language.
cy# var views = choose(SBIconView)
[#"<SBIconView: 0x159460fa0; frame = (27 92; 60 74); opaque = NO; gestureRecognizers = <NSArray: 0x159518ae0>; layer = <CALayer: 0x159461220>>",#"<SBIconView: 0x159468e50; frame = (114 356; 60 74); opaque = NO; gestureRecognizers = <NSArray: 0x15946d2f0>; layer = <CALayer: 0x1592c9a70>>",...
cy# for (var view of views) if ([[[view icon] application] displayName] == "Photos") photos = view; photos;
#"<SBIconView: 0x15fc75e90; frame = (201 4; 60 74); opaque = NO; gestureRecognizers = <NSArray: 0x15fbfacc0>; layer = <CALayer: 0x15fc76110>>"
If you want to get back multiple results, you can create an array, and then push all of the matching results. This, however, can get really irritating to do every time you want to add a filter. Luckily, JavaScript 1.7 has a feature called array comprehensions that can be used like a miniature query language to filter arrays of objects.
Additionally, Cycript has adopted the ?. syntax (as seen in various languages, including Swift) to provide functionality at the JavaScript level similar to how Objective-C allows any message (with a compatible return type) to be sent to the nil object, and the result will be nil. Calls and member access using ?. on null/undefined passes the null/undefined back.
cy# [for (view of views) if (view.icon?.application()?.displayName() == "Photos") view]
[#"<SBIconView: 0x15fc75e90; frame = (201 4; 60 74); opaque = NO; gestureRecognizers = <NSArray: 0x15fbfacc0>; layer = <CALayer: 0x15fc76110>>"]
Process Injection
Most usage of Cycript is actually working inside of other processes.
# cycript -p
While playing around in the local console can be a great learning experience for Objective-C itself, or even the usage of specific libraries, most of the real-world usage of Cycript involves learning about how existing applications are built, and that is usually best done (or even "only possible") by manipulating the process "from the inside".
To inject into another process, you can use cycript's -p flag. You can either pass the process id of another process you would like to target, or you can pass the name of a running program. If you pass a name, Cycript runs ps and attempts to find that process's identifier. Cycript might fail at this, but almost all of the time this works great.
You are then presented with an instance of the Cycript console where all commands will be sent to the remote process, executed in a JavaScript instance running inside of that process that has access to Cycript's bridge support, with any results sent back to your local console. This VM instance is shared between multiple consoles.
iPhone:~# cycript -p SpringBoard
cy# UIApp #"<SpringBoard: 0x10ea05f60>"
Limitations
Cycript performs this injection by throwing its library into the other process by remote thread injection. This isn't always possible. In particular, your user needs permissions to modify the other process. This often simply requires you to be root. If you attempt to use Cycript to inject into a process you can't access, you will get an error calling task_for_pid.
iPhone:~$ cycript -p SpringBoard *** _krncall(task_for_pid(self, pid, &task)):../Mach/Inject.cpp(65):InjectLibrary [return=0x5] iPhone:~$ sudo cycript -p SpringBoard
cy#
If Cycript successfully gets into the other process, there is no guarantee that it will succeed in loading its library. This is actually a fairly common occurence on very recent versions of Mac OS X due to the sandbox. In these cases, Cycript will try to get you an error message, and you can consult /var/log/system.log for more information.
MacBook:~$ sudo ./cycript -p Calculator
dlopen(/Users/saurik/Cycript.lib/libcycript-any.dylib, 5): no suitable image found. Did find:
/Users/saurik/Cycript.lib/libcycript-any.dylib: open() failed with errno=1
MacBook:~$ tail -n 100 /var/log/system.log | grep Calculator
Jan 21 09:12:34 Jays-MacBook-Air.local sandboxd[22293] ([22284]): Calculator(22284) deny file-read-data /Users/saurik/cycript/Cycript.lib/libcycript-any.dylib
To deal with the sandbox issues opening libraries, it is normally sufficient to simply install Cycript to /usr. To do this, place Cycript's dylib files into /usr/lib and Cycript's Cycript.lib/cycript binary as /usr/bin/cycript. You might also want to copy the Cycript modules directory (cycript0.9) to /usr/lib. Even from /usr, however, Cycript doesn't always work.
MacBook:~$ sudo cp -a Cycript.lib/*.dylib /usr/lib
MacBook:~$ sudo cp -a Cycript.lib/cycript-apl /usr/bin/cycript
MacBook:~$ sudo cycript -p Calculator
^C
MacBook:~$ tail -n 100 /var/log/system.log | grep Calculator
Jan 21 09:14:40 Jays-MacBook-Air.local sandboxd[22293] ([22284]): Calculator(22284) deny network-outbound /private/tmp/.s.cy.27221
In this case, the Calculator application is not allowed to connect back using the Unix domain socket allocated by the Cycript console. There are probably other ways of going about this, but I haven't had a chance to look into this much yet. You should not, however, run into any problems on either iOS or injecting into the iOS Simulator on Mac OS X.
iOS Static Library
You can embed Cycript into your app for debugging on un-jailbroken devices.
I talked about this at 360|iDev. Developers can watch the end of that talk to learn how to do this. (I also intend to add the documentation here, but I'm punting this for now.)
Cydia Substrate
Substrate is a more reasonable way of modifying runtime behavior than swizzling.
Substrate is a separate framework I provide, as mentioned at the beginning of this manual. In many ways, it has nothing to do with Cycript, and yet users tend to rapidly switch back/forth between them. In the latest release of Cycript, I've provided a module that helps you use more of Substrate's power from Cycript.
Learning to use Substrate itself is an entirely separate experience, and has its own website full of documentation. All I will be documenting here are the couple bits of Substrate I have exposed via the Cyript module. (In the future, this support might be documented by Substrate, not Cycript; the module might even come with Substrate.)
MS.hookMessage
When "swizzling" message implementations, there are interesting corner-cases that have to be dealt with if you mess with objects that did not already have the message in question (and only inherited it from a superclass). Substrate solves these problems, as well as making certain the class does not get "initialized". This calls through to MSHookMessageEx.
cy# @import com.saurik.substrate.MS
cy# var oldm = {};
cy# MS.hookMessage(NSObject, @selector(description), function() {
return oldm->call(this) + " (of doom)";
}, oldm)
cy# [new NSObject init]
#"<NSObject: 0x100203d10> (of doom)"
MS.hookFunction
Substrate also allows you to modify the behavior of C functions. As Substrate already can get most of the type signature information available from the function itself being hooked, you only need to pass the replacement function. You capture the original value as a simulation of a function pointer. This calls through to MSHookFunction.
cy# @import com.saurik.substrate.MS
cy# extern "C" void *fopen(char *, char *);
cy# var oldf = {}
cy# var log = []
cy# MS.hookFunction(fopen, function(path, mode) {
var file = (*oldf)(path, mode);
log.push([path.toString(), mode.toString(), file]);
return file;
}, oldf)
cy# fopen("/etc/passwd", "r");
(typedef void*)(0x7fff774ff2a0)
cy# log
[["/etc/passwd","r",(typedef void*)(0x7fff774ff2a0)]]
What Next?
Users who actually managed to read the entire manual might now feel anxious.
Recommendation
Download the iOS SDK from Apple and start trying to make cool modifications. If you need help learning about how Apple's code is put together, I recommend watching a few of the talks from this year's WWJC (World Wide JailbreakCon), starting with this talk by Adam Bell that uses Cycript. You might come up with something awesome!