一、寫在前面
1.為什么做免考?
相較於考試,免考更能鍛煉自身創新和探索能力,更有挑戰性。
2.選做微信推送防撤回原因?
- 微信已經成為日常溝通中必不可少的工具,如何知道別人在你沒看微信的情況下偷偷說了什么就變得很重要(嘿嘿嘿)
- CTF我只研究過雜項、密碼學和逆向,別的部分我也不擅長所以就不做了。
二、寫在中間
1.什么是防撤回?
- 指對方發出撤回請求,系統應答后,對方微信顯示成功撤回,而我方微信顯示對方撤回提示但消息依然顯示在界面。
2.實驗環境
- 實驗對象:wechatwin.dll (PC 2.6.7.57版本)
- 實驗工具:
- IDA 6.6(7.0也行,好像6.6已經下不到了,反正版本不影響)
- windbg(Windows應用商店里有)
- VC2017(12、15版不知道行不行)
3.撤回原理
- 撤回邏輯:
-
- 對方在一個終端上發起撤回請求,服務端收到請求,下發命令給我方終端
- 我方終端收到了服務端發來的撤回消息命令
- 我方終端執行撤回消息指令
- 撤回實現:
- 我方終端依舊響應服務器發來的撤回消息指令
- 我方終端執行撤回消息提醒,但不撤回消息內容
4.撤回代碼逆向分析
(1)將微信核心模塊放入IDA,搜索一下撤回有關的命令
(2)分析一哈,挺多的,但基本上邏輯順序與二、3的一致,那就動態調試找一下哪一條是服務器下的命令,很簡單就能發現是 On RevokeMsg svrId : %d ,那就好辦了 F5 一下
(3)再分析一哈,v191就應該是服務器下的命令,對應編號應該為10002(10000+2),然后查看一下這段程序調用的函數匯編,首先是 sub_10483FE0 函數
- 先看看如何調用
1 #對sub_10483FE0的匯編調用 2 sub esp, 14h 3 mov ecx, esp; 4 push 0 5 push dword ptr [eax+4] 6 call sub_10483FE0
- 再看看函數都調用了什么
1 #sub_10483FE0函數的匯編 2 push ebp 3 mov ebp, esp 4 sub esp, 8 5 push esi 6 mov esi, ecx 7 mov ecx, [ebp+arg_0] 8 push edi 9 mov dword ptr [esi], 0 10 mov dword ptr [esi+4], 0 11 mov dword ptr [esi+8], 0 12 mov dword ptr [esi+0Ch], 0 13 mov dword ptr [esi+10h], 0
- 先分析 sub_10483FE0 函數,左面就是匯編語言各種操作命令,右邊是操作對象。從第9行開始就出現了dword ,這里是對注冊表鍵值的處理嗎?再看看后面的4和8,應該就是對字符串編碼進行操作。然后搜索了一下(問百度),在Windows開發人員中心里發現了MultiByteToWideChar 功能,具體介紹如下:
- 搞了半天,原來 sub_10483FE0 這個類功能就是對寬字符進行操作,類似於C++里的wstring操作,應該是考慮到消息的編碼方式應該是Unicode編碼,再結合對 sub_10483FE0 函數的調用匯編代碼,可以看出 ecx 完成的是 wstring 操作, dword ptr [eax+4] 完成的是java new String(XXX, UTF-8) 操作
- 雖然知道了函數是干啥的,但也不知道具體實現過程,所以使用動態調試,將斷點設在call sub_10483FE0上, 查看此時的ecx的值
1 0:000:x86> dd 00efcb00-14 2 00efcb0c 13f0e7f0 000000d6 00000100 00000000
- 繼續運行,當調用結束后查看ecx 之后都干了啥
1 0:000:x86> du 13f0e7f0 2 13f0e7f0 "<sysmsg type="revokemsg"><revoke" 3 13f0e830 "msg><session>wxid_xxxxxxxxxxxxx" 4 13f0e870 "</session><msgid>18778893426</msg" 5 13f0e8b0 "id><newmsgid>5379365542789842691" 6 13f0e8f0 "</newmsgid><replacemsg><![CDATA[" 7 13f0e930 ""不知名網友" 撤回了一條消息]]></replacemsg" 8 13f0e970 "></revokemsg></sysmsg>"
(4)到這里就顯而易見了,出現了 <replacemsg> ,這不就是我們所想改的地方嗎!!跟之前找撤回命令一樣,找一下關於 replacemsg 在哪里,實際上也不用那么找,剛才執行完 sub_10483FE0 那一部分之后看看后面調用了啥就行,上面那個不是,那就是下面這個。
- 挺好的,要是搜索的話也是sub_10257570 是唯一一個處理replacemsg的函數,仔細看看發現sub_10257570 被sub_10252F80 調用
- 然后一步一步往下找,真的好麻煩(過程就省略一點),真的追吐血了(有時間的話你們可以追追看)
- 最后發現sub_10253250 函數,是整個處理replacemsg部分的實現函數
- 感覺離事實的真相更進一步了,查看了一下 sub_10253250 函數內部邏輯,發現有一處判斷語句
- 分別給v69賦值0和1,發現當為1時,有提示語並且消息沒了,但是為0時,提示語也有,但是消息還在,聽到這里開不開心?
(5)如何讓消息不撤回到這里已經很清楚了,只需要在這里給他按個補丁,一直讓他在else里走就好,但這里會有一個問題出現,你的微信上別人無法撤回消息,你的消息你也撤回不了,豈不是有點尷尬,那就要找一找微信是怎么區分自己發的撤回指令和其他人發的指令的函數。
- 到這里我就有點蒙了,sub_10253250 后面有好多,根本就不知道哪里是,直到看到了10000,是個熟悉的數字(10002為消息撤回參數)
1 if ( v50 == 10000 ) 2 goto LABEL_46;
- 也就是說在這之前就完成了消息撤回的步驟,再往上找,發現sub_1025A390 函數,在其附近的代碼段找到兩句調用函數
1 sub_1004E320((int)&v46); 2 ... 3 ... 4 ... 5 v11 = sub_1025A390 ( xmm0_0, v10, a3, a4, (int)&v46);
- 前一句是初始化v46(后來才知道這個變量是結構體變量),后一句是對v46的具體操作,那就要動態調試一下,看看兩種撤回情況對v46變量的影響
1 0:013> g 2 *** WARNING: Stack unwind information not available. 3 #別人撤回時 4 012fc2e0 00000001 00000000 00000002 4cdf683f 5 #自己撤回時 6 012fc2e0 00000001 00000001 00000002 4cdf689f
- 顯而易見,微信通過這兩個數據差異來判斷撤回的主體是誰,但也引出一個問題,當你自己撤回和別人撤回時會出現兩種提示
- 那么v46這個變量一定關聯着兩個函數或者一條判斷語句,那就繼續看sub_10253250 函數的后半部分,發現
- 再去觀察 sub_102541D0 函數,可以看出sub_102541D0 -> sub_102542A0的調用關系
- 再去看 sub_102542A0 函數,發現當變量為1時,就可以觸發重新編輯功能
(6)到這里所有撤回流程就很清楚了,當自己撤回時,就按照正常指令進行操作,當他人撤銷時,將消息撤回置位else,不讓他進入if(true)就可以實現防撤回的功能了。
三、寫在后面
1.防撤回實現
(1)生成匯編語言代碼,作為補丁,放入wechatwin.dll所在的文件夾重啟微信
(2)實現:
(3)彩蛋:撤回消息提醒是不是變了,原理全在 sub_102542A0 函數,可以自己試試。。。。嘿嘿嘿
2.感受:
逆向是真的麻煩,搞了半天補丁制作(借助了部分大佬的代碼),才弄出來,結果寫博客的時候微信升級了,雖然看了一下邏輯基本沒變,但是地址都變了,f**k,不想再做一遍補丁了,雖然這個功能挺好用的,但是需要電腦客戶端一直掛着微信就很麻煩,應該改進一下把撤回的消息都發給文件助手就會方便很多(網上有個大佬做過以前版本的),做到一半的時候,都想換題做個什么CTF雜項專題來着,幸好還是一點點堅持下來了,努力也沒算白費(版本一換就不行o(╥﹏╥)o),終於不用寫博客了(*^▽^*)。