吾愛破解 - LCG - LSG |安卓破解|病毒分析|破解軟件|www.mlqcje.live

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 3145|回復: 27
上一主題 下一主題

[原創] PCXX逆向:發送與接收消息的分析與代碼實現

  [復制鏈接]
跳轉到指定樓層
樓主
鬼手56 發表于 2019-6-22 16:52 回帖獎勵
本帖最后由 鬼手56 于 2019-7-27 10:31 編輯

定位微信的消息接收函數

我們先來定位一下消息接收函數,這對我們后面分析消息發送函數會有所幫助

定位消息接收函數的相關思路

與接收消息函數最直接相關的東西肯定是消息本身,所以消息本身的內容就是我們的切入點。我們可以首先找到存放消息內容的地址,然后對地址下斷,通過棧回溯最終定位到接收消息的函數

定位消息內容的地址

首先用另外一個微信給自己發一條消息,在不點開消息的狀態下用CE搜索消息內容

然后再發送一條消息

此時有效結果只剩下3個,把這三個地址加入到下方地址欄,右鍵->更改記錄->類型

將顯示范圍調大

其中有一個是最原始的未經處理的消息,也是顯示的最全的那一條,剩下的兩條是經過處理的。我們需要中間那個未經任何處理的消息

 定位接收消息函數的地址

既然消息內容的地址找到了,那么接下來就通過這個內容來找到接收消息的函數

在OD中找到這個地址,下內存寫入斷點。為什么是寫入不是訪問?因為這個是最原始的的消息,要想對這條消息進行處理就必須改寫當前的這條消息,所以在這個位置下內存寫入斷點,當客戶端對這條原始消息進行處理時,斷點就會斷下。

此時,再次發送一條消息,程序斷下,刪除內存寫入斷點,這個時候堆棧的返回地址里面一定有一個函數是用來接收消息的。我們點擊K查看調用堆棧。

經過排查確認是這個call,大家可以根據我圖中的函數特征直接找到這個call。我們在這個函數下斷點,讓程序再次斷下,分析附近的代碼。

分析接收消息函數

好友消息

此時我們點擊查看堆棧中esp寄存器的值,數據窗口跟隨

此時[esp+0x40]的位置是發送者的微信ID,[esp+0x68]的位置是消息內容(通過這個call我們還可以拿到文件助手的ID是filehelper,這對后面分析消息發送會有用,大家可以去試驗一下)

[esp+0x114]的位置是0,[esp+0x128]的位置是一串未知數據。

群消息

然后我們再發送一條群消息,看看有什么區別

此時[esp+0x40]的位置是群ID,[esp+0x68]的位置是消息內容

[esp+0x114]的地址不再是零,而是消息發送者的ID,[esp+0x128]的位置依舊是一串未知數據。大家可以用同樣的方式分析處圖片和表情在內存中的表現形式。

那么我們只要記錄下這個call的地址+偏移,然后寫一個dll注入到微信進程空間中,HOOK這個函數,就能攔截所有的消息,并顯示到我們的程序中。

總結

總結一下思路,尋找切入點->找地址->下斷->棧回溯分析。就是這么簡單粗暴

代碼實現

注入之后接收消息的代碼如下:

void RecieveMsg()
{
        wstring receivedMessage = L"";
        BOOL isFriendMsg = FALSE;
        //[[esp]]
        //信息塊位置
        DWORD** msgAddress = (DWORD * *)r_esp;

        //消息類型[[esp]]+0x30
        //[01文字] [03圖片] [31轉賬XML信息] [22語音消息] [02B視頻信息]
        //感謝:.順唭_自嘫ɑ、unravel提供類型消息。
        DWORD msgType = *((DWORD*)(**msgAddress + 0x30));
        receivedMessage.append(L"消息類型:");
        switch (msgType)
        {
        case 0x01:
                receivedMessage.append(L"文字 ");
                break;
        case 0x03:
                receivedMessage.append(L"圖片 ");
                break;

        case 0x22:
                receivedMessage.append(L"語音 ");
                break;
        case 0x25:
                receivedMessage.append(L"好友確認 ");
                break;
        case 0x28:
                receivedMessage.append(L"POSSIBLEFRIEND_MSG ");
                break;
        case 0x2A:
                receivedMessage.append(L"名片 ");
                break;
        case 0x2B:
                receivedMessage.append(L"視頻 ");
                break;
        case 0x2F:
                //石頭剪刀布
                receivedMessage.append(L"表情 ");
                break;
        case 0x30:
                receivedMessage.append(L"位置 ");
                break;
        case 0x31:
                //共享實時位置
                //文件
                //轉賬
                //鏈接
                receivedMessage.append(L"共享實時位置、文件、轉賬、鏈接 ");
                break;
        case 0x32:
                receivedMessage.append(L"VOIPMSG ");
                break;
        case 0x33:
                receivedMessage.append(L"微信初始化 ");
                break;
        case 0x34:
                receivedMessage.append(L"VOIPNOTIFY ");
                break;
        case 0x35:
                receivedMessage.append(L"VOIPINVITE ");
                break;
        case 0x3E:
                receivedMessage.append(L"小視頻 ");
                break;
        case 0x270F:
                receivedMessage.append(L"SYSNOTICE ");
                break;
        case 0x2710:
                //系統消息
                //紅包
                receivedMessage.append(L"紅包、系統消息 ");
                break;
        case 0x2712:
                receivedMessage.append(L"撤回消息 ");
                break;
        default:
                break;
        }
        receivedMessage.append(L"\r\n");

        //dc [[[esp]] + 0x114]
        //判斷是群消息還是好友消息
        //相關信息
        wstring msgSource2 = L"<msgsource />\n";
        wstring msgSource = L"";
        msgSource.append(GetMsgByAddress(**msgAddress + 0x168));

        if (msgSource.length() <= msgSource2.length())
        {
                receivedMessage.append(L"收到好友消息:\r\n");
                isFriendMsg = TRUE;
        }
        else
        {
                receivedMessage.append(L"收到群消息:\r\n");
                isFriendMsg = FALSE;
        }

        //好友消息
        if (isFriendMsg == TRUE)
        {
                receivedMessage.append(L"好友wxid:\r\n")
                        .append(GetMsgByAddress(**msgAddress + 0x40))
                        .append(L"\r\n\r\n");
        }
        else
        {
                receivedMessage.append(L"群號:\r\n")
                        .append(GetMsgByAddress(**msgAddress + 0x40))
                        .append(L"\r\n\r\n");

                receivedMessage.append(L"消息發送者:\r\n")
                        .append(GetMsgByAddress(**msgAddress + 0x114))
                        .append(L"\r\n\r\n");

                receivedMessage.append(L"相關信息:\r\n");
                receivedMessage += msgSource;
                receivedMessage.append(L"\r\n\r\n");
        }

        receivedMessage.append(L"消息內容:\r\n")
                .append(GetMsgByAddress(**msgAddress + 0x68))
                .append(L"\r\n\r\n");

        //文本框輸出信息
        USES_CONVERSION;
        SetWindowText(GetDlgItem(hWinDlg, IDC_MSG), W2A(receivedMessage.c_str()));
}

定位微信的消息發送函數

定位消息發送函數的相關思路

一個發消息的函數,至少需要兩個參數。第一個是發送給誰,第二個是發送的內容,所以我們可以從參數入手,然后通過棧回溯的方式找到發送消息的call。

至于突破口我們可以從發送的消息內容和消息的接收者的微信ID入手,比如文件傳輸助手的微信ID是filehelper,這個可以在接收消息的call中拿到。以這個微信ID為突破口會比從文本來追溯方便。

過濾當前聊天窗口的微信ID

首先將當前聊天窗口設置為文件傳輸助手,搜索filehelper

除了文件傳輸助手,我們還知道個人的微信ID都是以wxid_開頭的,所以將窗口切換到微信好友,搜索wxid_

接著我們選中所有地址,加入到下方地址欄

然后選擇全部地址右鍵->更改記錄->類型

將長度修改為50以顯示更多的內容

此時你會看到微信好友的ID,記錄下這個ID,待會有用

再將窗口切回文件助手,下方地址欄的ID會發生變化,將數值不是filehelper的全部剔除掉。剩下的地址中的某一個是當前窗口的微信ID,它會隨著你當前微信窗口ID進行變換。

定位當前聊天窗口的ID

這個當前聊天窗口的ID到底有什么作用呢?我們來測試一下

選中所有地址,右鍵->更改記錄->數值,將當前聊天窗口的ID改為filehelper,然后在當前好友的聊天窗口發送一條消息,你會發現此時消息發到了文件傳輸助手

當前聊天窗口的ID是誰 誰就會接收到這條消息,利用這個特性我們來找出那個唯一的當前窗口ID

選中一半地址,將其更改為filehelper,然后在當前窗口發送消息。如果消息發給了filehelper,那么選中的地址里面就有真正的當前聊天窗口的ID。重復這個步驟,可以找到真正的當前窗口ID

定位發送消息的函數

接著載入OD,在找到的當前窗口ID的地址中下一個內存訪問斷點。為什么是內存訪問斷點而不是內存寫入呢?因為當前微信窗口的ID肯定會被發送消息的當作參數傳入到堆棧中,所以必定會訪問這個ID,而不是寫入ID。

給好友發送一條消息,點擊發送,內存訪問斷點斷下。

此時eax指向當前窗口ID,接著刪除內存訪問斷點。點擊K查看調用堆棧,在堆棧的返回地址中逐個排查每一個函數,這個函數必須有兩個以上的參數,其中一個參數是消息內容,另外一個參數是消息ID

經過排查,可以在調用堆棧的第二層找到一個疑似發消息的call。在這個地方下斷點,讓程序斷下,分析附近代碼

分析發送消息的函數

普通消息

此時edx指向微信ID,[edx+4]保存的是微信ID的長度

ebx指向消息內容,[ebx+4]保存的是消息內容的長度。那么這個很有可能就是我們要找的發送消息的call。

找到了發送消息的函數,那么怎么驗證呢?利用微信ID。將edx指向的微信ID的地址和我們之前在CE中找到的當前窗口的微信ID對比,你會發現兩個地址是一樣的。

改變這個地址的微信ID和內容,就能直接改變消息的接收者和內容,這個剛才我們已經實驗過了。再結合這個函數傳入的參數有當前消息的內容,就可以確定這個call就是微信發送消息的函數。

艾特某人消息

除了以普通文本的方式發送消息以外,還可以以艾特某人的方式發送消息。那么當發送的消息是艾特某人的時候,這個函數和發送普通文本消息有什么區別呢?區別就在于eax寄存器的值

先發送一條普通消息程序斷下

此時eax的值為0,然后再發送一條艾特某人的消息

此時eax是有值的,數據窗口跟隨,看看這個14704C40的地址保存的是什么內容

里面的被艾特的人的微信ID,普通消息與艾特消息的區別就在于eax是否保存了被艾特人的微信ID。大家可以用同樣的方式分析處圖片和表情在內存中的表現形式。

接下來我們只要記錄下當前發送消息函數的地址+偏移,就能寫一個dll注入到微信進程空間中,直接調用發送消息的函數,就能實現用自己寫的程序給任何人發送消息。

總結

總結一下思路,尋找切入點->找地址->下斷->棧回溯分析。跟接收消息的步驟是一致的。找call的關鍵在于你能不能找到一個好的切入點,并且利用切入點與call之間的關系。

代碼實現

調用發送消息的函數代碼如下:

void SendTextMessage(wchar_t* wxid, wchar_t* msg)
{
        //拿到發送消息的call的地址
        DWORD dwSendCallAddr = GetWeChatWinAddr() + 0x2EB4E0;

        //微信ID/群ID
        wxMsg id = {0};
        id.pMsg = wxid;
        id.msgLen = wcslen(wxid);
        id.buffLen = wcslen(wxid)*2;

        //消息內容
        wxMsg text = { 0 };
        text.pMsg = msg;
        text.msgLen = wcslen(msg);
        text.buffLen = wcslen(msg)*2;

        //取出微信ID和消息的地址
        char* pWxid = (char*)&id.pMsg;
        char* pWxmsg = (char*)&text.pMsg;

        char buff[0x81C] = { 0 };
        //調用微信發送消息call
        __asm {
                mov edx, pWxid;
                push 1;
                mov eax, 0;
                push eax;
                mov ebx, pWxmsg;
                push ebx;
                lea ecx, buff;
                call dwSendCallAddr;
                add esp, 0xC;
        }
}

最終效果

發送消息

接收消息

目前微信機器人的成品已經發布,需要代碼請移步Github。還請親們幫忙點個star
https://github.com/TonyChen56/WeChatRobot





免費評分

參與人數 26威望 +1 吾愛幣 +37 熱心值 +26 收起 理由
Hmily + 1 + 7 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
黑色芝麻 + 1 + 1 太強了
vipwp + 1 + 1 用心討論,共獲提升!
想比你有錢 + 1 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
紅顏世家、 + 1 + 1 [email protected]
sangong + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
楓葉下的蟋蟀 + 1 + 1 [email protected]
ki11in9 + 1 + 1 [email protected]
marlborogolo + 1 + 1 [email protected]
anvx + 1 + 1 我很贊同!
Eaysuild.xean + 2 + 1 我很贊同!
cndmad + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
wmsuper + 3 + 1 [email protected]
qaz003 + 1 + 1 清新脫俗~~~
吃泡面加不起蛋 + 1 + 1 [email protected]
qsws3344 + 1 + 1 [email protected]
獨行風云 + 1 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
bjcar + 1 + 1 [email protected]
hui135135 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
nj001 + 1 + 1 熱心回復!
笙若 + 1 + 1 [email protected]
asq56747277 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
何故 + 3 + 1 我很贊同!
ss417841136 + 1 + 1 用心討論,共獲提升!
Takitooru + 1 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
旺仔大饅頭 + 1 + 1 [email protected]

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
by商人 發表于 2019-6-24 13:08
樓主,我嘗試把你這個方法套入到某騰xQx,已找到接受消息call,但是在堆棧esp數據窗口跟隨沒有找到可用信息,應該怎么處理啊?還有問一下,OD中的地址是動態的嗎?怎么轉換成基址呢
推薦
 樓主| 鬼手56 發表于 2019-6-23 10:08 <
Jper 發表于 2019-6-23 09:42
樓主能給個ce的鏈接嗎 我用的也是這個加強版ce 但是搜不出來地址

這個加強版的CE是有問題的 你用原版吧
沙發
旺仔大饅頭 發表于 2019-6-22 16:55
3#
豪~豪 發表于 2019-6-22 17:07
大牛啊啊啊  啊啊 啊啊啊!
4#
zeroro 發表于 2019-6-22 17:33
支持~~~~~
5#
酷鳥 發表于 2019-6-22 17:41
編輯標題 微信換成XX  比較好
6#
Joduska 發表于 2019-6-22 18:58
DAGE!
7#
gunxsword 發表于 2019-6-22 19:05
感謝分享,有了這樣的東西,就可以做到很多事情了
8#
newpowersky 發表于 2019-6-22 20:53
太牛B了。。。沒有關注 錯。
9#
newpowersky 發表于 2019-6-22 20:56
封個DLL,放在C#下來用用。這個C不會寫啊。。。。。。
10#
xiaoy 發表于 2019-6-22 21:19
這東西內存沒處理好啊,發圖片內存就跟著漲,漲到一定程度就崩了...和Q比起來還差遠了
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:禁止回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

GMT+8, 2019-8-12 07:44

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表
内蒙古11选5开奖查询百度