非越獄 QQ 逆向歷險記

Long Long Ago

小半年前,我曾寫下越獄平臺下的防QQ撤回的相關文章:逆向的嘗試:讓你的 QQ 不被撤回。前幾天,閱讀了楊蕭玉大神的 Make WeChat Great Again。(⊙v⊙)嗯,突發奇想,好,那繼續弄未越獄平臺下的 QQ 的防撤回吧!

配置歷險裝備

  1. iOSOpenDev 正常來說都會安裝失敗,這里給個修復的工具 iOSOpenDevInstallFix
  2. yololib dylib 注入工具
  3. machOview 查看 dylib 是否注入成功
  4. cycript 強大的調試工具
  5. class-dump 砸出頭文件
  6. iOS App Signer 圖形化重簽名工具

本文不涉及具體安裝過程,請自行 google!

歷險開始咯

1. 尋找目標 - 撤回函數

(⊙v⊙)嗯 ... 會不會和我半年前猜的一樣呢,經過驗證,果然是一樣的,具體請看逆向的嘗試:讓你的 QQ 不被撤回

撤回函數分別是如下幾個,這里只需干掉第一個就可以了。

- (void)handleRecallNotify:(struct RecallModel *)arg1 isOnline:(_Bool)arg2;
- (void)handleDiscussRecallNotify:(char *)arg1 bufferLen:(unsigned int)arg2 isOnline:(_Bool)arg3;
- (void)handleGroupRecallNotify:(char *)arg1 bufferLen:(unsigned int)arg2 isOnline:(_Bool)arg3;
- (void)handleC2CRecallNotify:(const void *)arg1 bufferLen:(int)arg2 subcmd:(int)arg3 isOnline:(_Bool)arg4;

2. 編寫 Tweak

這里我設置了一個 recallStatus,就是可以開關防撤回的功能。開的時候,就直接在函數中 return,不讓它繼續執行,就是這么簡單粗暴。

CHOptimizedMethod2(self, void, QQMessageRecallModule, handleRecallNotify, RecallModel *, arg1, isOnline, _Bool, arg2){
    if ([XRKQQInfos sharedInstance].recallStatus) {
        return ;
    }
    CHSuper2(QQMessageRecallModule, handleRecallNotify, arg1, isOnline, arg2);
}

3. dylib 注入

將第二步得到的 dylib 庫注入到從某助手下載的已經砸殼的 QQ 里面。

./yololib QQ hook_qq.dylib

使用 machOview 來查看是否注入成功。截圖中 dylib 的名稱和上面不同,就把它看成 hook_qq.dylib 就可以。

4. 重簽名

由于 QQ 會進行簽名驗證,所以你要找一個通用匹配的證書來重簽名。

5. 安裝

使用Xcode 自帶的安裝工具就可以。

Xcode -> Window -> Devices

6. 測試

原諒自言自語的我

歷險再啟程

弄個撤回好像有點單調,就問我同學有沒有好的意見,他是這樣說的。


1. 尋找目標 - QQ level 的設置

推薦閱讀:http://iphonedevwiki.net/index.php/Cycript_Tricks

1.1 注入 Cycript.framework 并安裝到手機

1.2 連上手機調試

觀察這個頁面,我最開始的思路是先找到這個 level 展示的 view 然后在它 controller 里面直接修改的。所以我就 choose(UILabel),然后再 iterm 里面搜索 Tsui YuenHong,找到它的 superview,再根據這個 superview找到它的 subviews,里面肯定包括這個 level 展示的 view,就可以修改到等級了。

$ ./cycript -r 192.168.0.202:8888
cy# choose(UILabel)
...... 一堆東西輸出
cy# #0x117f3b9e0
#"<UIButtonLabel: 0x117f3b9e0; frame = (0 6.5; 195.5 33.5); text = 'Tsui YuenHong'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x113b291e0>>"
cy# #0x117f3b9e0.superview
#"<UIButton: 0x113c69030; frame = (47 -3; 196 46); opaque = NO; layer = <CALayer: 0x17022f420>>"
cy# #0x117f3b9e0.superview.superview
#"<UIControl: 0x113bbc130; frame = (20 113.38; 283 85); layer = <CALayer: 0x11277edb0>>"
cy# #0x117f3b9e0.superview.superview.superview
#"<DrawerMyInfoView: 0x105d4abf0; baseClass = UIButton; frame = (0 0; 375 213.38); opaque = NO; layer = <CALayer: 0x17022f440>>"

找到了這個 view 的父容器是 DrawerMyInfoView,再找DrawerMyInfoView 的響應者就可以找到它對應的 controller。

cy# choose(DrawerMyInfoView)
[#"<DrawerMyInfoView: 0x105d4abf0; baseClass = UIButton; frame = (0 0; 375 213.38); opaque = NO; layer = <CALayer: 0x17022f440>>"]
cy# #0x105d4abf0.nextResponder
#"<UIView: 0x10f4595b0; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x175032400>>"
cy# #0x105d4abf0.nextResponder.nextResponder
#"<DrawerContentsViewController: 0x106828a00>"

是你了,皮皮蝦,DrawerMyInfoView & DrawerContentsViewController。興致勃勃地在 QQ 頭文件里面找,發現并木有引用到和 level 相關的方法。

好吧,那我就是搜索全部頭文件來找到和 level 相關的方法,excuse me?發現并木有。

1.3 轉戰 hopper

hopper 中偶然發現有個一個類叫 QQAcountModel,這個類有點特殊,在 class-dump 出來的頭文件是看不到的,而且它的 description 方法是被重寫了,這樣在一定程度可以避免被逆向的可能。這里我選擇 hookdescription
level 方法。

CHOptimizedMethod0(self, unsigned int, QQAccountsModel, level){
    return [XRKQQInfos sharedInstance].qqLevel;
}

CHOptimizedMethod0(self, NSString *, QQAccountsModel, description){
    return [NSString stringWithFormat:@"%p", self];
}

2. 其余步驟和防撤回功能一樣

...

歷險成果

  1. 防撤回功能
  2. 修改 Level(自娛自樂的功能)

為了方便修改,我還在設置界面新增自定義選項(不過,本地化并木有做,每次重啟都恢復默認項)。


如果你發現什么有趣的功能,我們可以一起探討,Make QQ Fun Again!

項目的 Github 地址:https://github.com/xurunkang/MakeQQFunAgain

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容