[原創]微信PC端技術研究(3)-如何找到訊息傳送介面

anhkgg發表於2019-02-25

微信PC端技術研究-如何找到訊息傳送介面
by anhkgg(公眾號:漢客兒)
2019年2月18日

0x0. 前言

準備工具:Cheat Engine,OllyDbg,IDA。

 

前一篇(微信PC端技術研究(2)-儲存聊天語音)已經說過CE是什麼,也應用CE研究瞭如何儲存微信語音,這篇繼續使用CE和OD來研究一下微信的訊息傳送介面。

 

思路大概是這樣:在訊息框中輸入內容之後,通過CE找到內容地址,然後通過記憶體斷點來找到傳送該資料的相關程式碼,從而找到訊息傳送介面。

0x2. 分析過程

查詢關鍵資料地址

在輸入框輸入一個比較特別的文字內容(避免搜尋時太多記憶體選項)後,使用CE搜尋該內容地址。

 

由於已經知道確切的訊息內容,很容易就能通過CE的Exact value->String來找到內容地址,修改內容多次篩選,最後留下兩個結果(詳細操作見上一篇文章)。

 

 

通過CE修改一下記憶體的內容,微信輸入框中內容同步改變,說明這個記憶體地址就是輸入框中內容地址,最終確認地址是2A1E1A8

 

接著在點選傳送按鈕之後,輸入框內容會被清空,所以第一想法就是對記憶體地址下記憶體寫入斷點,可以找到傳送過程中清空內容的程式碼。

 

開啟OD,掛載到WeChat.exe程式,在右下角資料視窗Ctrl+G輸入2A1E1A8,然後右鍵選擇斷點->記憶體寫入斷點。

 

 

F9讓OD跑起來,然後點選微信傳送按鈕,沒想到意外發生了,輸入框內容清空了,但是斷點卻沒有觸發。

 

怎麼回事?斷點弄錯?地址找錯?暫時沒有答案。

 

用CE多次重複前面的操作,地址依然是這個地址,斷點就是不觸發。

 

通過OD檢視到,在輸入框清空後,2A1E1A8的內容確實沒有變化,和傳送前一樣,並且在重新輸入新的內容之後,該記憶體內容同步更新。

 

所以結論就是輸入框內容地址確實是2A1E1A8,但是清空輸入框並不是清空該記憶體內容,猜測編輯框可能通過控制字串長短來控制顯示的,清空輸入框內容就是設定字串長度為0。

找到輸入框類

清空輸入框沒有進展了,那怎麼辦呢?

 

嘗試去找了其他資料,比如傳送按鈕的傳送(S),傳送按鈕的提示內容不能傳送空白內容等等,資料地址也可以很快找到,但是和我們的分析目標偏的太遠了。

 

幾番折騰後,作罷。

 

轉念一想,清空不行,傳送總的讀取輸入框中內容吧,那換成記憶體訪問斷點嘗試一下。

 

依然是在右下角資料視窗Ctrl+G輸入2A1E1A8,然後右鍵選擇斷點->記憶體訪問斷點。

 

 

完成後回到微信介面,沒想到直接斷下了,我還沒點傳送按鈕呢。根據以前的經驗,下意識就覺得是介面重新整理顯示文字觸發了斷點,這可能會影響分析,根本沒辦法通過傳送按鈕來觸發記憶體訪問斷點。

 

 

一般解決方法有:

  1. 條件斷點。也就是遮蔽掉重新整理介面觸發的斷點,但是好像記憶體斷點不支援條件斷點啊,要麼通過指令碼來完成,好麻煩。
  2. 找其他切入點。廢話,清空那邊的路都斷了,死心吧。
  3. 其他我不知道的...

放棄了一般的解決方法,我決定看看本次斷點究竟幹嘛了。

 

注意到斷點的位置不是微信模組WeChatWin.dll中,而是在msftedit.dll,很少見的一個模組。根據目錄可以看到是微軟系統的一個模組,名字中的edit也可以看出這應該是一個編輯框相關的模組。

可執行模組, 條目 20
 基址=6F050000
 大小=00094000 (606208.)
 入口=6F05D53D msftedit.<ModuleEntryPoint>
 名稱=msftedit (系統)
 檔案版本=5.41.21.2510
 路徑=C:\Windows\System32\msftedit.dll

好像和我們的分析目標很貼近嘛,在OD中資料視窗右鍵斷點->刪除記憶體斷點,然後按下Alt+F9回到使用者模組領空,也就是跳過系統模組的程式碼,直接回到微信的模組程式碼中,省過對系統程式碼的分析。

 

看到返回到6E20CCC2這個地址,上一行程式碼就是呼叫msftedit.dll的函式,我們對其下一個斷點,滑鼠點選到6E20CCBF這行程式碼,按下F2下一個int 3斷點,然後F9跳過本次分析。

 

 

OD繼續斷下,此次直接斷在了6E20CCBF這個位置,可以看到call呼叫了msftedit.6F05AD69,這是個什麼函式呢?

 

 

既然msftedit.dll是微軟模組,那麼肯定是有符號的嘛,嘿嘿。

 

這裡可以直接在OD中載入符號來分析,使用方法是:

1.在WingDbg目錄下拷貝dbgeng.dll,dbghelp.dll,srcsrv.dll,symbolcheck.dll,symsrv.dll,symsrv.yes,一共6個檔案至OD目錄下。

2.開啟OD,設定符號路徑。除錯--->選擇符號路徑。

3.設定StrongOD的外掛選項。選擇載入符號。

原文:https://blog.csdn.net/sr0ad/article/details/8253311

但是隻支援本地符號,也就是得自己下載了模組對應符號到本地,OD設定符號檔案路徑後,才能正常使用,有點麻煩。

 

我這時候一般就會使用IDA了,因為它會自己線上下載模組對應的符號,很方便。

 

用IDA開啟msftedit.dll,等待些許時間,IDA下載符號,解析等等完成後,我們去找到msftedit.6F05AD69對應的函式究竟是個什麼東西。

 

但是這裡msftedit.6F05AD69的模組基址是6F050000,而IDA解析使用的是預設基址0x6FCD0000,要麼修改IDA解析基址為6F050000,等待IDA重新解析,要麼通過偏移計算對應地址。

 

再解析等太久,直接計算吧,所以要安利我寫的一個小工具(偏移計算工具),能夠快速計算地址,具體使用見相關文章。

 

 

再IDA中按下g,輸入6fcdad69,找到msftedit.6F05AD69對應函式為CTxtEdit::OnTxInPlaceActivate

 

 

很明顯通過名字OnTxInPlaceActivate可以看出是編輯框中文字啟用狀態(顯示)下就會觸發該函式,這不是重點。

 

重點看CTxtEdit,不言而喻,這就是msftedit.dll中實現的編輯框的類。

 

如果寫過MFC相關程式碼,應該很快就能想到CTxtEdit肯定還有其他讀內容、寫內容的函式,叫做GetXXX或者SetXXX

 

在IDA的函式列表中翻看一下,果然很快就找到了CTxtEdit::GetTextExCTxtEdit::SetText

 

 

但到底這兩個函式是不是編輯框讀寫內容的函式呢,我們對這兩個函式下斷點試試,通過工具算到在ID除錯中這兩個函式的相應地址為6f0684376f056d37

 

 

在OD的底部命令視窗輸入bp 6f068437bp 6f056d37,刪掉之前CTxtEdit::OnTxInPlaceActivate的斷點,然後F9跑起來。

 

 

回到微信介面,這次能夠正常顯示了,點選傳送按鈕。OD觸發斷點,斷在了6f068437也就是CTxtEdit::GetTextEx上,很明顯這是傳送函式在讀取輸入框內容。

 

回溯找到傳送函式

此時的呼叫堆疊是這樣的:

呼叫堆疊 
地址       堆疊       函式過程 / 引數                       呼叫來自                      結構
0026E280   6F06842D   msftedit.6F068437                     msftedit.6F068428             0026E3FC //CTxtEdit::GetTextEx
0026E400   6E20D239   包含msftedit.6F06842D                   WeChatWi.6E20D233             0026E3FC
0026E43C   6DBD38EB   包含WeChatWi.6E20D239                   WeChatWi.6DBD38E8             0026E438 //TxtEdit_GetText
0026E5AC   6DC15B65   ? WeChatWi.6DBD3860                   WeChatWi.6DC15B60             0026E5A8 //sendBtn_GetText
0026E60C   6DC15DEE   WeChatWi.6DC15B10                     WeChatWi.6DC15DE9             0026E608 //sendbtn_click
0026E618   6E20BFB8   WeChatWi.6E20BEF4                     WeChatWi.6E20BFB3             0026E614
0026E62C   6E20362E   WeChatWi.6E20BF90                     WeChatWi.6E203629             0026E628
0026E6CC   6E203589   WeChatWi.6E2035A7                     WeChatWi.6E203584             0026E6C8
0026E820   6DC53695   ? WeChatWi.6E20352E                   WeChatWi.6DC53690             0026E81C

 

在OD中回溯呼叫堆疊跟蹤返回到WeChatWi.6E20D239,看到右側堆疊視窗已經獲取到輸入框中內容,證明前面的分析沒有問題。

 

 

再次回溯兩層到WeChatWi.6DC15B60,可以看到堆疊中的引數依然是獲取到的輸入框內容。

[0026E5E4] = 0828C070
[0828C070 + 4] = 0828CAF0 => a12bcAAAAA

 

此時函式首地址是WeChatWi.6DC15B10,進入到IDA中對應函式100d5b10(你要問我為什麼此時進入IDA檢視?我只好說其實這個步驟花費了很多時間,一邊OD除錯,一邊IDA輔助確認等等,過程並沒有這麼順利,篇幅原因省略),然後按下x回到上層函式,看到如下程式碼:

 

 

看到click很明顯可以看出這就是傳送按鈕的響應函式了(相關知識可以瞭解duilib程式設計,微信介面是duilib實現的)。

 

到目前找到了傳送訊息的函式,但還並不是訊息傳送介面,這還只是介面的操作函式,具體傳送訊息介面應該在該函式內部被呼叫。

有技巧找到傳送介面

先粗略地在OD中跟一遍WeChatWi.6DC15B10的程式碼邏輯,函式很多,沒法很快確認哪個函式是訊息傳送介面。

 

擷取部分程式碼感受一下,大概11個函式。根據OD跟的邏輯大概是sendBtn_GetText_10093860->sub_100DD340->sub_100C50C0->sub_10094100->sub_100DD9D0->sub_100C4450->sub_10323DF0->sub_100DE120

if ( sendBtn_GetText_10093860(a1->unk_560, (int)&savedregs, a2, a3, msg) <= 0 )// 這裡是獲取msg
  {                                             // x
    //省略一大段邏輯
  }
  if ( msg[0] != msg[1] )
  {                                             // x
    //省略一大段邏輯
  }
  if ( sub_100DD340() )
  {                                             // x
    //省略一大段邏輯
    sub_1047C070(&v34, v23);
    sub_100DB8C0((int)a1_, v34, v35, (int)v36, v37, (int)v38, v39, v40, (int)v41, msg_);
  }
  if ( sub_100C50C0((_DWORD *)(a1_->unk_558 + 2528), (int)msg, (int)v43) )
  {
    sub_10094100((_DWORD *)a1_->unk_560);//
    sub_100DD9D0(msg);                          
    sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);// 
    v31 = sub_10323DF0();
    sub_100DE120(v31, (int)a1_, (int)sub_100D6C40, 0, v40, (int)v41, msg_);// retn 18
    v12 = 1;
  }
  else
  {
    //省略一大段邏輯
    sub_10108D60(v30, *(&a1_->unk_558 + 1), v33, (int)v34, v35, v36, (int)v37, v38, v39, v40, v41);
  }

通常通過除錯每個函式的引數、返回結果等基本可以猜測到函式功能,然後來找到訊息傳送介面。

 

但這裡我偷懶了,因為引數結構複雜,一時半會沒法找到關鍵點,有點暈了。

 

所以我通過排除法來一一篩選函式,最多11次左右就能找到訊息傳送介面。舉個例子,如果sub_100DD340是訊息傳送介面,在我手工遮蔽其功能之後,訊息肯定發不出去了,那麼我就可以通過看到的結果(是否傳送成功)來確認sub_100DD340是不是要找到的訊息傳送介面。

 

具體遮蔽方法:

  1. 通過IDA或OD進入sub_100DD340函式內部,找到函式結尾,找到retn xx類似程式碼
  2. 用OD在sub_100DD340函式開始修改彙編程式碼為retn xx,雙擊輸入retn xx即可

這樣sub_100DD340函式直接在入口就返回了,功能沒有了,也保證了函式呼叫時的棧平衡。

 

 

在確認sub_100DD340並沒有影響訊息傳送之後,通過右鍵撤銷選擇處修改恢復修改的內容。

 

如此重複篩選其他的函式,最終確認sub_100C4450為傳送訊息函式。程式碼介面如下:

sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);//

msg是傳送內容,a1_->unk_558 + 2528)是當前聊天視窗的好友資訊,包括wxid和名字之類的資訊。

 

 

但作為介面依然不夠簡潔,需要構造好友資訊,比較複雜,所以繼續深入sub_100C4450內部,看看是否能夠找到最簡單的介面,比如:

sendmsg(wxid, msg); //傳入發給誰,發什麼即可

sub_100C4450內部依然很複雜,使用和前面同樣的方式,先大致跟一遍執行流程,然後通過排除法逐個篩選。

if ( !sub_100C43D0(msg_.buf, msg_.len, msg_.maxlen, wxid_) )// 是不是全是特殊字元\r\n\t等,是返回1,不是返回0
{
sub_1007D390();
msg_packet = sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);// 資料打包,傳送
sub_100494E0(msg_packet_, (size_t)msg_packet);//
sub_1004B550(&v67);              //
v11 = sub_102478D0();
v12 = sub_10402C10((int)v11);
v89 = (void **)v13;
if ( sub_10402C10((int)msg_packet_) != v12 || v14 != v89 )
{
    if ( sub_100C6770(this_) )        // 
    {
    sub_1004BBF0((int *)&msgpacket);//
    sub_10056940((int *)&msgpacket, (size_t)msg_packet_);// 
    sub_100C56D0(this___, (size_t)&msgpacket, 1);
    sub_10081210((LPVOID *)&msgpacket);
    v16 = sub_100C0EC0();
    sub_10247250((int **)v16, (int)path);
    }
}
}
if ( (signed int)(msg->msgend - (unsigned int)msg->msg) / 0x24 != 1 )
v9 = sub_10323DF0();
sub_10324E70(v9, msg_.len, msg_.maxlen, (int)wxid_, (int)path);
sub_100ADA10(&msg___);

這一次篩選遮蔽的方法換一種,直接在某個函式執行完成之後,通過jmp跳到sub_100C4450結尾,如果某次訊息傳送成功,最後執行的函式就是我們要的介面。

 

 

很幸運,這次在第三個函式就找到了訊息傳送函式sub_102DA4A0,看看它的引數:

sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);
sub_102DA4A0@<eax>(int wxid@<edx>, int a2@<ecx>, wxstring *msg, _DWORD *a4, int a5)

下圖是除錯中看到的資料,確認介面沒有問題。至於其他兩個引數,經過分析是用於接收輸出的,沒有實際作用,在此不贅述。

 

 

如此分析訊息傳送介面的工作完成,找到了和預期基本一致的介面函式。

0x3. 總結

篇幅好像有點長了,最後做一下此次分析的總結:

  1. ce找到編輯框中的內容記憶體
  2. 傳送後,編輯框內容刪除,寫斷點無效,神奇,猜測通過設定長度控制顯示
  3. 改為記憶體訪問斷點,進入介面就會斷下,徘徊幾次後,決定分析,沒想到找到了關鍵點CTxtEdit::OnTxInPlaceActivate
  4. 知道編輯框使用了msftedit.dll的CTxtEdit的類,用ida找到符號
  5. 查詢類似getvalue的介面,找到SetText、GetTextEx等,對這兩個函式下斷點
  6. 果然斷下,回溯找到了傳送的訊息響應函式
  7. 詳細分析響應函式,多次通過retn、jmp排除,找到真正傳送訊息函式,最後分析出介面函式

此次分析中CE找到地址是第一步非常關鍵的點,直接就進入了函式呼叫堆疊內部,對此次分析作用非常明顯。

 

再就是在傳送訊息響應函式內部,逐個分析找到訊息傳送介面函式中,通過修改指令來遮蔽函式功能來確認函式功能,比每個函式去分析引數猜測確認功能來的更快,效果更明顯。

 

除錯工具非常重要,動(OD)靜態(IDA)分析結合能夠提高分析速度。

 

OD適合分析函式引數、解析資料結構、確認函式功能,IDA適合分析函式邏輯、整體函式結構、程式碼框架等等,各有優勢。

 

最後,再次安利一下開源專案https://github.com/anhkgg/SuperWeChatPC,此次分析的傳送訊息介面也會在後續合入到專案中,歡迎starPR

 

相關文章:

  1. 微信PC端技術研究(2)-儲存聊天語音

  2. 偏移計算工具



[推薦]看雪企服平臺,提供安全分析、定製專案開發、APP等級保護、滲透測試等安全服務!

相關文章