by Jay Freeman (saurik)

Answers to issues people have before even using Cycript.

What and why?

Cycript is an interactive console that can be attached to running programs on iOS or Mac OS X. Developers can explore and even modify the running program using a hybrid of Objective-C++ and JavaScript syntax. The console has been designed with features like syntax highlighting and tab completion to make it more pleasant to use.

The result is a tool that most of the developers in iOS "jailbreak" community use to discover features and learn about the programs they intend to later modify using Substrate (a separate framework I provide for doing efficient and reasonably safe runtime code modification; Cycript can sometimes be thought of as a dynamic version of Substrate).

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.

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.)

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)
34359738368
cy# var a 3
..........^
  | syntax error, unexpected NumericLiteral, expecting ;
cy# function f(a) {
cy>    a = a + 5;
cy>    return ^C
cy# ^D
iPhone:~$ cycript

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

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).

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 I guess stands for "Cycript Object Notation" (or so says the iPhone Dev Wiki). 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]"

Sometimes, this is not what you actually want: when printing UIView's "recursiveDescription", you get back a massive string constant that would be much easier to understand if it were expanded as the string value to the console. You can use ?expand to toggle the behavior of whether the console attempts to print string syntax or string values.

cy# "hello\nthere"
"hello\nthere"
cy# ?expand
expand == true
cy# "hello\nthere"
"hello
there"

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 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 has improved 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, Mozilla pushed a new for-each-in feature with JavaScript 1.6. 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 there's a bug).

cy# var after = [];
[]
cy# ?debug
debug == true
cy# for each (var i in before) after.push(i)
cy= (function($cys,$cyt){for($cyt in $cys){i=$cys[$cyt];after.push(i)}})((before))
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})

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 recommend Google Closure.

iPhone:~$ echo '5 + 6' >file.cy
iPhone:~$ cycript -c file.cy; echo
11
iPhone:~$ 

How to interface with code written in Objective-C using Cycript syntax.

Types (typedef, @encode)

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. You can manually construct an instance of Type using Objective-C type encoding. Some types, in particular function pointers, have useless encodings, so I encourage you to not bother and instead use Cycript's native support for C type syntax when possible.

The way Cycript thinks about types is in terms of either the C typedef statement or the Objective-C @encode expression. Each of these is desugared to a JavaScript expression involving nested members of the Type objects. Type objects are themselves represented using @encode syntax in CYON. You thereby never have to work with encodings.

cy# new Type("^L")
@encode(unsigned long int*)
cy# ?debug
debug == true
cy# q = @encode(int (*)(int))
cy= q=int.functionWith(int).pointerTo()
@encode(int(*)(int))
cy# typedef q b[3]
cy= b=q.arrayOf(3)
@encode(int(*[3])(int))

In Objective-C, @encode returns a string. In Cycript, @encode returns an instance of Type. However, instances of Type implement .toString() and return their type using Objective-C type encoding syntax. If you concatenate Type objects using + or other operators that expect primitive values, you often get results you might expect.

cy# int.toString()
"i"
cy# @encode(int[3]).toString()
"[3i]"
cy# @encode(int(*)()).toString()
"^?"

Pointers and Casting

To really work with C means working with pointers. Cycript models pointers in JavaScript as objects of type Pointer. You can get the type of a Pointer using its type field. To allocate memory, you can use the JavaScript new operator on a Type object. In CYON, a pointer is represented as the address of its value. You need to free memory you allocate.

cy# p = new int
&0
cy# p.type
@encode(int)
cy# free(p)
cy#

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 use .valueOf(). Numbers can be cast to pointers using appropriate Type objects.

cy# p = new int
&0
cy# *p = 0x40a00000
1084227584
cy# *@encode(float *)(p)
5
cy# p.valueOf()
4298159072
cy# *@encode(int *)(4298159072)
1084227584

Function Pointers

To call functions from C, you need the address of the function. The most common way to do this is to use dlopen and dlsym. These functions are already exported for you by Cycript as part of its default bridge library. Constants, such as RTLD_DEFAULT, are also already available. The values you get back, however, will simply be void pointers.

cy# getuid = dlsym(RTLD_DEFAULT, "getuid")
0x7fff8a9225b0
cy# getuid.type
@encode(void)
cy# getuid()
TypeError: '[object Pointer]' is not a function (evaluating 'getuid()')

In order to use the values, we first need to provide a type signature, at which point instead of a Pointer you will have a Functor. You can do this either by casting to the function pointer type, which you can then indirect, or directly to the function type (as in, a type specification using C syntax that looks like a prototype).

cy# getuid = @encode(int())(dlsym(RTLD_DEFAULT, "getuid"))
0x7fff8a9225b0
cy# getuid.type
@encode(int())
cy# getuid()
501

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. (Many 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 = dlsym(RTLD_DEFAULT, "qsort")
0x7fff8d73d99f
cy# typedef long int size_t;
cy# typedef int (*comparator)(const void *, const void *)
cy# qsort = @encode(void (void *, size_t, size_t, comparator))(qsort)
0x7fff8d73d99f
cy# var array = new @encode(int[3])
[0,0,0]
cy# array[0] = 6; array[1] = 1; array[2] = 3; array
[6,1,3]
cy# qsort(array, array.length, int.size, [&](const int *l, const int *r)-> int {
cy>     return *l < *r ? -1 : *l > *r ? 1 : 0;
cy> })
cy# array
[1,3,6]

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 = @[2, 4, 5]
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"),sel_registerName("stringByReplacingOccurrencesOfString:withString:"),Instance.box("ell"),Instance.box("ipp"))
@"hippo"

In this case, sel_registerName is the low-level Objective-C runtime function that returns an Objective-C selector. (A selector, by the way, is simply an "interned" C string: as in, a C string for which there is a single copy somewhere so that values can be compared for equality using only their pointer.) To get one, you use @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>"

When you use the new syntax, what you get back is a special form of Instance that has been marked "uninitialized". When you send it a message, its value will be cleared to nil, as you should now be using the return value from the initializer (which is allowed in Objective-C to return an entirely unrelated object; NSDictionary actually does this).

cy# d = new NSDictionary
*** -[NSDictionary objectForKey:]: method sent to an uninitialized immutable dictionary object
cy# [d init]
@{}
cy# d
nil

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.

Properties, Fields, Descriptions

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}

In addition to fields (which Objective-C actually calls "instance variables" or "ivars"), Objective-C has a notion of "properties". These are really nothing more than Objective-C messages following a specific naming convention, but users of Objective-C 2.0 are now used to being able to use dot-notation. Cycript supports this whenever possible.

cy# UIApp.windows[0].contentView.subviews[0]
#"<SBFStaticWallpaperView: 0x11459fc40; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x11459ee70>>"

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 { [@[0] setObject:nil atIndex:0]; } catch (e) { a = e; throw e; }
*** -[__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).

Some of this stuff is more likely to be limited in scope or likely to change.

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, it made sense to expose similar functionality to the prototype chain for Objective-C message implementations. As this is a separate namespace from properties, it is a separate object: a messages property on Classes with entries for each selector.

cy# NSObject.messages
{classForArchiver:0x96b90d24,"replacementObjectForArchiver:":0x96bc74a6,classDescription:0x96bfb84d,attributeKeys:0x96bfb889,toOneRelationshipKeys:0x96bfb8c3,toManyRelationshipKeys:0x96bfb8fd,"inverseForRelationshipKey:":0x96bfb937,_scriptingCount:0x96c03c2f,"_scriptingCountOfValueForKey:":0x96c03c39,_scriptingCountNonrecursively:0x96c03cfc,"_scriptingRemoveValueForSpecifier:":0x96c12cd1,"_oldValueForKeyPath:":0x96c1398c,"takeValue:forKeyPath:":0x96c13a40,"valuesForKeys:":0x96c13b20,"takeValuesFromDictionary:":0x96c13c39,"handleQueryWithUnboundKey:":0x96c13d1a,"handleTakeValue:forUnboundKey:":0x96c13ef0,"unableToSetNilForKey:":0x96c13fd0,"_oldValueForKey:":0x96c1403b,"storedValueForKey:":...

This object can be useful to get a quick programmatic glimpse of all of the messages supported by an object (if nothing else, it is faster to use than the Objective-C runtime functions). However, the real purpose of this object is to allow for modification and extension of Objective-C objects, so let's do a demonstration of that.

cy# var oldm = NSObject.messages[@selector(description)]
cy# NSObject.messages[@selector(description)] = function() { return oldm.call(this) + ' (of doom)'; }
cy# [new NSObject init]
#"<NSObject: 0x66fca0> (of doom)"

If you find yourself in need of the type signature of one of these messages, you can get it using the type property of a Message object. (This actually returns the type of the underlying Objective-C message implementation that backs this object.) You can also use the type member function of Selector to query types from Classes.

cy# NSObject.messages[@selector(description)].type
@encode(id(id,SEL))
cy# @selector(description).type(NSObject)
@encode(id(id,SEL))

Structures

Technically, structures should be described under the section for the C bridge support, but due to current limitations in Cycript's support for C structs, you mostly come across them while working with Objective-C objects that return things like points, sizes, and rects. To begin with: yes, Cycript supports structs fairly well.

cy# UIApp.windows[0].frame
{origin:{x:0,y:0},size:{width:320,height:568}}

When possible, as in this example, Cycript renders the structure as an object with named properties for its fields. However, you can also access the fields using numeric index properties. This bridge works in both directions: you can use either an array or an object when you need a structure, and Cycript will convert it as required.

cy# var window = UIApp.windows[0]
cy# window.frame
{origin:{x:0,y:0},size:{width:320,height:568}}
cy# window.frame = [[1, 1],{width:2,height:2}]
cy# window.frame
{origin:{x:1,y:1},size:{width:2,height:2}}

When Cycript gets a structure, it allocates it on the heap and returns an object of type Struct that represents that object. When you access members of that structure, you are referring to parts of the original value. You thereby can operate on parts of the structure as you'd expect using JavaScript nested property syntax.

cy# var frame = UIApp.windows[0].frame
cy# frame.origin.x = 30
cy# var size = frame.size
cy# size.height = 56
cy# frame
{origin:{x:30,y:1},size:{width:2,height:56}}

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, as described earlier in this manual. The way a Pointer to a Struct is rendered is an ampersand followed by the rendering of the struct itself. This feature currently does not work for primitive values (I might fix this somehow).

cy# var frame = UIApp.windows[0].frame
cy# &frame
&{origin:{x:1,y:1},size:{width:2,height:2}}
cy# (&frame).valueOf()
4651591004
cy# (&frame.size).valueOf()
4651591020
cy# (&frame.size).type
@encode(CGSize)
cy# &frame.size.width
TypeError: 'undefined' is not a function (evaluating 'frame.size.width.$cya()')

Just as when sending messages, you can assign objects or arrays to the members of structures, and they will be converted in some reasonable manner. If you assign a part of some other struct to a different one, the values will be copied. (Note that I don't think there is currently a particularly good way to copy a structure in the general case.)

cy# frame1 = UIApp.windows[0].frame
{origin:{x:1,y:1},size:{width:2,height:2}}
cy# frame2 = UIApp.windows[1].frame
{origin:{x:0,y:0},size:{width:320,height:568}}
cy# frame1.size = frame2.size
{width:320,height:568}
cy# frame1
{origin:{x:1,y:1},size:{width:320,height:568}}

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)
cy> - description { return "hello"; }
cy> - (double) f:(int)v  { return v * 0.5; }
cy> @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 takes on functionality of Objective-C's @interface. 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, you will find that it simply calls Objective-C runtime functions.

cy# ?debug
debug == true
cy# @implementation A : NSObject {
cy>     int field;
cy> }
cy> - (int) message { return this->field; }
cy> @end
cy= (function(s,e,t,n,i,r){var e,t,n,i,r,s;e=object_getClass(s),t=objc_allocateClassPair(s,"A",0),r=object_getClass(t);i=int.toString(),n=new Type(i),class_addIvar(t,"field",n.size,n.alignment,i);n=sel_registerName("message"),i=int.toString()+"@:",class_addMethod(t,n,new Functor(function(e,t){var e,t,n;n=new objc_super(e,s);return function(){var $cy0=this;return $cy0.$cyi.field}.call(e)},i),i);objc_registerClassPair(t);return t})(NSObject)
#"A"
cy# ?debug
debug == false
cy# a = [new A init]
#"hello"
cy# a->field = 10
10
cy# [a message]
10

Cycript also supports anonymous classes as expressions. The syntax for this is somewhat hilarious, frankly. I imagine there are some cool use cases for it, though. That said, it must be pointed out that once a class is created it cannot ever be destroyed, so you don't want to even think of using this inside of a tight loop (or most any loop) ;P.

cy# [new @implementation : NSObject {} - description { return "yay!"; } @end init]
#"yay!"

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; }
#"<__NSMallocBlock__: 0x1005220a0>"
cy# block(10)
15

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: 0x114621970>"]

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: 0x115582e80; frame = (16 5; 60 74); opaque = NO; layer = <CALayer: 0x115582260>>",#"<SBIconView: 0x115587330; frame = (92 5; 60 74); opaque = NO; layer = <CALayer: 0x1155874e0>>",...
cy# for (var view of views) if (view.icon.displayName == "Photos") photos = view;
cy# photos
#"<SBIconView: 0x115582e80; frame = (16 5; 60 74); opaque = NO; layer = <CALayer: 0x115582260>>"

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.

cy# [view for (view of choose(SBIconView)) if (!/^com\.apple\./.test(view.icon.applicationBundleID))]
[#"<SBIconView: 0x115552630; frame = (168 93; 60 74); opaque = NO; layer = <CALayer: 0x1155527e0>>"]

Functionality from libraries can be used with either require() or @import.

require() -> @import

This is under-documented; thankfully, CommonJS has its own documentation, and is used by Node.js, so there are tons of examples. Note that right now modules are not loaded into separate contexts, but they will be in the future. I mapped require() to Objective-C semantic import by mapping periods to slashes and taking the final component as the assignment target.

cy# ?debug
debug == true
cy# @import com.saurik.substrate.MS
cy= MS=require("com/saurik/substrate/MS")

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 /usr/bin
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.

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.)

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() {
cy>     return oldm->call(this) + " (of doom)";
cy> }, oldm)
cy# [new NSObject init]
#"<NSObject: 0x100203d10> (of doom)"

MS.hookFunction

Substrate also allows you to modify the behvior 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# fopen = dlsym(RTLD_DEFAULT, "fopen")
0x7fff8d71b58c
cy# fopen = @encode(void *(char *, char *))(fopen)
0x7fff8d71b58c
cy# var oldf = {}
cy# var log = []
cy# MS.hookFunction(fopen, function(path, mode) {
cy>     var file = (*oldf)(path, mode);
cy>     log.push([path, mode, file]);
cy>     return file;
cy> }, oldf)
cy# fopen("/etc/passwd", "r");
0x7fff72c14280
cy# log
[["/etc/passwd","r",0x7fff72c14280]]

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!