不懂汇编,如何逆向(iOS)

$ 写给像我一样的小白

0x1 逆向一个APP有哪些步骤(不越狱)

  1. 砸壳
  2. dump出头文件
  3. 分析功能界面
  4. hopper || iDA 分析伪代码
  5. 写hook
  6. 打包动态库
  7. 注入动态库到APP
  8. APP重签名
  9. 安装到手机上

MonkeyDev

MonkeyDev是一个xcode插件, 此处先膜一下@庆哥

原有iOSOpenDev的升级,非越狱插件开发集成神器!

- 可以使用Xcode开发CaptainHook Tweak、Logos Tweak 和 Command-line Tool,在越狱机器开发插件,这是原来iOSOpenDev功能的迁移和改进。
- 只需拖入一个砸壳应用,自动集成class-dump、restore-symbol、Reveal、Cycript和注入的动态库并重签名安装到非越狱机器。
- 支持调试自己编写的动态库和第三方App
- 支持通过CocoaPods第三方应用集成SDK以及非越狱插件,简单来说就是通过CocoaPods搭建了一个非越狱插件商店。

庆哥的github如是说.

MonkeyDev解决了上面说到的50%的步骤, 再外加一个动态调试.

https://github.com/AloneMonkey/MonkeyDev

PS:问问题之前先熟读wiki

0x2 剩下的工作

砸壳

其实这个是非必要项, 自己手动砸壳需要已越狱的手机. 想手动砸壳可以参考这篇文章.

iOS逆向工程之Clutch砸壳

不想自己手动砸壳的可以去各大应用平台,如PP助手等.下载越狱版的软件,这些都是已经砸好壳的了.

分析功能界面(动态分析)

主流有两种方法:

  1. Cycript
  2. Reveal

但是使用了MonkeyDev之后多了一种方法. hook oc的消息通知函数,如:ANYMethodLog.

因为每个app启动的时候都会调用appDelegate里面的didFinishLaunchingWithOptions:这个方法, 也就是说每个app都会有这个方法. 那我们可以在class-dump出来的头文件中找到某个app的appDelegate文件名,然后hook掉didFinishLaunchingWithOptions:把所有基于UIViewController或者其他类执行的方法在运行的时候全部打出来.甚至连函数的参数都可打印出来.

分析伪代码(静态分析)

上面一步分析功能界面是为了定位具体要hook的函数, 当定位出来要hook的函数之后, 就要去分析函数的具体实现了.

Hopper iDA

这两个都是收费的,但都提供了demo版,只是做静态分析的话已经够用了.

IDA + Hopper 逆向开发近期学习

我们在这一步的目的只是为了搞清楚函数的实现和函数之间的调用关系, 所以并不需要去直接修改汇编或者二进制代码, 只是反编译出来的伪代码有可能也会带有一下寄存器或者内存地址等一些看不懂的信息,完全可以把这些当做成命名简单的变量,我们只需要看懂其中的逻辑就好了.

编写hook代码

OK, 现在要hook 的函数已经找到了,函数具体的实现也已经知道, 那下一步当然就是编写代码把函数hook掉.

captainhook

MonkeyDev中提供了logoscaptainhook两种语法,用来编写hook代码.如果原来做过越狱开发的应该比较喜欢用logos,网上的教程也比较多.但是, 我学习的时候选择用captainhook(两个都好用,纯粹个人喜好).这里简单说一下写代码的过程:

1) 如何调用已有的类和方法

如果需要使用到类的属性或类方法,最好自行创建一个头文件把@interface写进去,然后import这个头文件,写hook的时候就可以找到相应的属性了,但是如果你想通过这种方式给类添加属性是行不通的,我猜测是因为在程序运行的过程当中,内存的分配已经完成,想要添加属性值进去就需要对这个对象的内存进行扩容或者重新分配,但是通过写在自定义的头文件里面属性值,虽然是在同名的类下面,但是并不会添加在原来代码申请的内存当中,所以当你调用这个自己添加的属性的时候,原对象是找不到访问不了这个属性的,类似于Category.

如果一定要添加属性,必须实现该属性的getset方法,在里面调用runtime的外联方法objc_getAssociatedObject使对象和属性建立映射关系,这样在运行时对象才能找到你添加的属性.详细参考:Objective-C Associated Objects 的实现原理

123456789101112

@interface CMessageWrap@property(nonatomic, strong) NSString* m_nsContent; //发送消息的内容@property(nonatomic, strong) NSString* m_nsToUsr; //发送人@property(nonatomic, strong) NSString* m_nsFromUsr; //接收人@property(nonatomic, strong) NSMutableString *m_nsPushContent; + (BOOL)isSenderFromMsgWrap:(CMessageWrap*) msgWrap;- (CMessageWrap*)initWithMsgType:(int) type;@end

2) CHDeclareClass

123

#define CHDeclareClass(name) \ @class name; \ static CHClassDeclaration_ name ## $;

这个宏用于声明你要hook的类.

3) CHOptimizedMethod

12

#define CHOptimizedMethod1(optimization, return_type, class_type, name1, type1, arg1) \ CHMethod_ ## optimization ## _(return_type, class_type *, class_type, CHClass(class_type), CHSuperClass(class_type), name1 ## $, name1:, CHDeclareSig1_(return_type, type1), (self, _cmd, arg1), type1 arg1)

CHOptimizedMethod编写你要hook的方法,这个宏后面跟着一个数字,[0-9]代表着你要hook的方法的参数个数.

4) CHSuper

12

#define CHSuper1(class_type, name1, val1) \ CHSuper_(class_type, @selector(name1:), name1 ## $, val1)

CHSuper用于在CHOptimizedMethod内执行完自己的代码之后继续执行原函数的代码.

5) CHConstructor

1

#define CHConstructor static __attribute__((constructor)) void CHConcat(CHConstructor, __LINE__)()

__attribute__((constructor))后的内容能保证在 dylib 加载时运行.

6) CHLoadLateClass

1

#define CHLoadLateClass(name) CHLoadClass_(&name ## $, objc_getClass(#name))

加载需要hook的类,写在CHConstructor里面.

7) CHClassHook

1

#define CHClassHook1(class, name1) CHHook_(class, name1 ## $)

加载需要hook的方法,写在CHConstructor里面.

8) CHDeclareMethod

12

#define CHDeclareMethod1(return_type, class_type, name1, type1, arg1) \ CHDeclareMethod_(return_type, class_type *, class_type, CHClass(class_type), CHSuperClass(class_type), name1 ## $, name1:, CHDeclareSig1_(return_type, type1), (self, _cmd, arg1), type1 arg1)

声明和实现自己的方法, 要注意声明要写在调用之前.

9) Sample

这是我hook了微信聊天页面出现和消失两个代理方法的例子…

123456789101112131415161718192021222324252627282930313233343536373839

//聊天基本页面CHDeclareClass(BaseMsgContentViewController)//实现自己新加的方法CHDeclareMethod1(void, MMUIViewController, backToMsgContentViewController, id, sender){ [sender removeFromSuperview]; UINavigationController *navi = [objc_getClass("CAppViewControllerManager") getCurrentNavigationController]; LKButton *btn = (LKButton *)sender; [LKNewestMsgManager sharedInstance].didTouchBtnName = btn.username; [[NSNotificationCenter defaultCenter] postNotificationName:@"btnDidTouch" object:nil]; MMServiceCenter* serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter]; CContactMgr *contactMgr = [serviceCenter getService:[objc_getClass("CContactMgr") class]]; CContact *contact = [contactMgr getContactByName:btn.username]; MMMsgLogicManager *logicManager = [serviceCenter getService:[objc_getClass("MMMsgLogicManager") class]]; [logicManager PushOtherBaseMsgControllerByContact:contact navigationController:navi animated:YES];}//hook viewDidAppear 方法CHOptimizedMethod1(self, void, BaseMsgContentViewController, viewDidAppear, BOOL, flag){ [LKNewestMsgManager sharedInstance].currentChat = [(BaseMsgContentViewController*)[[LKNewestMsgManager sharedInstance] getCurrentVC]getCurrentChatName]; NSLog(@"%@", [LKNewestMsgManager sharedInstance].currentChat); CHSuper1(BaseMsgContentViewController, viewDidAppear, flag);}//hook viewWillDisappear 方法CHOptimizedMethod1(self, void, BaseMsgContentViewController, viewWillDisappear, BOOL, disappear){ [LKNewestMsgManager sharedInstance].currentChat = NULL; CHSuper1(BaseMsgContentViewController, viewWillDisappear, disappear);}//注册需要hook的类和方法CHConstructor{ CHLoadLateClass(BaseMsgContentViewController); CHClassHook1(BaseMsgContentViewController, viewWillDisappear); CHClassHook1(BaseMsgContentViewController, viewDidAppear);}

0x3 做些有趣的事情

我平常喜欢在微信公众号看些文章, 但是这时候如果有人发消息过来, 手机震了一下…….但是你并不知道是谁发来的消息, 这时候,按照微信培养的用户习惯….置顶保存文章,然后点击n个返回按钮然后点击close返回消息页面…..回了消息然后在回去看文章…..

so,这就是痛点所在,不能快速查看回复消息.

搞起来….

功能设想

在任意页面, 当接收到异步消息, 通知当前页面弹出一个按钮提示, 点击按钮 push 对应聊天页面, pop 可返回原来的页面.

定位函数

一开始只是因为看文章的痛点,只想hook webVC页面就好了,但是后来细想,当你在弄设置或者干其他事情的时候其实也有同样的问题,干脆直接搞底层UIViewController, 但是在分析的过程中,发现微信有一个自己实现的MMUIViewController.如此甚好, 直接搞它.

通过动态分析的方法快速定位到需要hook的类:

123

BaseMsgContentViewController //基本聊天页面MMUIViewController //VC基类CMessageMgr //消息接收类

对于BaseMsgContentViewControllerMMUIViewController我们目的很明确,就是监听通知,当有消息来的时候,弹出按钮.

这里可能有疑问,BaseMsgContentViewController应该也是继承MMUIViewController的,为什么还要单独hook. 原因很简单,因为你在和某人的聊天页面当中,当然不应该在弹出这个人的消息按钮.

接下来就是借助class-dumpHopper去定位和分析函数, 比如,我这里需要分析的就是点击按钮之后,如何跳转到对应的聊天页面.

hook

OK, 所有需要用到的消息都拿到了, 开始写hook代码.

下面是一些关键代码,全部的代码在github:LKMessageSwitchPod

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152

// hook 消息接收类CHDeclareClass(CMessageMgr)CHOptimizedMethod2(self, void, CMessageMgr, AsyncOnAddMsg, NSMutableString*, msg, MsgWrap, CMessageWrap*, wrap){ if(![wrap.m_nsPushContent isEqual: @""] && wrap.m_nsPushContent != NULL){ [LKNewestMsgManager sharedInstance].username = msg; [LKNewestMsgManager sharedInstance].content = wrap.m_nsPushContent; [[NSNotificationCenter defaultCenter] postNotificationName:@"LkWechatMessageNotification" object:nil]; } CHSuper2(CMessageMgr, AsyncOnAddMsg, msg, MsgWrap, wrap);}CHDeclareMethod1(void, MMUIViewController, backToMsgContentViewController, id, sender){ [sender removeFromSuperview]; UINavigationController *navi = [objc_getClass("CAppViewControllerManager") getCurrentNavigationController]; LKButton *btn = (LKButton *)sender; [LKNewestMsgManager sharedInstance].didTouchBtnName = btn.username; [[NSNotificationCenter defaultCenter] postNotificationName:@"btnDidTouch" object:nil]; MMServiceCenter* serviceCenter = [objc_getClass("MMServiceCenter") defaultCenter]; CContactMgr *contactMgr = [serviceCenter getService:[objc_getClass("CContactMgr") class]]; CContact *contact = [contactMgr getContactByName:btn.username]; MMMsgLogicManager *logicManager = [serviceCenter getService:[objc_getClass("MMMsgLogicManager") class]]; [logicManager PushOtherBaseMsgControllerByContact:contact navigationController:navi animated:YES];}CHDeclareMethod0(void, MMUIViewController, messageCallBack){ NSLog(@"收到消息!!!"); NSString *currentChatName = [LKNewestMsgManager sharedInstance].currentChat; if(self == [[LKNewestMsgManager sharedInstance]getCurrentVC] && ![currentChatName isEqual: [LKNewestMsgManager sharedInstance].username]){ LKButton *btn = [LKButton buttonWithType:UIButtonTypeRoundedRect]; btn.frame = CGRectMake(self.view.frame.size.width-100-2, 74, 100, 40); btn.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.8]; btn.tintColor = [UIColor whiteColor]; [btn setTitle:[LKNewestMsgManager sharedInstance].content forState:UIControlStateNormal];\ btn.username = [LKNewestMsgManager sharedInstance].username; btn.clipsToBounds = YES; btn.layer.cornerRadius = 10; btn.contentEdgeInsets = UIEdgeInsetsMake(2, 2, 2, 2); [btn addTarget:self action:@selector(backToMsgContentViewController:) forControlEvents:UIControlEventTouchUpInside]; [btn registerNotification]; btn.swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(handleSwipes:)]; btn.swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight; btn.swipeGestureRecognizer.numberOfTouchesRequired = 1; [btn addGestureRecognizer:btn.swipeGestureRecognizer]; [self.view addSubview:btn]; }}

效果

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏青玉伏案

iOS开发之自定义表情键盘(组件封装与自动布局)

  下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,Cor...

290100
来自专栏林德熙的博客

wpf DoEvents 用法原理存在的坑推荐方法

如果在执行一段卡UI的代码,这时如何让UI响应。如果存在代码需要获得依赖属性,那么代码就需要在UI线程执行,但是这时就会卡UI,为了让UI响应,所以就需要使用D...

87810
来自专栏Alice

ios tableview 上加 textfiled

ios tableview 上加 textfiled  首先附上我项目中用曾经用到的几张图  并说明一下我的用法: 图1: ? 图2: ? 图3: ? 心在你我...

26150
来自专栏技术总结

Photos存储、获取、更改照片详解

29490
来自专栏岑志军的专栏

ReactNative-综合案例(02)

19770
来自专栏酷玩时刻

微信公众号开发之自定义菜单

在Jfinal-weixin中有封装菜单的创建、查询、删除、以及个性化菜单的创建、查询、删除、测试个性化菜单匹配结果

16520
来自专栏CRPER折腾记

Angular 2 + 折腾记 :(6) 动手实现只有年月的小组件

这个组件实现并不是很复杂,我会尽量注释; 这货诞生的理由就是项目刚好有一个地方必须只能选择年月,而github上ng2+日期组件都涉及到年月日或时分秒; 效果用...

10310
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(67)-MVC与ECharts

ECharts 特性介绍 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10...

844100
来自专栏友弟技术工作室

GoLang实现google authenticator的CLI工具

两步认证在很多验证中都要使用。如果在手机客户端上,如果使用电脑,每次都要拿出手机,手动输入。还要担心会过时。效率不是很高。

18630
来自专栏Charlie's Road

Container ViewController自定义转场控制器。

最近接触到新公司的老项目改版。自从来了之后一直在忙另一个项目,也没有看老项目的实现逻辑。 看到设计稿的时候,并不是普通的树形标签导航的样子。大致效果如FaceU...

14310

扫码关注云+社区

领取腾讯云代金券