花費了很多天的原創(chuàng)文章,轉(zhuǎn)載請注明出處https://yohunl.com/ding-ding-qiang-hong-bao-cha-jian-iospian/ ,謝謝!
網(wǎng)絡上關于微信紅包的分析文章已經(jīng)非常多了,基本上照著做就可以弄個微信搶紅包插件出來,不過,隨著阿里巴巴的釘釘在企業(yè)中的流行,很多企業(yè)現(xiàn)在都采用釘釘來辦公了,順帶著,也就使用釘釘來發(fā)紅包了,學習了那么多逆向的理論后,需要拿一個東西來練練手,剛好,釘釘就符合這個要求,于是,便有了下面的這篇文章.套用騰訊的何兆林在文章 移動App入侵與逆向破解技術-iOS篇說的一段話
"破解有時候很耗時,和程序開發(fā)正好相反,它耗時不是耗在寫代碼上,而是耗在尋找注入點和逆向工程上,有可能你花了3天時間去找程序的破綻,但是最終的破解代碼可能就2行,不到一分鐘就搞定了;但是你也需要做好面對失敗的準備,如果路選錯了,有可能你這3天完全是在浪費腦細胞"
本文的源碼放在我的gitHub上 DingTalkNoJailTweak ,,其ReadMe文件說明了其中相關的代碼是起什么作用的.
當然了,這篇文件會涉及比較多的逆向的東西,這些理論等前序知識,可以參考網(wǎng)上的,網(wǎng)上的資料比較多了,比如TheOS的開發(fā)環(huán)境的搭建,Hopper逆向分析軟件的使用,ida Pro等,移動App入侵與逆向破解技術-iOS篇上就有很不錯的前序理論知識的介紹.
好了,閑話不多說,直接進入正題.
相關工具和環(huán)境
一臺越獄的手機,雖然不越獄的手機也可以用來分析,但是,那樣將會大大的減慢整個分析過程,因為非越獄的只能通過注入后打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開發(fā)
Hopper Disassembler v3 或者ida Pro
用來進行匯編分析的
xcode
insert_dylib
iTools Pro
iTools
方便的用來拷貝.瀏覽手機上的所有文件
砸殼
砸殼,是為了能夠使用class-dump來導出所有的頭文件,有了頭文件,我們分析起來才是有可能的.
ssh連上你的越獄手機/或者像我一樣直接在手機上的Terminal中操作,調(diào)用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就是 用來砸殼的,需要一個參數(shù)是要被砸殼的應用的bundleid,獲取釘釘?shù)腷undleid有很多方法,你可以直接使用 clutch -i 輸出手機上所有已經(jīng)安裝的應用,找到bundleid.釘釘?shù)腷undleid是com.laiwang.DingTalk
開始砸殼

稍等一會,砸殼完畢,會輸出砸完殼后的存放路徑
使用iTool Pro/PP助手等工具,將砸完殼的釘釘拷貝到電腦上
導出頭文件
在電腦上,將砸殼后的文件中讀取出頭文件, DingTalk是從砸殼后的ipa包中提取出來的釘釘?shù)目蓤?zhí)行文件,這個文件比較好找,一般都是沒有后綴的,大小最大的那個.
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,不然,都是在命令行輸出的,不會將內(nèi)容輸入到文件夾下!!
導出頭文件后,新建一個xcode工程,將所有的頭文件都導出到工程中,為什么做這一步呢,因為,Xcode的搜索能力還是不錯的,方便我們?nèi)ゲ榭催@些頭文件.當然,由于一次性導入xcode工程的文件量比較大,在導入的過程中,xcode可能會假死,稍微耐心一點.
開始分析
在這里,強烈的推薦Flipboard的FLEX,這個簡直就是iOS分析界的神器啊.以前還要用命令才能一步步的將界面所對應的視圖,以及相應的ViewController分析出來,現(xiàn)在有了它,這個過程可以大大加速了.
如果你安裝了FlexInjected,打開設置那里FlexInjected中釘釘?shù)拈_關,這樣,當釘釘啟動的時候,就加載了Flex了,加載了Flex后,會在釘釘中出現(xiàn)Flex的界面,點擊界面可以進行相應的操作.
首先,第一步,我們先要找到搶紅包的視圖所對應的對象以及相應的VC,這個事情利用Flex還是很簡單的.
設置中打開 FlexInjected中的釘釘,這樣,釘釘啟動的時候,就會加載FLex.


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

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

2 點擊后,搶紅包的頁面的視圖是
DTOpenLuckyMoneyView
----DTOpenLuckyMoneyEntityView

3 所有的聊天的會話的控制器是DTConversationListViewController.
我們已經(jīng)知道了搶紅包的關鍵視圖是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
通過上面的兩個類和一個協(xié)議的方法定義,我們很容易的就可以得出它們之間的關系大概是如下這樣的
當用戶點擊了拆紅包按鈕時
DTOpenLuckyMoneyEntityView中的button的響應事件
- (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
}
這只是大概的流程,我們可以進一步細化這個流程,這個時候就要用到Hopper Disassembler或者iDA Pro了,用Hopper打開釘釘?shù)目蓤?zhí)行文件,等待分析完畢,打開DTOpenLuckyMoneyView的didClickViewMore實現(xiàn),(這里我只截取了部分)

從這個方法的匯編代碼中,我們大體上可以得到如下的信息:
DTOpenLuckyMoneyView didClickOpenLuckyMoney:(DTOpenLuckyMoneyEntityView *)arg1;
[self statusModel]; //DTBizRedEnvelopClusterPickingStatus *statusModel,其中有一個屬性是DTBizRedEnvelopCluster,包含了紅紅包的相關信息,看上面的截圖
[self utOpenRedEnvelop:statusModel]
[self beginLoading]//其中會調(diào)用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,可以得到

我們又得到了幾個關聯(lián)的類,尤其是其中的DTRedEnvelopService.
到這里,你可能還是很迷惑,下步該做什么?
不要緊,TheOS給我們提供了一個logify.pl腳本(theos/bin/logify),這個腳本可以將一個類中的所有方法都加上日志.
大概的使用方式就是
$THEOS/bin/logify.pl ./DTMessageMTMViewController.h 在終端顯示結(jié)果
$THEOS/bin/logify.pl ./DTMessageMTMViewController.h > /out/to/DTMessageMTMViewController.xm
我們使用Theos建立一個tweak,這個tweak的作用就是輸出日志,有關theos的安裝和使用,可以參考我的另外一篇博客iOS 越獄的Tweak開發(fā),我們將我們覺得可疑的類都加上日志,編譯一個tweak,安裝到手機上.
你的這個tweak.xm文件中的內(nèi)容應該是大致如下
%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ā)一個紅包,然后你點擊拆紅包,拆掉紅包,將整個的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>]
當然了,這個分析日志的過程,你可能會持續(xù)非常多的次數(shù),因為在分析的過程中,你可能會發(fā)現(xiàn)另外的某個對象可能是你要的分析,你就要再修改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]是一個類方法,所以不需要我們構造的,那么現(xiàn)在的問題是,在[DTRedEnvelopServiceFactory defaultServiceIMP]周后,有沒有調(diào)用其它的方法來配置這個構造出來的DTRedEnvelopServiceIMP對象呢?
我們可以繼續(xù)根據(jù)輸出的日志來分析....
這里,我直接告訴你分析的結(jié)果
[DTRedEnvelopServiceFactory defaultServiceIMP]方法中,會初始化一個全局的DTRedEnvelopServiceIMP對象.并且配置好它,例如綁定支付信息等等 (你可以從匯編中大概看一下).返回給的DTRedEnvelopServiceIMP對象就是一個完全可用的對象了,只要調(diào)用這個對象的pickRedEnvelopCluster: clusterId: successBlock: failureBlock方法,就可以完成拆紅包這個動作了.
那么,自動拆紅包這個,就變?yōu)槟阍趺茨玫竭@個方法所需的4個參數(shù)了,后兩個block,一看就知道可以傳nil的,因為我們對成功失敗的回調(diào)不感興趣,問題就變?yōu)樵趺茨玫角皟蓚€參數(shù)了.
第一個參數(shù)是一個 long long類型,第二個是一個 NSString.
FLex有一個非常好用的功能,是其中帶有的Heap Objects,這個可以用來查看當前內(nèi)存中有哪些方法.結(jié)合我們上面的日志分析,會發(fā)現(xiàn)
DTOpenLuckyMoneyEntityView 對象中的方法 - (DTBizRedEnvelopCluster *)currentCluster;
其中包含了我們需要的紅包的相關信息
接下來,我們讓別人再發(fā)一個紅包,然后,點擊出現(xiàn)拆紅包視圖,這個時候,利用Flex的Heap Objects查內(nèi)存中的DTBizRedEnvelopCluster方法,從中看到@property(copy, nonatomic) NSString *clusterId;和@property(nonatomic) long long sender,記下來
再搜尋 DTRedEnvelopServiceIMP對象,Flex可以直接調(diào)用搜尋出來的某個對象,在Flex中直接調(diào)用方法pickRedEnvelopCluster,傳遞給其,注意,這里在Flex中第二個參數(shù)是字符串,使用""而不是@"".
點擊右上角的call,調(diào)用這個方法,不出意外,你會發(fā)現(xiàn),紅包已經(jīng)被拆了,到此,驗證了,的確是這個方法,而且也知道了參數(shù)怎么拿到的.
OK,問題又來了,如果沒有出現(xiàn)拆紅包視圖,那么就沒有這個DTBizRedEnvelopCluster類呀,那怎么獲取其中的兩個參數(shù)出來呢?
這的確是個問題,你要再回到匯編和頭文件中,去分析了....
每當來一個新消息,消息的列表頁面就已經(jīng)收到了,這說明在這個頁面的時候,應該已經(jīng)獲取到了搶紅包所需要的相關信息了.我們接下來找到他們之間的聯(lián)系
我們平時看到的聊天的集合信息頁是 DTConversationListController,瀏覽頭文件,看到DTConversationListDataSource *_dataSource;
進入DTConversationListDataSource,看到其中有
@property(retain, nonatomic) NSMutableArray<DTBizConversation * WKBizConversation *等> *conversationList
從Heap Object進去,查看這個conversationList對象中的內(nèi)容,可以看到其中存放的大概是DTBizConversation或者是WKBizConversation,繼續(xù)瀏覽
我們看到DTBizConversation中,有一個對象
@property(retain, nonatomic) DTBizMessage *lastMessage;
進一步瀏覽內(nèi)存中的這個對象,你就會發(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ā)財,大吉大利!",
"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,只要我們開啟釘釘,第一個頁面就是,所以可以認為這個是單例,它的_dataSource也是一直存在的.
所以我們要拿到紅包的信息結(jié)構就很簡單了.
接下來,最后一個問題了.
那么我們怎么知道什么時候,分析這個DTConversationListController的dataSource的conversationList的DTBizConversation的DTBizMessage的attachmentsJson呢?也就是什么時候觸發(fā)我們的自動搶紅包動作呢??
比較容易想到的肯定是 當來新信息的時候.
經(jīng)過再一次的日志分析等各種分析(應該是一個比較漫長,繁瑣的過程),你可以得到 每當有新消息來的收,DTConversationListDataSource的
- (void)controller:(id)arg1 didChangeObject:(id)arg2 atIndex:(unsigned long long)arg3 forChangeType:(long long)arg4 newIndex:(unsigned long long)arg5;
方法會被調(diào)用,經(jīng)過日志輸出,我們可以得到,其第二個參數(shù)就是要插入到datasouce中的WKBizConversation/DTBizConversation
所以,只要我們hook這個方法,就可以在這觸發(fā)我們的搶紅包動作了.
核心源碼
@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.此源碼可以用在越獄環(huán)境中,也可以使用在非越獄環(huán)境中.
源碼說明
參見github上的源碼說明 https://github.com/yohunl/DingTalkNoJailTweak