APP逆向分析之釘釘搶紅包插件的實(shí)現(xiàn)-iOS篇

花費(fèi)了很多天的原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處https://yohunl.com/ding-ding-qiang-hong-bao-cha-jian-iospian/ ,謝謝!

網(wǎng)絡(luò)上關(guān)于微信紅包的分析文章已經(jīng)非常多了,基本上照著做就可以弄個(gè)微信搶紅包插件出來,不過,隨著阿里巴巴的釘釘在企業(yè)中的流行,很多企業(yè)現(xiàn)在都采用釘釘來辦公了,順帶著,也就使用釘釘來發(fā)紅包了,學(xué)習(xí)了那么多逆向的理論后,需要拿一個(gè)東西來練練手,剛好,釘釘就符合這個(gè)要求,于是,便有了下面的這篇文章.套用騰訊的何兆林在文章 移動(dòng)App入侵與逆向破解技術(shù)-iOS篇說的一段話
"破解有時(shí)候很耗時(shí)妓笙,和程序開發(fā)正好相反,它耗時(shí)不是耗在寫代碼上椎侠,而是耗在尋找注入點(diǎn)和逆向工程上姿染,有可能你花了3天時(shí)間去找程序的破綻伐憾,但是最終的破解代碼可能就2行勉痴,不到一分鐘就搞定了;但是你也需要做好面對(duì)失敗的準(zhǔn)備树肃,如果路選錯(cuò)了蒸矛,有可能你這3天完全是在浪費(fèi)腦細(xì)胞"

本文的源碼放在我的gitHub上 DingTalkNoJailTweak ,,其ReadMe文件說明了其中相關(guān)的代碼是起什么作用的.

當(dāng)然了,這篇文件會(huì)涉及比較多的逆向的東西,這些理論等前序知識(shí),可以參考網(wǎng)上的,網(wǎng)上的資料比較多了,比如TheOS的開發(fā)環(huán)境的搭建,Hopper逆向分析軟件的使用,ida Pro等,移動(dòng)App入侵與逆向破解技術(shù)-iOS篇上就有很不錯(cuò)的前序理論知識(shí)的介紹.
好了,閑話不多說,直接進(jìn)入正題.

相關(guān)工具和環(huán)境

一臺(tái)越獄的手機(jī),雖然不越獄的手機(jī)也可以用來分析,但是,那樣將會(huì)大大的減慢整個(gè)分析過程,因?yàn)榉窃姜z的只能通過注入后打log日志來分析,耗時(shí)耗力,所以,越獄手機(jī)是必備

越獄的手機(jī)上安裝了

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>
當(dāng)然你可以使用同步助手,PP助手,iFunBox,iTools Pro來安裝到手機(jī)的/usr/bin下
如果不是用SSH從電腦上登陸手機(jī)的命令行的話,你可以cydia下載MTerminal,然后使用su切換用戶到root,再使用命令 chmod +x clutch來提升權(quán)限

MTerminal

cydia中下載,可以方便的在你的手機(jī)上使用終端命令行.

一臺(tái)MAC電腦安裝了

class-dump

class-dump,用來dump出二進(jìn)制的頭文件.

Theos

可以參考我的另外一篇文章來安裝 iOS 越獄的Tweak開發(fā)

Hopper Disassembler v3 或者ida Pro

用來進(jìn)行匯編分析的

xcode

insert_dylib

iTools Pro

iTools
方便的用來拷貝.瀏覽手機(jī)上的所有文件

砸殼

砸殼,是為了能夠使用class-dump來導(dǎo)出所有的頭文件,有了頭文件,我們分析起來才是有可能的.
ssh連上你的越獄手機(jī)/或者像我一樣直接在手機(jī)上的Terminal中操作,調(diào)用clutch來進(jìn)行砸殼

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就是 用來砸殼的,需要一個(gè)參數(shù)是要被砸殼的應(yīng)用的bundleid,獲取釘釘?shù)腷undleid有很多方法,你可以直接使用 clutch -i 輸出手機(jī)上所有已經(jīng)安裝的應(yīng)用,找到bundleid.釘釘?shù)腷undleid是com.laiwang.DingTalk
開始砸殼



稍等一會(huì),砸殼完畢,會(huì)輸出砸完殼后的存放路徑

使用iTool Pro/PP助手等工具,將砸完殼的釘釘拷貝到電腦上

導(dǎo)出頭文件

在電腦上,將砸殼后的文件中讀取出頭文件, DingTalk是從砸殼后的ipa包中提取出來的釘釘?shù)目蓤?zhí)行文件,這個(gè)文件比較好找,一般都是沒有后綴的,大小最大的那個(gè).

class-dump -H DingTalk -o DingTalkHeaders

class-dump的簡(jiǎn)單使用方式
最簡(jiǎn)單的使用方式
class-dump -H DDMMerchant -o yicommonHeaders
class-dump -H Payload/WeChat.app/WeChat -o wechatHeaders

還可以加上 -S -s來對(duì)生成的方法,類都進(jìn)行排序
class-dump -S -s -H Payload/WeChat.app/WeChat -o wechatHeaders
一定要寫 -H,不然,都是在命令行輸出的,不會(huì)將內(nèi)容輸入到文件夾下!!

導(dǎo)出頭文件后,新建一個(gè)xcode工程,將所有的頭文件都導(dǎo)出到工程中,為什么做這一步呢,因?yàn)?Xcode的搜索能力還是不錯(cuò)的,方便我們?nèi)ゲ榭催@些頭文件.當(dāng)然,由于一次性導(dǎo)入xcode工程的文件量比較大,在導(dǎo)入的過程中,xcode可能會(huì)假死,稍微耐心一點(diǎn).

開始分析

在這里,強(qiáng)烈的推薦Flipboard的FLEX,這個(gè)簡(jiǎn)直就是iOS分析界的神器啊.以前還要用命令才能一步步的將界面所對(duì)應(yīng)的視圖,以及相應(yīng)的ViewController分析出來,現(xiàn)在有了它,這個(gè)過程可以大大加速了.

如果你安裝了FlexInjected,打開設(shè)置那里FlexInjected中釘釘?shù)拈_關(guān),這樣,當(dāng)釘釘啟動(dòng)的時(shí)候,就加載了Flex了,加載了Flex后,會(huì)在釘釘中出現(xiàn)Flex的界面,點(diǎn)擊界面可以進(jìn)行相應(yīng)的操作.
首先,第一步,我們先要找到搶紅包的視圖所對(duì)應(yīng)的對(duì)象以及相應(yīng)的VC,這個(gè)事情利用Flex還是很簡(jiǎn)單的.

設(shè)置中打開 FlexInjected中的釘釘,這樣,釘釘啟動(dòng)的時(shí)候,就會(huì)加載FLex.


打開釘釘,就會(huì)出現(xiàn)Flex的菜單了,Flex的基本操作,可以參考FLex官網(wǎng)的.


通過Flex,我們很容易的得到下面的結(jié)果
1 聊天頁面的紅包視圖的View是DTMessageBubbleRedEnvelopView



2 點(diǎn)擊后,搶紅包的頁面的視圖是

DTOpenLuckyMoneyView
----DTOpenLuckyMoneyEntityView


3 所有的聊天的會(huì)話的控制器是DTConversationListViewController.

我們已經(jīng)知道了搶紅包的關(guān)鍵視圖是DTOpenLuckyMoneyEntityView 和DTOpenLuckyMoneyView.打開我們dump出來的所有的頭文件,查看DTOpenLuckyMoneyEntityView頭文件定義
DTOpenLuckyMoneyEntityView的定義(截取我們需要的關(guān)鍵部分)

@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的定義(截取我們需要的關(guān)鍵部分)

@interface DTOpenLuckyMoneyView : UIView <DTOpenLuckyMoneyEntityViewDelegate>
//通過此方法,構(gòu)造出來視圖
+ (void)showLuckyMoneyWithPickingStatus:(DTBizRedEnvelopClusterPickingStatus *)arg1 withController:(DTMessageOTOViewController *)arg2 delegate:(id <DTOpenLuckyMoneyViewDelegate>)arg3;
- (void)didClickOpenLuckyMoney:(DTOpenLuckyMoneyEntityView *)arg1;
@end

通過上面的兩個(gè)類和一個(gè)協(xié)議的方法定義,我們很容易的就可以得出它們之間的關(guān)系大概是如下這樣的

當(dāng)用戶點(diǎn)擊了拆紅包按鈕時(shí)

DTOpenLuckyMoneyEntityView中的button的響應(yīng)事件 
- (void)didClickOpenLuckyMoneyBtn:(id)arg1;
    其中  先獲取id <DTOpenLuckyMoneyEntityViewDelegate> _delegate; (取值是 DTOpenLuckyMoneyView (@interface DTOpenLuckyMoneyView : UIView <DTOpenLuckyMoneyEntityViewDelegate>))
    轉(zhuǎn)給其方法 - (void)didClickViewMore:(DTOpenLuckyMoneyEntityView *)arg1;
也就是  
DTOpenLuckyMoneyEntityView
- (void)didClickOpenLuckyMoneyBtn:(id)sender {
   [self.delegate didClickOpenLuckyMoney:self];//_delegate; (取值是 DTOpenLuckyMoneyView
}

這只是大概的流程,我們可以進(jìn)一步細(xì)化這個(gè)流程,這個(gè)時(shí)候就要用到Hopper Disassembler或者iDA Pro了,用Hopper打開釘釘?shù)目蓤?zhí)行文件,等待分析完畢,打開DTOpenLuckyMoneyView的didClickViewMore實(shí)現(xiàn),(這里我只截取了部分)


從這個(gè)方法的匯編代碼中,我們大體上可以得到如下的信息:

DTOpenLuckyMoneyView didClickOpenLuckyMoney:(DTOpenLuckyMoneyEntityView *)arg1;
   [self statusModel]; //DTBizRedEnvelopClusterPickingStatus *statusModel,其中有一個(gè)屬性是DTBizRedEnvelopCluster,包含了紅紅包的相關(guān)信息,看上面的截圖
   [self utOpenRedEnvelop:statusModel]
   [self beginLoading]//其中會(huì)調(diào)用DTOpenLuckyMoneyEntityView的loading方法等
   [DTRedEnvelopServiceFactory defaultServiceIMP]獲取一個(gè) 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這個(gè)類,還有pickRedEnvelopCluster方法,在頭文件中搜索pickRedEnvelopCluster,可以得到



我們又得到了幾個(gè)關(guān)聯(lián)的類,尤其是其中的DTRedEnvelopService.

到這里,你可能還是很迷惑,下步該做什么?

不要緊,TheOS給我們提供了一個(gè)logify.pl腳本(theos/bin/logify),這個(gè)腳本可以將一個(gè)類中的所有方法都加上日志.
大概的使用方式就是

$THEOS/bin/logify.pl ./DTMessageMTMViewController.h  在終端顯示結(jié)果
$THEOS/bin/logify.pl ./DTMessageMTMViewController.h > /out/to/DTMessageMTMViewController.xm
 

我們使用Theos建立一個(gè)tweak,這個(gè)tweak的作用就是輸出日志,有關(guān)theos的安裝和使用,可以參考我的另外一篇博客iOS 越獄的Tweak開發(fā),我們將我們覺得可疑的類都加上日志,編譯一個(gè)tweak,安裝到手機(jī)上.
你的這個(gè)tweak.xm文件中的內(nèi)容應(yīng)該是大致如下

%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

接著,你在釘釘中,讓別人發(fā)一個(gè)紅包,然后你點(diǎn)擊拆紅包,拆掉紅包,將整個(gè)的Log輸出,復(fù)制出來,用于分析.
這里,我將某一次的日志部分放出來,讓你對(duì)這個(gè)日志有個(gè)概念

<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>]


當(dāng)然了,這個(gè)分析日志的過程,你可能會(huì)持續(xù)非常多的次數(shù),因?yàn)樵诜治龅倪^程中,你可能會(huì)發(fā)現(xiàn)另外的某個(gè)對(duì)象可能是你要的分析,你就要再修改tweak.xm文件中的hook,將這個(gè)對(duì)象的方法全hook住,反反復(fù)復(fù)

這個(gè)時(shí)候,你應(yīng)該能夠得到如下的關(guān)鍵信息

<DTRedEnvelopServiceFactory: 0x103303bd8> defaultServiceIMP]

DTRedEnvelopServiceFactory通過方法defaultServiceIMP構(gòu)造了一個(gè)DTRedEnvelopServiceIMP對(duì)象,然后通過這個(gè)DTRedEnvelopServiceIMP對(duì)象的pickRedEnvelopCluster: clusterId: successBlock: failureBlock方法去拆紅包

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

這個(gè)方法就是最后的拆紅包功能的方法,到此
拆紅包這個(gè)就很簡(jiǎn)答了,由于[DTRedEnvelopServiceFactory defaultServiceIMP]是一個(gè)類方法,所以不需要我們構(gòu)造的,那么現(xiàn)在的問題是,在[DTRedEnvelopServiceFactory defaultServiceIMP]周后,有沒有調(diào)用其它的方法來配置這個(gè)構(gòu)造出來的DTRedEnvelopServiceIMP對(duì)象呢?
我們可以繼續(xù)根據(jù)輸出的日志來分析....

這里,我直接告訴你分析的結(jié)果
[DTRedEnvelopServiceFactory defaultServiceIMP]方法中,會(huì)初始化一個(gè)全局的DTRedEnvelopServiceIMP對(duì)象.并且配置好它,例如綁定支付信息等等 (你可以從匯編中大概看一下).返回給的DTRedEnvelopServiceIMP對(duì)象就是一個(gè)完全可用的對(duì)象了,只要調(diào)用這個(gè)對(duì)象的pickRedEnvelopCluster: clusterId: successBlock: failureBlock方法,就可以完成拆紅包這個(gè)動(dòng)作了.

那么,自動(dòng)拆紅包這個(gè),就變?yōu)槟阍趺茨玫竭@個(gè)方法所需的4個(gè)參數(shù)了,后兩個(gè)block,一看就知道可以傳nil的,因?yàn)槲覀儗?duì)成功失敗的回調(diào)不感興趣,問題就變?yōu)樵趺茨玫角皟蓚€(gè)參數(shù)了.
第一個(gè)參數(shù)是一個(gè) long long類型,第二個(gè)是一個(gè) NSString.

FLex有一個(gè)非常好用的功能,是其中帶有的Heap Objects,這個(gè)可以用來查看當(dāng)前內(nèi)存中有哪些方法.結(jié)合我們上面的日志分析,會(huì)發(fā)現(xiàn)
DTOpenLuckyMoneyEntityView 對(duì)象中的方法 - (DTBizRedEnvelopCluster *)currentCluster;
其中包含了我們需要的紅包的相關(guān)信息


接下來,我們讓別人再發(fā)一個(gè)紅包,然后,點(diǎn)擊出現(xiàn)拆紅包視圖,這個(gè)時(shí)候,利用Flex的Heap Objects查內(nèi)存中的DTBizRedEnvelopCluster方法,從中看到@property(copy, nonatomic) NSString *clusterId;和@property(nonatomic) long long sender,記下來

再搜尋 DTRedEnvelopServiceIMP對(duì)象,Flex可以直接調(diào)用搜尋出來的某個(gè)對(duì)象,在Flex中直接調(diào)用方法pickRedEnvelopCluster,傳遞給其,注意,這里在Flex中第二個(gè)參數(shù)是字符串,使用""而不是@"".


點(diǎn)擊右上角的call,調(diào)用這個(gè)方法,不出意外,你會(huì)發(fā)現(xiàn),紅包已經(jīng)被拆了,到此,驗(yàn)證了,的確是這個(gè)方法,而且也知道了參數(shù)怎么拿到的.

OK,問題又來了,如果沒有出現(xiàn)拆紅包視圖,那么就沒有這個(gè)DTBizRedEnvelopCluster類呀,那怎么獲取其中的兩個(gè)參數(shù)出來呢?

這的確是個(gè)問題,你要再回到匯編和頭文件中,去分析了....

每當(dāng)來一個(gè)新消息,消息的列表頁面就已經(jīng)收到了,這說明在這個(gè)頁面的時(shí)候,應(yīng)該已經(jīng)獲取到了搶紅包所需要的相關(guān)信息了.我們接下來找到他們之間的聯(lián)系

我們平時(shí)看到的聊天的集合信息頁是 DTConversationListController,瀏覽頭文件,看到DTConversationListDataSource *_dataSource;
進(jìn)入DTConversationListDataSource,看到其中有

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

從Heap Object進(jìn)去,查看這個(gè)conversationList對(duì)象中的內(nèi)容,可以看到其中存放的大概是DTBizConversation或者是WKBizConversation,繼續(xù)瀏覽
我們看到DTBizConversation中,有一個(gè)對(duì)象
@property(retain, nonatomic) DTBizMessage *lastMessage;
進(jìn)一步瀏覽內(nèi)存中的這個(gè)對(duì)象,你就會(huì)發(fā)現(xiàn) ,我靠,踏破鐵下無覓處,紅包信息就在這了.其中的 attachmentsJson中存放了完整的 附件信息的json字符串
大體上如下

{
  "contentType" : 901,
  "@Type" : "WKIDLContentModel",
  "attachments" : [
    {
      "@Type" : "WKIDLAttachmentModel",
      "size" : 0,
      "type" : 0,
      "extension" : {
        "amount" : "0.01",
        "clusterid" : "5PnPxu8C",
        "sname" : "XXX",
        "size" : "1",
        "congrats" : "恭喜發(fā)財(cái),大吉大利胸嘴!",
        "sid" : "45049990",
        "type" : "0",
        "oid" : "0"
      },
      "isPreload" : false
    }
  ]
}

經(jīng)過各種分析...,可以知道,紅包的contentType為901或者902.

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

DTConversationListController,只要我們開啟釘釘,第一個(gè)頁面就是,所以可以認(rèn)為這個(gè)是單例,它的_dataSource也是一直存在的.

所以我們要拿到紅包的信息結(jié)構(gòu)就很簡(jiǎn)單了.

接下來,最后一個(gè)問題了.
那么我們?cè)趺粗朗裁磿r(shí)候,分析這個(gè)DTConversationListController的dataSource的conversationList的DTBizConversation的DTBizMessage的attachmentsJson呢?也就是什么時(shí)候觸發(fā)我們的自動(dòng)搶紅包動(dòng)作呢??

比較容易想到的肯定是 當(dāng)來新信息的時(shí)候.
經(jīng)過再一次的日志分析等各種分析(應(yīng)該是一個(gè)比較漫長(zhǎng),繁瑣的過程),你可以得到 每當(dāng)有新消息來的收,DTConversationListDataSource的

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

方法會(huì)被調(diào)用,經(jīng)過日志輸出,我們可以得到,其第二個(gè)參數(shù)就是要插入到datasouce中的WKBizConversation/DTBizConversation

所以,只要我們hook這個(gè)方法,就可以在這觸發(fā)我們的搶紅包動(dòng)作了.

核心源碼

@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
有關(guān)源碼怎么使用,請(qǐng)參考源碼的Redeme.此源碼可以用在越獄環(huán)境中,也可以使用在非越獄環(huán)境中.

源碼說明

參見github上的源碼說明 https://github.com/yohunl/DingTalkNoJailTweak

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雏掠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子劣像,更是在濱河造成了極大的恐慌乡话,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耳奕,死亡現(xiàn)場(chǎng)離奇詭異绑青,居然都是意外死亡诬像,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門闸婴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颅停,“玉大人,你說我怎么就攤上這事掠拳。” “怎么了纸肉?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵溺欧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我柏肪,道長(zhǎng)姐刁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任烦味,我火速辦了婚禮聂使,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谬俄。我一直安慰自己柏靶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布溃论。 她就那樣靜靜地躺著屎蜓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钥勋。 梳的紋絲不亂的頭發(fā)上炬转,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音算灸,去河邊找鬼扼劈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛菲驴,可吹牛的內(nèi)容都是我干的荐吵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼谢翎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼捍靠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起森逮,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤榨婆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后褒侧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體良风,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谊迄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烟央。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片统诺。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖疑俭,靈堂內(nèi)的尸體忽然破棺而出粮呢,到底是詐尸還是另有隱情,我是刑警寧澤钞艇,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布啄寡,位于F島的核電站,受9級(jí)特大地震影響哩照,放射性物質(zhì)發(fā)生泄漏挺物。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一飘弧、第九天 我趴在偏房一處隱蔽的房頂上張望识藤。 院中可真熱鬧,春花似錦次伶、人聲如沸痴昧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剪个。三九已至,卻和暖如春版确,著一層夾襖步出監(jiān)牢的瞬間扣囊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工绒疗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侵歇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓吓蘑,卻偏偏與公主長(zhǎng)得像惕虑,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子磨镶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容