美文网首页iOS 开发随笔iOS学习笔记iOS备忘录
APP逆向分析之钉钉抢红包插件的实现-iOS篇

APP逆向分析之钉钉抢红包插件的实现-iOS篇

作者: yohunl | 来源:发表于2016-12-12 17:27 被阅读736次

    花费了很多天的原创文章,转载请注明出处https://yohunl.com/ding-ding-qiang-hong-bao-cha-jian-iospian/ ,谢谢!

    网络上关于微信红包的分析文章已经非常多了,基本上照着做就可以弄个微信抢红包插件出来,不过,随着阿里巴巴的钉钉在企业中的流行,很多企业现在都采用钉钉来办公了,顺带着,也就使用钉钉来发红包了,学习了那么多逆向的理论后,需要拿一个东西来练练手,刚好,钉钉就符合这个要求,于是,便有了下面的这篇文章.套用腾讯的何兆林在文章 移动App入侵与逆向破解技术-iOS篇说的一段话
    "破解有时候很耗时,和程序开发正好相反,它耗时不是耗在写代码上,而是耗在寻找注入点和逆向工程上,有可能你花了3天时间去找程序的破绽,但是最终的破解代码可能就2行,不到一分钟就搞定了;但是你也需要做好面对失败的准备,如果路选错了,有可能你这3天完全是在浪费脑细胞"

    本文的源码放在我的gitHub上 DingTalkNoJailTweak ,,其ReadMe文件说明了其中相关的代码是起什么作用的.

    当然了,这篇文件会涉及比较多的逆向的东西,这些理论等前序知识,可以参考网上的,网上的资料比较多了,比如TheOS的开发环境的搭建,Hopper逆向分析软件的使用,ida Pro等,移动App入侵与逆向破解技术-iOS篇上就有很不错的前序理论知识的介绍.
    好了,闲话不多说,直接进入正题.

    相关工具和环境

    一台越狱的手机,虽然不越狱的手机也可以用来分析,但是,那样将会大大的减慢整个分析过程,因为非越狱的只能通过注入后打log日志来分析,耗时耗力,所以,越狱手机是必备

    越狱的手机上安装了

    cycript

    从cydia中安装

    FlexInjected

    从cydia中安装,在cydia中搜索Flipboard FLEX loader

    clutch

    https://github.com/KJCracks/Clutch clutch下载
    下载后,按照上面的指示,
    Clone the repo
    git clone git@github.com:KJCracks/Clutch.git
    cd Clutch
    Build
    xcodebuild -project Clutch.xcodeproj -configuration Release ARCHS="armv7 armv7s arm64" build
    Install
    scp /path/to/Clutch root@<your.device.ip>:/usr/bin/
    ssh root@<your.device.ip>
    当然你可以使用同步助手,PP助手,iFunBox,iTools Pro来安装到手机的/usr/bin下
    如果不是用SSH从电脑上登陆手机的命令行的话,你可以cydia下载MTerminal,然后使用su切换用户到root,再使用命令 chmod +x clutch来提升权限

    MTerminal

    cydia中下载,可以方便的在你的手机上使用终端命令行.

    一台MAC电脑安装了

    class-dump

    class-dump,用来dump出二进制的头文件.

    Theos

    可以参考我的另外一篇文章来安装 iOS 越狱的Tweak开发

    Hopper Disassembler v3 或者ida Pro

    用来进行汇编分析的

    xcode

    insert_dylib

    iTools Pro

    iTools
    方便的用来拷贝.浏览手机上的所有文件

    砸壳

    砸壳,是为了能够使用class-dump来导出所有的头文件,有了头文件,我们分析起来才是有可能的.
    ssh连上你的越狱手机/或者像我一样直接在手机上的Terminal中操作,调用clutch来进行砸壳

    YohunlIp6:~ root# clutch
    Usage: clutch [OPTIONS]
    -b --binary-dump <value> Only dump binary files from specified bundleID 
    -d --dump <value>        Dump specified bundleID into .ipa file 
    -i --print-installed     Print installed applications 
       --clean               Clean /var/tmp/clutch directory 
       --version             Display version and exit 
    -? --help                Display this help and exit 
    -n --no-color            Print with colors disabled 
    -v --verbose             Print verbose messages 
    

    以上是clutch支持的命令
    其中的 -b就是 用来砸壳的,需要一个参数是要被砸壳的应用的bundleid,获取钉钉的bundleid有很多方法,你可以直接使用 clutch -i 输出手机上所有已经安装的应用,找到bundleid.钉钉的bundleid是com.laiwang.DingTalk
    开始砸壳



    稍等一会,砸壳完毕,会输出砸完壳后的存放路径

    使用iTool Pro/PP助手等工具,将砸完壳的钉钉拷贝到电脑上

    导出头文件

    在电脑上,将砸壳后的文件中读取出头文件, DingTalk是从砸壳后的ipa包中提取出来的钉钉的可执行文件,这个文件比较好找,一般都是没有后缀的,大小最大的那个.

    class-dump -H DingTalk -o DingTalkHeaders
    

    class-dump的简单使用方式
    最简单的使用方式
    class-dump -H DDMMerchant -o yicommonHeaders
    class-dump -H Payload/WeChat.app/WeChat -o wechatHeaders

    还可以加上 -S -s来对生成的方法,类都进行排序
    class-dump -S -s -H Payload/WeChat.app/WeChat -o wechatHeaders
    一定要写 -H,不然,都是在命令行输出的,不会将内容输入到文件夹下!!

    导出头文件后,新建一个xcode工程,将所有的头文件都导出到工程中,为什么做这一步呢,因为,Xcode的搜索能力还是不错的,方便我们去查看这些头文件.当然,由于一次性导入xcode工程的文件量比较大,在导入的过程中,xcode可能会假死,稍微耐心一点.

    开始分析

    在这里,强烈的推荐Flipboard的FLEX,这个简直就是iOS分析界的神器啊.以前还要用命令才能一步步的将界面所对应的视图,以及相应的ViewController分析出来,现在有了它,这个过程可以大大加速了.

    如果你安装了FlexInjected,打开设置那里FlexInjected中钉钉的开关,这样,当钉钉启动的时候,就加载了Flex了,加载了Flex后,会在钉钉中出现Flex的界面,点击界面可以进行相应的操作.
    首先,第一步,我们先要找到抢红包的视图所对应的对象以及相应的VC,这个事情利用Flex还是很简单的.

    设置中打开 FlexInjected中的钉钉,这样,钉钉启动的时候,就会加载FLex.


    打开钉钉,就会出现Flex的菜单了,Flex的基本操作,可以参考FLex官网的.


    通过Flex,我们很容易的得到下面的结果
    1 聊天页面的红包视图的View是DTMessageBubbleRedEnvelopView



    2 点击后,抢红包的页面的视图是

    DTOpenLuckyMoneyView
    ----DTOpenLuckyMoneyEntityView


    3 所有的聊天的会话的控制器是DTConversationListViewController.

    我们已经知道了抢红包的关键视图是DTOpenLuckyMoneyEntityView 和DTOpenLuckyMoneyView.打开我们dump出来的所有的头文件,查看DTOpenLuckyMoneyEntityView头文件定义
    DTOpenLuckyMoneyEntityView的定义(截取我们需要的关键部分)

    @interface DTOpenLuckyMoneyEntityView : UIView <DTUserNameLabelDelegate>{
        id <DTOpenLuckyMoneyEntityViewDelegate> _delegate;
        ....
    }
    @property(nonatomic) __weak id <DTOpenLuckyMoneyEntityViewDelegate> delegate;
    - (void)didClickOpenLuckyMoneyBtn:(UIButton *)arg1;
    ......
    @end
    

    DTOpenLuckyMoneyEntityViewDelegate

    @protocol DTOpenLuckyMoneyEntityViewDelegate <NSObject>
    - (void)didClickViewMore:(DTOpenLuckyMoneyEntityView *)arg1;
    - (void)didClickOpenLuckyMoney:(DTOpenLuckyMoneyEntityView *)arg1;
    @end
    

    DTOpenLuckyMoneyView的定义(截取我们需要的关键部分)

    @interface DTOpenLuckyMoneyView : UIView <DTOpenLuckyMoneyEntityViewDelegate>
    //通过此方法,构造出来视图
    + (void)showLuckyMoneyWithPickingStatus:(DTBizRedEnvelopClusterPickingStatus *)arg1 withController:(DTMessageOTOViewController *)arg2 delegate:(id <DTOpenLuckyMoneyViewDelegate>)arg3;
    - (void)didClickOpenLuckyMoney:(DTOpenLuckyMoneyEntityView *)arg1;
    @end
    

    通过上面的两个类和一个协议的方法定义,我们很容易的就可以得出它们之间的关系大概是如下这样的

    当用户点击了拆红包按钮时

    DTOpenLuckyMoneyEntityView中的button的响应事件 
    - (void)didClickOpenLuckyMoneyBtn:(id)arg1;
        其中  先获取id <DTOpenLuckyMoneyEntityViewDelegate> _delegate; (取值是 DTOpenLuckyMoneyView (@interface DTOpenLuckyMoneyView : UIView <DTOpenLuckyMoneyEntityViewDelegate>))
        转给其方法 - (void)didClickViewMore:(DTOpenLuckyMoneyEntityView *)arg1;
    也就是  
    DTOpenLuckyMoneyEntityView
    - (void)didClickOpenLuckyMoneyBtn:(id)sender {
       [self.delegate didClickOpenLuckyMoney:self];//_delegate; (取值是 DTOpenLuckyMoneyView
    }
    
    

    这只是大概的流程,我们可以进一步细化这个流程,这个时候就要用到Hopper Disassembler或者iDA Pro了,用Hopper打开钉钉的可执行文件,等待分析完毕,打开DTOpenLuckyMoneyView的didClickViewMore实现,(这里我只截取了部分)


    从这个方法的汇编代码中,我们大体上可以得到如下的信息:
    DTOpenLuckyMoneyView didClickOpenLuckyMoney:(DTOpenLuckyMoneyEntityView *)arg1;
       [self statusModel]; //DTBizRedEnvelopClusterPickingStatus *statusModel,其中有一个属性是DTBizRedEnvelopCluster,包含了红红包的相关信息,看上面的截图
       [self utOpenRedEnvelop:statusModel]
       [self beginLoading]//其中会调用DTOpenLuckyMoneyEntityView的loading方法等
       [DTRedEnvelopServiceFactory defaultServiceIMP]获取一个 DTRedEnvelopServiceIMP
       self redEnvelopCluster
       serverFormatWithCountryCode:number:
       clusterId
       luckyMoneyEntityView
       //DTRedEnvelopServiceIMP - (void)pickRedEnvelopCluster:(long long)arg1 clusterId:(id)arg2 successBlock:(CDUnknownBlockType)arg3 failureBlock:(CDUnknownBlockType)arg4;
       pickRedEnvelopCluster:clusterId:successBlock:failureBlock:
    

    我们看到其中有 DTRedEnvelopServiceFactory这个类,还有pickRedEnvelopCluster方法,在头文件中搜索pickRedEnvelopCluster,可以得到



    我们又得到了几个关联的类,尤其是其中的DTRedEnvelopService.

    到这里,你可能还是很迷惑,下步该做什么?

    不要紧,TheOS给我们提供了一个logify.pl脚本(theos/bin/logify),这个脚本可以将一个类中的所有方法都加上日志.
    大概的使用方式就是

    $THEOS/bin/logify.pl ./DTMessageMTMViewController.h  在终端显示结果
    $THEOS/bin/logify.pl ./DTMessageMTMViewController.h > /out/to/DTMessageMTMViewController.xm
     
    

    我们使用Theos建立一个tweak,这个tweak的作用就是输出日志,有关theos的安装和使用,可以参考我的另外一篇博客iOS 越狱的Tweak开发,我们将我们觉得可疑的类都加上日志,编译一个tweak,安装到手机上.
    你的这个tweak.xm文件中的内容应该是大致如下

    %hook DTOpenLuckyMoneyView
    + (void)showLuckyMoneyWithPickingStatus:(NSObject *)arg1 withController:(id)arg2 delegate:(id)arg3 
    { 
        NSLog(@"WithPickingStatus = %@,className = %@",arg1,NSStringFromClass(arg1.class));
        %log; %orig;
    }
    + (void)queryAndOpenPageWithClusterId:(id)arg1 senderId:(long long)arg2 withController:(id)arg3 { %log; %orig; }
    ........
    
    %end
    

    接着,你在钉钉中,让别人发一个红包,然后你点击拆红包,拆掉红包,将整个的Log输出,复制出来,用于分析.
    这里,我将某一次的日志部分放出来,让你对这个日志有个概念

    <Warning>: <L_UI> -[DTMessageBubbleTapHandler messageBubbleViewCell:didTappedWithGestureRecognizer:] #81 [INFO] tap msg mid = 12685858885 , msgType = 902
    [m +[<DTRedEnvelopServiceFactory: 0x103303bd8> defaultServiceIMP]
    [m -[<DTRedEnvelopServicePersistenceIMP: 0x14de243b0> initWithDbConnection:<OpenDatabase: 0x14f247e00>]
    [m  = <DTRedEnvelopServicePersistenceIMP: 0x14de243b0>
    [m +[<DTRedEnvelopServiceFactory: 0x103303bd8> createServiceIMPWithPersistence:<DTRedEnvelopServicePersistenceIMP: 0x14de243b0> network:<DTRedEnvelopServiceNetworkIMP: 0x14fa743c0>]
    [m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> init]
    [m  = <DTRedEnvelopServiceIMP: 0x14fec6a70>
    [m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> setPersistenceIMP:<DTRedEnvelopServicePersistenceIMP: 0x14de243b0>]
    [m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> setNetworkIMP:<DTRedEnvelopServiceNetworkIMP: 0x14fa743c0>]
    [m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> sendInitAlipay]
    [m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> getBindAlipaySuccessBlock:(null) failureBlock:(null)]
    [m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> networkIMP]
    [m  = 0x<DTRedEnvelopServiceNetworkIMP: 0x14fa743c0>
    [m -[<DTRedEnvelopServiceNetworkIMP: 0x14fa743c0> getBindAlipaySuccessBlock:<__NSStackBlock__: 0x16fdb5310> failureBlock:<__NSStackBlock__: 0x16fdb52e8>]
    [m  = <DTRedEnvelopServiceIMP: 0x14fec6a70>
    [m  = <DTRedEnvelopServiceIMP: 0x14fec6a70>
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:239[m [0;30;46mDEBUG:[m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> checkRedEnvelopClusterPickingStatus:88160518 clusterId:5PnAJfRG successBlock:<__NSStackBlock__: 0x16fdb5638> failureBlock:<__NSStackBlock__: 0x16fdb5608>]
    <Warning>: <L_LWP> -[LWPTransactionService enqueue:] #154 [Info] enqueue uri=/r/Adaptor/DingPayI/getBindAlipay [mid:1df66b00]
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:211[m [0;30;46mDEBUG:[m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> networkIMP]
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:211[m [0;30;46mDEBUG:[m  = 0x<DTRedEnvelopServiceNetworkIMP: 0x14fa743c0>
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:265[m [0;30;46mDEBUG:[m -[<DTRedEnvelopServiceNetworkIMP: 0x14fa743c0> checkRedEnvelopClusterPickingStatus:88160518 clusterId:5PnAJfRG successBlock:<__NSStackBlock__: 0x16fdb5518> failureBlock:<__NSStackBlock__: 0x16fdb54e0>]
    <Warning>: <L_LWP> -[LWPMessenger lwpConnection:willSendMessage:isFirstMessage:] #1075 [Info] willSend [0][[id:85b80100(addr:0x14f2461c0, idx:0) - Master]]: 1df66b00 0
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:172[m [0;30;46mDEBUG:[m +[<DTRedEnvelopPickIService: 0x1033227b8> _serviceKey__]
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:172[m [0;30;46mDEBUG:[m  = Adaptor/RedEnvelopPickIService
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:171[m [0;30;46mDEBUG:[m +[<DTRedEnvelopPickIService: 0x1033227b8> _appname__]
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:171[m [0;30;46mDEBUG:[m  = (null)
    <Warning>: <L_LWP> -[LWPTransactionService enqueue:] #154 [Info] enqueue uri=/r/Adaptor/RedEnvelopPickI/checkRedEnvelopClusterPickingStatus [mid:7e426c00]
    <Warning>: <L_LWP> -[LWPMessenger lwpConnection:willSendMessage:isFirstMessage:] #1075 [Info] willSend [0][[id:85b80100(addr:0x14f2461c0, idx:0) - Master]]: 7e426c00 0
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:206[m [0;30;46mDEBUG:[m -[<DTRedEnvelopServiceIMP: 0x14fec6a70> setBindAlipayAccount:yoh***@163.com]
    <Warning>: <L_LWP> -[LWPTransactionService destoryV2:withError:withResponse:] #258 [Info] destory: mid:1df66b00 200
    <Warning>: <L_LWP> -[LWPTransactionService destoryV2:withError:withResponse:] #258 [Info] destory: mid:7e426c00 200
    <Warning>: __98-[DTRedEnvelopServiceIMP checkRedEnvelopClusterPickingStatus:clusterId:successBlock:failureBlock:]_block_invoke #280 [INFO] clusterId=(null), flowCount=0 pickMoney=0 pickstatus=0
    <Warning>: WithPickingStatus = <DTBizRedEnvelopClusterPickingStatus: 0x14deac5a0>,className = DTBizRedEnvelopClusterPickingStatus
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:109[m [0;30;46mDEBUG:[m +[<DTOpenLuckyMoneyView: 0x1032edd10> showLuckyMoneyWithPickingStatus:<DTBizRedEnvelopClusterPickingStatus: 0x14deac5a0> withController:<DTMessageOTOViewController: 0x14eb6d400> delegate:<DTMessageOTOViewController: 0x14eb6d400>]
    <Notice>: [1;36m[DingTalkTweak] [m[0;36mTweak.xm:116[m [0;30;46mDEBUG:[m +[<DTOpenLuckyMoneyView: 0x1032edd10> viewWithDelegate:<DTMessageOTOViewController: 0x14eb6d400>]
    
    
    

    当然了,这个分析日志的过程,你可能会持续非常多的次数,因为在分析的过程中,你可能会发现另外的某个对象可能是你要的分析,你就要再修改tweak.xm文件中的hook,将这个对象的方法全hook住,反反复复

    这个时候,你应该能够得到如下的关键信息

    <DTRedEnvelopServiceFactory: 0x103303bd8> defaultServiceIMP]
    

    DTRedEnvelopServiceFactory通过方法defaultServiceIMP构造了一个DTRedEnvelopServiceIMP对象,然后通过这个DTRedEnvelopServiceIMP对象的pickRedEnvelopCluster: clusterId: successBlock: failureBlock方法去拆红包

    <DTRedEnvelopServiceFactory: 0x103303bd8> defaultServiceIMP]
    DTRedEnvelopServiceIMP - (void)pickRedEnvelopCluster:(long long)arg1 clusterId:(id)arg2 successBlock:(CDUnknownBlockType)arg3 failureBlock:(CDUnknownBlockType)arg4;
    

    这个方法就是最后的拆红包功能的方法,到此
    拆红包这个就很简答了,由于[DTRedEnvelopServiceFactory defaultServiceIMP]是一个类方法,所以不需要我们构造的,那么现在的问题是,在[DTRedEnvelopServiceFactory defaultServiceIMP]周后,有没有调用其它的方法来配置这个构造出来的DTRedEnvelopServiceIMP对象呢?
    我们可以继续根据输出的日志来分析....

    这里,我直接告诉你分析的结果
    [DTRedEnvelopServiceFactory defaultServiceIMP]方法中,会初始化一个全局的DTRedEnvelopServiceIMP对象.并且配置好它,例如绑定支付信息等等 (你可以从汇编中大概看一下).返回给的DTRedEnvelopServiceIMP对象就是一个完全可用的对象了,只要调用这个对象的pickRedEnvelopCluster: clusterId: successBlock: failureBlock方法,就可以完成拆红包这个动作了.

    那么,自动拆红包这个,就变为你怎么拿到这个方法所需的4个参数了,后两个block,一看就知道可以传nil的,因为我们对成功失败的回调不感兴趣,问题就变为怎么拿到前两个参数了.
    第一个参数是一个 long long类型,第二个是一个 NSString.

    FLex有一个非常好用的功能,是其中带有的Heap Objects,这个可以用来查看当前内存中有哪些方法.结合我们上面的日志分析,会发现
    DTOpenLuckyMoneyEntityView 对象中的方法 - (DTBizRedEnvelopCluster *)currentCluster;
    其中包含了我们需要的红包的相关信息


    接下来,我们让别人再发一个红包,然后,点击出现拆红包视图,这个时候,利用Flex的Heap Objects查内存中的DTBizRedEnvelopCluster方法,从中看到@property(copy, nonatomic) NSString *clusterId;和@property(nonatomic) long long sender,记下来

    再搜寻 DTRedEnvelopServiceIMP对象,Flex可以直接调用搜寻出来的某个对象,在Flex中直接调用方法pickRedEnvelopCluster,传递给其,注意,这里在Flex中第二个参数是字符串,使用""而不是@"".


    点击右上角的call,调用这个方法,不出意外,你会发现,红包已经被拆了,到此,验证了,的确是这个方法,而且也知道了参数怎么拿到的.

    OK,问题又来了,如果没有出现拆红包视图,那么就没有这个DTBizRedEnvelopCluster类呀,那怎么获取其中的两个参数出来呢?

    这的确是个问题,你要再回到汇编和头文件中,去分析了....

    每当来一个新消息,消息的列表页面就已经收到了,这说明在这个页面的时候,应该已经获取到了抢红包所需要的相关信息了.我们接下来找到他们之间的联系

    我们平时看到的聊天的集合信息页是 DTConversationListController,浏览头文件,看到DTConversationListDataSource *_dataSource;
    进入DTConversationListDataSource,看到其中有

    @property(retain, nonatomic) NSMutableArray<DTBizConversation *  WKBizConversation *等> *conversationList
    

    从Heap Object进去,查看这个conversationList对象中的内容,可以看到其中存放的大概是DTBizConversation或者是WKBizConversation,继续浏览
    我们看到DTBizConversation中,有一个对象
    @property(retain, nonatomic) DTBizMessage *lastMessage;
    进一步浏览内存中的这个对象,你就会发现 ,我靠,踏破铁下无觅处,红包信息就在这了.其中的 attachmentsJson中存放了完整的 附件信息的json字符串
    大体上如下

    {
      "contentType" : 901,
      "@Type" : "WKIDLContentModel",
      "attachments" : [
        {
          "@Type" : "WKIDLAttachmentModel",
          "size" : 0,
          "type" : 0,
          "extension" : {
            "amount" : "0.01",
            "clusterid" : "5PnPxu8C",
            "sname" : "XXX",
            "size" : "1",
            "congrats" : "恭喜发财,大吉大利!",
            "sid" : "45049990",
            "type" : "0",
            "oid" : "0"
          },
          "isPreload" : false
        }
      ]
    }
    

    经过各种分析...,可以知道,红包的contentType为901或者902.

    整理下思路,
    DTConversationListController --->DTConversationListDataSource *_dataSource ---> @property(retain, nonatomic) NSMutableArray<DTBizConversation * WKBizConversation *等> *conversationList ---> DTBizConversation ---> DTBizMessage ---> attachmentsJson

    DTConversationListController,只要我们开启钉钉,第一个页面就是,所以可以认为这个是单例,它的_dataSource也是一直存在的.

    所以我们要拿到红包的信息结构就很简单了.

    接下来,最后一个问题了.
    那么我们怎么知道什么时候,分析这个DTConversationListController的dataSource的conversationList的DTBizConversation的DTBizMessage的attachmentsJson呢?也就是什么时候触发我们的自动抢红包动作呢??

    比较容易想到的肯定是 当来新信息的时候.
    经过再一次的日志分析等各种分析(应该是一个比较漫长,繁琐的过程),你可以得到 每当有新消息来的收,DTConversationListDataSource的

    - (void)controller:(id)arg1 didChangeObject:(id)arg2 atIndex:(unsigned long long)arg3 forChangeType:(long long)arg4 newIndex:(unsigned long long)arg5;
    

    方法会被调用,经过日志输出,我们可以得到,其第二个参数就是要插入到datasouce中的WKBizConversation/DTBizConversation

    所以,只要我们hook这个方法,就可以在这触发我们的抢红包动作了.

    核心源码

    @implementation YLHongBaoViewController
    + (void)load {
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
          Class class = objc_getClass("DTConversationListDataSource") ;//[self class];
          //- (void)controller:(id)arg1 didChangeObject:(id)arg2 atIndex:(unsigned long long)arg3 forChangeType:(long long)arg4 newIndex:(unsigned long long)arg5;
    
          void (^hook_block)(id<AspectInfo> aspectinfo,id controller,id didChangeObject,unsigned long long atIndex,long long forChangeType,unsigned long long newIndex) = ^(id<AspectInfo> aspectinfo,id controller,id didChangeObject,unsigned long long atIndex,long long forChangeType,unsigned long long newIndex){
            if (![YLHongBaoViewController isEnabled]) {
              NSLog(@" 红包分析 走原来的逻辑");
              return;
            }
            NSMutableArray *attachArr = [DingTalkRedEnvelop disposeConversation:didChangeObject];
            [attachArr enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) {
              DTRedEnvelopServiceIMP *imp = [objc_getClass("DTRedEnvelopServiceFactory") defaultServiceIMP];
              long long sid = [obj[@"sid"] longLongValue];
              NSString *cluseId = obj[@"clusterid"];
              NSLog(@"lingdaiping_sid = %lld,cluseid = %@",sid,cluseId);
              if (cluseId.length > 0){
                [imp pickRedEnvelopCluster:sid clusterId:cluseId successBlock:nil failureBlock:nil];
              }
              
            }];
            
            
          };
          aspect_add(class, @selector(controller:didChangeObject:atIndex:forChangeType:newIndex:), AspectPositionAfter, hook_block, nil);
        });
    }
    
    
    + (NSMutableArray *)disposeConversation:(WKBizConversation *)converdation {
      NSLog(@"disposeConversation_sation = %@", converdation);
        //WKBizConversation *converdation = sation;
      NSMutableArray *retArr = [NSMutableArray new];
      NSLog(@"disposeConversation_converdation.latestMessage = %@", converdation.latestMessage);
      NSString *attachmentsJson = converdation.latestMessage.attachmentsJson;
      NSLog(@"disposeConversation_attachmentsJson = %@", attachmentsJson);
      if (attachmentsJson.length > 0) {
        NSData* jsonData = [attachmentsJson dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:nil];
        NSLog(@"disposeConversation_dict = %@", dict);
        NSNumber *contentType = dict[@"contentType"];
        NSLog(@"disposeConversation_contentType = %@", contentType);
        if (contentType.integerValue == 902 || contentType.integerValue == 901) {//红包
          NSMutableDictionary *retDict = [NSMutableDictionary new];
          retDict[@"contentType"] = contentType;
          NSArray *arr = dict[@"attachments"];
          NSLog(@"disposeConversation_arr = %@", arr);
          if (arr.count > 0) {
            [arr enumerateObjectsUsingBlock:^(NSDictionary *attachmentDict, NSUInteger idx, BOOL * _Nonnull stop) {
              NSDictionary *extension = attachmentDict[@"extension"];
              NSLog(@"disposeConversation_extension = %@", extension);
              retDict[@"clusterid"] = extension[@"clusterid"];
              retDict[@"sid"] = extension[@"sid"];
              NSLog(@"disposeConversation_retDict = %@", retDict);
              [retArr addObject:retDict];
            }];
          }
          
        }
      }
      return retArr;  
    }
    
    

    备注

    本文的源码放在本文的源码放在我的gitHub上 DingTalkNoJailTweak
    有关源码怎么使用,请参考源码的Redeme.此源码可以用在越狱环境中,也可以使用在非越狱环境中.

    源码说明

    参见github上的源码说明 https://github.com/yohunl/DingTalkNoJailTweak

    相关文章

      网友评论

      • PotterSun:另外最新版的钉钉(3.5.2) 使用此插件 GPS定位打卡时会出现 "钉钉定位中,请稍后重试",请问楼主怎么破
      • PotterSun:请问你砸壳的钉钉版本号是多少的?我使用Clutch 砸壳 钉钉(3.5.0)版本的 结果失败,出现如下提示 直接就终断和手机ssh连接了
        iPhone:/usr/bin root# Clutch -b com.laiwang.DingTalk
        com.laiwang.DingTalk contains watchOS 2 compatible application. It's not possible to dump watchOS 2 apps with Clutch 2.0.4 at this moment.
        Connection to 192.168.0.148 closed by remote host.
        Connection to 192.168.0.148 closed.
        PotterSun:@yohunl 去掉了 生成的DingTalk.decrypted 直接去掉后缀改成DingTalk 这样做,有没有问题
        yohunl:@孙清泉 你去掉原来的签名文件夹没
        PotterSun:请问楼主有什么解决方案吗?另外我还使用了 dumpdecrypted砸壳,可以砸壳成功,出现 DingTalk.decrypted文件,我把这个文件去掉后缀后(即DingTalk)放回.app里面后 重新打包,安装 会出现安装失败,我已经确认安装失败不是证书问题,我使用同样方法打包别的应用都没有问题,请问楼主这个问题怎么解?
      • 萝卜丝是我:写得很不错!

      本文标题:APP逆向分析之钉钉抢红包插件的实现-iOS篇

      本文链接:https://www.haomeiwen.com/subject/pcommttx.html