Cycript(四):常用脚本

本文探讨了MDCycript、MD.cy、mjcript.cy、libcycript.cy、cycript-utils/utils.cy等脚本库,介绍了如何利用它们在iOS和JavaScript环境中进行安全性和功能扩展,如Hook函数、操作Objective-C对象、访问系统资源等。

MDCycript/MS.cy

git 地址:https://github.com/AloneMonkey/MDCycript/blob/master/MS.cy

(function(ms) {
   
   
	// 获取存储 libcycript.dylib 的目录的路径
    let GetLibraryPath = function() {
   
   
        let handle = dlopen(NULL, RTLD_NOLOAD);
        if (handle == null)
            return null;

        try {
   
   
            let CYListenServer = dlsym(handle, "CYListenServer");
            if (CYListenServer == null)
                return null;

            let info = new Dl_info;
            if (dladdr(CYListenServer, info) == 0)
                return null;

            let path = info->dli_fname;
            let slash = path.lastIndexOf('/');
            if (slash == -1)
                return null;

            path = path.substr(0, slash);

            GetLibraryPath = function() {
   
   
                return path;
            };

            return GetLibraryPath();
        } finally {
   
   
            dlclose(handle);
        }
    };
    
	// 安全性判断: libcycript.dylib 必须存在
    var libcycript = dlopen(GetLibraryPath() + "/libcycript.dylib", RTLD_NOLOAD);
    if (libcycript == null) {
   
   
        return;
    }
    
	// 安全性判断: libsubstrate.dylib 必须存在
    var libsubstrate = dlopen(GetLibraryPath() + "/libsubstrate.dylib", RTLD_GLOBAL | RTLD_LAZY);
    if (libsubstrate == null) {
   
   
        return;
    }
    
	// 导入 libsubstrate.dylib 中的 C 函数
    extern "C" void* MSGetImageByName(const char *);
    extern "C" void* MSFindSymbol(void *, const char *);
    extern "C" void MSHookFunction(void *, void *, void **);
    extern "C" void MSHookMessageEx(Class, SEL, void *, void **);

    var slice = Array.prototype.slice;
    
	// 用于封装 Cydia Substrate 的 MSHookFunction 函数
	// @param.func 原始函数
	// @param.hook 替换函数
	// @param.old  用于保存原始实现
    ms.HookFunction = function(func, hook, old) {
   
   
        var type = typeid(func);

        var pointer;
        if (old == null || typeof old === "undefined")
            pointer = null;
        else {
   
   
            pointer = new (typedef void **);
            *old = function() {
   
    return type(*pointer).apply(null, arguments); };
        }

        MSHookFunction(func.valueOf(), type(hook), pointer);
    };
    
	// 用于封装 Cydia Substrate 的 MSHookMessageEx 函数
	// @param.isa 要 hook 的 OC 类
	// @param.sel 要 hook 的方法的名称
	// @param.imp 替换实现
	// @param.old 用于保存原始实现
    ms.HookMessage = function(isa, sel, imp, old) {
   
   
        var type = sel.type(isa);

        var pointer;
        if (old == null || typeof old === "undefined")
            pointer = null;
        else {
   
   
            pointer = new (typedef void **);
            *old = function() {
   
    return type(*pointer).apply(null, [this, sel].concat(slice.call(arguments))); };
        }

        MSHookMessageEx(isa, sel, type(function(self, sel) {
   
    return imp.apply(self, slice.call(arguments, 2)); }), pointer);
    };
    
	// 将当前脚本的 ms 中定义的函数暴露给 Cycript 的全局作用域
    for (var k in ms) {
   
   
        if(ms.hasOwnProperty(k)) {
   
   
            var f = ms[k];
            if(typeof f === 'function') {
   
   
                Cycript.all[k] = f;
            }
        }
    }

})(exports);

MDCycript/md.cy

git 地址:https://github.com/AloneMonkey/MDCycript/blob/master/md.cy

(function(utils) {
   
   

    utils.constants = {
   
   
        APPID:  	 NSBundle.mainBundle.bundleIdentifier,	// 当前 App 的 BundleID
        APPPATH:     NSBundle.mainBundle.bundlePath,		// 当前 App 的工程主目录
        APPHOME:	 NSHomeDirectory(),						// 当前 App 的沙盒主目录
        APPDOC:      NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0],	// 当前 App 的沙盒目录(Documents)
        APPLIBRARY:  NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0],		// 当前 App 的沙盒目录(Library)
        APPCACHE:    NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]		// 当前 App 的沙盒目录(Caches)
    };
    
	// 打印当前 App 主窗口(keyWindow)的视图层级的递归描述
    utils.pviews = function() {
   
   
        return UIApp.keyWindow.recursiveDescription().toString();
    };
    
	// 打印当前 App 主窗口(keyWindow)的控制器层级的递归描述
    utils.pvcs = function() {
   
   
        return UIWindow.keyWindow().rootViewController._printHierarchy().toString();
    };
    
	// 打印从指定的响应者(target)开始的响应者链条
	// @param.target 因为 nextResponder 方法位于 UIResponder 中
	//				 所以 target 必须是继承自 UIResponder 的类
	//				 即,target 可以是 UIApplication、UIViewController、UIWindow、UIView、等等
    utils.rp = function(target) {
   
   
        var result = "" + target.toString();
        while(target.nextResponder){
   
   
            result += "\n" + target.nextResponder.toString();
            target = target.nextResponder;
        }
        return result;
    };
    
	// 打印指定的控件(target)的所有 Target 和 Action
	// @param.target 因为 allTargets 方法位于 UIControl 中
	//				 所以 target 必须是继承自 UIControl 的类
	//				 即,target 可以是 UIButton、UITextField、等等
    utils.pactions = function(target) {
   
   
		var result = '';
		var objs = target.allTargets.allObjects();
		for(var i = 0; i < objs.length; i++){
   
   
			var actions = [target actionsForTarget:objs[i] forControlEvent:0];
			result += objs[i] + " " + [actions componentsJoinedByString:@","];
		}
		return result;
	}
	
	// 将当前脚本的 utils 中定义的常量暴露给 Cycript 的全局作用域
    for (var k in utils.constants) {
   
   
        Cycript.all[k] = utils.constants[k];
    }
    
	// 将当前脚本的 utils 中定义的函数暴露给 Cycript 的全局作用域
    for (var k in utils) {
   
   
        if(utils.hasOwnProperty(k)) {
   
   
            var f = utils[k];
            if(typeof f === 'function') {
   
   
                Cycript.all[k] = f;
            }
        }
    }
    
})(exports);

mjcript/mjcript.cy

git 地址:https://github.com/CoderMJLee/mjcript/blob/master/mjcript.cy

(function(exports) {
   
   
	var invalidParamStr = 'Invalid parameter';
	var missingParamStr = 'Missing parameter';

	// 当前 App 的 BundleID
	MJAppId = [NSBundle mainBundle].bundleIdentifier;

	// 当前 App 的工程主目录
	MJAppPath = [NSBundle mainBundle].bundlePath;

	// 当前 App 的沙盒目录(Documents)
	MJDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

	// 当前 App 的沙盒目录(Caches)
	MJCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; 

	// 加载系统动态库
	// @param.name 动态库的名称,例如: UIKit、Foundation、等等
	MJLoadFramework = 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;
	};

	// 当前 App 的主窗口
	MJKeyWin = function() {
   
   
		return UIApp.keyWindow;
	};

	// 当前 App 的主窗口的根控制器
	MJRootVc = function() {
   
   
		return UIApp.keyWindow.rootViewController;
	};

	var _MJFrontVc = function(vc) {
   
   
		if (vc.presentedViewController) {
   
   
        	return _MJFrontVc(vc.presentedViewController);
	    } else if ([vc isKindOfClass:[UITabBarController class]]) {
   
   
	        return _MJFrontVc(vc.selectedViewController);
	    } else if ([vc isKindOfClass:[UINavigationController class]]) {
   
   
	        return _MJFrontVc(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 = _MJFrontVc(childVc);
    				break;
    			}
    		}
	        return vc;
    	}
	};

	// 找到当前 App 的主窗口中显示在最前面的控制器
	MJFrontVc = function() {
   
   
		return _MJFrontVc(UIApp.keyWindow.rootViewController);
	};

	// 递归打印指定的控制器 vc 的视图的层级结构
	MJVcSubviews = function(vc) {
   
    
		if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
		return vc.view.recursiveDescription().toString(); 
	};

	// 递归打印当前 App 的主窗口中显示在最前面的控制器的视图的层级结构
	MJFrontVcSubViews = function() {
   
   
		return MJVcSubviews(_MJFrontVc(UIApp.keyWindow.rootViewController));
	};

	// 获取指定的按钮 btn 绑定的所有 TouchUpInside 事件的方法名
	MJBtnTouchUpEvent = 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);
    		}
       }
	   return events;
	};

	// 封装 CGPointMake 函数
	MJPointMake = function(x, y) {
   
    
		return {
   
   0 : x, 1 : y}; 
	};

	// 封装 CGSizeMake 函数
	MJSizeMake = function(w, h) {
   
    
		return {
   
   0 : w, 1 : h}; 
	};

	// 封装 CGRectMake 函数
	MJRectMake = function(x, y, w, h) {
   
    
		return {
   
   0 : MJPointMake(x, y), 1 : MJSizeMake(w, h)}; 
	};

	// 递归打印指定的控制器 vc 的层级结构
	MJChildVcs = function(vc) {
   
   
		if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
		return [vc _printHierarchy].toString();
	};

	// 递归打印指定的视图 view 的层级结构
	MJSubviews = function(view) {
   
    
		if (![view isKindOfClass:[UIView class]]) throw new Error(invalidParamStr)
<think>我们正在讨论的是在iOS平台上对微信(com.tencent.xin)的Protobuf数据进行Hook操作。由于微信使用了Protobuf进行网络通信,我们需要拦截并解析这些数据。在iOS上,这通常通过越狱和运行时Hook来实现。步骤概述:1.准备越狱环境:由于需要注入代码到微信进程,所以设备需要越狱。2.选择Hook工具:常用的有Theos、Logos、Frida等。3.定位目标方法:找到微信中处理Protobuf数据的类和方法。4.编写Hook代码:使用选定的工具编写Hook代码,拦截方法调用并解析Protobuf数据。5.测试与部署:将Hook代码编译成动态库并注入到微信进程中。详细步骤:1.越狱环境-确保设备已越狱(如使用checkra1n、unc0ver等)。-安装必要的开发工具,如Theos、ldid(用于签名)、Make等。2.选择Hook工具-这里我们选择Theos和Logos(因为它们是iOS越狱开发的标准工具)。-也可以选择Frida,它提供了动态注入和脚本支持,但这里我们以Theos为例。3.定位目标方法-使用逆向工程工具(如HopperDisassembler、IDAPro)分析微信二进制文件,或者使用运行时调试工具(如Cycript、Frida)动态跟踪。-由于微信的Protobuf数据通常通过特定的类进行序列化和反序列化,我们需要找到这些类和方法。-常见的线索:查找类名中包含“Protobuf”、“PB”、“CMessage”等字样的类,或者跟踪网络层相关的方法。-例如,微信中处理消息的类可能是`CMessageService`,而具体处理Protobuf数据的类可能是`PB`开头的类。-通过分析,我们可能会发现类似`-[SomeClassdeserializeProtobufData:]`或`-[SomeClasshandleProtobufMessage:]`这样的方法。4.编写Hook代码(使用Theos/Logos)-创建一个Theos项目,编写Tweak.xm文件。-假设我们已经找到了一个处理Protobuf数据的方法,例如`-[WCPbBaseMessagedeserializeProtobufData:]`(这只是一个示例,实际类名和方法名需要通过逆向得到)。-示例代码:```objc%hookWCPbBaseMessage//Hook反序列化方法,传入的是Protobuf二进制数据-(id)deserializeProtobufData:(NSData*)protobufData{//先调用原始方法,得到解析后的对象idresult=%orig(protobufData);//打印原始Protobuf数据的16进制字符串(用于调试)NSString*hexString=[protobufDatadescription];NSLog(@"[ProtobufHook]deserializeProtobufData:%@",hexString);//这里可以尝试解析Protobuf数据,需要知道对应的.proto定义//如果没有.proto文件,可以尝试使用工具动态解析(如ProtobufDumper)returnresult;}%end```-另外,我们也可以Hook序列化方法,以获取发送出去的Protobuf数据。5.解析Protobuf数据-如果没有.proto文件,解析原始Protobuf数据是困难的。我们可以:a)寻找微信的.proto文件(通常很难获取)。b)使用第三方工具(如protobuf-inspector)动态解析。c)通过多次捕获相同类型的数据包,分析结构,自己编写.proto文件。-在Hook代码中,我们可以将捕获的Protobuf数据保存到文件,然后在电脑上用工具解析。6.编译与安装-使用Theos编译Tweak项目,生成一个deb包。-将deb包安装到越狱设备上,重启微信(或使用`killallWeChat`重启微信)。7.查看日志-在设备上使用syslog或console查看输出,或者将日志写入文件。注意事项:-微信有反调试越狱检测,可能需要先绕过这些检测。-每次微信更新,类名和方法名可能会改变,需要重新逆向。-操作需遵守法律法规,仅限于学习研究。工具推荐:-逆向工具:IDAPro(付费)、HopperDisassembler(付费)、Ghidra(免费)-动态分析:Frida、Cycript(已停止维护,但仍有使用)、LLDB-Protobuf解析:protobuf-inspector(https://github.com/mildsunrise/protobuf-inspector)示例:使用Frida进行动态Hook如果你选择使用Frida,可以编写JavaScript脚本进行Hook:```javascript//假设我们已经知道目标方法在类'WCPbBaseMessage'中,方法名为'deserializeProtobufData:'Interceptor.attach(ObjC.classes.WCPbBaseMessage['-deserializeProtobufData:'].implementation,{onEnter:function(args){//args[0]是self,args[1]是SEL,args[2]是protobufData(NSData对象)varprotobufData=newObjC.Object(args[2]);//将NSData转为ArrayBuffervarlength=protobufData.length();varbytes=protobufData.bytes();varbuf=newUint8Array(ArrayBuffer(length));for(vari=0;i<length;i++){buf[i]=Memory.readU8(bytes.add(i));}//将buf转为hex字符串varhexString=
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值