@itx wrote:
工具
- cycript
- iResign
- Xcode
分析前
- ssh 连接到越狱的手机
- cycript -p WeChat
找到聊天界面所在的viewController 以及内存地址
- 打印App的rootViewController
//通过keyWindow获取rootViewController,0x15ceb31b0是rootcontroller对象的内存地址 cy# rootcontroller = [[UIApplication sharedApplication ] keyWindow ].rootViewController #"<MMTabBarController: 0x15ceb31b0>"
2.显示MMTabBarController的子controller
cy# rootcontroller.viewControllers @[ #"<MMUINavigationController: 0x15ceaa330>", #"<MMUINavigationController: 0x15ceaee40>", #"<MMUINavigationController: 0x15cd3c9d0>", #"<MMUINavigationController: 0x15ceb12b0>" ]
3.找到聊天界面所在viewController
//可以通过‘#’号来获取指定内存地址的对象,并赋值给变量navcontroller cy# navcontroller=#0x15ceaa330 #"<MMUINavigationController: 0x15ceaa330>" //打印出子controller cy# navcontroller.viewControllers @[#"<NewMainFrameViewController: 0x15d923000>",#"<BaseMsgContentViewController: 0x15da32200>"] //取指定内存地址对象,并赋值给变量msgviewcontroller cy# msgviewcontroller=#0x15da32200 #"<BaseMsgContentViewController: 0x15da32200>"
找到“按住 说话”按钮所在的view和内存地址
1.显示聊天界面的views
cy# #0x15da32200.view.subviews() @[ //可以发现该view包含了3个子view #"<MMMultiSelectToolView: 0x15cea00e0; frame = (0 568; 320 50); hidden = YES; layer = <CALayer: 0x170837a00>>", #"<MMTableView: 0x15db28c00; baseClass = UITableView; frame = (0 0; 320 568); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x1704580f0>; layer = <CALayer: 0x170838be0>; contentOffset: {0, 80}; contentSize: {320, 598}>", #"<MMInputToolView: 0x15cf63070; frame = (0 0; 320 568); text = ''; layer = <CALayer: 0x17443d2a0>>" ]
2.打印MMInputToolView的subviews
///由第一步可以猜测出我们要找的按钮在MMInputToolView上,0x15cf63070即为MMInputToolView对象的内存地址 cy# _inputtoolview=#0x15cf63070 #"<MMInputToolView: 0x15cf63070; frame = (0 0; 320 568); text = ''; layer = <CALayer: 0x17443d2a0>>" cy# _inputtoolview.subviews() @[ #"<UIButton: 0x15cf6b430; frame = (215 408; 105 110); hidden = YES; opaque = NO; layer = <CALayer: 0x170835760>>", //底部的语音按钮所在的view,0x15cd93e30地址在下一步用到 #"<InputToolViewBar: 0x15cd93e30; baseClass = UIImageView; frame = (0 518; 320 50); clipsToBounds = YES; layer = <CALayer: 0x170227140>>", #"<MMUIView: 0x15cd964a0; frame = (0 568; 320 224); layer = <CALayer: 0x1746217c0>>", #"<RecordView: 0x15cd97190; frame = (0 0; 320 300); hidden = YES; layer = <CALayer: 0x170835d20>>", #"<SelectAttachmentView: 0x15cd4b610; frame = (0 568; 320 224); autoresize = W; layer = <CALayer: 0x17482b460>>", #"<ShortVideoToolbar: 0x15ce90f00; frame = (0 588; 320 348.154); layer = <CALayer: 0x171034a60>>", #"<UIButton: 0x15cf67fd0; frame = (240 408; 81 110); alpha = 0; opaque = NO; layer = <CALayer: 0x1708330a0>>" ]
3.对上面输出的view逐个进行hidden = YES 调试以确定“按住说话”按钮
cy# #0x15cd93e30.hidden=YES true
4.打印InputToolViewBar
cy# #0x15cd93e30.subviews() @[ #"<UIVisualEffectView: 0x15e16f210; frame = (0 0; 320 50); autoresize = W+H; tag = 102289; layer = <CALayer: 0x174a29640>>", #"<MMGrowTextView: 0x15cd88d20; frame = (37 3; 209 44); text = ''; layer = <CALayer: 0x174a243c0>>", #"<UIView: 0x15cd942a0; frame = (0 0; 320 0.5); autoresize = W; layer = <CALayer: 0x17443c920>>", //同样通过逐个view设置hidden,可以定位到这里就是"按住说话"按钮,地址0x15cf669c0在下面会用到 #"<MMTransparentButton: 0x15cf669c0; baseClass = UIButton; frame = (37 2.5; 209 45); alpha = 0; opaque = NO; autoresize = W; layer = <CALayer: 0x170832240>> hightlighted = 0", #"<UIButton: 0x15cd943b0; frame = (1 8; 35 35); opaque = NO; tag = 10088; layer = <CALayer: 0x17443e100>>", #"<UIButton: 0x15cd95e80; frame = (284 8; 35 35); opaque = NO; autoresize = LM; layer = <CALayer: 0x17443db20>>", #"<UIButton: 0x15cd95a60; frame = (247 8; 35 35); opaque = NO; autoresize = LM; layer = <CALayer: 0x17443e480>>", //注意,这里的按钮并不是"按住说话"按钮 #"<MMTransparentButton: 0x15cf69430; baseClass = UIButton; frame = (0 2.5; 320 40); alpha = 0; opaque = NO; layer = <CALayer: 0x1708355e0>> hightlighted = 0" ]
5.确定“按住说话”按钮所属的对象以及内存地址
cy# #0x15df32830.subviews() @[ #"<UIImageView: 0x15df3b1f0; frame = (0 0; 209 45); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170830a80>>", //text对应的值应该是一个ascii编码,对应中文就是:按住 说话 #"<UIButtonLabel: 0x15df33520; frame = (70.5 14.5; 68.5 19.5); text = '\xe6\x8c\x89\xe4\xbd\x8f \xe8\xaf\xb4\xe8\xaf\x9d'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x17068f3c0>>" ]
到这里,可以确定button所在的类:MMInputToolView ,以及button的内存地址:0x15cf69430。下一步通过WeChat 头文件中的函数来逐个测试,看看那个函数调起了语音,哪个函数停止语音,哪个函数发送语音等额那个。下面会用到button的内存地址进行调试。
通过使用下面两两句代码来获取button的action,发现为空的。于是就想到,按钮应该是通过触摸事件来实现点击、取消等效果的,那么直接看看头文件先吧!(这段是后面补上的)
[button allTargets]; [button actionsForTarget:<#(nullable id)#> forControlEvent:<#(UIControlEvents)#>]
查找头文件,找到开始录音、停止录音函数
1.在
MMInputToolView.h
文件的219行和249行- (void)resalStartRecording;//开始录音 - (void)stopRecording:(id)arg1;//停止录音,并发送
2.进行方法调用,并观察微信聊天界面,可以发现已经开始录音!!
//开始录音 cy# [_inputtoolview resalStartRecording ] cy# //结束录音,这里的内存地址0x15df32830 就是上面所说的“按住 说话”按钮的的对象内存地址 cy# [_inputtoolview stopRecording:#0x15df32830] cy#
开始hook
这里直接上代码
//一键录音 CHMethod(2, void,MMInputToolView,MMTransparentButton_touchesEnded,id,arg1,withEvent,id,arg2) { //SpreadButtonManager 是我自己写的一个单例,大家可以忽略 if ([SpreadButtonManager sharedInstance].oneKeyRecord) { return; } CHSuper(2, MMInputToolView,MMTransparentButton_touchesEnded,arg1,withEvent,arg2); } //一键录音 CHMethod(2, void,MMInputToolView,MMTransparentButton_touchesMoved,id,arg1,withEvent,id,arg2) { if (![SpreadButtonManager sharedInstance].oneKeyRecord) { return; } CHSuper(2, MMInputToolView,MMTransparentButton_touchesMoved,arg1,withEvent,arg2); } //一键录音 CHMethod(2, void,MMInputToolView,MMTransparentButton_touchesCancelled,id,arg1,withEvent,id,arg2) { if (![SpreadButtonManager sharedInstance].oneKeyRecord) { return; } CHSuper(2, MMInputToolView,MMTransparentButton_touchesCancelled,arg1,withEvent,arg2); } //一键录音 CHMethod(2, void,MMInputToolView,MMTransparentButton_touchesBegan,id,arg1,withEvent,id,arg2) { if (![SpreadButtonManager sharedInstance].oneKeyRecord) { CHSuper(2, MMInputToolView,MMTransparentButton_touchesBegan,arg1,withEvent,arg2); return; } UIViewController *selfVC = [UIApplication itx_topViewController]; Ivar inputToolViewIvar = class_getInstanceVariable(objc_getClass("BaseMsgContentViewController"), "_inputToolView"); UIView *inputtoolView = object_getIvar(selfVC, inputToolViewIvar); NSLog(@"调用toolView resalStartRecording方法:%@",inputtoolView); [inputtoolView performSelector:@selector(resalStartRecording) withObject:nil]; }
下面这段代码,仅仅只是用来修改按钮名字
//每次显示都修改按钮名字 CHMethod(1,void,BaseMsgContentViewController,viewWillAppear,BOOL, animated) { CHSuper(1, BaseMsgContentViewController,viewWillAppear,animated); UIViewController *selfVC = [UIApplication itx_topViewController]; if (![selfVC isKindOfClass:objc_getClass("BaseMsgContentViewController")]) { return; } Ivar inputToolViewIvar = class_getInstanceVariable(objc_getClass("BaseMsgContentViewController"), "_inputToolView"); id inputToolView = object_getIvar(selfVC, inputToolViewIvar); NSLog(@"逆向日志>> selfVC:%@, 变量toolView:%@",selfVC,inputToolView); //获取录音按钮对象 Ivar recordButtonIvar = class_getInstanceVariable(objc_getClass("MMInputToolView"),"_recordButton"); UIButton *recordButton = object_getIvar(inputToolView, recordButtonIvar); if ([SpreadButtonManager sharedInstance].oneKeyRecord) { [(UIButton *)recordButton setTitle:@"一键 说话" forState:0]; }else{ [(UIButton *)recordButton setTitle:@"按住 说话" forState:0]; } }
到此,一键录音的功能已经修改完成!但是,实际上还需要一个“结束录音”的按钮!弄了一下下,发现有几个坑没跳过去..就等后面再分析分析。
结束语
没什么技术含量,第一次分享!!多多包涵!
Posts: 1
Participants: 1