转载: URL http://dev.qq.com/topic/577e0acc896e9ebb6865f321ios
若是您有耐心看完这篇文章,您将懂得如何着手进行app的分析、追踪、注入等实用的破解技术,另外,经过“入侵”,将帮助您理解如何规避常见的安全漏洞,文章大纲:spring
敏锐的嗅觉 有时候经过一个函数名,一个类名,就能大体的判断出它的做用,这就是嗅觉;功力已臻化境时,甚至可使用第六感判断出一些注入点shell
面对失败的勇气 破解有时候很耗时,和程序开发正好相反,它耗时不是耗在写代码上,而是耗在寻找注入点和逆向工程上,有可能你花了3天时间去找程序的破绽,可是最终的破解代码可能就2行,不到一分钟就搞定了;可是你也须要作好面对失败的准备,若是路选错了,有可能你这3天彻底是在浪费脑细胞windows
洪荒之力 洪荒之力-即入侵过程当中须要借助的各类工具,工欲善其事,必先利其器,工具都是前人智慧的结晶,能用工具解决的,毫不要手动去搞api
iOS的入侵离不开越狱开发,一切的破解、入侵都是创建在越狱的基础上的,若是没有拿到系统级权限,一切的想法都是空谈了,固然,市面上存在免越狱的破解补丁,可是它的开发过程,也是基于越狱环境的xcode
在iOS的黑客界,要作破解或越狱开发,就必须了解tweak,它是各类破解补丁的统称,在google上,若是你想搜索一些越狱开发资料或者开源的破解补丁代码,它是最好的关键字。sass
iOS的tweak大体分为两种:安全
第一种是在cydia上发布的,须要越狱才能安装,大部分是deb格式的安装包,iOS在越狱后,会默认安装一个名叫mobilesubstrate的动态库,它的做用是提供一个系统级的入侵管道,全部的tweak均可以依赖它来进行开发,目前主流的开发工具备theos和iOSOpenDev,前者是采用makefile的一个编译框架,后者提供了一套xcode项目模版,能够直接使用xcode开发可调试,可是这个项目已经中止更新了,对高版本的xcode支持很差,你们酌情选择(本文中的例子所有采用theos)微信
第二种是直接打包成ipa安装包,并使用本身的开发证书或者企业证书签名,不需越狱也能够安装,可直接放到本身的网站上,可实如今线安装;对于没有越狱的手机,因为权限的限制,咱们是没有办法写系统级的tweak的,例如springboard的补丁是无法运行的,这种tweak大可能是针对某个app,把目标app进行修改注入处理,再从新签名和发布,有点相似于windows软件的xxx破解版、xxx免注册版网络
没有越狱的机器因为系统中没有mobilesubstrate这个库,咱们有二个选择,第一个是直接把这个库打包进ipa当中,使用它的api实现注入,第二个是直接修改汇编代码;第一个适用于较为复杂的破解行为,并且越狱tweak代码能够复用,第二种适用于破解一些if…else…之类的条件语句
下面的图展现的就是oc届著名的method swizzling技术,他就是iOS的注入原理,相似于windows的钩子,因此咱们注入也称为hook
Mobilesubstrate为了方便tweak开发,提供了三个重要的模块:
MobileHooker 就是用来作上面所说的这件事的,它定义一系列的宏和函数,底层调用objc-runtime和fishhook来替换系统或者目标应用的函数
MobileLoader 用来在目标程序启动时根据规则把指定目录的第三方的动态库加载进去,第三方的动态库也就是咱们写的破解程序,他的原理下面会简单讲解一下
Safe mode 相似于windows的安全模式,好比咱们写的一些系统级的hook代码发生crash时,mobilesubstrate会自动进入安全模式,安全模式下,会禁用全部的第三方动态库
上面讲到了mobileloader,他是怎么作到把第三方的lib注入进目标程序的呢?这个咱们要从二进制文件的结构提及,从下面的图来看,Mach-O文件的数据主体可分为三大部分,分别是头部(Header)、加载命令(Load commands)、和最终的数据(Data)。mobileloader会在目标程序启动时,会根据指定的规则检查指定目录是否存在第三方库,若是有,则会经过修改二进制的loadCommands,来把本身注入进全部的app当中,而后加载第三方库。
为了让你们看的更清楚,下面我用machoview来打开一个真实的二进制文件给你们看看,能够看出,二进制当中全部引用到的动态库都放在Load commands段当中,因此,经过给这个段增长记录,就能够注入咱们本身写的动态库了
那么问题来了,在这里插入咱们本身的动态库有什么用?咱们本身写的代码没有执行的入口,咱们同样没发干坏事,嗯,恭喜你问到点子上了,咱们还须要一个"main"函数来执行咱们本身的代码,这个"main"函数在oc里面称为构造函数,只要在函数前声明 “attribute((constructor)) static” 便可,有了它咱们就能够发挥想象力,进行偷天换日干点坏事了:
#import <CaptainHook/CaptainHook.h> CHDeclareClass(AnAppClass); CHMethod(1, void, AnAppClass, say, id, arg1) { NSString* tmp=@"Hello, iOS!"; CHSuper(1, AnAppClass, say, tmp); } __attribute__((constructor)) static void entry() { NSLog(@"Hello, Ice And Fire!"); CHLoadLateClass(AnAppClass); CHClassHook(1, AnAppClass,say); }
到这里为止,咱们已经知道了怎么在目标程序注入本身的代码,那么咱们怎么知道须要hook哪些方法?怎么找到关键点进行实际的破解呢?下面讲一下常见的app入侵分析方法
###iOS逆向分析方法
逆向分析最经常使用的有三种方法:
网络分析 经过分析和篡改接口数据,能够有效的破解经过接口数据来控制客户端行为的app,经常使用的抓包工具备Tcpdump, WireShark, Charles等,windows平台有fidller
静态分析 经过砸壳、反汇编、classdump头文件等技术来分析app行为,经过这种方式能够有效的分析出app实用的一些第三方库,甚至分析出app的架构等内容,经常使用的工具备dumpdecrypted(砸壳)、hopper disassembler(反汇编)、class_dump(导头文件)
动态分析 有静就有动,万物都是相生相克的,动态分析指的是经过分析app的运行时数据,来定位注入点或者获取关键数据,经常使用的工具备cycript(运行时控制台)、 lldb+debugserver(远程断点调试)、logify(追踪)
上面讲了不少原理性的东西,相信你们已经看的不耐烦了,下面咱们一块儿动点真格的,咱们从头开始,一步一步的作一个微信的自动抢红包插件,固然,网上可能已经有相关的开源代码了,可是我这里要讲的是,这些代码是怎么得出来的,我么重点讲一讲分析过程
一台越狱的手机,并装有如下软件
一台苹果电脑,并装有如下软件
###寻找注入点
首先咱们要作的就是把微信的壳砸掉,砸壳实际上是为了把它的头文件classdump出来,由于从appstore下载的app二进制都是通过加密的,直接进行classdump操做是啥也看不出来的
xxx$ cp /usr/lib/dumpdecrypted.dylib /path/to/app/document xxx$ DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /path/to/WeChat
执行完这几行命令后,会在微信的documents目录生成一个WeChat.decrypted文件,这就是砸壳后的二进制文件;固然了,这一步不是必须的,咱们能够直接从91或者pp助手下载一个已经砸过壳的版本
要想实现自动抢红包,咱们必须找到收到红包消息的handler方法,怎么入手呢?咱们先从界面出发,进入微信的消息首发窗口:
ps aux | grep WeChat
cycript -p pidxxx
UIApp.keyWindow.recursiveDescription().toString()
最终的输出以下,内容太多,你们确定看不清楚,不过不要紧,这个不是重点,这里只是展现一下打印的结果形式:
咱们能够随机的选取一个节点不要太靠树叶,也不要太靠树根,例如我选的是标红的部分,把这个节点的内存地址copy出来,这个内存地址,就表明了这个节点的view对象,ios开发的老油条们都知道,经过view的nextResponder方法,能够找出它所属的视图控制器ViewController,因此我么在cycript的控制台中持续输入以下的命令:
看到没有,经过四个nextResponder方法调用,我么找到了当前聊天窗口的ViewController类名,他就是BaseMsgContentViewController,如今咱们缩小了目标范围,下面咱们还须要继续缩小范围,要找到具体的消息处理函数才行。
要继续缩小范围,就得祭起神器Logify了,它是theos的一个模块,做用就是根据头文件自动生成tweak,生成的tweak会在头文件的全部方法中注入NSLog来打印方法的入参和出参,很是适合追踪方法的调用和数据传递
如今咱们根据此前砸壳后class_dump出来的头文件,找到BaseMsgContentViewController在pc终端执行以下命令:
logify.pl /path/to/BaseMsgContentViewController.h > /out/to/Tweak.xm
输出的tweak文件大概是这个样子的:
这里带百分号的关键字,例如 %hook、%log、%orig 都是mobilesubstrate的MobileHooker模块提供的宏,其实也就是把method swizzling相关的方法封装成了各类宏标记,使用起来更简单,你们想要更深刻了解各类标记,能够google一下logos语言
上面咱们用logify生成了一个tweak代码,咱们要把它安装到手机上,首先须要使用theos进行编译,安装了theos以后,在pc终端输入nic.pl:
首先选择项目模版固然是tweak啦,而后是项目名称、做者,后面两个选项要注意:
最后一切都完成后,在当前目录会生成下列文件:
把上面logify生成的tweak文件覆盖到当前目录,并用文本编辑器打开makefile文件,在文件的开头增长你的ios设备的ip地址和ssh端口:
最后在pc终端进入项目目录,输入 make package install 命令:
期间会让你输入设备的ssh密码,越狱机器的默认ssh密码是alpine,make命令会生成deb安装包,放在debs目录,咱们若是想对外发布本身的插件,能够把生成的安装包上传到cydia便可
安装成功后再次进入微信的聊天界面,并使用另一个微信在群里发个普通消息,链接xcode打开越狱机器控制台,查看输出,会发现有相似下面的输出:
Jun 7 09:56:13 Administratorde-iPhone WeChat[85972] <Notice>: [1;36m[WxMsgPreview] [m[0;36mTweak.xm:308[m [0;30;46mDEBUG:[m -[<BaseMsgContentViewController: 0x15e0c9a00> addMessageNode:{m_uiMesLocalID=2, m_ui64MesSvrID=0, m_nsFromUsr=ccg*675~9, m_nsToUsr=1037957572@chatroom, m_uiStatus=1, type=1, msgSource="(null)"} layout:1 addMoreMsg:0]
看出来了吧,消息处理函数是BaseMsgContentViewController的**addMessageNode:layout:addMoreMsg:**方法,你们能够看出,方法的参数内容也打印出来了
到目前为止,我么已经把范围缩小到了具体的函数,看起来注入点已经找到了,可是请你们思考一下,若是咱们在这个函数中注入抢红包逻辑,那咱们的tweak会不会有什么致命的缺陷?
是的,由于BaseMsgContentViewController这个类是微信群聊天窗口对应的controller,我么必须进入到群的聊天界面,这个类才会建立,若是不进入聊天窗口,咱们的插件就不生效了,并且,即便进入聊天窗口,也只是能自动枪当前群的红包而已,其余群就无能为力了,是否是有点low?
因此为了使咱们的插件显得上流一些,我么还要继续追根溯源,寻找消息的源头,这里就用到了lldb远程调试,使用lldb打断点的方式,经过调用栈,咱们能够就能够看到当消息来到时,方法的调用顺序,找到最早执行的消息处理函数。
要在刚刚追踪到的**addMessageNode:layout:addMoreMsg:**方法中打断点,首先咱们得知道它在运行时的内存地址,那么内存地址怎么来呢?有这么一个公式:
首先偏移量咱们能够经过反汇编工具hooper来查,在pc上用hooper打开微信的二进制文件(注意,打开时会让你选择armv7或者arm64,这须要根据你越狱手机的cpu类型来选,必定要和你的手机一致),hooper的界面很是简洁,左侧有个搜索框,能够输入函数名,直接找到函数在二进制中的位置
经过左侧的搜索框搜addMessageNode关键字,找到它的偏移量是0x00000001017d7c6c:
找到了偏移量,还须要进程的基地址,这个地址须要连lldb,因此下面讲一下如何链接lldb进行远程调试,先ssh进越狱手机的终端,在终端输入以下命令(注意,你的手机必须连xcode调试过才会有这个命令):
debugserver *:19999 -a WeChat
而后在pc端新起一个终端窗口,输入以下命令来链接手机端进行调试:
lldb -> process connect connect://deviceIP:19999
若是链接成功,会进入lldb的控制台,咱们在lldb的控制台输入以下命令来获取微信进程的基地址:
image list -o -f
执行这个命令会打印不少行数据,像下面图中这样,我么要找到微信的二进制文件所在的行,记录它的内存地址0X00000000000E800:
到这里咱们两个地址都找到了,再经过br命令打断点:
br s -a '0X00000000000E800+0x00000001017d7c6c'
打好断点后继续向群里面发消息,咱们会发现进程被断掉了,这时输入bt指令,就能够看到当前的调用栈,就像下图这样:
分析堆栈的时候,重点找出模块时WeChat的项,这些都是微信模块的方法调用,有了堆栈,咱们须要根据堆栈的内存地址找出它的具体函数名,思路仍是先根据上面讲到的公式来计算出栈地址在二进制中的偏移量,而后用hooper找到偏移量对应的函数名
例如根据箭头所指的内存地址和刚刚获得的进程基地址,计算偏移量:
0x0000000101ad02f4 – 0x00000000000e8000 = 1019E82F4
而后在hooper中搜索这个地址,获得结果以下:
最终把全部的栈都进行还原,得出调用栈是这个样子的:
-[CMessageMgr MainThreadNotifyToExt:]: –> -[BaseMsgContentLogicController OnAddMsg:MsgWrap:]: ——> -[RoomContentLogicController DidAddMsg:] ———-> -[BaseMsgContentLogicController DidAddMsg:] —————-> -[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]:
CMessageMgr这个类浮出水面了,是时候发挥黑客的嗅觉了,根据方法名咱们能判断出MainThreadNotifyToExt:这个方法仅仅是用来发送通知的,若是hook这个方法,咱们是拿不到消息内容的
因为这里多是一个异步调用,用断点的方式,可能已经打印不出来栈信息了,因此还得使用logify来继续追踪CMessageMgr这个类,讲过的内容我就不重复了,直接获得最终的消息处理函数:
-(void)AsyncOnAddMsg:(id)message MsgWrap:(CMessageWrap* )msgWrap
上一节咱们已经找到了hook的关键点,那么该如何去实现抢的动做?一样咱们须要结合动态分析和静态分析,首先获得红包消息体的数据特征,而后再分析处理消息的关键点
首先咱们的代码须要分辨哪些才是红包消息,方法很简单,用logify追踪BaseMsgContentViewController,而后向微信群发一个红包,观察手机日志输出,咱们能够看出消息的数据结构中有个type字段,值是49,这个type应该就是标记消息类型的,若是不肯定,能够再发个图片或者文本之类的消息,这个值是不一样的:
Administratorde-iPhone WeChat[47410] <Notice>: [1;36m[WxMsgPreview] [m[0;36mTweak.xm:308[m [0;30;46mDEBUG:[m -[<BaseMsgContentViewController: 0x15e0c9a00> addMessageNode:{m_uiMesLocalID=16, m_ui64MesSvrID=1452438635530425509, m_nsFromUsr=1037957572@chatroom, m_nsToUsr=ccg*675~9, m_uiStatus=4, type=49, msgSource="<msgsource> <silence>0</silence> <membercount>3</membercount> </msgsource> "} layout:1 addMoreMsg:0]
如今咱们能分辨消息类型了,重点来了,怎么实现抢这个事呢,可能聪明人已经猜到了,从ui入手,先找到微信自己的抢红包函数,咱们本身来给它构造参数并调用他不就好了?
把红包点开后,用cycript打印出当前view的层次,就像下面这个,一眼就能够看到重点,WCRedEnvelopesReceiveHomeView就是开红包弹框的类名
知道类名后,用cycript追踪它,点击开红包,在日志中找到了下图中的内容,从名字来看,这是一个事件处理函数,咱们如今要作的,就是把他还原成oc代码,真正实现抢红包功能
Administratorde-iPhone WeChat[91173] <Notice>: [1;36m[WxMsgPreview] [m[0;36mTweak.xm:8[m [0;30;46mDEBUG:[m -[<WCRedEnvelopesReceiveHomeView: 0x13cdda8c0> OnOpenRedEnvelopes]
怎么把他还原成oc代码,真正实现抢红包功能呢?还得借助一点点汇编技能,只是一点点而已,由于如今的反汇编工具已经很强大了,咱们不须要挨个去看寄存器了
在pc上用hooper打开微信的二进制文件,搜索OnOpenRedEnvelopes,查看汇编代码,注意在图片中最后一行调用了一个WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes函数
继续搜索WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes这个方法,找到它的汇编代码
最终反解出的代码以下,是否是很简单?
NSString *nativeUrl = [[msgWrap m_oWCPayInfoItem] m_c2cNativeUrl]; nativeUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]]; NSDictionary *nativeUrlDict = [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"];
继续往下看, 在这里前面三行建立了一个mutable dictionary:
最终获得的代码以下:
NSMutableDictionary *args = [[%c(NSMutableDictionary) alloc] init]; [args setObject:nativeUrlDict[@"msgtype"] forKey:@"msgType"]; [args setObject:nativeUrlDict[@"sendid"] forKey:@"sendId"]; [args setObject:nativeUrlDict[@"channelid"] forKey:@"channelId"];
继续往下看从箭头所指的几处,咱们能够看见,它的代码是这样的,共分为四步
最终还原的到的代码以下:
CContactMgr *contactManager = [[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]]; CContact *selfContact = [contactManager getSelfContact];
继续往下看,这里使用刚刚获得的selfcontact来获取displayname和headimgurl,并把它们设置到刚刚的字典里面了,key分别是nickname和headimg
最终的代码:
[args setObject:[selfContact getContactDisplayName] forKey:@"nickName"]; [args setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"];
接着看,接下来这两段就比较蛋疼了,彻底是从内存地址里面取的值,我也不知道他从哪里来,怎么办呢?有没有不懂汇编就能搞定它的捷径呢,答案是有!
最终的结果以下:
[args setObject:nativeUrl forKey:@"nativeUrl"]; [args setObject:xxx forKey:@"sessionUserName"];
继续往下看,接下来这一段仍是用mmservicecenter来获取WCRedLogicMgr对象,而后调用WCRedLogicMgr的open方法来拆红包,能够想象open方法的参数就是上面咱们辛苦组装的字典
代码以下:
[[[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]] OpenRedEnvelopesRequest:args];
到这里,咱们再总结一下咱们上面分析的过程…
最终的抢红包代码合并起来以下:
#import "WxMsgPreview.h" %hook CMessageMgr -(void)AsyncOnAddMsg:(id)message MsgWrap:(CMessageWrap* )msgWrap { %log; %orig; if(msgWrap.m_uiMessageType == 49){ CContactMgr *contactManager = [[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]]; CContact *selfContact = [contactManager getSelfContact]; if ([msgWrap.m_nsContent rangeOfString:@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao"].location != NSNotFound) { // 红包 NSString *nativeUrl = [[msgWrap m_oWCPayInfoItem] m_c2cNativeUrl]; nativeUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]]; NSDictionary *nativeUrlDict = [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"]; NSMutableDictionary *args = [[%c(NSMutableDictionary) alloc] init]; [args setObject:nativeUrlDict[@"msgtype"] forKey:@"msgType"]; [args setObject:nativeUrlDict[@"sendid"] forKey:@"sendId"]; [args setObject:nativeUrlDict[@"channelid"] forKey:@"channelId"]; [args setObject:[selfContact getContactDisplayName] forKey:@"nickName"]; [args setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"]; [args setObject:nativeUrl forKey:@"nativeUrl"]; [args setObject:msgWrap.m_nsFromUsr forKey:@"sessionUserName"]; [[[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]] OpenRedEnvelopesRequest:args]; } } }