What is Cycript

From official documents: Cycript is a hybrid of ECMAScript Some6, 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”.

Clear, 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.

In short: Cycript is a scripting language developed by Cydia founder Saurik that combines ECMAScript 6.0 (ES6), Objective-C ++, and Java syntactic interpreters. This means we can use OC or JavaScript, or both, in one command. Cycript is currently primarily used for working backwards on iOS, using Cydia Substrate to inject running processes (similar to debuggers), making it an ideal tool for “exploration”.

How to use

1. Install

Download the SDK locally. In order to use it directly each time, it is recommended to configure the path of the executable file cycript into the environment variable (export in.bash_profile/.zshrc [depending on which terminal you are using]), open the terminal, Execute the cycript command:

As shown above, the cy# prompt indicates that you are in the JavaScript console. Everything you type will be run by JavaScriptCore, Apple’s implementation of the JavaScript language used by Safari. And as you type, your commands will be syntactically highlighted using Cycript’s lexical analyzer and will be prompted if a syntax error occurs. You can press CTRL +C to cancel typing, or CTRL +D to exit the environment.

2. Use

Preheat preparation

As for the use of Cycript, this article will focus only on iOS reverse engineering, which is currently the most widely used area of Cycript. In contrast to the LLDB mentioned in the previous article, Cycript can be dynamically injected, and the values of objects in the program can be retrieved and modified at run time. LLDB can only perform breakpoint static debugging analysis in both forward development and reverse engineering, which is obviously less efficient than Cycript.

In order to implement dynamic debugging with Cycript, your application has a port that can be connected to it. Since not everyone has a jailbreak machine, this article mainly introduces how to use Cycript for debugging in a non-jailbreak environment, so that everyone can have practical conditions. There is one more tool we need to prepare before we start using Cycript.

In a series of articles over the past two months, I’ve covered iOS app signing, auto-re-signing scripts, and code injection, all of which are integrated with tools that, as you might have guessed, do. This tool is MonkeyDev —- the original iOSOpenDev upgrade, non-jailbreak plug-in development integration magic! The installation of MonkeyDev will not be described here. After the successful installation, create a new MonkeyApp project (MonkeyDevDemo) :

To open MonkeyDevDemo, simply drop the IPA /app you want to debug (unpacking is still necessary) into the directory of the new project:

Run the project to run the application directly to your real machine:

In the figure above, CYListenServer(6666) marked in red box; This is the premise of implementing dynamic debugging in Cycript: a remote port number, 6666, can also be found in the console:

I’m sure you noticed this line in the log:

The Download cycript (https://cydia.saurik.com/api/latest/3), then run: / cycript - r 192.168.199.236:6666

/ cycript-r 192.168.199.236:6666 to connect to the running application./ cycript-r 192.168.199.236:6666 192.168.199.236 is the IP address of my current mobile phone. And the way it works is, in a nutshell, Is the hook in the AppDelegate application: (UIApplication *) application didFinishLaunchingWithOptions (NSDictionary *) method, In this method, enable Cycript and bind to port 6666.

Simple to use

MonkeyDev supports several useful commands for chisel, a plug-in recommended by the LLDB:

NSString* pvc(void);

NSString* pviews(void);

NSString* pactions(vm_address_t address);

NSString* pblock(vm_address_t address);

NSString* methods(const char * classname);

NSString* ivars(vm_address_t address);

NSString* choose(const char* classname);

NSString* vmmap();
Copy the code

Try it now: Find the “Tao” button at the bottom of taobao home page and hide it:

Is not suddenly want to take wechat to send a ¥0.01 red envelope, and then use the newly learned operation wave:

Without further discussion, the commands listed above can basically help you quickly understand the structure of each complex page of an APP, and also accurately locate and modify the UI of the target view. If you are not familiar with Cycript, you are advised to read the official documents first, familiarize yourself with the syntax and data structures supported, and find a few small cases for targeted practice. You will soon be able to start playing. This is a great way for students who want to learn the design and implementation of dachang’s excellent app.

1. Enter CYAfter the # JavaScript console, you are in a process, so the variables defined remain available throughout the life of the process.
2. # 0x10C144D00: #+ object address = Get the object
Copy the code

Senior play

Similar to the enhanced LLDB version, which modifs.lldbinit files & plug-in installation, Cycript supports loading custom scripts, which greatly improves debugging efficiency. The available shortcut commands listed in the previous brief use are not native to Cycript. The author of MonkeyDev loaded his own web script to support it:

.cy file source

And then what? Then we can also make a script that we often use for debugging. Here we recommend mJcript written by a small coder.

There is also a way to load.cy scripts for you: use MonkeyDev to load the network or your own CY scripts.

To feel the efficiency of a wave of custom scripts:

(function(exports) {
	var invalidParamStr = 'Invalid parameter';
	var missingParamStr = 'Missing parameter'; // app id CJAppId = [NSBundle mainBundle].bundleIdentifier; // mainBundlePath CJAppPath = [NSBundle mainBundle].bundlePath; // document path CJDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; // caches path CJCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; // Load the system dynamic library CJLoadFramework =function(name) {
		var head = "/System/Library/";
		var foot = "Frameworks/" + name + ".framework";
		var bundle = [NSBundle bundleWithPath:head + foot] || [NSBundle bundleWithPath:head + "Private" + foot];
  		[bundle load];
  		return bundle;
	};

	// keyWindow
	CJKeyWin = function() {
		returnUIApp.keyWindow; }; // Root controller CJRootVc =function() {
		returnUIApp.keyWindow.rootViewController; }; // Find the controller shown first var _CJFrontVc =function(vc) {
		if (vc.presentedViewController) {
        	return _CJFrontVc(vc.presentedViewController);
	    }else if ([vc isKindOfClass:[UITabBarController class]]) {
	        return _CJFrontVc(vc.selectedViewController);
	    } else if ([vc isKindOfClass:[UINavigationController class]]) {
	        return _CJFrontVc(vc.visibleViewController);
	    } else {
	    	var count = vc.childViewControllers.count;
    		for (var i = count - 1; i >= 0; i--) {
    			var childVc = vc.childViewControllers[i];
    			if (childVc && childVc.view.window) {
    				vc = _CJFrontVc(childVc);
    				break; }}returnvc; }}; CJFrontVc =function() {
		return_CJFrontVc(UIApp.keyWindow.rootViewController); }; // Recursively prints the hierarchy of UIViewController view CJVcSubviews =function(vc) {
		if(! [vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);returnvc.view.recursiveDescription().toString(); }; CJFrontVcSubViews = prints the top UIViewController view hierarchy recursivelyfunction() {
		returnCJVcSubviews(_CJFrontVc(UIApp.keyWindow.rootViewController)); }; // Get method names of all TouchUpInside events bound to the button CJBtnTouchUpEvent =function(btn) {
		var events = [];
		var allTargets = btn.allTargets().allObjects()
		var count = allTargets.count;
    	for (var i = count - 1; i >= 0; i--) { 
    		if(btn ! = allTargets[i]) { var e = [btn actionsForTarget:allTargets[i]forControlEvent:UIControlEventTouchUpInside]; events.push(e); }}returnevents; }; // CG function CJPointMake =function(x, y) {
		return {0 : x, 1 : y}; 
	};

	CJSizeMake = function(w, h) {
		return {0 : w, 1 : h}; 
	};

	CJRectMake = function(x, y, w, h) {
		return{0 : CJPointMake(x, y), 1 : CJSizeMake(w, h)}; }; // Recursively print the controller hierarchy CJChildVcs =function(vc) {
		if(! [vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);return[vc _printHierarchy].toString(); }; // Print the view hierarchy recursively CJSubviews =function(view) {
		if(! [view isKindOfClass:[UIView class]]) throw new Error(invalidParamStr);returnview.recursiveDescription().toString(); }; // Check whether it is a string"str" @"str"
	CJIsString = function(str) {
		return typeof str == 'string'|| str instanceof String; }; // Check whether it is array [], @[] CJIsArray =function(arr) {
		returnarr instanceof Array; }; // check whether num is a number CJIsNumber =function(num) {
		return typeof num == 'number' || num instanceof Number;
	};

	var _CJClass = function(className) {
		if(! className) throw new Error(missingParamStr);if (CJIsString(className)) {
			return NSClassFromString(className);
		} 
		if(! className) throw new Error(invalidParamStr); // Object or classreturnclassName.class(); }; Print all subclasses CJSubclasses =function(className, reg) {
		className = _CJClass(className);

		return [c for each (c in ObjectiveC.classes) 
		if(c ! = className && class_getSuperclass(c) && [c isSubclassOfClass:className] && (! reg || reg.test(c))) ]; }; // Print all methods var _CJGetMethods =function(className, reg, clazz) {
		className = _CJClass(className);

		var count = new new Type('I');
		var classObj = clazz ? className.constructor : className;
		var methodList = class_copyMethodList(classObj, count);
		var methodsArray = [];
		var methodNamesArray = [];
		for(var i = 0; i < *count; i++) {
			var method = methodList[i];
			var selector = method_getName(method);
			var name = sel_getName(selector);
			if(reg && ! reg.test(name))continue;
			methodsArray.push({
				selector : selector, 
				type : method_getTypeEncoding(method)
			});
			methodNamesArray.push(name);
		}
		free(methodList);
		return [methodsArray, methodNamesArray];
	};

	var _CJMethods = function(className, reg, clazz) {
		return_CJGetMethods(className, reg, clazz)[0]; }; // Prints all method names var _CJMethodNames =function(className, reg, clazz) {
		return_CJGetMethods(className, reg, clazz)[1]; }; // Print all object methods CJInstanceMethods =function(className, reg) {
		return_CJMethods(className, reg); }; // Print all object method names CJInstanceMethodNames =function(className, reg) {
		return_CJMethodNames(className, reg); }; // Print all class methods CJClassMethods =function(className, reg) {
		return _CJMethods(className, reg, true); }; // Prints all the class method names CJClassMethodNames =function(className, reg) {
		return _CJMethodNames(className, reg, true); }; // Prints all the member variables CJIvars =function(obj, reg){
		if(! obj) throw new Error(missingParamStr); var x = {};for(var i in *obj) { 
			try { 
				var value = (*obj)[i];
				if(reg && ! reg.test(i) && ! reg.test(value))continue;
				x[i] = value; 
			} catch(e){} 
		} 
		returnx; }; // Prints all member variable names CJIvarNames =function(obj, reg) {
		if(! obj) throw new Error(missingParamStr); var array = [];for(var name in *obj) { 
			if(reg && ! reg.test(name))continue;
			array.push(name);
		}
		return array;
	};
})(exports);
Copy the code

As long as you want, as long as you can, more positions, waiting for you to unlock.

summary

Learning without scenarios is mostly a waste of time, and unused knowledge produces no value. Cycript is no exception. If you just play it for a couple of hours out of curiosity and then say goodbye, it really doesn’t matter. Some students think that extensive reading, in the interview can talk, can increase a little “big man” feeling, I personally do not agree with, a little deeper questions you can not say or simply do not understand pretend to know it will be counterproductive. Therefore, I still suggest that since you have learned something, you should try your best to learn more deeply and keep thinking about how to use what you have learned to improve efficiency in your work. Cycript is still very useful in reverse engineering, forward development and everyday learning.

May you gain something