本文不涉及IDA+LLDB,并结合自己的开发经验讲一下其他方面,纯技术研究学习,不做其他用途
0x01 基本功课
逆向工程
逆向工程(又称反向工程),是一种技术过程,即对一项目标产品进行逆向分析及研究,从而演绎并得出该产品的处理流程、组织结构、功能性能规格等设计要素,以制作出功能相近,但又不完全一样的产品。逆向工程源于商业及军事领域中的硬件分析。其主要目的是,在不能轻易获得必要的生产信息下,直接从成品的分析,推导出产品的设计原理。
runtime
- RunTime简称运行时。OC就是
运行时机制
,也就是在运行时候的一些机制,其中最主要的是消息机制。 - 对于C语言,
函数的调用在编译的时候会决定调用哪个函数
。 - 对于OC的函数,属于
动态调用过程
,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。 - 在编译阶段,OC可以
调用任何函数
,即使这个函数并未实现,只要声明过就不会报错。 - 在编译阶段,C语言调用
未实现的函数
就会报错。
Method Swizzling(方法调配)
先了解一下SEL和IMP的概念,
SEL可以理解为函数名的意思,我们常用的@selector()就是通过字符串获得SEL
IMP可以理解成函数指针的意思,是能正确读取到函数的实现
一般是这样的(借用网络图片):
我们要做的就是把链接线解开,然后连到我们自定义的函数IMP上,如果有需要的话,我们再连回原来的IMP上(iOS平台上很多统计或者bug上报SDK之间的冲突就是hook后没有抛回)
会用到的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Method method1 = class_getInstanceMethod(class, origSelector);//获取SEL的实例方法 Method method2 = class_getClassMethod(class, origSelector);//获取SEL的类方法 //Method结构体 struct objc_method { SEL method_name OBJC2_UNAVAILABLE; char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; } IMP origIMP = method_getImplementation(origMethod); //获取Method中的IMP(方法实现) //在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段。编译器会将消息发送转换成对objc_msgSend方法的调用。也就是说,OC里面对象调用方法的本质是给对象发送一个消息 objc_msgSend(receiver, selector); |
0x02 先举一个简单的TCP IM例子
长连接
这里拿一个博主以前用Socket开发的IM来演示说明,因为这个例子比较形象易懂。
这里面可以看出,每一个IM长连接建立Stream之后,上面收发的都是Packet包,其中Message消息包含在Packet包中。
包&消息分析
一个IM长连接中有多种类型的包,比如控制包(对连接进行建立和管理)、信息包(真正承载我们发送聊天信息的包)、回执包和心跳包等等,这些都是通过包的Type字段去区分的。
如果收到的是信息包,则再根据对应信息包的消息Type,也就是下图的t(target)来判断是何种消息,比如(1:私聊,2:群聊,3:公众号等等)。
0x03 回到对微信的分析和操作
过程:
1.解密微信可执行文件(Mach-O)俗称砸壳->2.dump微信可执行文件(获取头文件)->3.编写dylib代码->4.为微信可执行文件注入dylib->5.给微信重新签名并打包
1.砸壳(敲壳)
因为从Appstore下载安装的应用都是加密过的,所以我们需要用一些工具来为下载的App解密,俗称砸壳。这样才能便于后面分析App的代码结构,下图展示2种砸壳。
砸壳之后把砸壳出来的文件传到本机,这里可以直接使用SCP命令
1 |
scp root@192.168.2.2:/var/mobile/Documents/WeChat.decrypted ./ |
也可以使用Transmit这类的SFTP软件,可视化下载
2.dump头文件
使用class-dump命令,把刚刚砸壳后的WeChat.decrypted,导出其中的头文件,使用xcode新建项目放进去头文件方便查看,怎么调试定位到类和方法这里不讲。
找到CMessageMgr.h和WCRedEnvelopesLogicMgr.h这两文件和如下方法
- (void)AsyncOnAddMsg:(id)arg1 MsgWrap:(id)arg2;
- (void)OpenRedEnvelopesRequest:(id)arg1;
越狱手机也可以通过Flex来查看APP头文件:
3.编写dylib
这里我把代码做了最精简的简化,方便解读
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//Ivar:定义对象的实例变量 Ivar uiMessageTypeIvar = class_getInstanceVariable(objc_getClass("CMessageWrap"), "m_uiMessageType"); //获取成员变量的偏移量 ptrdiff_t offset = ivar_getOffset(uiMessageTypeIvar); unsigned char *stuffBytes = (unsigned char *)(__bridge void *)arg2; //获取到消息类型 NSUInteger m_uiMessageType = * ((NSUInteger *)(stuffBytes + offset)); Ivar nsFromUsrIvar = class_getInstanceVariable(objc_getClass("CMessageWrap"), "m_nsFromUsr"); //获取到消息来源用户 id m_nsFromUsr = object_getIvar(arg2, nsFromUsrIvar); Ivar nsContentIvar = class_getInstanceVariable(objc_getClass("CMessageWrap"), "m_nsContent"); //获取到消息内容 id m_nsContent = object_getIvar(arg2, nsContentIvar); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
//微信的服务中心 Method methodMMServiceCenter = class_getClassMethod(objc_getClass("MMServiceCenter"), @selector(defaultCenter)); IMP impMMSC = method_getImplementation(methodMMServiceCenter); id MMServiceCenter = impMMSC(objc_getClass("MMServiceCenter"), @selector(defaultCenter)); //红包控制器 id logicMgr = ((id (*)(id, SEL, Class))objc_msgSend)(MMServiceCenter, @selector(getService:),objc_getClass("WCRedEnvelopesLogicMgr")); //通讯录管理器,稍后拼接红包参数用 id contactManager = ((id (*)(id, SEL, Class))objc_msgSend)(MMServiceCenter, @selector(getService:),objc_getClass("CContactMgr")); Method methodGetSelfContact = class_getInstanceMethod(objc_getClass("CContactMgr"), @selector(getSelfContact)); IMP impGS = method_getImplementation(methodGetSelfContact); id selfContact = impGS(contactManager, @selector(getSelfContact)); if ([m_nsContent rangeOfString:@"wxpay://"].location != NSNotFound) { NSString *nativeUrl = m_nsContent; NSRange rangeStart = [m_nsContent rangeOfString:@"wxpay://c2cbizmessagehandler/hongbao"]; if (rangeStart.location != NSNotFound) { NSUInteger locationStart = rangeStart.location; nativeUrl = [nativeUrl substringFromIndex:locationStart]; } NSRange rangeEnd = [nativeUrl rangeOfString:@"]]"]; if (rangeEnd.location != NSNotFound) { NSUInteger locationEnd = rangeEnd.location; nativeUrl = [nativeUrl substringToIndex:locationEnd]; } NSString *naUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]]; NSArray *parameterPairs =[naUrl componentsSeparatedByString:@"&"]; NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity:[parameterPairs count]]; for (NSString *currentPair in parameterPairs) { NSRange range = [currentPair rangeOfString:@"="]; if(range.location == NSNotFound) continue; NSString *key = [currentPair substringToIndex:range.location]; NSString *value =[currentPair substringFromIndex:range.location + 1]; [parameters setObject:value forKey:key]; } //红包参数 NSMutableDictionary *params = [@{} mutableCopy]; [params setObject:parameters[@"msgtype"]?:@"null" forKey:@"msgType"]; [params setObject:parameters[@"sendid"]?:@"null" forKey:@"sendId"]; [params setObject:parameters[@"channelid"]?:@"null" forKey:@"channelId"]; id getContactDisplayName = objc_msgSend(selfContact, @selector(getContactDisplayName)); id m_nsHeadImgUrl = objc_msgSend(selfContact, @selector(m_nsHeadImgUrl)); [params setObject:getContactDisplayName forKey:@"nickName"]; [params setObject:m_nsHeadImgUrl forKey:@"headImg"]; [params setObject:[NSString stringWithFormat:@"%@", nativeUrl]?:@"null" forKey:@"nativeUrl"]; [params setObject:m_nsFromUsr?:@"null" forKey:@"sessionUserName"]; //自动抢红包 ((void (*)(id, SEL, NSMutableDictionary*))objc_msgSend)(logicMgr, @selector(OpenRedEnvelopesRequest:), params); return; } |
4.注入dylib
原理就是如下,这块我没仔细研究过
通过修改可执行文件的Load Commands,增加一个LC_LOAD_DYLIB,写入dylib路径。这样程序执行的时候就会来编译这个LC_LOAD_DYLIB找到要注入的dylib,从而进行加载。
用工具yololib进行注入
1 |
./yololib WeChat libautoGetRedEnv.dylib |
5.重签名打包
重签名生成ipa有多种方法,这里只写2种。
第一种是先对dylib和.app单独签名,之后再利用xcode自身的打包功能重打包
1 2 |
codesign -f -s "iPhone Distribution: XXXXX Technology Co., Ltd." libautoGetRedEnv.dylib codesign -f -s "iPhone Distribution: XXXXX Technology Co., Ltd." --entitlements Entitlements.plist WeChat.app |
1 |
xcrun -sdk iphoneos PackageApplication -v WeChat.app -o ~/Desktop/zrwechat/WeChat.ipa |
第二种方法是直接利用xcode重打包,之后利用重签名软件进行签名,其实软件也是先解压包,单独签名之后再重新打包
0x04 对我们日常开发有什么帮助
更深入了解iOS安全机制,锻炼逆向『思维』去分析其他APP功能来辅助开发自己的需求
客户端总是系统数据的一个闭环出口,在平时的开发设计中注意培养数据分离和对敏感安保的内容加固的意识
0x05 过程中遇见的一些问题
遇见codesign failed with exit code 1
检查钥匙串的证书里面是不是有重复添加过的签名证书,如果有,删掉多余并保留正确的证书
砸壳的时候,遇见Killed: 9
错误
登录mobile用户:su mobile,切换到mobile有写入权限的路径:cd /var/mobile/document/
参考文章:
iOS逆向工程-内部钩子(Method Swizzling)
使用到的工具和源码
源码和工具 :链接: https://pan.baidu.com/s/1pKShJT5 密码: wxrb