《 iOS应用逆向工程 》第一作者沙梓社——iOS逆向工程一隅

侍凝蕊

2017/11/14 发布于 技术 分类

iDev 全平台开发者大会 2016会议上,沙梓社带来《fabiaiOS逆向工程一隅》,沙梓社,《 iOS应用逆向工程 》第一作者;非典型技术男。做事标准较高,喜欢归纳总结,推崇深度思考,乐于讨论分享。渴望结识优秀人才,督促自己不断成长。

文字内容
1. 沙梓社 iOS逆向工程一隅 ⼤大家好,我是沙梓社,《iOS应⽤用逆向⼯工程》的第⼀一作者。 我的⺴⽹网名叫“⼤大名狗剩”,很多朋友可能对这个称呼更熟悉⼀一些。 我从事iOS逆向⼯工程的学习和⼯工作,已经有6个年头了;在座的估计也有很多低调的⾼高⼿手是这个⾏行业的资深从业⼈人员。毕竟北京是我们国家互联⺴⽹网⾏行业最发达的城市。▶
2. 北京 从我懂事后开始算起,这是我第8次来北京。 我还记得第1次来北京玩的时候,是⼤大三升⼤大四的暑假。那个夏天,我住在同学家,⽩白天跟同学⼀一起出去感受北京的⻛风⼟土⼈人情,晚上回到他的家⾥里,他妈妈做饭,⼤大家⼀一起 吃,感觉⾮非常好。▶
3. 有⼀一天,我们准备去爬⻓长城。 阿姨就说,爬⻓长城挺累的,犒劳犒劳你们,晚上给你们包饺⼦子吃。 听说北⽅方不是过年吃饺⼦子,过⽣生⽇日吃饺⼦子,国庆节吃饺⼦子,⼤大⼤大⼩小⼩小的庆祝活动都要吃饺⼦子,所以我那个同学很⾼高兴,⾮非常期待。 但是呢,我的兴致不⾼高。为啥呢? 我的妈妈那边,外公外婆,来⾃自陕北延安那边的穷⼭山沟沟⾥里,穷嘛,饮⾷食就不太讲究,虽然也遵从我们的习俗,逢年过节包饺⼦子吃,但我从来没有觉得家⾥里的饺⼦子好吃过, 每次吃饺⼦子我都像完成任务⼀一样,“必须吃15个,吃不完不让出去玩”,所以对饺⼦子的印象不是太好。 我当时⼼心⾥里还想,要不爬完⻓长城在外边吃点再回去得了,后来拗不住⾯面⼦子,就算了。 爬完⻓长城,⽩白天累了⼀一天,晚上吭哧吭哧回去了。 很戏剧的⼀一件事发⽣生了。不知道是饿了还是怎么,觉得阿姨包的饺⼦子太好吃了,毫不夸张地说,是我到⺫⽬目前为⽌止吃过最好吃的饺⼦子。说它的⽪皮⼉儿特别薄呢也不是,说它的⾁肉 特别多呢也不是,就是感觉⽪皮⼉儿和馅⼉儿的搭配,⼝口感特别好。▶
4. 回家自己做着吃??????   不是原来的配方?????? 既然这饺⼦子这么好吃,那就想着学两⼿手回去⾃自⼰己做着吃嘛。但是呢▶ 不是原来的配⽅方,⾃自然也没有熟悉的味道,⾃自⼰己做的就始终做不出来那个味道,不好吃。那怎么办呢? 如果饺⼦子是朋友做的,就跟我的情况⼀一样,配⽅方呢,是可能可以打听到的; 但是,如果这个饺⼦子是餐馆做的,那餐馆肯定是不会把配⽅方给我的,对吧? 这个是商业机密,⼈人家就指着这个挣钱呢。怎么会给你呢? 好了,在这种情况下,我们吃到了好吃的饺⼦子,想回去⾃自⼰己尝试着包⼀一下,但是拿不到它的配⽅方,怎么办呢?▶
5. 会包饺⼦子的⼈人,都知道做饺⼦子⼤大概就是这么⼏几步:▶和⾯面、拌饺⼦子馅、包饺⼦子、煮饺⼦子。对不对? 但是呢,虽然说这样能把饺⼦子包出来,能吃,但是好不好吃,影响饺⼦子⼝口感的主要在于配⽅方的细节。和⾯面的时候加不加其他东⻄西?绊馅的时候,放多少茴⾹香,多少⾁肉,煮的 时候,煮多⻓长时间,等等。这些细节不搞清楚,我们就不可能做出⼀一模⼀一样的饺⼦子,对吧? 好了,那应该如何搞清这些细节呢?▶
6. 照着配方包饺子,是正向开发   吃着饺子推配方,是逆向工程 其实从刚才吃饺⼦子这件事⼉儿⾥里,我们就可以⼀一窥逆向⼯工程的⾯面⺫⽬目。▶读图…… 在刚才的场景中,如果我们掌握了逆向⼯工程的技能,就可以达到我们学习饺⼦子配⽅方的⺫⽬目的了。▶
7. 配方:API  调用顺序   效果:闭源  实现原理 对于包饺⼦子来说,它的配⽅方是是⽔水、⾯面粉、⽕火候这些东⻄西。▶ 但是对于iOS逆向⼯工程来说,它的配⽅方内容当然就不是这些东⻄西了,⽽而是API以及它们的调⽤用顺序。▶ iOS逆向⼯工程的效果,就是在闭源的环境下,通过还原程序所使⽤用的API及它们的调⽤用顺序,从⽽而倒推出它的实现原理。 也就是说,我们通过品味煮好的饺⼦子,来分析出它在包的时候⽤用了多少⾯面粉,馅⾥里各种材料的⽐比例,煮了多久等等细节,来还原出这个饺⼦子的配⽅方。▶
8. 解决啥问题? 好了,当我们通过这个饺⼦子的例⼦子,对iOS逆向⼯工程的概念有了初步的认识后,⼤大家可能想知道,它可以解决什么问题呢?▶
9. 咱们还是回到饺⼦子上。 在吃完好吃的饺⼦子,酒⾜足饭饱之后,基于饺⼦子,我们可能会有⼀一些其他的想法,⽐比如说:▶ 如果在和⾯面的时候打个鸡蛋,饺⼦子⽪皮会不会更劲道⼀一些?▶ 我⼝口味不重,感觉饺⼦子馅有点太咸了,如果少放点盐,饺⼦子⼝口感会不会更好⼀一些?▶ 有的朋友呢,喜欢吃酸甜⼝口。如果饺⼦子醋⾥里加点⽩白糖,会不会更合我们的胃⼝口呢?▶
10. iOS逆向⼯工程⼀一隅 ▸ 增加新的功能模块。 ▸ 分析已有功能模块: ▸ 修复bug; ▸ 修改定制; ▸ 学习提⾼高。 ▸ 换个⾓角度看待⾃自⼰己曾经熟悉的事物。 我们把刚才对饺⼦子提出的这些问题,对应到iOS逆向⼯工程中。可以解决的问题主要有:▶ 我们给⾯面粉⾥里打⼀一个鸡蛋,相当于给现有的程序增加新的功能模块▶ 我们吃饺⼦子觉得有点咸,或者说觉得饺⼦子醋不够甜,相当于我们分析了现有的功能模块▶ 饺⼦子太咸了,少放点盐,就相当于修复bug了;⼤大家如果感兴趣,可以看看论坛上干货分享区我写的⼀一篇⽂文章,就通过逆向⼯工程来修复了QQ国际版iOS客户端的⼀一个bug▶ 我喜欢酸甜⼝口味,给饺⼦子醋⾥里加点糖,相当于我们根据⾃自⼰己的需求来定制、修改现有的功能模块▶ 当然,饺⼦子的配⽅方就是我们逆向出来的,相当于我们学习了其他的好产品,来提⾼高⾃自⼰己的⽔水平▶ 除此之外呢,我认为最重要的⼀一点,就是逆向⼯工程,能够促使我们从另⼀一个⾓角度来看待⾃自⼰己曾经熟悉的事物,从⽽而激发出⾮非常多的灵感;绝⼤大多数时候,不是做不到,⽽而是 没想到。 ⼀一旦我们养成了逆向思维的习惯,它就会⾃自然⽽而然地去促使我们去“Think Different”。就我个⼈人来说,这是我学习逆向⼯工程中最⼤大的收获▶
11. 这⾥里的2张截图,就是iOS逆向⼯工程的2个⽤用例。我们刚才也提到过: 左边的,是“给现有的程序增加新的功能模块”的例⼦子,我给记事本增加了⼀一个新的导航栏按钮; 右边的,是“修改定制已有功能模块”的例⼦子,我把记事本标题的内容和字体颜⾊色都给换掉了。 当然,这2个例⼦子只是证明,逆向⼯工程可以干这些事,⾄至于这些事的意义⼤大不⼤大,那就另说了。 不过呢,以此为出发点,我们可以做的事情就⽐比纯正向开发多了很多,相当于打开了另⼀一扇⻔门。在这扇⻔门后⾯面,哪些事情是有意义的,值得做的,就需要⼤大家发挥各⾃自的聪 明才智,来集思⼲⼴广益了。 关于这些细节,欢迎⼤大家到我们的论坛上来讨论;等会⼉儿的讨论环节或者是我的分享结束后,我们也可以进⼀一步地沟通交流。▶
12. 咋解决问题? 刚才我们抛出了iOS逆向⼯工程能够解决的问题,接下来我们看看,如何解决这些问题。在我所专注的iOS应⽤用层上,解决逆向⼯工程问题,其实是有⽐比较明显的规律可循的▶
13. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; 解决iOS应⽤用层的逆向⼯工程问题,⼀一般呢,分为8个步骤。第⼀一步,读⽂文字。 当我们对⼀一个App感兴趣的时候,绝⼤大多数情况下是我们在界⾯面上看到了或者听到了感兴趣的东⻄西,尤其是看到了感兴趣的东⻄西,对吧……简单介绍。 我拿⾃自⼰己⽇日常使⽤用的⼀一个App,Pixlr,来举个例⼦子▶
14. ⽐比如,这是⼀一个图⽚片处理软件,我们可以给照⽚片加上各种滤镜,来美化照⽚片▶Hagrid这个滤镜的效果,就是右边这2张照⽚片所展⽰示出来的,上⾯面的图在经过滤镜处理后,锐化 了很多。我所感兴趣的,就是这个滤镜是怎么做的,它的实现原理是什么。▶
15. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; 第⼆二步,读⽂文字。 所有从AppStore下载的App,苹果都会给它加密,⽤用逆向⼯工具分析时,看到的都是密⽂文、乱码。那么想要对App进⾏行分析,就要先把它解密,业界俗称砸壳,⽤用到的⼯工具主 要是dumpdecrypted。▶
16. FunMaker-SE:~/Containers/Data/Application/1A3596AC-1B07-4772-8C00-C7048DE75BE9/Documents'>FunMaker-SE:~/Containers/Data/Application/1A3596AC-1B07-4772-8C00-C7048DE75BE9/Documents mobile$ DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/containers/Bundle/Application/ 8AC7077C-0479-4494-88B5-0A4E25FAF7D7/PixlrExpressPlus.app/PixlrExpressPlus mach-o decryption dumper DISCLAIMER: This tool is only meant for security research purposes, not for application crackers. iOSRE: uid = 501, euid = 501, gid = 501, egid = 501. [+] detected 64bit ARM binary in memory. [+] offset to cryptid found: @0x100034d98(from 0x100034000) = d98 [+] Found encrypted data at address 00004000 of length 7438336 bytes - type 1. [+] Opening /private/var/containers/Bundle/Application/8AC7077C-0479-4494-88B5-0A4E25FAF7D7/ PixlrExpressPlus.app/PixlrExpressPlus for reading. [+] Reading header [+] Detecting header type [+] Executable is a plain MACH-O image [+] Opening PixlrExpressPlus.decrypted for writing. [+] Copying the not encrypted start of the file [+] Dumping the decrypted data into the file [+] Copying the not encrypted remainder of the file [+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset d98 [+] Closing original file [+] Closing dump file FunMaker-SE:~/Containers/Data/Application/1A3596AC-1B07-4772-8C00-C7048DE75BE9/Documents'>FunMaker-SE:~/Containers/Data/Application/1A3596AC-1B07-4772-8C00-C7048DE75BE9/Documents mobile$ ls PixlrExpressPlus.decrypted dumpdecrypted.dylib 砸壳是在命令⾏行下操作的,⼤大概的过程,就是先把⼆二进制⽂文件读到内存中,待解密之后把已经解密的部分拷⻉贝到本地,然后把头部的加密标识给改掉。最后⽣生成的这个后缀 是decrypted的⽂文件,就是砸壳之后的明⽂文⼆二进制⽂文件▶
17. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; ▸ ⽤用class-dump导出Objective-C头⽂文件; 第三步,读⽂文字。 ⼤大家应该知道,Objective-C是⼀一⻔门很强调“运⾏行时”的语⾔言,它的很多功能不是在编译时,⽽而是在运⾏行时决定的。要⽀支持这种“运⾏行时”特性,常⻅见的可执⾏行⽂文件和动态链接库 都在⽂文件头⾥里存放了很多信息,这些信息可以⽤用来完整地还原⼀一个⼆二进制⽂文件的Objective-C头⽂文件。 这段话说的有点绕啊,我们直接看看效果▶
18. FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr snakeninny$ ls PixlrExpressPlus.decrypted FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr snakeninny$ class-dump -SsH ./PixlrExpressPlus.decrypted -o ./ FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr snakeninny$ ls ACGCoverVerticalOverCurrentContextAnimatedTransition.h IMRenderView.h ACGDownloadManager.h IMRenderViewController.h ACGFeatureManager-Session.h IMRenderViewDelegate-Protocol.h ACGFeatureManager.h IMRenderViewDismissDelegate-Protocol.h ACTStub.h IMRenderingUtilities.h ADTEaseBackIn.h IMRequest.h ADTEaseBackInOut.h IMRequestBuilder.h ADTEaseBackOut.h IMRequestStatus.h ADTEaseLiner.h IMResizeProperties.h ADTEaseNoParam-Protocol.h IMResponse.h ADTEaseParam1-Protocol.h IMRichMediaDelegate-Protocol.h ... IMPublisherProvidedInfo.h __ARCLiteKeyedSubscripting__-Protocol.h IMRdbmsDataStore.h awManifestManager.h FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr'>FunMaker-AMBP:Pixlr snakeninny$ 这是class-dump之前和之后,⺫⽬目录下⽂文件的对⽐比。dump前,只有⼀一个砸过壳的decrpted⽂文件;dump之后,多了⼀一⼤大堆头⽂文件▶
19. // // Generated by class-dump 3.5 (64 bit). // // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. // #import "NSObject.h" __attribute__((visibility("hidden"))) @interface PXRDevice : NSObject { } + (_Bool)isIOS8; + (_Bool)isIPadMini; + (_Bool)isIPhone4_4s; + (_Bool)isIPhone5_5s; + (_Bool)isIPhone6Plus_6sP; + (_Bool)isIPhone6_6s; @end 打开其中⼀一个头⽂文件,跟我们⾃自⼰己写的基本没啥差别,可读性相当⾼高。从这些⽅方法名出发,我们可以从很⼤大程度上了解这个类的作⽤用,给我们接下来的逆向⼯工程提供了相当 深⼊入的线索。▶
20. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; ▸ ⽤用class-dump导出Objective-C头⽂文件; ▸ ⽤用Cycript定位⺫⽬目标视图; 第4步,读⽂文字。 刚才提到,我们感兴趣的,⼀一般是在界⾯面上观察到的现象。从这⼀一步开始,我们就会从界⾯面⼊入⼿手,⽤用Cycript这个⼯工具来定位我们感兴趣界⾯面的类名和各种属性。▶
21. FunMaker-SE:~/Containers/Data/Application/1A3596AC-1B07-4772-8C00-C7048DE75BE9/Documents mobile$ cycript -p PixlrExpressPlus cy# [[UIApp keyWindow] recursiveDescription].toString() ... <PXRPackDetailViewCell: 0x13040c0a0; baseClass = UICollectionViewCell; frame = (215 210; 100 100); clipsToBounds = YES; opaque = NO; layer = <CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer: 0x13040bfe0>> <UIView:'>UIView: 0x13040c2d0; frame = (0 0; 100 100); gestureRecognizers = <NSArray: 0x13040de00>; layer = <CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer: 0x13040c440>> <UIImageView:'>UIImageView:'>UIImageView:'>UIImageView: 0x13040c460; frame = (0 0; 100 100); opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer: 0x13040c610>> <UIView:'>UIView: 0x13040c700; frame = (0 85.5; 100 14.5); autoresize = RM+BM; layer = <CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer: 0x13040c870>> <CAGradientLayer: 0x13040e180> (layer) <UILabel: 0x13040c940; frame = (0 85.5; 100 14.5); text = 'Hagrid'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x13040cb50>> <UIImageView:'>UIImageView:'>UIImageView:'>UIImageView: 0x130031900; frame = (3 308.5; 594 2.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer: 0x12ed1fe70>> <UIImageView:'>UIImageView:'>UIImageView:'>UIImageView: 0x130031ab0; frame = (314.5 510; 2.5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer:'>CALayer: 0x130009320>> ... cy# Cycript,是Cydia之⽗父Saurik开发的⼀一款⼯工具,能够动态地注⼊入其他进程,来⽅方便地运⾏行我们的代码。这段代码展⽰示的,就是我注⼊入了Pixlr进程后,打印出当前界⾯面所有 UIView的效果。 如果这个界⾯面⽐比较复杂,就会像这个图上⼀一样,信息量⾮非常⼤大。因为我们刚才感兴趣的滤镜是“Hagrid”,▶所以⼀一个简单的全⽂文搜索,就可以迅速定位到“Hagrid”所在的控 件,是⼀一个UILabel。 同时我们可以看到,这种缩进的显⽰示⽅方式,很明确地展⽰示了不同UIView之间的关系,缩进多的,是缩进少的subview。所以这个UILabel的上层,就是⼀一个 PXRPackDetailViewCell,它是UICollectionViewCell的⼀一个⼦子类。▶
22. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; ▸ ⽤用class-dump导出Objective-C头⽂文件; ▸ ⽤用Cycript定位⺫⽬目标视图; ▸ 获取⺫⽬目标视图的UIViewController或delegate; 第5步,读⽂文字。 在MVC设计模式⾥里,View只是⼀一个展板,它不承载具体的业务逻辑或者数据逻辑,对吧。 所以我们的核⼼心其实在这个View对应的Controller和Model上。▶
23. cy# [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString() ", state:'>state:'>state:'>state: appeared, view:'>view:'>view:'>view: \n , state:'>state:'>state:'>state: disappeared, view:'>view:'>view:'>view: not in the window\n , state:'>state:'>state:'>state: appeared, view:'>view:'>view:'>view: " 要拿⼀一个界⾯面的controller,是⽐比较简单的,我们只需要调⽤用这个私有⽅方法,“_printHierarchy”,▶然后找当前状态是“appeared”的controller,就可以定位到当前界⾯面的 controller。▶
24. cy# [#0x13040c0a0 nextResponder] #"<UICollectionView: 0x12f8d2600; frame = (0 80; 320 314); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x1301bce80>; layer = <CALayer:'>CALayer: 0x1301bb650>; contentOffset: {0, 0}; contentSize: {320, 940}> collection view layout: <UICollectionViewFlowLayout: 0x1301bd710>" cy# [#0x12f8d2600 delegate] #"<PXRPackDetailView: 0x1301ba110; frame = (0 0; 320 394); autoresize = W +H; layer = <CALayer:'>CALayer: 0x1301b7d60>>" 要找⼀一个view的delegate,也很简单。我先调⽤用⼀一次nextResponder,拿到刚才那个PXRPackDetailViewCell的superview,发现是⼀一个UICollectionView;然后调⽤用⼀一次 delegate,就可以拿到它的UICollectionViewDelegate,也就是PXRPackDetailView▶
25. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; ▸ ⽤用class-dump导出Objective-C头⽂文件; ▸ ⽤用Cycript定位⺫⽬目标视图; ▸ 获取⺫⽬目标视图的UIViewController或delegate; ▸ 在controller的头⽂文件中寻找蛛丝⻢马迹; 拿到了⺫⽬目标视图的delegate,即PXRPackDetailView之后,就可以在class-dump出的头⽂文件中寻找这个类所在的⽂文件,看看能不能找到什么蛛丝⻢马迹。▶
26. // // Generated by class-dump 3.5 (64 bit). // // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. // #import "UIView.h" @class EffectPack, MenuItem, NSArray, NSLayoutConstraint, PXRProgressIndicator, UIButton, UICollectionView, UILabel; __attribute__((visibility("hidden"))) @interface PXRPackDetailView : UIView { _Bool _isSetupMenuItemDownloadObserver; MenuItem *_packMenuItem; CDUnknownBlockType _effectTappedBlock; CDUnknownBlockType _downloadTappedBlock; UIView *_topContainerView; UILabel *_titleLabel; UICollectionView *_colView; UIButton *_downloadButton; NSLayoutConstraint *_downloadButtonTrailingConstraint; PXRProgressIndicator *_downloadProgressIndicator; EffectPack *_effectPack; NSArray *_effects; NSArray *_stylizeEffects; } - (void).cxx_destruct; - (void)awakeFromNib; @property(nonatomic) __weak UICollectionView *colView; // @synthesize colView=_colView; - (id)collectionView:(id)arg1'>collectionView:(id)arg1'>collectionView:(id)arg1'>collectionView:(id)arg1 cellForItemAtIndexPath:(id)arg2; - (void)collectionView:(id)arg1'>collectionView:(id)arg1'>collectionView:(id)arg1'>collectionView:(id)arg1 didHighlightItemAtIndexPath:(id)arg2; - (void)collectionView:(id)arg1'>collectionView:(id)arg1'>collectionView:(id)arg1'>collectionView:(id)arg1 didSelectItemAtIndexPath:(id)arg2; … 这就是PXRPackDetailView的头⽂文件,各种实例变量和⽅方法名都很清楚。▶ 点击⼀一个UICollectionView,红框⾥里的这个⽅方法得到调⽤用,滤镜就⽣生效了▶
27. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; ▸ ⽤用class-dump导出Objective-C头⽂文件; ▸ ⽤用Cycript定位⺫⽬目标视图; ▸ 获取⺫⽬目标视图的UIViewController或delegate; ▸ 在controller的头⽂文件中寻找蛛丝⻢马迹; ▸ ⽤用Hopper和LLDB的组合还原调⽤用逻辑; 接下来,就是逆向⼯工程的重中之重,⽤用Hopper和LLDB,⼀一静⼀一动的组合拳,来还原刚才那个⽅方法的前后逻辑了。▶
28. 在Hopper⾥里,我们可以看到这个⽅方法的具体实现,只不过它是以汇编语⾔言的形式呈现给我们的。▶ ⼤大家注意这个⽅方法名,从字⾯面意思上,我们可以⼤大概猜到,它就是点击了⼀一个效果cell之后得到调⽤用的block。这个block是怎么实现的呢?▶
29. (lldb) br s -a 0x0000000000068000+0x000000010005cc80 Breakpoint 1: where = PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:] + 52, address = 0x00000001000c4c80 Process 4056 stopped * thread #1: tid = 0x5c863, 0x00000001000c4c80 PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:] + 52, queue = 'com.apple.main-thread', activity = 'starting resolver activity', 32 messages, stop reason = breakpoint 1.1 frame #0: 0x00000001000c4c80 PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:] + 52 PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]: -> 0x1000c4c80 <+52>: bl 0x100608a00 ; symbol stub for:'>for:'>for:'>for: objc_msgSend 0x1000c4c84 <+56>: mov x29, x29 0x1000c4c88 <+60>: bl 0x100608a60 ; symbol stub for:'>for:'>for:'>for: objc_retainAutoreleasedReturnValue 0x1000c4c8c <+64>: mov x22, x0 (lldb) p (char *)$x1 (char *) $0 = 0x00000001006d94d7 "effectTappedBlock" (lldb) ni Process 4056 stopped * thread #1: tid = 0x5c863, 0x00000001000c4c84 PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:] + 56, queue = 'com.apple.main-thread', activity = 'starting resolver activity', 32 messages, stop reason = instruction step over frame #0: 0x00000001000c4c84 PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:] + 56 PixlrExpressPlus`-[PXRPackDetailView collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]'>collectionView:didSelectItemAtIndexPath:]: -> 0x1000c4c84 <+56>: mov x29, x29 0x1000c4c88 <+60>: bl 0x100608a60 ; symbol stub for:'>for:'>for:'>for: objc_retainAutoreleasedReturnValue 0x1000c4c8c <+64>: mov x22, x0 0x1000c4c90 <+68>: cbz x22, 0x1000c4d14 ; <+200> (lldb) po $x0 <__NSMallocBlock__: 0x12fe04930> (lldb) memory read --size 8 --format x 0x12fe04930 0x12fe04930: 0x000000019f449658 0x00000000c3000002 0x12fe04940: 0x0000000100088b74 0x0000000100787e00 0x12fe04950: 0x000000012e716ed0 0x000000012f000400 0x12fe04960: 0x000001a19f44bf19 0x0000000100000788 (lldb) 这就轮到LLDB出⻢马了。LLDB就是⼤大家在Xcode⾥里写代码所⽤用到的动态调试器,应该都不陌⽣生,只不过在Xcode调试时,⼀一般不会深⼊入到汇编这⼀一层。 这⾥里我就不展开了,感兴趣的朋友回看⼀一下幻灯⽚片,有什么问题我们在私下交流,好吧!▶
30. iOS逆向⼯工程常规套路 ▸ 观察、猜测,寻找分析切⼊入点; ▸ ⽤用dumpdecrypted给App砸壳; ▸ ⽤用class-dump导出Objective-C头⽂文件; ▸ ⽤用Cycript定位⺫⽬目标视图; ▸ 获取⺫⽬目标视图的UIViewController或delegate; ▸ 在controller的头⽂文件中寻找蛛丝⻢马迹; ▸ ⽤用Hopper和LLDB的组合还原调⽤用逻辑; ▸ ⽤用Theos编写插件。 最后⼀一步,就是写代码了。逆向⼯工程相关的开发,⼀一般会⽤用到Theos,⽤用它可以很⽅方便地修改第三⽅方进程的功能逻辑,达到我们⾃自⼰己定制化的⺫⽬目的。▶
31. /* How to Hook with Logos Hooks are written with syntax similar to that of an Objective-C @implementation. You don't need to #include , it will be done automatically, as will the generation of a class list and an automatic constructor. %hook ClassName // Hooking a class method + (id)sharedInstance { return %orig; } // Hooking an instance method with an argument. - (void)messageName:(int)argument { %log; // Write a message about this call, including its class, name and arguments, to the system log. %orig; // Call through to the original function with its original arguments. %orig(nil); // Call through to the original function with a custom argument. // If you use %orig(), you MUST supply all arguments (except for self and _cmd, the automatically generated ones.) } // Hooking an instance method with no arguments. - (id)noArguments { %log; id awesome = %orig; [awesome doSomethingElse]; return awesome; } // Always make sure you clean up after yourself; Not doing so could have grave consequences! %end */ 这些是Theos的基本语法,⾮非常简单,⼤大家⼀一看就懂了,⼏几乎不需要后⾯面这些注释。我也不展开了▶
32. 刚才那些步骤的细节,在书中都有详细的介绍,如果⼤大家感兴趣,可以买⼀一本看看,外⾯面机械⼯工业出版社的摊位就有卖的;不买,光看看也⾏行。等会1点的时候,我会在那⾥里 接待⼤大家,感兴趣的朋友可以过来随便聊聊天。▶ 有⼈人会问,这哥们谁啊? 图中这位⼤大哥,叫Rasmus,是PHP这⻔门语⾔言的创始⼈人;这张照⽚片是我在今年5⽉月份DevLink,也就是我们的主办⽅方,举办的的PHP开发者⼤大会上给他照的。 我作为Rasmus的随⾝身翻译,全程参与了这个活动。他给我印象最深的,是他对PHP的看法▶
33. PHP is a hammer. So does iOSRE. 他认为,PHP is a hammer,也就是⼀一把锤⼦子。对于⼤大家来说,重要的不是锤⼦子本⾝身,不是去⽐比谁的锤⼦子更重,材料更好,⽽而是我们⽤用这把锤⼦子去做什么,我们怎么样⽤用它, 让这个社会,让这个国家变得更好。他把这个问题留给了所有PHP的⽤用户,然后就退居⼆二线了,所以今天我们程序员界流传着这样⼀一句话:“PHP是世界上最好的语⾔言”。 那么作为iOS逆向⼯工程的推⼲⼴广⼈人,我也⽃斗胆模仿Rasmus,将iOS逆向⼯工程定义为⼀一把锤⼦子。▶ ⼤大家⽤用这把锤⼦子去做什么呢?有⼈人⽤用它来做⿊黑灰产,发了笔横财;有⼈人⽤用它来做安全研究,把iOS给越狱了;有⼈人学习了它的思维⽅方式,让⾃自⼰己的事业更上⼀一层楼。我想,对 逆向⼯工程的运⽤用也要因⼈人⽽而异,所以,请⼤大家尽情发挥想象⼒力吧!我就不再举例限制⼤大家的思维了。 希望⼤大家都能⽤用这把锤⼦子,敲碎⾃自⼰己职业的发展瓶颈,打造⼈人⽣生发展的光辉前程。▶
34. 谢谢! 我今天的分享就到这⾥里,谢谢⼤大家! 这个⼆二维码是我们的官⽅方论坛,⼤大家如果有任何技术相关的问题,都可以来这⾥里畅所欲⾔言。 接下来的时间,我们随便聊聊吧;技术、⼈人⽣生、理想,什么话题都可以▶ iosre.com
35. (本环节结束后)谢谢!▶ 交流讨论
36. 后⾯面还有⼀一张⼆二维码⼤大图 谢谢! iosre.com