本文作者:鬼手56(信安之路病毒分析小組成員 & 信安之路 2019 年度優秀作者)
成員招募:信安之路病毒分析小組尋找志同道合的朋友
目前微信版本

尋找微信二維碼基址
PNG 檔案格式
微信二維碼在記憶體中存放形式是 png 格式的二進制資料,是以我們需要眼熟一下 png 的檔案格式,如圖
前 32 個位元組是固定的,分别是 btPngSignature 和 struct PNG_CHUNK chunk 結構,其中儲存有 png 圖檔的辨別。
其中 NG 和 IHDR 是每個 PNG 檔案都會有的辨別,眼熟一下就好。微信的二維碼圖檔就是通過這種格式在記憶體中存放
使用 CE 過濾基址
首先在微信未登入狀态下附加微信,此時二維碼還未加載
然後選擇未知的數值,點選首次掃描
出現三百萬個結果
此時我們再次點選切換賬号,出現二維碼,讓儲存二維碼的位址被指派
然後選擇變動的數值 再次掃描
此時還剩下七萬個結果
然後用手機掃描二維碼 不要點選登入,再次掃描變動的數值,此時還剩三萬多個結果
接着随意移動微信框,點選未變動的數值,還剩一萬多個結果。傳回二維碼登入重複以上操作,直到位址欄還剩下兩個綠色的基址,這兩個綠色的基址就是我們要的。
因為随機基址的存在,這個位址在各位的電腦上是不一樣的。但是低四位是一緻的,這兩個位址應該是 xxxx9194 和 xxxx919C。
使用 OD 确定二維碼基址
然後重新開機一次微信,再用 CE 附加,回到這個狀态
用 OD 附加微信,在找到的第一個位址 xxxx9194 下記憶體寫入斷點
點選切換賬号,在二維碼未加載時程式會斷下。注意,這個地方會斷下來兩次,第二次才是我們要的結果。
因為二維碼是存放在微信的核心子產品 WeChatWin 中的,是以我們在堆棧中找到所有的 WeChatWin 中的函數
像這種 API 的調用就可以直接排除掉,然後在每一個疑似函數上下斷點。找的時候堆棧盡量往下拉,這個函數會比較靠後。
因為我已經找過一遍了,是以直接告訴你們是這一個。特征是有一個 ecx 傳參。
接着在這個函數上下斷點,删除記憶體通路斷點,F9 運作
然後掃一下二維碼,點選傳回二維碼登入,程式斷下
此時觀察 ecx 指針的内容,明顯是一個結構體,結構體的第一個是位址,第二個好像是大小。然後在這個位址上資料視窗跟随
裡面是 PNG 檔案的二進制資料,這個就是我們要找的微信二維碼的基址
驗證二維碼基址
打開 PCHunter,選擇微信程序,檢視->檢視程序記憶體,輸入位址和大小,然後将記憶體 dump 下來
打開圖檔
現在已經确定就是我們需要的二維碼。然後我們将這個 call 的位址減去子產品基址,記錄下偏移。待會需要 HOOK 這個 call
尋找微信二維碼内容的基址
微信二維碼的存儲内容
二維碼其實是一種開放性的資訊存儲器,它将固定的資訊存儲在自己的黑白小方塊之間。大部分的二維碼都有一個特點,就是裡面存放的其實是一段文本。我們可以利用這個文本來尋找突破口
将微信的二維碼截圖儲存,然後用線上的二維碼解碼器解析微信的二維碼
可以看到解碼之後的結果是一段網址
使用 CE 尋找二維碼内容的基址
如果直接搜尋這段網址是找不到任何結果的,原因是因為微信在儲存這段位置的時候,實際上是将它分為了兩部分存儲
複制
第一部分的固定不變的,第二部分被當作一個參數傳入,用戶端從伺服器擷取的隻是第二部分的内容。是以我們去搜尋第二部分。
另外,微信的二維碼會定時重新整理,重新整理的時候會改變第二部分的内容。如果你搜不到的話可能是因為之前的文本已經失效了。從解析文本到搜尋文本最好在一分鐘之内完成
此時 我們直接搜尋第二部分的文本
搜尋完成之後,等待二維碼自動重新整理,然後找到那個變化之後的位址,用截圖上傳的方式確定找到的是正确的位址
然後用 OD 附加微信,在找到的位址上下記憶體寫入斷點
等待二維碼自動重新整理,二維碼重新整理時會往原來存放二維碼的位址寫入新的二維碼資料,程式就會斷下
此時 eax 指向二維碼的文本内容,我們找到堆棧中的第一個位址,在資料視窗顯示,此時就能找到存放微信二維碼資料的基址了
然後我們在 CE 中添加 WeChatWin.dll 子產品,找到子產品基址,算出偏移(用 0x104CF618-0xF250000=127F618)。然後将這個位址換成子產品基址+偏移的方式添加到 CE 位址欄。
驗證基址
重新打開微信,用 CE 載入
保留目前清單,然後将二維碼内容指針的值添加到清單
點選确定。此時二維碼的内容和解析出來的内容一緻,說明基址有效
定制微信登入二維碼的可能性
那麼我們拿到這個二維碼的内容有什麼作用呢?我們可以将這個擷取到的二維碼内容調用二維碼生成器的 API 接口進行再次編碼,然後生成一個更加漂亮好看專屬二維碼,效果如圖:
使用 hook 截取二維碼
接着我們編寫一個 dll,将這個 dll 注入到微信程序中,利用 IAT Hook 截取微信的二維碼。部分關鍵代碼如下:
開啟 HOOK
void StartHook(DWORD dwHookOffset,LPVOID pFunAddr, HWND hWnd)
{
hDlg = hWnd;
//拿到子產品基址
DWORD dwWeChatWinAddr = GetWeChatWinAddr();
//需要HOOK的位址
DWORD dwHookAddr = dwWeChatWinAddr + dwHookOffset;
//填充資料
jmpCode[0] = 0xE9;
//計算偏移
*(DWORD*)(&jmpCode[1]) = (DWORD)pFunAddr - dwHookAddr-5;
// 儲存以前的屬性用于還原
DWORD OldProtext = 0;
// 因為要往代碼段寫入資料,又因為代碼段是不可寫的,是以需要修改屬性
VirtualProtect((LPVOID)dwHookAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtext);
//儲存原有的指令
memcpy(backCode, (void*)dwHookAddr, 5);
//寫入自己的代碼
memcpy((void*)dwHookAddr, jmpCode, 5);
// 執行完了操作之後需要進行還原
VirtualProtect((LPVOID)dwHookAddr, 5, OldProtext, &OldProtext);
}
複制
解除安裝 HOOK
void UnHook(DWORD dwHookOffset)
{
DWORD dwWeChatWinAddr = GetWeChatWinAddr();
DWORD dwHookAddr = dwWeChatWinAddr + dwHookOffset;
// 儲存以前的屬性用于還原
DWORD OldProtext = 0;
// 因為要往代碼段寫入資料,又因為代碼段是不可寫的,是以需要修改屬性
VirtualProtect((LPVOID*)dwHookAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtext);
// Hook 就是向其中寫入自己的代碼
memcpy((LPVOID*)dwHookAddr, backCode, 5);
// 執行完了操作之後需要進行還原
VirtualProtect((LPVOID*)dwHookAddr, 5, OldProtext, &OldProtext);
}
複制
儲存圖檔
void SaveImg(DWORD qrcode)
{
//擷取圖檔長度
DWORD dwPicLen = qrcode + 0x4;
size_t cpyLen = (size_t)*((LPVOID*)dwPicLen);
//拷貝圖檔的資料
char PicData[0xFFF] = { 0 };
memcpy(PicData, *((LPVOID*)qrcode), cpyLen);
//将檔案寫到本地
HANDLE hFile = CreateFileA("E:\\qrcode.png",GENERIC_ALL,0,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile==NULL)
{
MessageBox(NULL, "建立圖檔檔案失敗", "錯誤", 0);
return;
}
DWORD dwRead = 0;
if (WriteFile(hFile, PicData, cpyLen, &dwRead, NULL) == 0)
{
MessageBox(NULL, "寫入圖檔檔案失敗", "錯誤", 0);
return;
}
CloseHandle(hFile);
//顯示圖檔
CImage img;
CRect rect;
//拿到控件的句柄
HWND hPic = GetDlgItem(hDlg, IDC_QRPIC);
GetClientRect(hPic, &rect);
//載入圖檔
img.Load("E:\\qrcode.png");
img.Draw(GetDC(hPic), rect);
//顯示二維碼内容
ShowQrCodeContent(hDlg);
//完成之後解除安裝HOOK
UnHook(QrCodeOffset);
}
複制
最終效果
最終效果如圖:
推薦閱讀
原創 PC 端微信技術研究之儲存聊天語言
原創 微信PC端技術研究(3)-如何找到消息發送接口
原創 PC微信逆向:發送與接收消息的分析與代碼實作
原創 PC微信逆向:分析發送xml名片call
原創 PC微信逆向:兩種姿勢教你解密資料庫檔案